1use bytes::Bytes;
46use ndn_packet::{Name, NameComponent, tlv_type};
47use ndn_tlv::TlvWriter;
48
49use crate::scope::sd_services;
50use crate::wire::{parse_raw_data, write_name_tlv, write_nni};
51
52const T_ANNOUNCED_PREFIX: u32 = 0xD0;
55const T_SD_NODE_NAME: u32 = 0xD1;
56const T_FRESHNESS_MS: u32 = 0xD2;
57const T_SD_CAPABILITIES: u32 = 0xD3;
58
59#[derive(Clone, Debug, PartialEq)]
66pub struct ServiceRecord {
67 pub announced_prefix: Name,
69 pub node_name: Name,
71 pub freshness_ms: u64,
74 pub capabilities: u8,
76}
77
78impl ServiceRecord {
79 pub fn new(announced_prefix: Name, node_name: Name) -> Self {
81 Self {
82 announced_prefix,
83 node_name,
84 freshness_ms: 30_000,
85 capabilities: 0,
86 }
87 }
88
89 pub fn encode(&self) -> Bytes {
91 let mut w = TlvWriter::new();
92 let prefix_bytes = encode_name_raw(&self.announced_prefix);
94 w.write_tlv(T_ANNOUNCED_PREFIX.into(), &prefix_bytes);
95 let node_bytes = encode_name_raw(&self.node_name);
97 w.write_tlv(T_SD_NODE_NAME.into(), &node_bytes);
98 if self.freshness_ms > 0 {
100 write_nni_to_writer(&mut w, T_FRESHNESS_MS, self.freshness_ms);
101 }
102 if self.capabilities != 0 {
104 w.write_tlv(T_SD_CAPABILITIES.into(), &[self.capabilities]);
105 }
106 w.finish()
107 }
108
109 pub fn decode(b: &[u8]) -> Option<Self> {
111 let mut pos = 0;
112 let mut announced_prefix: Option<Name> = None;
113 let mut node_name: Option<Name> = None;
114 let mut freshness_ms = 0u64;
115 let mut capabilities = 0u8;
116
117 while pos < b.len() {
118 let (typ, len, header_len) = read_tlv_header(b, pos)?;
119 let val_start = pos + header_len;
120 let val_end = val_start + len;
121 if val_end > b.len() {
122 return None;
123 }
124 let val = &b[val_start..val_end];
125 match typ {
126 T_ANNOUNCED_PREFIX => {
127 announced_prefix = Some(decode_name_raw(val)?);
128 }
129 T_SD_NODE_NAME => {
130 node_name = Some(decode_name_raw(val)?);
131 }
132 T_FRESHNESS_MS => {
133 freshness_ms = read_nni(val)?;
134 }
135 T_SD_CAPABILITIES => {
136 capabilities = *val.first()?;
137 }
138 _ => {} }
140 pos = val_end;
141 }
142
143 Some(Self {
144 announced_prefix: announced_prefix?,
145 node_name: node_name?,
146 freshness_ms,
147 capabilities,
148 })
149 }
150
151 pub fn make_name(&self, timestamp_ms: u64) -> Name {
157 make_record_name(&self.announced_prefix, &self.node_name, timestamp_ms)
158 }
159
160 pub fn build_data(&self, timestamp_ms: u64) -> Bytes {
165 let name = self.make_name(timestamp_ms);
166 let content = self.encode();
167 let freshness_period = if self.freshness_ms > 0 {
168 self.freshness_ms
169 } else {
170 30_000
171 };
172
173 let mut w = TlvWriter::new();
174 w.write_nested(tlv_type::DATA, |w: &mut TlvWriter| {
175 write_name_tlv(w, &name);
176 w.write_nested(tlv_type::META_INFO, |w: &mut TlvWriter| {
177 write_nni(w, tlv_type::FRESHNESS_PERIOD, freshness_period);
178 });
179 w.write_tlv(tlv_type::CONTENT, &content);
180 w.write_nested(tlv_type::SIGNATURE_INFO, |w: &mut TlvWriter| {
181 w.write_tlv(tlv_type::SIGNATURE_TYPE, &[0u8]);
182 });
183 w.write_tlv(tlv_type::SIGNATURE_VALUE, &[0u8; 32]);
184 });
185 w.finish()
186 }
187
188 pub fn from_data_packet(raw: &Bytes) -> Option<Self> {
193 let parsed = parse_raw_data(raw)?;
194 if !parsed.name.has_prefix(sd_services()) {
195 return None;
196 }
197 let content = parsed.content?;
198 Self::decode(&content)
199 }
200}
201
202pub fn make_record_name(announced_prefix: &Name, node_name: &Name, timestamp_ms: u64) -> Name {
208 let hash = fnv1a_hash_name(announced_prefix);
209 let hash_hex = format!("{hash:016x}");
210
211 let mut comps: Vec<NameComponent> = sd_services().components().to_vec();
213
214 comps.push(NameComponent {
216 typ: tlv_type::NAME_COMPONENT,
217 value: hash_hex.as_bytes().to_vec().into(),
218 });
219
220 comps.extend(node_name.components().iter().cloned());
222
223 comps.push(NameComponent {
226 typ: 0x0D,
227 value: timestamp_ms.to_be_bytes().to_vec().into(),
228 });
229
230 Name::from_components(comps)
231}
232
233pub fn build_browse_interest() -> Bytes {
238 let prefix = sd_services();
239 let mut w = TlvWriter::new();
240 w.write_nested(tlv_type::INTEREST, |w: &mut TlvWriter| {
241 write_name_tlv(w, prefix);
242 w.write_tlv(0x21, &[]);
244 w.write_tlv(tlv_type::MUST_BE_FRESH, &[]);
246 write_nni(w, tlv_type::INTEREST_LIFETIME, 4000);
247 });
248 w.finish()
249}
250
251fn fnv1a_hash_name(name: &Name) -> u64 {
258 const OFFSET: u64 = 14695981039346656037;
259 const PRIME: u64 = 1099511628211;
260 let s = name.to_string();
261 s.bytes()
262 .fold(OFFSET, |h, b| (h ^ b as u64).wrapping_mul(PRIME))
263}
264
265fn encode_name_raw(name: &Name) -> Bytes {
269 let mut w = TlvWriter::new();
270 write_name_tlv(&mut w, name);
271 w.finish()
272}
273
274fn decode_name_raw(b: &[u8]) -> Option<Name> {
276 if b.is_empty() || b[0] != 0x07 {
278 return None;
279 }
280 use std::str::FromStr;
281 let (_, len, hl) = read_tlv_header(b, 0)?;
285 let comps_bytes = &b[hl..hl + len];
286 let mut comps = Vec::new();
287 let mut pos = 0;
288 while pos < comps_bytes.len() {
289 let (typ, clen, chl) = read_tlv_header(comps_bytes, pos)?;
290 let val = comps_bytes[pos + chl..pos + chl + clen].to_vec();
291 comps.push(NameComponent {
292 typ: typ as u64,
293 value: val.into(),
294 });
295 pos += chl + clen;
296 }
297 if comps.is_empty() {
298 return Some(Name::root());
299 }
300 let uri = {
302 let mut s = String::new();
303 for comp in &comps {
304 s.push('/');
305 for b in comp.value.iter() {
306 if b.is_ascii_alphanumeric() || b"-.~_".contains(b) {
307 s.push(*b as char);
308 } else {
309 s.push_str(&format!("%{:02X}", b));
310 }
311 }
312 }
313 if s.is_empty() { "/".to_string() } else { s }
314 };
315 Name::from_str(&uri).ok()
316}
317
318fn write_nni_to_writer(w: &mut TlvWriter, typ: u32, val: u64) {
320 let bytes = nni_bytes(val);
321 w.write_tlv(typ.into(), &bytes);
322}
323
324fn nni_bytes(val: u64) -> Vec<u8> {
325 if val <= 0xFF {
326 vec![val as u8]
327 } else if val <= 0xFFFF {
328 (val as u16).to_be_bytes().to_vec()
329 } else if val <= 0xFFFF_FFFF {
330 (val as u32).to_be_bytes().to_vec()
331 } else {
332 val.to_be_bytes().to_vec()
333 }
334}
335
336fn read_nni(b: &[u8]) -> Option<u64> {
337 match b.len() {
338 1 => Some(b[0] as u64),
339 2 => Some(u16::from_be_bytes(b.try_into().ok()?) as u64),
340 4 => Some(u32::from_be_bytes(b.try_into().ok()?) as u64),
341 8 => Some(u64::from_be_bytes(b.try_into().ok()?)),
342 _ => None,
343 }
344}
345
346fn read_tlv_header(b: &[u8], pos: usize) -> Option<(u32, usize, usize)> {
352 if pos >= b.len() {
353 return None;
354 }
355 let (typ, t_len) = read_varnumber(b, pos)?;
356 let (len, l_len) = read_varnumber(b, pos + t_len)?;
357 Some((typ as u32, len as usize, t_len + l_len))
358}
359
360fn read_varnumber(b: &[u8], pos: usize) -> Option<(u64, usize)> {
361 let first = *b.get(pos)?;
362 match first {
363 0xFD => {
364 let hi = *b.get(pos + 1)? as u64;
365 let lo = *b.get(pos + 2)? as u64;
366 Some(((hi << 8) | lo, 3))
367 }
368 0xFE => {
369 let v = u32::from_be_bytes(b[pos + 1..pos + 5].try_into().ok()?);
370 Some((v as u64, 5))
371 }
372 0xFF => {
373 let v = u64::from_be_bytes(b[pos + 1..pos + 9].try_into().ok()?);
374 Some((v, 9))
375 }
376 _ => Some((first as u64, 1)),
377 }
378}
379
380#[cfg(test)]
383mod tests {
384 use std::str::FromStr;
385
386 use super::*;
387
388 fn n(s: &str) -> Name {
389 Name::from_str(s).unwrap()
390 }
391
392 #[test]
393 fn record_encode_decode_roundtrip() {
394 let rec = ServiceRecord {
395 announced_prefix: n("/ndn/sensor/temp"),
396 node_name: n("/ndn/site/router1"),
397 freshness_ms: 60_000,
398 capabilities: 0x03,
399 };
400 let encoded = rec.encode();
401 let decoded = ServiceRecord::decode(&encoded).unwrap();
402 assert_eq!(decoded.announced_prefix, rec.announced_prefix);
403 assert_eq!(decoded.node_name, rec.node_name);
404 assert_eq!(decoded.freshness_ms, rec.freshness_ms);
405 assert_eq!(decoded.capabilities, rec.capabilities);
406 }
407
408 #[test]
409 fn make_name_under_sd_services() {
410 let rec = ServiceRecord::new(n("/ndn/sensor/temp"), n("/ndn/site/router1"));
411 let name = rec.make_name(1_700_000_000_000);
412 assert!(
413 name.has_prefix(sd_services()),
414 "name should be under sd/services"
415 );
416 }
417
418 #[test]
419 fn data_packet_roundtrip() {
420 let rec = ServiceRecord::new(n("/ndn/edu/ucla/cs"), n("/ndn/site/node42"));
421 let pkt = rec.build_data(42_000);
422 let decoded = ServiceRecord::from_data_packet(&pkt).unwrap();
423 assert_eq!(decoded.announced_prefix, rec.announced_prefix);
424 assert_eq!(decoded.node_name, rec.node_name);
425 }
426
427 #[test]
428 fn fnv1a_hash_is_deterministic() {
429 let h1 = fnv1a_hash_name(&n("/ndn/sensor/temp"));
430 let h2 = fnv1a_hash_name(&n("/ndn/sensor/temp"));
431 assert_eq!(h1, h2);
432 }
433
434 #[test]
435 fn different_prefixes_different_hashes() {
436 let h1 = fnv1a_hash_name(&n("/ndn/sensor/temp"));
437 let h2 = fnv1a_hash_name(&n("/ndn/sensor/pressure"));
438 assert_ne!(h1, h2);
439 }
440
441 #[test]
442 fn browse_interest_has_sd_prefix() {
443 use crate::wire::parse_raw_interest;
444 let pkt = build_browse_interest();
445 let parsed = parse_raw_interest(&pkt).unwrap();
446 assert!(parsed.name.has_prefix(sd_services()));
447 }
448}