@@ -515,15 +515,42 @@ impl fmt::Display for Ipv6Addr {
515515}
516516
517517/// A wrapper around `sockaddr_un`.
518- ///
519- /// This also tracks the length of `sun_path` address (excluding
520- /// a terminating null), because it may not be null-terminated. For example,
521- /// unconnected and Linux abstract sockets are never null-terminated, and POSIX
522- /// does not require that `sun_len` include the terminating null even for normal
523- /// sockets. Note that the actual sockaddr length is greater by
524- /// `offset_of!(libc::sockaddr_un, sun_path)`
525518#[ derive( Clone , Copy , Debug ) ]
526- pub struct UnixAddr ( pub libc:: sockaddr_un , pub usize ) ;
519+ pub struct UnixAddr {
520+ // INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
521+ sun : libc:: sockaddr_un ,
522+ path_len : usize ,
523+ }
524+
525+ // linux man page unix(7) says there are 3 kinds of unix socket:
526+ // pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
527+ // unnamed: addrlen = sizeof(sa_family_t)
528+ // abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
529+ //
530+ // what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
531+ #[ derive( PartialEq , Eq , Hash ) ]
532+ enum UnixAddrKind < ' a > {
533+ Pathname ( & ' a Path ) ,
534+ Unnamed ,
535+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
536+ Abstract ( & ' a [ u8 ] ) ,
537+ }
538+ impl < ' a > UnixAddrKind < ' a > {
539+ /// Safety: sun & path_len must be valid
540+ unsafe fn get ( sun : & ' a libc:: sockaddr_un , path_len : usize ) -> Self {
541+ if path_len == 0 {
542+ return Self :: Unnamed ;
543+ }
544+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
545+ if sun. sun_path [ 0 ] == 0 {
546+ let name =
547+ slice:: from_raw_parts ( sun. sun_path . as_ptr ( ) . add ( 1 ) as * const u8 , path_len - 1 ) ;
548+ return Self :: Abstract ( name) ;
549+ }
550+ let pathname = slice:: from_raw_parts ( sun. sun_path . as_ptr ( ) as * const u8 , path_len - 1 ) ;
551+ Self :: Pathname ( Path :: new ( OsStr :: from_bytes ( pathname) ) )
552+ }
553+ }
527554
528555impl UnixAddr {
529556 /// Create a new sockaddr_un representing a filesystem path.
@@ -537,15 +564,15 @@ impl UnixAddr {
537564
538565 let bytes = cstr. to_bytes ( ) ;
539566
540- if bytes. len ( ) > ret. sun_path . len ( ) {
567+ if bytes. len ( ) >= ret. sun_path . len ( ) {
541568 return Err ( Error :: from ( Errno :: ENAMETOOLONG ) ) ;
542569 }
543570
544571 ptr:: copy_nonoverlapping ( bytes. as_ptr ( ) ,
545572 ret. sun_path . as_mut_ptr ( ) as * mut u8 ,
546573 bytes. len ( ) ) ;
547574
548- Ok ( UnixAddr ( ret, bytes. len ( ) ) )
575+ Ok ( UnixAddr :: from_raw_parts ( ret, bytes. len ( ) + 1 ) )
549576 }
550577 } ) ?
551578 }
@@ -564,7 +591,7 @@ impl UnixAddr {
564591 .. mem:: zeroed ( )
565592 } ;
566593
567- if path. len ( ) + 1 > ret. sun_path . len ( ) {
594+ if path. len ( ) >= ret. sun_path . len ( ) {
568595 return Err ( Error :: from ( Errno :: ENAMETOOLONG ) ) ;
569596 }
570597
@@ -574,28 +601,39 @@ impl UnixAddr {
574601 ret. sun_path . as_mut_ptr ( ) . offset ( 1 ) as * mut u8 ,
575602 path. len ( ) ) ;
576603
577- Ok ( UnixAddr ( ret, path. len ( ) + 1 ) )
604+ Ok ( UnixAddr :: from_raw_parts ( ret, path. len ( ) + 1 ) )
605+ }
606+ }
607+
608+ /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `path_len` is the "addrlen"
609+ /// of this address, but minus `offsetof(struct sockaddr_un, sun_path)`. Basically the length
610+ /// of the data in `sun_path`.
611+ ///
612+ /// # Safety
613+ /// This pair of sockaddr_un & path_len must be a valid unix addr, which means:
614+ /// - path_len <= sockaddr_un.sun_path.len()
615+ /// - if this is a unix addr with a pathname, sun.sun_path is a nul-terminated fs path and
616+ /// sun.sun_path[path_len - 1] == 0 || sun.sun_path[path_len] == 0
617+ pub ( crate ) unsafe fn from_raw_parts ( sun : libc:: sockaddr_un , mut path_len : usize ) -> UnixAddr {
618+ if let UnixAddrKind :: Pathname ( _) = UnixAddrKind :: get ( & sun, path_len) {
619+ if sun. sun_path [ path_len - 1 ] != 0 {
620+ assert_eq ! ( sun. sun_path[ path_len] , 0 ) ;
621+ path_len += 1
622+ }
578623 }
624+ UnixAddr { sun, path_len }
579625 }
580626
581- fn sun_path ( & self ) -> & [ u8 ] {
582- unsafe { slice:: from_raw_parts ( self . 0 . sun_path . as_ptr ( ) as * const u8 , self . 1 ) }
627+ fn kind ( & self ) -> UnixAddrKind < ' _ > {
628+ // SAFETY: our sockaddr is always valid because of the invariant on the struct
629+ unsafe { UnixAddrKind :: get ( & self . sun , self . path_len ) }
583630 }
584631
585632 /// If this address represents a filesystem path, return that path.
586633 pub fn path ( & self ) -> Option < & Path > {
587- if self . 1 == 0 || self . 0 . sun_path [ 0 ] == 0 {
588- // unnamed or abstract
589- None
590- } else {
591- let p = self . sun_path ( ) ;
592- // POSIX only requires that `sun_len` be at least long enough to
593- // contain the pathname, and it need not be null-terminated. So we
594- // need to create a string that is the shorter of the
595- // null-terminated length or the full length.
596- let ptr = & self . 0 . sun_path as * const libc:: c_char ;
597- let reallen = unsafe { libc:: strnlen ( ptr, p. len ( ) ) } ;
598- Some ( Path :: new ( <OsStr as OsStrExt >:: from_bytes ( & p[ ..reallen] ) ) )
634+ match self . kind ( ) {
635+ UnixAddrKind :: Pathname ( path) => Some ( path) ,
636+ _ => None ,
599637 }
600638 }
601639
@@ -605,39 +643,63 @@ impl UnixAddr {
605643 /// leading null byte. `None` is returned for unnamed or path-backed sockets.
606644 #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
607645 pub fn as_abstract ( & self ) -> Option < & [ u8 ] > {
608- if self . 1 >= 1 && self . 0 . sun_path [ 0 ] == 0 {
609- Some ( & self . sun_path ( ) [ 1 ..] )
610- } else {
611- // unnamed or filesystem path
612- None
646+ match self . kind ( ) {
647+ UnixAddrKind :: Abstract ( name) => Some ( name) ,
648+ _ => None ,
613649 }
614650 }
651+
652+ /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
653+ #[ inline]
654+ pub fn path_len ( & self ) -> usize {
655+ self . path_len
656+ }
657+ /// Returns a pointer to the raw `sockaddr_un` struct
658+ #[ inline]
659+ pub fn as_ptr ( & self ) -> * const libc:: sockaddr_un {
660+ & self . sun
661+ }
662+ /// Returns a mutable pointer to the raw `sockaddr_un` struct
663+ #[ inline]
664+ pub fn as_mut_ptr ( & mut self ) -> * mut libc:: sockaddr_un {
665+ & mut self . sun
666+ }
667+ }
668+
669+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
670+ fn fmt_abstract ( abs : & [ u8 ] , f : & mut fmt:: Formatter ) -> fmt:: Result {
671+ use fmt:: Write ;
672+ f. write_str ( "@\" " ) ?;
673+ for & b in abs {
674+ use fmt:: Display ;
675+ char:: from ( b) . escape_default ( ) . fmt ( f) ?;
676+ }
677+ f. write_char ( '"' ) ?;
678+ Ok ( ( ) )
615679}
616680
617681impl fmt:: Display for UnixAddr {
618682 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
619- if self . 1 == 0 {
620- f. write_str ( "<unbound UNIX socket>" )
621- } else if let Some ( path) = self . path ( ) {
622- path. display ( ) . fmt ( f)
623- } else {
624- let display = String :: from_utf8_lossy ( & self . sun_path ( ) [ 1 ..] ) ;
625- write ! ( f, "@{}" , display)
683+ match self . kind ( ) {
684+ UnixAddrKind :: Pathname ( path) => path. display ( ) . fmt ( f) ,
685+ UnixAddrKind :: Unnamed => f. pad ( "<unbound UNIX socket>" ) ,
686+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
687+ UnixAddrKind :: Abstract ( name) => fmt_abstract ( name, f) ,
626688 }
627689 }
628690}
629691
630692impl PartialEq for UnixAddr {
631693 fn eq ( & self , other : & UnixAddr ) -> bool {
632- self . sun_path ( ) == other. sun_path ( )
694+ self . kind ( ) == other. kind ( )
633695 }
634696}
635697
636698impl Eq for UnixAddr { }
637699
638700impl Hash for UnixAddr {
639701 fn hash < H : Hasher > ( & self , s : & mut H ) {
640- ( self . 0 . sun_family , self . sun_path ( ) ) . hash ( s)
702+ self . kind ( ) . hash ( s)
641703 }
642704}
643705
@@ -805,12 +867,12 @@ impl SockAddr {
805867 } ,
806868 mem:: size_of_val ( addr) as libc:: socklen_t
807869 ) ,
808- SockAddr :: Unix ( UnixAddr ( ref addr , len ) ) => (
870+ SockAddr :: Unix ( UnixAddr { ref sun , path_len } ) => (
809871 // This cast is always allowed in C
810872 unsafe {
811- & * ( addr as * const libc:: sockaddr_un as * const libc:: sockaddr )
873+ & * ( sun as * const libc:: sockaddr_un as * const libc:: sockaddr )
812874 } ,
813- ( len + offset_of ! ( libc:: sockaddr_un, sun_path) ) as libc:: socklen_t
875+ ( path_len + offset_of ! ( libc:: sockaddr_un, sun_path) ) as libc:: socklen_t
814876 ) ,
815877 #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
816878 SockAddr :: Netlink ( NetlinkAddr ( ref sa) ) => (
@@ -1376,11 +1438,8 @@ mod tests {
13761438 let name = String :: from ( "nix\0 abstract\0 test" ) ;
13771439 let addr = UnixAddr :: new_abstract ( name. as_bytes ( ) ) . unwrap ( ) ;
13781440
1379- let sun_path1 = addr. sun_path ( ) ;
1380- let sun_path2 = [ 0u8 , 110 , 105 , 120 , 0 , 97 , 98 , 115 , 116 , 114 , 97 , 99 , 116 , 0 , 116 , 101 , 115 , 116 ] ;
1381- assert_eq ! ( sun_path1. len( ) , sun_path2. len( ) ) ;
1382- for i in 0 ..sun_path1. len ( ) {
1383- assert_eq ! ( sun_path1[ i] , sun_path2[ i] ) ;
1384- }
1441+ let sun_path1 = unsafe { & ( * addr. as_ptr ( ) ) . sun_path [ ..addr. path_len ( ) ] } ;
1442+ let sun_path2 = [ 0 , 110 , 105 , 120 , 0 , 97 , 98 , 115 , 116 , 114 , 97 , 99 , 116 , 0 , 116 , 101 , 115 , 116 ] ;
1443+ assert_eq ! ( sun_path1, sun_path2) ;
13851444 }
13861445}
0 commit comments