1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 use std::ffi::OsStr;
15 use std::io::{Error, ErrorKind, Result};
16 use std::os::unix::ffi::OsStrExt;
17 use std::path::Path;
18 use std::{ascii, fmt};
19
sun_path_offset(sockaddr: &libc::sockaddr_un) -> usize20 fn sun_path_offset(sockaddr: &libc::sockaddr_un) -> usize {
21 let path = &sockaddr.sun_path as *const _ as usize;
22 let base = sockaddr as *const _ as usize;
23 path - base
24 }
25
socket_addr_trans_un(path: &Path) -> Result<(libc::sockaddr_un, libc::socklen_t)>26 pub(crate) fn socket_addr_trans_un(path: &Path) -> Result<(libc::sockaddr_un, libc::socklen_t)> {
27 let sockaddr: std::mem::MaybeUninit<libc::sockaddr_un> =
28 std::mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
29
30 let mut sockaddr = unsafe { sockaddr.assume_init() };
31
32 sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
33
34 let bytes = path.as_os_str().as_bytes();
35 if bytes.first() == Some(&0) {
36 return Err(Error::new(
37 ErrorKind::InvalidInput,
38 "paths must not start with interior null bytes",
39 ));
40 }
41 // Checks path len.
42 if bytes.len() >= sockaddr.sun_path.len() {
43 return Err(Error::new(
44 ErrorKind::InvalidInput,
45 "path must be shorter than libc::sockaddr_un.sun_path",
46 ));
47 }
48
49 for (dest, source) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) {
50 *dest = *source as libc::c_char;
51 }
52
53 let mut socklen = sun_path_offset(&sockaddr) + bytes.len();
54
55 match bytes.first() {
56 Some(&0) | None => {}
57 Some(_) => socklen += 1,
58 }
59
60 Ok((sockaddr, socklen as libc::socklen_t))
61 }
62
63 /// This structure is necessary because the `socketaddr` returned
64 /// when the `listener` calls `accept` cannot be derived from `std`.
65 pub struct SocketAddr {
66 sockaddr: libc::sockaddr_un,
67 socklen: libc::socklen_t,
68 }
69
70 impl SocketAddr {
from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr71 pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr {
72 SocketAddr { sockaddr, socklen }
73 }
74 }
75
76 impl fmt::Debug for SocketAddr {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result77 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
78 let offset = sun_path_offset(&self.sockaddr);
79 if (self.socklen as usize) < offset {
80 return write!(fmt, "(unnamed)");
81 }
82 let len = self.socklen as usize - offset;
83 let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) };
84
85 if len == 0 || (cfg!(not(target_os = "linux")) && self.sockaddr.sun_path[0] == 0) {
86 write!(fmt, "(unnamed)")
87 } else if self.sockaddr.sun_path[0] == 0 {
88 write!(fmt, "\"")?;
89 let name = &path[1..len];
90 for byte in name.iter().cloned().flat_map(ascii::escape_default) {
91 write!(fmt, "{}", byte as char)?;
92 }
93 write!(fmt, "\"(abstract)")
94 } else {
95 write!(
96 fmt,
97 "{:?} (pathname)",
98 <std::ffi::OsStr as std::convert::AsRef<Path>>::as_ref(OsStr::from_bytes(
99 &path[..len - 1]
100 ))
101 )
102 }
103 }
104 }
105
106 #[cfg(test)]
107 mod test {
108 use crate::SocketAddr;
109
110 /// UT for uds sockaddr debug info
111 ///
112 /// # Brief
113 /// 1. Create an UDS socket address
114 /// 2. Check if the debug info is correct
115 #[test]
ut_uds_socket_addr_debug_info()116 fn ut_uds_socket_addr_debug_info() {
117 let sock_addr = libc::sockaddr_un {
118 sun_family: 1,
119 sun_path: [2; 108],
120 };
121
122 let addr = SocketAddr::from_parts(sock_addr, 10);
123 let fmt = format!("{addr:?}");
124 assert!(fmt.contains("\"\\u{2}\\u{2}\\u{2}\\u{2}\\u{2}\\u{2}\\u{2}\" (pathname)"));
125 }
126 }
127