@@ -46,6 +46,7 @@ use core::fmt::Debug;
46
46
use core:: ops:: Deref ;
47
47
#[ cfg( feature = "std" ) ]
48
48
use core:: str:: FromStr ;
49
+ use core:: fmt:: Display ;
49
50
use crate :: io:: { self , Cursor , Read } ;
50
51
use crate :: io_extras:: read_to_end;
51
52
@@ -984,6 +985,45 @@ pub fn parse_onion_address(host: &str, port: u16) -> Result<SocketAddress, Socke
984
985
}
985
986
}
986
987
988
+ /// [`SocketAddress::OnionV2`] to debug represent
989
+ #[ inline]
990
+ fn to_onion_v2_string ( bytes : & [ u8 ; 12 ] ) -> String {
991
+ format ! ( "OnionV2({:?})" , bytes)
992
+ }
993
+
994
+ /// [`SocketAddress::OnionV3`] to onion address string
995
+ #[ inline]
996
+ fn to_onion_v3_string ( key : & [ u8 ; 32 ] , checksum : & u16 , version : & u8 , port : & u16 ) -> String {
997
+ let [ first_checksum_flag, second_checksum_flag] = checksum. to_be_bytes ( ) ;
998
+ let mut addr = vec ! [ * version, first_checksum_flag, second_checksum_flag] ;
999
+ addr. extend_from_slice ( key) ;
1000
+ let onion = base32:: Alphabet :: RFC4648 { padding : false } . encode ( & addr) ;
1001
+ format ! ( "{}.onion:{}" , onion, port)
1002
+ }
1003
+
1004
+ impl Display for SocketAddress {
1005
+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
1006
+ match self {
1007
+ SocketAddress :: TcpIpV4 { addr, port} => write ! (
1008
+ f, "{}.{}.{}.{}:{}" , addr[ 0 ] , addr[ 1 ] , addr[ 2 ] , addr[ 3 ] , port) ?,
1009
+ SocketAddress :: TcpIpV6 { addr, port} => write ! (
1010
+ f,
1011
+ "[{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}]:{}" ,
1012
+ addr[ 0 ] , addr[ 1 ] , addr[ 2 ] , addr[ 3 ] , addr[ 4 ] , addr[ 5 ] , addr[ 6 ] , addr[ 7 ] , addr[ 8 ] , addr[ 9 ] , addr[ 10 ] , addr[ 11 ] , addr[ 12 ] , addr[ 13 ] , addr[ 14 ] , addr[ 15 ] , port
1013
+ ) ?,
1014
+ SocketAddress :: OnionV2 ( bytes) => write ! ( f, "{}" , to_onion_v2_string( & bytes) ) ?,
1015
+ SocketAddress :: OnionV3 {
1016
+ ed25519_pubkey,
1017
+ checksum,
1018
+ version,
1019
+ port,
1020
+ } => write ! ( f, "{}" , to_onion_v3_string( & ed25519_pubkey, checksum, version, port) ) ?,
1021
+ SocketAddress :: Hostname { hostname, port } => write ! ( f, "{}:{}" , hostname, port) ?,
1022
+ }
1023
+ Ok ( ( ) )
1024
+ }
1025
+ }
1026
+
987
1027
#[ cfg( feature = "std" ) ]
988
1028
impl FromStr for SocketAddress {
989
1029
type Err = SocketAddressParseError ;
@@ -4066,32 +4106,41 @@ mod tests {
4066
4106
#[ test]
4067
4107
#[ cfg( feature = "std" ) ]
4068
4108
fn test_socket_address_from_str ( ) {
4069
- assert_eq ! ( SocketAddress :: TcpIpV4 {
4109
+ let tcpip_v4 = SocketAddress :: TcpIpV4 {
4070
4110
addr : Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) . octets ( ) ,
4071
4111
port : 1234 ,
4072
- } , SocketAddress :: from_str( "127.0.0.1:1234" ) . unwrap( ) ) ;
4112
+ } ;
4113
+ assert_eq ! ( tcpip_v4, SocketAddress :: from_str( "127.0.0.1:1234" ) . unwrap( ) ) ;
4114
+ assert_eq ! ( tcpip_v4, SocketAddress :: from_str( & tcpip_v4. to_string( ) ) . unwrap( ) ) ;
4073
4115
4074
- assert_eq ! ( SocketAddress :: TcpIpV6 {
4116
+ let tcpip_v6 = SocketAddress :: TcpIpV6 {
4075
4117
addr : Ipv6Addr :: new ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ) . octets ( ) ,
4076
4118
port : 1234 ,
4077
- } , SocketAddress :: from_str( "[0:0:0:0:0:0:0:1]:1234" ) . unwrap( ) ) ;
4078
- assert_eq ! (
4079
- SocketAddress :: Hostname {
4119
+ } ;
4120
+ assert_eq ! ( tcpip_v6, SocketAddress :: from_str( "[0:0:0:0:0:0:0:1]:1234" ) . unwrap( ) ) ;
4121
+ assert_eq ! ( tcpip_v6, SocketAddress :: from_str( & tcpip_v6. to_string( ) ) . unwrap( ) ) ;
4122
+
4123
+ let hostname = SocketAddress :: Hostname {
4080
4124
hostname : Hostname :: try_from ( "lightning-node.mydomain.com" . to_string ( ) ) . unwrap ( ) ,
4081
4125
port : 1234 ,
4082
- } , SocketAddress :: from_str( "lightning-node.mydomain.com:1234" ) . unwrap( ) ) ;
4083
- assert_eq ! (
4084
- SocketAddress :: Hostname {
4085
- hostname: Hostname :: try_from( "example.com" . to_string( ) ) . unwrap( ) ,
4086
- port: 1234 ,
4087
- } , SocketAddress :: from_str( "example.com:1234" ) . unwrap( ) ) ;
4088
- assert_eq ! ( SocketAddress :: OnionV3 {
4126
+ } ;
4127
+ assert_eq ! ( hostname, SocketAddress :: from_str( "lightning-node.mydomain.com:1234" ) . unwrap( ) ) ;
4128
+ assert_eq ! ( hostname, SocketAddress :: from_str( & hostname. to_string( ) ) . unwrap( ) ) ;
4129
+
4130
+ let onion_v2 = SocketAddress :: OnionV2 ( [ 40 , 4 , 64 , 185 , 202 , 19 , 162 , 75 , 90 , 200 , 38 , 7 ] , ) ;
4131
+ assert_eq ! ( "OnionV2([40, 4, 64, 185, 202, 19, 162, 75, 90, 200, 38, 7])" , & onion_v2. to_string( ) ) ;
4132
+ assert_eq ! ( Err ( SocketAddressParseError :: InvalidOnionV3 ) , SocketAddress :: from_str( "FACEBOOKCOREWWWI.onion:9735" ) ) ;
4133
+
4134
+ let onion_v3 = SocketAddress :: OnionV3 {
4089
4135
ed25519_pubkey : [ 37 , 24 , 75 , 5 , 25 , 73 , 117 , 194 , 139 , 102 , 182 , 107 , 4 , 105 , 247 , 246 , 85 ,
4090
4136
111 , 177 , 172 , 49 , 137 , 167 , 155 , 64 , 221 , 163 , 47 , 31 , 33 , 71 , 3 ] ,
4091
4137
checksum : 48326 ,
4092
4138
version : 121 ,
4093
4139
port : 1234
4094
- } , SocketAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234" ) . unwrap( ) ) ;
4140
+ } ;
4141
+ assert_eq ! ( onion_v3, SocketAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234" ) . unwrap( ) ) ;
4142
+ assert_eq ! ( onion_v3, SocketAddress :: from_str( & onion_v3. to_string( ) ) . unwrap( ) ) ;
4143
+
4095
4144
assert_eq ! ( Err ( SocketAddressParseError :: InvalidOnionV3 ) , SocketAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234" ) ) ;
4096
4145
assert_eq ! ( Err ( SocketAddressParseError :: InvalidInput ) , SocketAddress :: from_str( "127.0.0.1@1234" ) ) ;
4097
4146
assert_eq ! ( Err ( SocketAddressParseError :: InvalidInput ) , "" . parse:: <SocketAddress >( ) ) ;
0 commit comments