1+ use arrayref:: array_ref;
2+ use byteorder:: { BigEndian , ByteOrder } ;
13use bytes:: { Buf , BufMut } ;
4+ use data_encoding:: BASE32 ;
25use std:: {
36 borrow:: Cow ,
47 fmt,
@@ -7,7 +10,7 @@ use std::{
710 str:: { self , FromStr } ,
811} ;
912
10- use crate :: error:: Error ;
13+ use crate :: { error:: Error , Onion3Addr } ;
1114
1215const DNS4 : u32 = 0x36 ;
1316const DNS6 : u32 = 0x37 ;
@@ -19,6 +22,7 @@ const TLS: u32 = 0x01c0;
1922const WS : u32 = 0x01dd ;
2023const WSS : u32 = 0x01de ;
2124const MEMORY : u32 = 0x0309 ;
25+ const ONION3 : u32 = 0x01bd ;
2226
2327const SHA256_CODE : u64 = 0x12 ;
2428const SHA256_SIZE : u8 = 32 ;
@@ -37,6 +41,7 @@ pub enum Protocol<'a> {
3741 Wss ,
3842 /// Contains the "port" to contact. Similar to TCP or UDP, 0 means "assign me a port".
3943 Memory ( u64 ) ,
44+ Onion3 ( Onion3Addr < ' a > ) ,
4045}
4146
4247impl < ' a > Protocol < ' a > {
@@ -87,6 +92,11 @@ impl<'a> Protocol<'a> {
8792 let s = iter. next ( ) . ok_or ( Error :: InvalidProtocolString ) ?;
8893 Ok ( Protocol :: Memory ( s. parse ( ) ?) )
8994 }
95+ "onion3" => iter
96+ . next ( )
97+ . ok_or ( Error :: InvalidProtocolString )
98+ . and_then ( |s| read_onion3 ( & s. to_uppercase ( ) ) )
99+ . map ( |( a, p) | Protocol :: Onion3 ( ( a, p) . into ( ) ) ) ,
90100 _ => Err ( Error :: UnknownProtocolString ) ,
91101 }
92102 }
@@ -101,6 +111,14 @@ impl<'a> Protocol<'a> {
101111 }
102112 Ok ( input. split_at ( n) )
103113 }
114+
115+ fn split_at ( n : usize , input : & [ u8 ] ) -> Result < ( & [ u8 ] , & [ u8 ] ) , Error > {
116+ if input. len ( ) < n {
117+ return Err ( Error :: DataLessThanLen ) ;
118+ }
119+ Ok ( input. split_at ( n) )
120+ }
121+
104122 let ( id, input) = decode:: u32 ( input) ?;
105123 match id {
106124 DNS4 => {
@@ -160,6 +178,14 @@ impl<'a> Protocol<'a> {
160178 let num = rdr. get_u64 ( ) ;
161179 Ok ( ( Protocol :: Memory ( num) , rest) )
162180 }
181+ ONION3 => {
182+ let ( data, rest) = split_at ( 37 , input) ?;
183+ let port = BigEndian :: read_u16 ( & data[ 35 ..] ) ;
184+ Ok ( (
185+ Protocol :: Onion3 ( ( array_ref ! ( data, 0 , 35 ) , port) . into ( ) ) ,
186+ rest,
187+ ) )
188+ }
163189 _ => Err ( Error :: UnknownProtocolId ( id) ) ,
164190 }
165191 }
@@ -213,6 +239,11 @@ impl<'a> Protocol<'a> {
213239 w. put ( encode:: u32 ( MEMORY , & mut buf) ) ;
214240 w. put_u64 ( * port)
215241 }
242+ Protocol :: Onion3 ( addr) => {
243+ w. put ( encode:: u32 ( ONION3 , & mut buf) ) ;
244+ w. put ( addr. hash ( ) . as_ref ( ) ) ;
245+ w. put_u16 ( addr. port ( ) ) ;
246+ }
216247 }
217248 }
218249
@@ -229,6 +260,7 @@ impl<'a> Protocol<'a> {
229260 Protocol :: Ws => Protocol :: Ws ,
230261 Protocol :: Wss => Protocol :: Wss ,
231262 Protocol :: Memory ( a) => Protocol :: Memory ( a) ,
263+ Protocol :: Onion3 ( addr) => Protocol :: Onion3 ( addr. acquire ( ) ) ,
232264 }
233265 }
234266}
@@ -247,6 +279,10 @@ impl<'a> fmt::Display for Protocol<'a> {
247279 Ws => write ! ( f, "/ws" ) ,
248280 Wss => write ! ( f, "/wss" ) ,
249281 Memory ( port) => write ! ( f, "/memory/{}" , port) ,
282+ Onion3 ( addr) => {
283+ let s = BASE32 . encode ( addr. hash ( ) ) ;
284+ write ! ( f, "/onion3/{}:{}" , s. to_lowercase( ) , addr. port( ) )
285+ }
250286 }
251287 }
252288}
@@ -291,3 +327,53 @@ fn check_p2p(data: &[u8]) -> Result<(), Error> {
291327 }
292328 Ok ( ( ) )
293329}
330+
331+ macro_rules! read_onion_impl {
332+ ( $name: ident, $len: expr, $encoded_len: expr) => {
333+ fn $name( s: & str ) -> Result <( [ u8 ; $len] , u16 ) , Error > {
334+ let mut parts = s. split( ':' ) ;
335+
336+ // address part (without ".onion")
337+ let b32 = parts. next( ) . ok_or( Error :: InvalidMultiaddr ) ?;
338+ if b32. len( ) != $encoded_len {
339+ return Err ( Error :: InvalidMultiaddr ) ;
340+ }
341+
342+ // port number
343+ let port = parts
344+ . next( )
345+ . ok_or( Error :: InvalidMultiaddr )
346+ . and_then( |p| str :: parse( p) . map_err( From :: from) ) ?;
347+
348+ // port == 0 is not valid for onion
349+ if port == 0 {
350+ return Err ( Error :: InvalidMultiaddr ) ;
351+ }
352+
353+ // nothing else expected
354+ if parts. next( ) . is_some( ) {
355+ return Err ( Error :: InvalidMultiaddr ) ;
356+ }
357+
358+ if $len
359+ != BASE32
360+ . decode_len( b32. len( ) )
361+ . map_err( |_| Error :: InvalidMultiaddr ) ?
362+ {
363+ return Err ( Error :: InvalidMultiaddr ) ;
364+ }
365+
366+ let mut buf = [ 0u8 ; $len] ;
367+ BASE32
368+ . decode_mut( b32. as_bytes( ) , & mut buf)
369+ . map_err( |_| Error :: InvalidMultiaddr ) ?;
370+
371+ Ok ( ( buf, port) )
372+ }
373+ } ;
374+ }
375+
376+ // Parse a version 3 onion address and return its binary representation.
377+ //
378+ // Format: <base-32 address> ":" <port number>
379+ read_onion_impl ! ( read_onion3, 35 , 56 ) ;
0 commit comments