@@ -896,7 +896,7 @@ impl Readable for NetAddress {
896896
897897/// NetAddress error variants
898898#[ cfg( feature = "std" ) ]
899- #[ derive( Debug ) ]
899+ #[ derive( Debug , Eq , PartialEq , Clone ) ]
900900pub enum NetAddressError {
901901 /// Socket address(IPv4/IPv6) parsing error
902902 SocketAdrrParseError ( std:: net:: AddrParseError ) ,
@@ -910,24 +910,24 @@ pub enum NetAddressError {
910910
911911#[ cfg( feature = "std" ) ]
912912impl FromStr for NetAddress {
913- type Err = NetAddressError ;
914-
915- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
916- match std:: net:: SocketAddr :: from_str ( s) {
917- Ok ( addr) => {
918- let port: u16 = addr. port ( ) ;
919- match addr {
920- std:: net:: SocketAddr :: V4 ( addr) => {
921- let addr = addr. ip ( ) . octets ( ) ;
922- return Ok ( NetAddress :: IPv4 { addr, port } ) ;
923- } ,
924- std:: net:: SocketAddr :: V6 ( addr) => {
925- let addr = addr. ip ( ) . octets ( ) ;
926- return Ok ( NetAddress :: IPv6 { addr, port } ) ;
927- } ,
928- }
929- } ,
930- Err ( e) => {
913+ type Err = NetAddressError ;
914+
915+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
916+ match std:: net:: SocketAddr :: from_str ( s) {
917+ Ok ( addr) => {
918+ let port: u16 = addr. port ( ) ;
919+ match addr {
920+ std:: net:: SocketAddr :: V4 ( addr) => {
921+ let addr = addr. ip ( ) . octets ( ) ;
922+ return Ok ( NetAddress :: IPv4 { addr, port } ) ;
923+ } ,
924+ std:: net:: SocketAddr :: V6 ( addr) => {
925+ let addr = addr. ip ( ) . octets ( ) ;
926+ return Ok ( NetAddress :: IPv6 { addr, port } ) ;
927+ } ,
928+ }
929+ } ,
930+ Err ( e) => {
931931 let trimmed_input = match s. rfind ( ":" ) {
932932 Some ( pos) => pos,
933933 None => return Err ( NetAddressError :: InvalidInput ( "Invalid input. Expected format: \" <host>:<port>\" " . to_string ( ) ) ) ,
@@ -937,36 +937,48 @@ impl FromStr for NetAddress {
937937 Ok ( port) => port,
938938 Err ( _) => return Err ( NetAddressError :: InvalidPort ) ,
939939 } ;
940- if host. ends_with ( ".onion" ) {
941- let onion = match host. split ( ".onion" ) . nth ( 0 ) {
942- Some ( onion) => onion,
943- None => return Err ( NetAddressError :: InvalidOnionV3 ) ,
944- } ;
945- let onion = match ( base32:: Alphabet :: RFC4648 { padding : false } . decode ( & onion) ) {
946- Ok ( onion) => onion,
947- Err ( _) => return Err ( NetAddressError :: InvalidOnionV3 ) ,
948- } ;
949- match ( onion. get ( 0 ) , onion. get ( 1 ) , onion. get ( 2 ) ) {
950- ( Some ( version) , Some ( first_checksum_flag) , Some ( second_checksum_flag) ) => {
951- let checksum = u16:: from_be_bytes ( [ * first_checksum_flag, * second_checksum_flag] ) ;
952- let ed25519_pubkey = match onion[ 3 ..35 ] . try_into ( ) {
953- Ok ( ed25519_pubkey) => ed25519_pubkey,
954- Err ( _) => return Err ( NetAddressError :: InvalidOnionV3 ) ,
955- } ;
956- return Ok ( NetAddress :: OnionV3 { ed25519_pubkey, checksum, version : * version, port } ) ;
957-
940+ let host: Vec < & str > = host. split ( "." ) . collect ( ) ;
941+ if host. len ( ) == 2 {
942+ match ( host. get ( 0 ) , host. get ( 1 ) ) {
943+ ( Some ( domain) , Some ( suffix) ) => {
944+ if suffix. to_string ( ) == "onion" . to_owned ( ) {
945+ if domain. len ( ) != 56 {
946+ return Err ( NetAddressError :: InvalidOnionV3 ) ;
947+ }
948+ let onion = match ( base32:: Alphabet :: RFC4648 { padding : false } . decode ( & domain) ) {
949+ Ok ( onion) => onion,
950+ Err ( _) => return Err ( NetAddressError :: InvalidOnionV3 ) ,
951+ } ;
952+ if onion. len ( ) < 35 {
953+ return Err ( NetAddressError :: InvalidOnionV3 ) ;
954+ }
955+ match ( onion. get ( 0 ) , onion. get ( 1 ) , onion. get ( 2 ) ) {
956+ ( Some ( version) , Some ( first_checksum_flag) , Some ( second_checksum_flag) ) => {
957+ let checksum = u16:: from_be_bytes ( [ * first_checksum_flag, * second_checksum_flag] ) ;
958+ let ed25519_pubkey = match onion[ 3 ..35 ] . try_into ( ) {
959+ Ok ( ed25519_pubkey) => ed25519_pubkey,
960+ Err ( _) => return Err ( NetAddressError :: InvalidOnionV3 ) ,
961+ } ;
962+ return Ok ( NetAddress :: OnionV3 { ed25519_pubkey, checksum, version : * version, port } ) ;
963+
964+ } ,
965+ _ => return Err ( NetAddressError :: InvalidOnionV3 ) ,
966+ }
967+ } else {
968+ if let Ok ( hostname) = Hostname :: try_from ( s[ ..trimmed_input] . to_string ( ) ) {
969+ return Ok ( NetAddress :: Hostname { hostname, port } ) ;
970+ } else {
971+ return Err ( NetAddressError :: InvalidInput ( "Invalid input. Expected format: \" <host>:<port>\" " . to_string ( ) ) ) ;
972+ }
973+ }
958974 } ,
959- _ => return Err ( NetAddressError :: InvalidOnionV3 ) ,
960- }
961- }
962-
963- if let Ok ( hostname) = Hostname :: try_from ( host. to_string ( ) ) {
964- return Ok ( NetAddress :: Hostname { hostname, port } ) ;
965- }
966- return Err ( NetAddressError :: SocketAdrrParseError ( e) )
967- } ,
968- }
969- }
975+ _ => return Err ( NetAddressError :: SocketAdrrParseError ( e) )
976+ } ;
977+ }
978+ return Err ( NetAddressError :: SocketAdrrParseError ( e) )
979+ } ,
980+ }
981+ }
970982}
971983
972984/// Represents the set of gossip messages that require a signature from a node's identity key.
@@ -3816,21 +3828,35 @@ mod tests {
38163828 addr: Ipv4Addr :: new( 127 , 0 , 0 , 1 ) . octets( ) ,
38173829 port: 1234 ,
38183830 } , NetAddress :: from_str( "127.0.0.1:1234" ) . unwrap( ) ) ;
3831+
38193832 assert_eq ! ( NetAddress :: IPv6 {
38203833 addr : Ipv6Addr :: new( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ) . octets( ) ,
38213834 port: 1234 ,
38223835 } , NetAddress :: from_str( "[0:0:0:0:0:0:0:1]:1234" ) . unwrap( ) ) ;
3836+
38233837 assert_eq ! (
38243838 NetAddress :: Hostname {
38253839 hostname : Hostname :: try_from( "example.com" . to_string( ) ) . unwrap( ) ,
38263840 port: 1234 ,
38273841 } , NetAddress :: from_str( "example.com:1234" ) . unwrap( ) ) ;
3842+
38283843 assert_eq ! ( NetAddress :: OnionV3 {
38293844 ed25519_pubkey: [ 37 , 24 , 75 , 5 , 25 , 73 , 117 , 194 , 139 , 102 , 182 , 107 , 4 , 105 , 247 , 246 , 85 ,
38303845 111 , 177 , 172 , 49 , 137 , 167 , 155 , 64 , 221 , 163 , 47 , 31 , 33 , 71 , 3 ] ,
38313846 checksum: 48326 ,
38323847 version: 121 ,
38333848 port: 1234
38343849 } , NetAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234" ) . unwrap( ) ) ;
3850+
3851+ let invalid_onion_address = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234" ;
3852+ assert_eq ! ( super :: NetAddressError :: InvalidOnionV3 , NetAddress :: from_str( invalid_onion_address) . unwrap_err( ) . into( ) ) ;
3853+
3854+ assert_eq ! ( super :: NetAddressError :: InvalidInput ( "Invalid input. Expected format: \" <host>:<port>\" " . to_string( ) ) , NetAddress :: from_str( "127.0.0.1@1234" ) . unwrap_err( ) . into( ) ) ;
3855+
3856+ assert ! ( NetAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:9735:94" ) . is_err( ) ) ;
3857+
3858+ assert_eq ! ( super :: NetAddressError :: InvalidInput ( "Invalid input. Expected format: \" <host>:<port>\" " . to_string( ) ) , NetAddress :: from_str( "wrong$%#.com:1234" ) . unwrap_err( ) . into( ) ) ;
3859+
3860+ assert_eq ! ( super :: NetAddressError :: InvalidPort , NetAddress :: from_str( "example.com:wrong" ) . unwrap_err( ) . into( ) ) ;
38353861 }
38363862}
0 commit comments