mdns.rs (14415B)
1 // MDNS responder, mostly but not entirely standards-compliant 2 3 // ** Probe and Announce at startup 4 5 // at startup it does Probe and Announce for "all those resource 6 // records that a Multicast DNS responder desires to be unique on the 7 // local link". "Probe" means send packets as QU (unicast response) 8 // containing ANY requests for each name: 3 packets with 250ms between 9 // them; "Announce" means send 2 <= n <= 8 packets one second apart, 10 // containing all the records we know 11 12 // ** additional data in responses 13 14 // * for SRV, we send all the A and AAAA it names 15 16 // * for PTR "Service Instance Enumeration or Selective Instance 17 // Enumeration (subtype)": i.e. when the record data names an 18 // instance of the service, we sent the SRV and TXT records for the 19 // instance _and_ all the A/AAAA named by the SRV 20 21 // I haven't investigated what happens if the packet gets too 22 // large. There may be bugs 23 24 // ** don't answer queries if the answers in the query already include what we'd send 25 26 // This is unimplemented for now. mdns is so fricking noisy already I 27 // don't know if this is worth the extra effort 28 29 // ** set the CACHE_FLUSH bit on unique records 30 31 // "all the records with that name, rrtype, and rrclass are conceptually 32 // under the control or ownership of a single responder" 33 34 // ** NSEC? 35 36 // not done 37 38 // ** unicast response requested (QU) 39 40 // - top bit in the class field of a DNS question is the unicast-response bit 41 // - send response unicast, unless the record has not been sent recently 42 // ("within one quarter of its TTL") 43 // - also for queries received fom unicast 44 // - but verify the source is local lan 45 // - and the port is 5353 (otherwise see "legacy") 46 47 // [ mostly done; we don't check "has been sent recently" and as we 48 // didn't open a unicast socket then we can't receive queries from 49 // unicast ] 50 51 52 // ** Legacy Unicast Responses 53 54 // if source port != 5353 55 56 // - respond via unicast to source 57 // - query id and question 58 // - don't set cache-flush bit 59 // - ttl <= 10s 60 61 // This is implemented but has not really been tested 62 63 // ** response compression 64 65 // Unimplemented 66 67 68 use alloc::vec::Vec; 69 use alloc::string::String; 70 use alloc::collections::BTreeSet; 71 use crate::alloc::string::ToString; 72 73 use embassy_net::{ 74 udp::{UdpSocket, UdpMetadata, PacketMetadata}, 75 IpAddress, 76 IpEndpoint, 77 Stack 78 }; 79 80 use embassy_time::{Timer, Duration, Instant}; 81 use esp_alloc as _; 82 use esp_backtrace as _; 83 84 use log::{info,error}; 85 86 pub const DISCOVERY_NAME : &str = "_services._dns-sd._udp.local"; 87 88 use dns_protocol::{Message, Question, 89 MessageType, 90 ResourceRecord, ResourceType, Flags}; 91 92 const MDNS_IP: IpAddress = IpAddress::v4(224, 0, 0, 251); 93 const MDNS_PORT: u16 = 5353; 94 const MDNS_ENDPOINT: IpEndpoint = IpEndpoint::new(MDNS_IP, MDNS_PORT); 95 96 const CACHE_FLUSH : u16 = 1<<15; 97 98 pub enum ResponderState { 99 Probing(Instant, usize), 100 ProbeFailed, 101 Announcing(Instant, usize), 102 Running 103 } 104 105 pub struct Responder<'a> { 106 sock : &'a UdpSocket<'a>, 107 database : Database, 108 pub state : ResponderState, 109 } 110 111 impl<'a> Responder<'a> { 112 pub fn new(sock: &'a UdpSocket<'a>, 113 records: Vec<Resource>) -> Responder<'a> { 114 115 Self { 116 sock: sock, 117 database: Database(records), 118 state: ResponderState::Probing(Instant::now(), 0) 119 } 120 } 121 122 async fn advance_state(&mut self) { 123 match self.state { 124 ResponderState::Probing(next_when, number_sent) => { 125 let ts = Instant::now(); 126 if next_when < ts { 127 if number_sent >= 3 { 128 self.state = ResponderState::Announcing(ts, 0); 129 } else { 130 self.send_probe().await; 131 self.state = ResponderState::Probing(ts + Duration::from_millis(250), number_sent + 1); 132 } 133 } 134 }, 135 ResponderState::Announcing(next_when, number_sent) => { 136 let ts = Instant::now(); 137 if next_when < ts { 138 if number_sent > 6 { 139 self.state = ResponderState::Running; 140 } else { 141 self.advertise().await; 142 self.state = ResponderState::Announcing(ts + Duration::from_millis(1000), number_sent + 1); 143 } 144 } 145 }, 146 ResponderState::Running => (), 147 ResponderState::ProbeFailed => (), 148 } 149 } 150 151 async fn send_message(&self, message: Message<'_, '_>, endpoint: IpEndpoint) { 152 let mut data_buf = [0; 4096]; 153 // Serialize the message into the buffer 154 assert!(message.space_needed() <= data_buf.len()); 155 156 match message.write(&mut data_buf) { 157 Result::Ok(len) => { 158 if let Result::Err(x) = self.sock.send_to(&data_buf[..len], endpoint).await { 159 info!("sock.send failed: {:?}", x); 160 } 161 }, 162 Result::Err(err) => { 163 error!("message.write {:?}", err); 164 } 165 }; 166 } 167 168 async fn send_probe(&self) { 169 info!("sending probe at {}", Instant::now().as_millis()); 170 171 let db = &self.database; 172 173 let mut questions : Vec<Question> = db.advertised().iter().filter_map( |record| { 174 // FIXME this sends duplicates because there's both an SRV 175 // and a TXT for the same name 176 if record.is_unique() { 177 Some( 178 Question::new( 179 &record.name[..], 180 ResourceType::Wildcard, 181 1 | (1<<15) // QU 182 )) 183 } else { 184 None 185 } 186 }).collect(); 187 188 let message = Message::new( 189 0, Flags::standard_query(), &mut questions, &mut [], &mut [], &mut [] 190 ); 191 192 self.send_message(message, MDNS_ENDPOINT).await; 193 } 194 195 async fn advertise(&self) { 196 info!("advertise at {}", Instant::now().as_millis()); 197 self.send_response(0, MDNS_ENDPOINT, None, self.database.advertised()).await; 198 } 199 200 pub async fn may_recv(&mut self) -> bool { 201 self.advance_state().await; 202 match self.state { 203 ResponderState::ProbeFailed => false, 204 _ => self.sock.may_recv() 205 } 206 } 207 208 async fn send_response(&self, 209 id: u16, 210 endpoint: IpEndpoint, 211 question: Option<&Question<'_>>, 212 answers: Vec<&Resource>) { 213 214 let legacy = question != None; 215 216 let db = &self.database; 217 let mut flags = Flags::new(); 218 219 flags.set_qr(MessageType::Reply); 220 flags.set_authoritative(true); 221 222 let mut additional : BTreeSet<ResourceRecord> = BTreeSet::new(); 223 let mut answers : Vec<ResourceRecord> = { 224 let mut rrs = Vec::new(); 225 for a in answers { 226 rrs.push(a.to_resource_record()); 227 for r in db.all_additional(a).into_iter() { 228 additional.insert(r.to_resource_record()); 229 }; 230 } 231 rrs 232 }; 233 234 // don't send anything in "additional" that's already in the answers 235 for rr in &answers { 236 additional.remove(&rr); 237 } 238 239 let mut additional : Vec<ResourceRecord> = additional.into_iter().collect(); 240 241 let questions = match question { 242 Some(q) =>&mut [*q][..], 243 None => &mut [][..] 244 }; 245 246 if legacy { 247 // force class = IN (no cache-flush) and ttl to 10 seconds 248 answers = answers.iter().map(|rr| { 249 ResourceRecord::new(rr.name(), rr.ty(), 1, 10, rr.data()) 250 }).collect(); 251 additional = additional.iter().map(|rr| { 252 ResourceRecord::new(rr.name(), rr.ty(), 1, 10, rr.data()) 253 }).collect(); 254 } 255 256 let message = Message::new( 257 id, 258 flags, 259 questions, 260 &mut answers, 261 &mut [], 262 &mut additional); 263 264 self.send_message(message, endpoint).await; 265 } 266 267 async fn process_received_message(&mut self, 268 message: &Message<'_,'_>, 269 meta: &UdpMetadata) { 270 271 match self.state { 272 ResponderState::Probing(_,_) => { 273 for rr in message.answers() { 274 info!("received answer {:?}", rr.name()); 275 if self.database.find(rr.name().to_string(), ResourceType::Wildcard) 276 .iter().any(|record| record.is_unique()) { 277 info!("probe failed, received answer for {:?}", rr.name()); 278 self.state = ResponderState::ProbeFailed; 279 } 280 } 281 }, 282 ResponderState::ProbeFailed => (), 283 _ => { 284 let q_id = message.id(); 285 286 for q in message.questions() { 287 let relevant = self.database.find(q.name().to_string(), q.ty()); 288 if relevant.len() > 0 { 289 log::info!("question {:?} {:?} {:?} flags {:?} from {:?}", 290 q.name(), q.ty(), q.class(), 291 message.flags().raw(), 292 meta.endpoint); 293 294 let unicast = (q.class() & (1<<15)) != 0; 295 let legacy = meta.endpoint.port != 5353; 296 if legacy { 297 self.send_response(q_id, meta.endpoint, Some(q), relevant).await 298 } else if unicast { 299 self.send_response(0, meta.endpoint, None, relevant).await 300 } else { 301 self.send_response(0, MDNS_ENDPOINT, None, relevant).await 302 } 303 } 304 } 305 } 306 } 307 } 308 309 pub async fn process_packet(&mut self) { 310 let mut questions = [Question::default(); 64]; 311 let mut answers = [ResourceRecord::default(); 64]; 312 let mut authorities = [ResourceRecord::default(); 64]; 313 let mut addl = [ResourceRecord::default(); 64]; 314 315 let mut data_buf = [0; 4096]; 316 317 let res = self.sock.recv_from(&mut data_buf).await; 318 if let Ok((n, meta)) = res { 319 let message = Message::read( 320 &data_buf[..n], 321 &mut questions, 322 &mut answers, 323 &mut authorities, 324 &mut addl, 325 ); 326 327 match message { 328 Result::Ok(message) => { 329 self.process_received_message(&message, &meta).await; 330 }, 331 Result::Err(err) => { 332 log::error!("parsing dns message from {:?} : {:?}", 333 meta.endpoint, 334 err); 335 } 336 } 337 } 338 } 339 } 340 341 342 // dns_protocol's ResourceRecord doesn't make a good representation for 343 // our needs because it doesn't own its "data" attribute - it's just a 344 // reference. So we need structures that can own the data 345 346 #[derive(Clone,Debug)] 347 pub enum RData { 348 Srv { target: String, port: u16, priority: u16, weight: u16 }, 349 Ptr(String), 350 A([u8; 4]), 351 Txt(Vec<String>) 352 } 353 354 impl RData { 355 fn copy_string<'a>(s: &str, out: &'a mut [u8], offset: usize) -> usize { 356 let end = s.len() + offset + 1; 357 out[offset] = (s.len() & 0xff) as u8; 358 let _ = &out[(offset+1) .. end].clone_from_slice(s.as_bytes()); 359 end 360 } 361 362 fn encode_as_name<'a>(name: &str, out: &'a mut [u8], offset: usize) -> &'a [u8] { 363 let mut offset = offset; 364 365 for l in name.split('.') { 366 offset = Self::copy_string(l, out, offset); 367 } 368 out[offset] = 0; 369 &out[..offset+1] 370 } 371 372 fn encode_as_strings<'a>(strings: &[String], out: &'a mut [u8], offset: usize) -> &'a [u8] { 373 let mut offset = offset; 374 375 for l in strings.into_iter() { 376 offset = Self::copy_string(l, out, offset); 377 } 378 out[offset] = 0; 379 &out[..offset+1] 380 } 381 382 fn ty(&self) -> ResourceType { 383 match self { 384 RData::Srv { .. } => ResourceType::Srv, 385 RData::Ptr(_name) => ResourceType::Ptr, 386 RData::A(_octets) => ResourceType::A, 387 RData::Txt(_strings) => ResourceType::Txt 388 } 389 } 390 391 fn to_bytes<'a>(&self, out: &'a mut [u8]) -> &'a [u8] { 392 match self { 393 RData::Srv { target, port, priority, weight } => { 394 out[0] = (priority >> 8) as u8; 395 out[1] = (priority & 0xff) as u8; 396 out[2] = (weight >> 8) as u8; 397 out[3] = (weight & 0xff) as u8; 398 out[4] = (port >> 8) as u8; 399 out[5] = (port & 0xff) as u8; 400 Self::encode_as_name(target, out, 6) 401 }, 402 RData::Ptr(name) => { 403 Self::encode_as_name(name, out, 0) 404 } 405 RData::A(octets) => { 406 out[..4].clone_from_slice(octets); 407 &out[..4] 408 }, 409 RData::Txt(strings) => { 410 Self::encode_as_strings(strings, out, 0) 411 } 412 } 413 } 414 } 415 416 #[derive(Debug)] 417 pub struct Resource { 418 name : String, 419 data : RData, 420 data_raw : Vec<u8> 421 } 422 423 impl Resource { 424 pub fn new( 425 name: impl Into<String>, 426 data: RData 427 ) -> Self { 428 let mut buf : [u8; 80] = [0; 80]; 429 let rdata_raw = Vec::from(data.to_bytes(&mut buf)); 430 Self { 431 name: name.into(), 432 data, 433 data_raw: rdata_raw 434 } 435 } 436 437 // Should we send cache-flush with this record? If it's "unique", 438 // which means "all the records with that name, rrtype, and 439 // rrclass are conceptually under the control or ownership of a 440 // single responder". This works out (perhaps by coincidence) to 441 // be everything except the PTR records 442 fn is_unique(&self) -> bool { 443 self.data.ty() != ResourceType::Ptr 444 } 445 fn class(&self) -> u16 { 446 if self.is_unique() { 447 1 | CACHE_FLUSH 448 } else { 449 1 450 } 451 } 452 453 fn to_resource_record(&self) -> ResourceRecord<'_> { 454 let ttl = match self.data.ty() { 455 ResourceType::A => 120, 456 ResourceType::Srv => 120, 457 _ => 4500 458 }; 459 460 ResourceRecord::new( 461 &self.name[..], 462 self.data.ty(), 463 self.class(), 464 ttl, 465 &self.data_raw[..] 466 ) 467 } 468 } 469 470 struct Database(Vec<Resource>); 471 472 impl Database { 473 fn find<'a>(&self, name: impl Into<String>, ty: ResourceType) -> Vec<&Resource> { 474 let name = name.into(); 475 let results : Vec<&Resource> = 476 self.0.iter().filter( 477 |r| 478 ((ty == ResourceType::Wildcard) || (r.data.ty() == ty)) && 479 name == r.name 480 ).collect(); 481 results 482 } 483 484 // the records we announce unsolicited at startup 485 fn advertised(&self) -> Vec<&Resource> { 486 self.0.iter().filter( |r| r.name != DISCOVERY_NAME ).collect() 487 } 488 489 fn additional(&self, r: &Resource) -> Vec<&Resource> { 490 match &r.data { 491 RData::Srv { target, port:_, priority:_, weight:_ } => { 492 self.find(target, ResourceType::Wildcard) 493 }, 494 RData::Ptr(name) => { 495 if r.name.eq(DISCOVERY_NAME) { 496 Vec::from([]) 497 } else { 498 self.find(name, ResourceType::Wildcard) 499 } 500 }, 501 _ => Vec::from([]) 502 } 503 } 504 505 fn all_additional_<'a>(&'a self, r: &Resource, collect: &mut Vec<&'a Resource>) { 506 for r in self.additional(r) { 507 collect.push(r); 508 self.all_additional_(r, collect); 509 } 510 } 511 512 fn all_additional(&self, r: &Resource) -> Vec<&Resource> { 513 let mut collect : Vec<&Resource> = Vec::new(); 514 self.all_additional_(r, &mut collect); 515 collect 516 } 517 } 518 519 pub async fn responder(stack: Stack<'_>, records: Vec<Resource>) { 520 let mut rx_buffer = [0u8; 4096]; 521 let mut tx_buffer = [0u8; 4096]; 522 let mut rx_meta = [PacketMetadata::EMPTY; 512]; 523 let mut tx_meta = [PacketMetadata::EMPTY; 512]; 524 525 let mut sock = UdpSocket::new( 526 stack, 527 &mut rx_meta, 528 &mut rx_buffer, 529 &mut tx_meta, 530 &mut tx_buffer, 531 ); 532 sock.set_hop_limit(Some(255)); 533 log::info!("sock.bind(5353) res: {:?}", sock.bind(MDNS_PORT)); 534 log::info!( 535 "multicast_res: {:?}", 536 stack.join_multicast_group(MDNS_IP) 537 ); 538 539 let mut mdns_responder = Responder::new(&mut sock, records); 540 541 loop { 542 match mdns_responder.state { 543 ResponderState::ProbeFailed => { 544 sock.close(); 545 _ = stack.leave_multicast_group(MDNS_IP); 546 return ; 547 }, 548 _ => { 549 if mdns_responder.may_recv().await { 550 mdns_responder.process_packet().await; 551 } else { 552 Timer::after(Duration::from_millis(50)).await; 553 } 554 } 555 } 556 } 557 }