@@ -36,14 +36,17 @@ use crate::ln::onion_utils;
36
36
use crate :: onion_message;
37
37
38
38
use crate :: prelude:: * ;
39
+ use core:: convert:: TryFrom ;
39
40
use core:: fmt;
40
41
use core:: fmt:: Debug ;
42
+ use core:: str:: FromStr ;
41
43
use crate :: io:: { self , Read } ;
42
44
use crate :: io_extras:: read_to_end;
43
45
44
46
use crate :: events:: { MessageSendEventsProvider , OnionMessageProvider } ;
45
47
use crate :: util:: logger;
46
48
use crate :: util:: ser:: { LengthReadable , Readable , ReadableArgs , Writeable , Writer , WithoutLength , FixedLengthReader , HighZeroBytesDroppedBigSize , Hostname , TransactionU16LenLimited , BigSize } ;
49
+ use crate :: util:: base32;
47
50
48
51
use crate :: ln:: { PaymentPreimage , PaymentHash , PaymentSecret } ;
49
52
@@ -900,6 +903,106 @@ impl Readable for NetAddress {
900
903
}
901
904
}
902
905
906
+ /// [`NetAddress`] error variants
907
+ #[ derive( Debug , Eq , PartialEq , Clone ) ]
908
+ pub enum NetAddressParseError {
909
+ /// Socket address (IPv4/IPv6) parsing error
910
+ SocketAddrParse ,
911
+ /// Invalid input format
912
+ InvalidInput ,
913
+ /// Invalid port
914
+ InvalidPort ,
915
+ /// Invalid onion v3 address
916
+ InvalidOnionV3 ,
917
+ }
918
+
919
+ impl fmt:: Display for NetAddressParseError {
920
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
921
+ match self {
922
+ NetAddressParseError :: SocketAddrParse => write ! ( f, "Socket address (IPv4/IPv6) parsing error" ) ,
923
+ NetAddressParseError :: InvalidInput => write ! ( f, "{}" , "Invalid input format. \
924
+ Expected: \" <ipv4>:<port>\" , \" [<ipv6>]:<port>\" , \" <onion address>.onion:<port>\" or \" <hostname>:<port>\" ") ,
925
+ NetAddressParseError :: InvalidPort => write ! ( f, "Invalid port" ) ,
926
+ NetAddressParseError :: InvalidOnionV3 => write ! ( f, "Invalid onion v3 address" ) ,
927
+ }
928
+ }
929
+ }
930
+
931
+ #[ cfg( feature = "std" ) ]
932
+ impl From < std:: net:: SocketAddrV4 > for NetAddress {
933
+ fn from ( addr : std:: net:: SocketAddrV4 ) -> Self {
934
+ NetAddress :: IPv4 { addr : addr. ip ( ) . octets ( ) , port : addr. port ( ) }
935
+ }
936
+ }
937
+
938
+ #[ cfg( feature = "std" ) ]
939
+ impl From < std:: net:: SocketAddrV6 > for NetAddress {
940
+ fn from ( addr : std:: net:: SocketAddrV6 ) -> Self {
941
+ NetAddress :: IPv6 { addr : addr. ip ( ) . octets ( ) , port : addr. port ( ) }
942
+ }
943
+ }
944
+
945
+ #[ cfg( feature = "std" ) ]
946
+ impl From < std:: net:: SocketAddr > for NetAddress {
947
+ fn from ( addr : std:: net:: SocketAddr ) -> Self {
948
+ match {
949
+ addr
950
+ } {
951
+ std:: net:: SocketAddr :: V4 ( addr) => addr. into ( ) ,
952
+ std:: net:: SocketAddr :: V6 ( addr) => addr. into ( ) ,
953
+ }
954
+ }
955
+ }
956
+
957
+ fn parse_onion_address ( host : & str , port : u16 ) -> Result < NetAddress , NetAddressParseError > {
958
+ if host. ends_with ( ".onion" ) {
959
+ let domain = & host[ ..host. len ( ) - ".onion" . len ( ) ] ;
960
+ if domain. len ( ) != 56 {
961
+ return Err ( NetAddressParseError :: InvalidOnionV3 ) ;
962
+ }
963
+ let onion = base32:: Alphabet :: RFC4648 { padding : false } . decode ( & domain) . map_err ( |_| NetAddressParseError :: InvalidOnionV3 ) ?;
964
+ if onion. len ( ) != 35 {
965
+ return Err ( NetAddressParseError :: InvalidOnionV3 ) ;
966
+ }
967
+ let version = onion[ 0 ] ;
968
+ let first_checksum_flag = onion[ 1 ] ;
969
+ let second_checksum_flag = onion[ 2 ] ;
970
+ let mut ed25519_pubkey = [ 0 ; 32 ] ;
971
+ ed25519_pubkey. copy_from_slice ( & onion[ 3 ..35 ] ) ;
972
+ let checksum = u16:: from_be_bytes ( [ first_checksum_flag, second_checksum_flag] ) ;
973
+ return Ok ( NetAddress :: OnionV3 { ed25519_pubkey, checksum, version, port } ) ;
974
+
975
+ } else {
976
+ return Err ( NetAddressParseError :: InvalidInput ) ;
977
+ }
978
+ }
979
+
980
+ #[ cfg( feature = "std" ) ]
981
+ impl FromStr for NetAddress {
982
+ type Err = NetAddressParseError ;
983
+
984
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
985
+ match std:: net:: SocketAddr :: from_str ( s) {
986
+ Ok ( addr) => Ok ( addr. into ( ) ) ,
987
+ Err ( _) => {
988
+ let trimmed_input = match s. rfind ( ":" ) {
989
+ Some ( pos) => pos,
990
+ None => return Err ( NetAddressParseError :: InvalidInput ) ,
991
+ } ;
992
+ let host = & s[ ..trimmed_input] ;
993
+ let port: u16 = s[ trimmed_input + 1 ..] . parse ( ) . map_err ( |_| NetAddressParseError :: InvalidPort ) ?;
994
+ if host. ends_with ( ".onion" ) {
995
+ return parse_onion_address ( host, port) ;
996
+ } ;
997
+ if let Ok ( hostname) = Hostname :: try_from ( s[ ..trimmed_input] . to_string ( ) ) {
998
+ return Ok ( NetAddress :: Hostname { hostname, port } ) ;
999
+ } ;
1000
+ return Err ( NetAddressParseError :: SocketAddrParse )
1001
+ } ,
1002
+ }
1003
+ }
1004
+ }
1005
+
903
1006
/// Represents the set of gossip messages that require a signature from a node's identity key.
904
1007
pub enum UnsignedGossipMessage < ' a > {
905
1008
/// An unsigned channel announcement.
@@ -2472,12 +2575,14 @@ impl_writeable_msg!(GossipTimestampFilter, {
2472
2575
2473
2576
#[ cfg( test) ]
2474
2577
mod tests {
2578
+ use std:: convert:: TryFrom ;
2475
2579
use bitcoin:: blockdata:: constants:: ChainHash ;
2476
2580
use bitcoin:: { Transaction , PackedLockTime , TxIn , Script , Sequence , Witness , TxOut } ;
2477
2581
use hex;
2478
2582
use crate :: ln:: { PaymentPreimage , PaymentHash , PaymentSecret } ;
2479
2583
use crate :: ln:: features:: { ChannelFeatures , ChannelTypeFeatures , InitFeatures , NodeFeatures } ;
2480
2584
use crate :: ln:: msgs:: { self , FinalOnionHopData , OnionErrorPacket } ;
2585
+ use crate :: ln:: msgs:: NetAddress ;
2481
2586
use crate :: routing:: gossip:: { NodeAlias , NodeId } ;
2482
2587
use crate :: util:: ser:: { Writeable , Readable , Hostname , TransactionU16LenLimited } ;
2483
2588
@@ -2493,11 +2598,13 @@ mod tests {
2493
2598
2494
2599
use crate :: io:: { self , Cursor } ;
2495
2600
use crate :: prelude:: * ;
2496
- use core:: convert:: TryFrom ;
2497
2601
use core:: str:: FromStr ;
2498
-
2499
2602
use crate :: chain:: transaction:: OutPoint ;
2500
2603
2604
+ #[ cfg( feature = "std" ) ]
2605
+ use std:: net:: { Ipv4Addr , Ipv6Addr } ;
2606
+ use crate :: ln:: msgs:: NetAddressParseError ;
2607
+
2501
2608
#[ test]
2502
2609
fn encoding_channel_reestablish ( ) {
2503
2610
let public_key = {
@@ -2663,32 +2770,32 @@ mod tests {
2663
2770
} ;
2664
2771
let mut addresses = Vec :: new ( ) ;
2665
2772
if ipv4 {
2666
- addresses. push ( msgs :: NetAddress :: IPv4 {
2773
+ addresses. push ( NetAddress :: IPv4 {
2667
2774
addr : [ 255 , 254 , 253 , 252 ] ,
2668
2775
port : 9735
2669
2776
} ) ;
2670
2777
}
2671
2778
if ipv6 {
2672
- addresses. push ( msgs :: NetAddress :: IPv6 {
2779
+ addresses. push ( NetAddress :: IPv6 {
2673
2780
addr : [ 255 , 254 , 253 , 252 , 251 , 250 , 249 , 248 , 247 , 246 , 245 , 244 , 243 , 242 , 241 , 240 ] ,
2674
2781
port : 9735
2675
2782
} ) ;
2676
2783
}
2677
2784
if onionv2 {
2678
- addresses. push ( msgs :: NetAddress :: OnionV2 (
2785
+ addresses. push ( NetAddress :: OnionV2 (
2679
2786
[ 255 , 254 , 253 , 252 , 251 , 250 , 249 , 248 , 247 , 246 , 38 , 7 ]
2680
2787
) ) ;
2681
2788
}
2682
2789
if onionv3 {
2683
- addresses. push ( msgs :: NetAddress :: OnionV3 {
2790
+ addresses. push ( NetAddress :: OnionV3 {
2684
2791
ed25519_pubkey : [ 255 , 254 , 253 , 252 , 251 , 250 , 249 , 248 , 247 , 246 , 245 , 244 , 243 , 242 , 241 , 240 , 239 , 238 , 237 , 236 , 235 , 234 , 233 , 232 , 231 , 230 , 229 , 228 , 227 , 226 , 225 , 224 ] ,
2685
2792
checksum : 32 ,
2686
2793
version : 16 ,
2687
2794
port : 9735
2688
2795
} ) ;
2689
2796
}
2690
2797
if hostname {
2691
- addresses. push ( msgs :: NetAddress :: Hostname {
2798
+ addresses. push ( NetAddress :: Hostname {
2692
2799
hostname : Hostname :: try_from ( String :: from ( "host" ) ) . unwrap ( ) ,
2693
2800
port : 9735 ,
2694
2801
} ) ;
@@ -3296,10 +3403,10 @@ mod tests {
3296
3403
let shutdown = msgs:: Shutdown {
3297
3404
channel_id : [ 2 ; 32 ] ,
3298
3405
scriptpubkey :
3299
- if script_type == 1 { Address :: p2pkh ( & :: bitcoin:: PublicKey { compressed : true , inner : pubkey_1} , Network :: Testnet ) . script_pubkey ( ) }
3406
+ if script_type == 1 { Address :: p2pkh ( & :: bitcoin:: PublicKey { compressed : true , inner : pubkey_1} , Network :: Testnet ) . script_pubkey ( ) }
3300
3407
else if script_type == 2 { Address :: p2sh ( & script, Network :: Testnet ) . unwrap ( ) . script_pubkey ( ) }
3301
3408
else if script_type == 3 { Address :: p2wpkh ( & :: bitcoin:: PublicKey { compressed : true , inner : pubkey_1} , Network :: Testnet ) . unwrap ( ) . script_pubkey ( ) }
3302
- else { Address :: p2wsh ( & script, Network :: Testnet ) . script_pubkey ( ) } ,
3409
+ else { Address :: p2wsh ( & script, Network :: Testnet ) . script_pubkey ( ) } ,
3303
3410
} ;
3304
3411
let encoded_value = shutdown. encode ( ) ;
3305
3412
let mut target_value = hex:: decode ( "0202020202020202020202020202020202020202020202020202020202020202" ) . unwrap ( ) ;
@@ -3504,7 +3611,7 @@ mod tests {
3504
3611
} . encode( ) , hex:: decode( "00000000014001010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202" ) . unwrap( ) ) ;
3505
3612
let init_msg = msgs:: Init { features : InitFeatures :: from_le_bytes ( vec ! [ ] ) ,
3506
3613
networks : Some ( vec ! [ mainnet_hash] ) ,
3507
- remote_network_address : Some ( msgs :: NetAddress :: IPv4 {
3614
+ remote_network_address : Some ( NetAddress :: IPv4 {
3508
3615
addr : [ 127 , 0 , 0 , 1 ] ,
3509
3616
port : 1000 ,
3510
3617
} ) ,
@@ -3869,4 +3976,47 @@ mod tests {
3869
3976
}
3870
3977
Ok ( encoded_payload)
3871
3978
}
3979
+
3980
+ #[ test]
3981
+ #[ cfg( feature = "std" ) ]
3982
+ fn test_net_address_from_str ( ) {
3983
+ assert_eq ! ( NetAddress :: IPv4 {
3984
+ addr: Ipv4Addr :: new( 127 , 0 , 0 , 1 ) . octets( ) ,
3985
+ port: 1234 ,
3986
+ } , NetAddress :: from_str( "127.0.0.1:1234" ) . unwrap( ) ) ;
3987
+
3988
+ assert_eq ! ( NetAddress :: IPv6 {
3989
+ addr: Ipv6Addr :: new( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ) . octets( ) ,
3990
+ port: 1234 ,
3991
+ } , NetAddress :: from_str( "[0:0:0:0:0:0:0:1]:1234" ) . unwrap( ) ) ;
3992
+ assert_eq ! (
3993
+ NetAddress :: Hostname {
3994
+ hostname: Hostname :: try_from( "lightning-node.mydomain.com" . to_string( ) ) . unwrap( ) ,
3995
+ port: 1234 ,
3996
+ } , NetAddress :: from_str( "lightning-node.mydomain.com:1234" ) . unwrap( ) ) ;
3997
+ assert_eq ! (
3998
+ NetAddress :: Hostname {
3999
+ hostname: Hostname :: try_from( "example.com" . to_string( ) ) . unwrap( ) ,
4000
+ port: 1234 ,
4001
+ } , NetAddress :: from_str( "example.com:1234" ) . unwrap( ) ) ;
4002
+ assert_eq ! ( NetAddress :: OnionV3 {
4003
+ ed25519_pubkey: [ 37 , 24 , 75 , 5 , 25 , 73 , 117 , 194 , 139 , 102 , 182 , 107 , 4 , 105 , 247 , 246 , 85 ,
4004
+ 111 , 177 , 172 , 49 , 137 , 167 , 155 , 64 , 221 , 163 , 47 , 31 , 33 , 71 , 3 ] ,
4005
+ checksum: 48326 ,
4006
+ version: 121 ,
4007
+ port: 1234
4008
+ } , NetAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234" ) . unwrap( ) ) ;
4009
+ assert_eq ! ( Err ( NetAddressParseError :: InvalidOnionV3 ) , NetAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234" ) ) ;
4010
+ assert_eq ! ( Err ( NetAddressParseError :: InvalidInput ) , NetAddress :: from_str( "127.0.0.1@1234" ) ) ;
4011
+ assert_eq ! ( Err ( NetAddressParseError :: InvalidInput ) , "" . parse:: <NetAddress >( ) ) ;
4012
+ assert ! ( NetAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:9735:94" ) . is_err( ) ) ;
4013
+ assert ! ( NetAddress :: from_str( "wrong$%#.com:1234" ) . is_err( ) ) ;
4014
+ assert_eq ! ( Err ( NetAddressParseError :: InvalidPort ) , NetAddress :: from_str( "example.com:wrong" ) ) ;
4015
+ assert ! ( "localhost" . parse:: <NetAddress >( ) . is_err( ) ) ;
4016
+ assert ! ( "localhost:invalid-port" . parse:: <NetAddress >( ) . is_err( ) ) ;
4017
+ assert ! ( "invalid-onion-v3-hostname.onion:8080" . parse:: <NetAddress >( ) . is_err( ) ) ;
4018
+ assert ! ( "b32.example.onion:invalid-port" . parse:: <NetAddress >( ) . is_err( ) ) ;
4019
+ assert ! ( "invalid-address" . parse:: <NetAddress >( ) . is_err( ) ) ;
4020
+ assert ! ( NetAddress :: from_str( "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:1234" ) . is_err( ) ) ;
4021
+ }
3872
4022
}
0 commit comments