1
- //! todo
1
+ //! This module contains functions and structs to lookup node information from DNS
2
+ //! and to encode node information in Pkarr signed packets.
2
3
3
4
use std:: { collections:: HashMap , fmt, str:: FromStr } ;
4
5
@@ -9,23 +10,23 @@ use url::Url;
9
10
10
11
use crate :: { key:: SecretKey , AddrInfo , NodeAddr , NodeId } ;
11
12
12
- ///
13
- pub const ATTR_DERP : & str = "derp" ;
14
- ///
15
- pub const ATTR_NODE_ID : & str = "node" ;
16
- ///
17
- pub const ATTR_ADDR : & str = "addr" ;
18
- ///
13
+ const ATTR_DERP : & str = "derp" ;
14
+ const ATTR_NODE_ID : & str = "node" ;
15
+
16
+ /// The label for the node info TXT record
19
17
pub const IROH_NODE_TXT_LABEL : & str = "_iroh_node" ;
20
18
19
+ /// Lookup node info by domain name
21
20
///
21
+ /// The domain name must either contain an _iroh_node TXT record or be a CNAME record that leads to
22
+ /// an _iroh_node TXT record.
22
23
pub async fn lookup_by_domain ( domain : & str ) -> Result < NodeAddr > {
23
24
let name = Name :: from_str ( domain) ?;
24
25
let info = lookup_node_info ( name) . await ?;
25
26
Ok ( info. into ( ) )
26
27
}
27
28
28
- ///
29
+ /// Lookup node info by node id and origin domain name.
29
30
pub async fn lookup_by_id ( node_id : & NodeId , origin : & str ) -> Result < NodeAddr > {
30
31
let domain = format ! ( "{}.{}" , to_z32( node_id) , origin) ;
31
32
lookup_by_domain ( & domain) . await
@@ -45,12 +46,29 @@ fn ensure_iroh_node_txt_label(name: Name) -> Result<Name, ProtoError> {
45
46
}
46
47
}
47
48
49
+ /// Encode a [`NodeId`] in [`z-base-32`] encoding.
50
+ ///
51
+ /// [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
52
+ pub fn to_z32 ( node_id : & NodeId ) -> String {
53
+ z32:: encode ( node_id. as_bytes ( ) )
54
+ }
55
+
56
+ /// Parse a [`NodeId`] from [`z-base-32`] encoding.
48
57
///
58
+ /// [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
59
+ pub fn from_z32 ( s : & str ) -> Result < NodeId > {
60
+ let bytes = z32:: decode ( s. as_bytes ( ) ) . map_err ( |_| anyhow ! ( "invalid z32" ) ) ?;
61
+ let bytes: & [ u8 ; 32 ] = & bytes. try_into ( ) . map_err ( |_| anyhow ! ( "not 32 bytes long" ) ) ?;
62
+ let node_id = NodeId :: from_bytes ( bytes) ?;
63
+ Ok ( node_id)
64
+ }
65
+
66
+ /// Node info contained in a DNS _iroh_node TXT record.
49
67
#[ derive( derive_more:: Debug , Clone , Eq , PartialEq ) ]
50
68
pub struct NodeInfo {
51
- ///
69
+ /// The node id
52
70
pub node_id : NodeId ,
53
- ///
71
+ /// Home Derp server for this node
54
72
#[ debug( "{:?}" , self . derp_url. as_ref( ) . map( |s| s. to_string( ) ) ) ]
55
73
pub derp_url : Option < Url > ,
56
74
}
@@ -74,22 +92,16 @@ impl From<NodeInfo> for AddrInfo {
74
92
}
75
93
76
94
impl NodeInfo {
77
- ///
95
+ /// Create a new [`NodeInfo`] from its parts.
78
96
pub fn new ( node_id : NodeId , derp_url : Option < Url > ) -> Self {
79
97
Self { node_id, derp_url }
80
98
}
81
- ///
82
- pub fn node_domain ( & self , origin : & str ) -> String {
83
- format ! ( "{}.{}" , to_z32( & self . node_id) , origin)
84
- }
85
-
86
- ///
87
- pub fn node_info_domain ( & self , origin : & str ) -> String {
88
- format ! ( "{}.{}" , IROH_NODE_TXT_LABEL , self . node_domain( origin) )
89
- }
90
99
100
+ /// Convert this node info into a DNS attribute string.
91
101
///
92
- pub fn to_attr_string ( & self ) -> String {
102
+ /// It will look like this:
103
+ /// node=b32encodednodeid derp=https://myderp.example
104
+ pub fn into_attribute_string ( & self ) -> String {
93
105
let mut attrs = vec ! [ ] ;
94
106
attrs. push ( fmt_attr ( ATTR_NODE_ID , self . node_id ) ) ;
95
107
if let Some ( derp) = & self . derp_url {
@@ -98,12 +110,12 @@ impl NodeInfo {
98
110
attrs. join ( " " )
99
111
}
100
112
101
- ///
113
+ /// Try to parse a [`NodeInfo`] from the lookup result of our DNS resolver.
102
114
pub fn from_hickory_lookup ( lookup : & hickory_resolver:: lookup:: Lookup ) -> Result < Self > {
103
115
Self :: from_hickory_records ( lookup. records ( ) )
104
116
}
105
117
106
- ///
118
+ /// Try to parse a [`NodeInfo`] from a set of DNS records.
107
119
pub fn from_hickory_records ( records : & [ hickory_proto:: rr:: Record ] ) -> Result < Self > {
108
120
use hickory_proto:: rr;
109
121
let ( node_id, txt) = records
@@ -122,7 +134,9 @@ impl NodeInfo {
122
134
Ok ( node_info)
123
135
}
124
136
137
+ /// Parse the [`NodeInfo`] from an attribute string.
125
138
///
139
+ /// See [Self::into_attribute_string] for the expected format.
126
140
pub fn parse_from_attributes ( attrs : & str ) -> Result < Self > {
127
141
let attrs = parse_attrs ( attrs) ;
128
142
let Some ( node) = attrs. get ( ATTR_NODE_ID ) else {
@@ -143,7 +157,8 @@ impl NodeInfo {
143
157
} )
144
158
}
145
159
146
- ///
160
+ /// Create a [`pkarr::SignedPacket`] by constructing a DNS packet and
161
+ /// signing it with a [`SecretKey`].
147
162
pub fn into_pkarr_signed_packet (
148
163
& self ,
149
164
secret_key : & SecretKey ,
@@ -155,12 +170,11 @@ impl NodeInfo {
155
170
Ok ( signed_packet)
156
171
}
157
172
158
- ///
159
- pub fn into_pkarr_dns_packet ( & self , ttl : u32 ) -> Result < pkarr:: dns:: Packet < ' static > > {
173
+ fn into_pkarr_dns_packet ( & self , ttl : u32 ) -> Result < pkarr:: dns:: Packet < ' static > > {
160
174
use pkarr:: dns:: { self , rdata} ;
161
175
let name = dns:: Name :: new ( IROH_NODE_TXT_LABEL ) ?. into_owned ( ) ;
162
176
let rdata = {
163
- let value = self . to_attr_string ( ) ;
177
+ let value = self . into_attribute_string ( ) ;
164
178
let txt = rdata:: TXT :: new ( ) . with_string ( & value) ?. into_owned ( ) ;
165
179
rdata:: RData :: TXT ( txt)
166
180
} ;
@@ -172,7 +186,7 @@ impl NodeInfo {
172
186
Ok ( packet)
173
187
}
174
188
175
- ///
189
+ /// Try to parse a [`NodeInfo`] from a [`pkarr::SignedPacket`].
176
190
pub fn from_pkarr_signed_packet ( packet : & pkarr:: SignedPacket ) -> Result < Self > {
177
191
use pkarr:: dns:: { self , rdata:: RData } ;
178
192
let pubkey = packet. public_key ( ) ;
@@ -194,11 +208,11 @@ impl NodeInfo {
194
208
195
209
let txt_record = txt_record. to_owned ( ) ;
196
210
let txt = String :: try_from ( txt_record) ?;
197
- let an = Self :: parse_from_attributes ( & txt) ?;
198
- if an . node_id != node_id {
211
+ let info = Self :: parse_from_attributes ( & txt) ?;
212
+ if info . node_id != node_id {
199
213
bail ! ( "node id mismatch between record name and TXT value" ) ;
200
214
}
201
- Ok ( an )
215
+ Ok ( info )
202
216
}
203
217
}
204
218
@@ -231,19 +245,3 @@ fn parse_attrs<'a>(s: &'a str) -> HashMap<&'a str, Vec<&'a str>> {
231
245
map
232
246
}
233
247
234
- /// Encode a [`NodeId`] in [`z-base-32`] encoding.
235
- ///
236
- /// [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
237
- pub fn to_z32 ( node_id : & NodeId ) -> String {
238
- z32:: encode ( node_id. as_bytes ( ) )
239
- }
240
-
241
- /// Parse a [`NodeId`] from [`z-base-32`] encoding.
242
- ///
243
- /// [z-base-32]: https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
244
- pub fn from_z32 ( s : & str ) -> Result < NodeId > {
245
- let bytes = z32:: decode ( s. as_bytes ( ) ) . map_err ( |_| anyhow ! ( "invalid z32" ) ) ?;
246
- let bytes: & [ u8 ; 32 ] = & bytes. try_into ( ) . map_err ( |_| anyhow ! ( "not 32 bytes long" ) ) ?;
247
- let node_id = NodeId :: from_bytes ( bytes) ?;
248
- Ok ( node_id)
249
- }
0 commit comments