1 // Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com> 2 // 3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5 // http://opensource.org/licenses/MIT>, at your option. This file may not be 6 // copied, modified, or distributed except according to those terms. 7 use std::iter::Chain; 8 use std::slice::Iter; 9 use std::vec; 10 11 use log::info; 12 13 use crate::rr::{DNSClass, Name, RData, Record, RecordType}; 14 15 #[cfg(feature = "dnssec")] 16 use crate::rr::dnssec::SupportedAlgorithms; 17 18 /// Set of resource records associated to a name and type 19 #[derive(Clone, Debug, PartialEq)] 20 pub struct RecordSet { 21 name: Name, 22 record_type: RecordType, 23 dns_class: DNSClass, 24 ttl: u32, 25 records: Vec<Record>, 26 rrsigs: Vec<Record>, 27 serial: u32, // serial number at which this record was modified 28 } 29 30 impl RecordSet { 31 /// Creates a new Resource Record Set. 32 /// 33 /// # Arguments 34 /// 35 /// * `name` - The label for the `RecordSet` 36 /// * `record_type` - `RecordType` of this `RecordSet`, all records in the `RecordSet` must be of the 37 /// specified `RecordType`. 38 /// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and 39 /// signing for DNSSec after updates. 40 /// 41 /// # Return value 42 /// 43 /// The newly created Resource Record Set 44 /// TODO: make all cloned params pass by value new(name: &Name, record_type: RecordType, serial: u32) -> Self45 pub fn new(name: &Name, record_type: RecordType, serial: u32) -> Self { 46 RecordSet { 47 name: name.clone(), 48 record_type, 49 dns_class: DNSClass::IN, 50 ttl: 0, 51 records: Vec::new(), 52 rrsigs: Vec::new(), 53 serial, 54 } 55 } 56 57 /// Creates a new Resource Record Set. 58 /// 59 /// # Arguments 60 /// 61 /// * `name` - The label for the `RecordSet` 62 /// * `record_type` - `RecordType` of this `RecordSet`, all records in the `RecordSet` must be of the 63 /// specified `RecordType`. 64 /// * `ttl` - time-to-live for the `RecordSet` in seconds. 65 /// 66 /// # Return value 67 /// 68 /// The newly created Resource Record Set 69 /// TODO: make all cloned params pass by value with_ttl(name: Name, record_type: RecordType, ttl: u32) -> Self70 pub fn with_ttl(name: Name, record_type: RecordType, ttl: u32) -> Self { 71 RecordSet { 72 name, 73 record_type, 74 dns_class: DNSClass::IN, 75 ttl, 76 records: Vec::new(), 77 rrsigs: Vec::new(), 78 serial: 0, 79 } 80 } 81 82 /// # Return value 83 /// 84 /// Label of the Resource Record Set name(&self) -> &Name85 pub fn name(&self) -> &Name { 86 &self.name 87 } 88 89 /// # Return value 90 /// 91 /// `RecordType` of the Resource Record Set record_type(&self) -> RecordType92 pub fn record_type(&self) -> RecordType { 93 self.record_type 94 } 95 96 /// Sets the DNSClass to the specified value 97 /// 98 /// This will traverse every record and associate with it the specified dns_class set_dns_class(&mut self, dns_class: DNSClass)99 pub fn set_dns_class(&mut self, dns_class: DNSClass) { 100 self.dns_class = dns_class; 101 for r in &mut self.records { 102 r.set_dns_class(dns_class); 103 } 104 } 105 106 /// Returns the `DNSClass` of the RecordSet dns_class(&self) -> DNSClass107 pub fn dns_class(&self) -> DNSClass { 108 self.dns_class 109 } 110 111 /// Sets the TTL, in seconds, to the specified value 112 /// 113 /// This will traverse every record and associate with it the specified ttl set_ttl(&mut self, ttl: u32)114 pub fn set_ttl(&mut self, ttl: u32) { 115 self.ttl = ttl; 116 for r in &mut self.records { 117 r.set_ttl(ttl); 118 } 119 } 120 121 /// Returns the time-to-live for the record. 122 /// 123 /// # Return value 124 /// 125 /// TTL, time-to-live, of the Resource Record Set, this is the maximum length of time that an 126 /// RecordSet should be cached. ttl(&self) -> u32127 pub fn ttl(&self) -> u32 { 128 self.ttl 129 } 130 131 /// Returns a Vec of all records in the set. 132 /// 133 /// # Arguments 134 /// 135 /// * `and_rrsigs` - if true, RRSIGs will be returned if they exist 136 /// * `supported_algorithms` - the RRSIGs will be filtered by the set of supported_algorithms, 137 /// and then only the maximal RRSIG algorithm will be returned. 138 #[cfg(feature = "dnssec")] records( &self, and_rrsigs: bool, supported_algorithms: SupportedAlgorithms, ) -> RrsetRecords139 pub fn records( 140 &self, 141 and_rrsigs: bool, 142 supported_algorithms: SupportedAlgorithms, 143 ) -> RrsetRecords { 144 if and_rrsigs { 145 self.records_with_rrsigs(supported_algorithms) 146 } else { 147 self.records_without_rrsigs() 148 } 149 } 150 151 /// Returns a Vec of all records in the set, with RRSIGs, if present. 152 /// 153 /// # Arguments 154 /// 155 /// * `supported_algorithms` - the RRSIGs will be filtered by the set of supported_algorithms, 156 /// and then only the maximal RRSIG algorithm will be returned. 157 #[cfg(feature = "dnssec")] records_with_rrsigs(&self, supported_algorithms: SupportedAlgorithms) -> RrsetRecords158 pub fn records_with_rrsigs(&self, supported_algorithms: SupportedAlgorithms) -> RrsetRecords { 159 if self.records.is_empty() { 160 RrsetRecords::Empty 161 } else { 162 let rrsigs = RrsigsByAlgorithms { 163 rrsigs: self.rrsigs.iter(), 164 supported_algorithms, 165 }; 166 RrsetRecords::RecordsAndRrsigs(RecordsAndRrsigsIter(self.records.iter().chain(rrsigs))) 167 } 168 } 169 170 /// Returns a Vec of all records in the set, without any RRSIGs. records_without_rrsigs(&self) -> RrsetRecords171 pub fn records_without_rrsigs(&self) -> RrsetRecords { 172 if self.records.is_empty() { 173 RrsetRecords::Empty 174 } else { 175 RrsetRecords::RecordsOnly(self.records.iter()) 176 } 177 } 178 179 /// Returns an iterator over the records in the set 180 #[deprecated(note = "see `records_without_rrsigs`")] iter(&self) -> Iter<Record>181 pub fn iter(&self) -> Iter<Record> { 182 self.records.iter() 183 } 184 185 /// Returns true if there are no records in this set is_empty(&self) -> bool186 pub fn is_empty(&self) -> bool { 187 self.records.is_empty() 188 } 189 190 /// Returns the serial number at which the record was updated. serial(&self) -> u32191 pub fn serial(&self) -> u32 { 192 self.serial 193 } 194 195 /// Returns a slice of all the Records signatures in the RecordSet rrsigs(&self) -> &[Record]196 pub fn rrsigs(&self) -> &[Record] { 197 &self.rrsigs 198 } 199 200 /// Inserts a Signature for the Record set 201 /// 202 /// Many can be associated with the RecordSet. Once added, the RecordSet should not be changed 203 /// 204 /// # Arguments 205 /// 206 /// * `rrsig` - A signature which covers the RecordSet. insert_rrsig(&mut self, rrsig: Record)207 pub fn insert_rrsig(&mut self, rrsig: Record) { 208 self.rrsigs.push(rrsig) 209 } 210 211 /// Useful for clearing all signatures when the RecordSet is updated, or keys are rotated. clear_rrsigs(&mut self)212 pub fn clear_rrsigs(&mut self) { 213 self.rrsigs.clear() 214 } 215 updated(&mut self, serial: u32)216 fn updated(&mut self, serial: u32) { 217 self.serial = serial; 218 self.rrsigs.clear(); // on updates, the rrsigs are invalid 219 } 220 221 /// creates a new Record as part of this RecordSet, adding the associated RData 222 /// 223 /// this interface may be deprecated in the future. new_record(&mut self, rdata: &RData) -> &Record224 pub fn new_record(&mut self, rdata: &RData) -> &Record { 225 self.add_rdata(rdata.clone()); 226 227 self.records 228 .iter() 229 .find(|r| r.rdata() == rdata) 230 .expect("insert failed") 231 } 232 233 /// creates a new Record as part of this RecordSet, adding the associated RData add_rdata(&mut self, rdata: RData) -> bool234 pub fn add_rdata(&mut self, rdata: RData) -> bool { 235 debug_assert_eq!(self.record_type, rdata.to_record_type()); 236 237 let mut record = Record::with(self.name.clone(), self.record_type, self.ttl); 238 record.set_rdata(rdata); 239 self.insert(record, 0) 240 } 241 242 /// Inserts a new Resource Record into the Set. 243 /// 244 /// If the record is inserted, the ttl for the most recent record will be used for the ttl of 245 /// the entire resource record set. 246 /// 247 /// This abides by the following restrictions in RFC 2136, April 1997: 248 /// 249 /// ```text 250 /// 1.1.5. The following RR types cannot be appended to an RRset. If the 251 /// following comparison rules are met, then an attempt to add the new RR 252 /// will result in the replacement of the previous RR: 253 /// 254 /// SOA compare only NAME, CLASS and TYPE -- it is not possible to 255 /// have more than one SOA per zone, even if any of the data 256 /// fields differ. 257 /// 258 /// CNAME compare only NAME, CLASS, and TYPE -- it is not possible 259 /// to have more than one CNAME RR, even if their data fields 260 /// differ. 261 /// ``` 262 /// 263 /// # Arguments 264 /// 265 /// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`. 266 /// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and 267 /// signing for DNSSec after updates. The serial will only be updated if the 268 /// record was added. 269 /// 270 /// # Return value 271 /// 272 /// True if the record was inserted. 273 /// 274 /// TODO: make a default add without serial number for basic usage insert(&mut self, record: Record, serial: u32) -> bool275 pub fn insert(&mut self, record: Record, serial: u32) -> bool { 276 assert_eq!(record.name(), &self.name); 277 assert_eq!(record.rr_type(), self.record_type); 278 279 // RFC 2136 DNS Update April 1997 280 // 281 // 1.1.5. The following RR types cannot be appended to an RRset. If the 282 // following comparison rules are met, then an attempt to add the new RR 283 // will result in the replacement of the previous RR: 284 match record.rr_type() { 285 // SOA compare only NAME, CLASS and TYPE -- it is not possible to 286 // have more than one SOA per zone, even if any of the data 287 // fields differ. 288 RecordType::SOA => { 289 assert!(self.records.len() <= 1); 290 291 if let Some(soa_record) = self.records.iter().next() { 292 match soa_record.rdata() { 293 &RData::SOA(ref existing_soa) => { 294 if let RData::SOA(ref new_soa) = *record.rdata() { 295 if new_soa.serial() <= existing_soa.serial() { 296 info!( 297 "update ignored serial out of data: {:?} <= {:?}", 298 new_soa, existing_soa 299 ); 300 return false; 301 } 302 } else { 303 // not panicking here, b/c this is a bad record from the client or something, ignore 304 info!("wrong rdata for SOA update: {:?}", record.rdata()); 305 return false; 306 } 307 } 308 rdata => panic!("wrong rdata: {:?}", rdata), // valid panic, never should happen 309 } 310 } 311 312 // if we got here, we're updating... 313 self.records.clear(); 314 } 315 // RFC 1034/1035 316 // CNAME compare only NAME, CLASS, and TYPE -- it is not possible 317 // to have more than one CNAME RR, even if their data fields 318 // differ. 319 // 320 // ANAME https://tools.ietf.org/html/draft-ietf-dnsop-aname-02 321 // 2.2. Coexistence with other types 322 // 323 // Only one ANAME <target> can be defined per <owner>. An ANAME RRset 324 // MUST NOT contain more than one resource record. 325 // 326 // An ANAME's sibling address records are under the control of ANAME 327 // processing (see Section 5) and are not first-class records in their 328 // own right. They MAY exist in zone files, but they can subsequently 329 // be altered by ANAME processing. 330 // 331 // ANAME records MAY freely coexist at the same owner name with other RR 332 // types, except they MUST NOT coexist with CNAME or any other RR type 333 // that restricts the types with which it can itself coexist. 334 // 335 // Like other types, ANAME records can coexist with DNAME records at the 336 // same owner name; in fact, the two can be used cooperatively to 337 // redirect both the owner name address records (via ANAME) and 338 // everything under it (via DNAME). 339 RecordType::CNAME | RecordType::ANAME => { 340 assert!(self.records.len() <= 1); 341 self.records.clear(); 342 } 343 _ => (), 344 } 345 346 // collect any records to update based on rdata 347 let to_replace: Vec<usize> = self 348 .records 349 .iter() 350 .enumerate() 351 .filter(|&(_, rr)| rr.rdata() == record.rdata()) 352 .map(|(i, _)| i) 353 .collect::<Vec<usize>>(); 354 355 // if the Records are identical, ignore the update, update all that are not (ttl, etc.) 356 let mut replaced = false; 357 for i in to_replace { 358 if self.records[i] == record { 359 return false; 360 } 361 362 // TODO: this shouldn't really need a clone since there should only be one... 363 self.records.push(record.clone()); 364 self.records.swap_remove(i); 365 self.ttl = record.ttl(); 366 self.updated(serial); 367 replaced = true; 368 } 369 370 if !replaced { 371 self.ttl = record.ttl(); 372 self.updated(serial); 373 self.records.push(record); 374 true 375 } else { 376 replaced 377 } 378 } 379 380 /// Removes the Resource Record if it exists. 381 /// 382 /// # Arguments 383 /// 384 /// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`. Removes 385 /// any `record` if the record data, `RData`, match. 386 /// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and 387 /// signing for DNSSec after updates. The serial will only be updated if the 388 /// record was added. 389 /// 390 /// # Return value 391 /// 392 /// True if a record was removed. remove(&mut self, record: &Record, serial: u32) -> bool393 pub fn remove(&mut self, record: &Record, serial: u32) -> bool { 394 assert_eq!(record.name(), &self.name); 395 assert!(record.rr_type() == self.record_type || record.rr_type() == RecordType::ANY); 396 397 match record.rr_type() { 398 // never delete the last NS record 399 RecordType::NS => { 400 if self.records.len() <= 1 { 401 info!("ignoring delete of last NS record: {:?}", record); 402 return false; 403 } 404 } 405 // never delete SOA 406 RecordType::SOA => { 407 info!("ignored delete of SOA"); 408 return false; 409 } 410 _ => (), // move on to the delete 411 } 412 413 // remove the records, first collect all the indexes, then remove the records 414 let to_remove: Vec<usize> = self 415 .records 416 .iter() 417 .enumerate() 418 .filter(|&(_, rr)| rr.rdata() == record.rdata()) 419 .map(|(i, _)| i) 420 .collect::<Vec<usize>>(); 421 422 let mut removed = false; 423 for i in to_remove { 424 self.records.remove(i); 425 removed = true; 426 self.updated(serial); 427 } 428 429 removed 430 } 431 } 432 433 impl From<Record> for RecordSet { from(record: Record) -> Self434 fn from(record: Record) -> Self { 435 RecordSet { 436 name: record.name().clone(), 437 record_type: record.rr_type(), 438 dns_class: record.dns_class(), 439 ttl: record.ttl(), 440 records: vec![record], 441 rrsigs: vec![], 442 serial: 0, 443 } 444 } 445 } 446 447 /// Types which implement this can be converted into a RecordSet 448 #[deprecated(note = "use From/Into")] 449 pub trait IntoRecordSet: Sized { 450 /// Performs the conversion to a RecordSet into_record_set(self) -> RecordSet451 fn into_record_set(self) -> RecordSet; 452 } 453 454 #[allow(deprecated)] 455 impl IntoRecordSet for RecordSet { into_record_set(self) -> Self456 fn into_record_set(self) -> Self { 457 self 458 } 459 } 460 461 impl IntoIterator for RecordSet { 462 type Item = Record; 463 type IntoIter = Chain<vec::IntoIter<Record>, vec::IntoIter<Record>>; 464 into_iter(self) -> Self::IntoIter465 fn into_iter(self) -> Self::IntoIter { 466 self.records.into_iter().chain(self.rrsigs.into_iter()) 467 } 468 } 469 470 /// An iterator over all the records and their signatures 471 #[cfg(feature = "dnssec")] 472 #[derive(Debug)] 473 pub struct RecordsAndRrsigsIter<'r>(Chain<Iter<'r, Record>, RrsigsByAlgorithms<'r>>); 474 475 #[cfg(feature = "dnssec")] 476 impl<'r> Iterator for RecordsAndRrsigsIter<'r> { 477 type Item = &'r Record; 478 next(&mut self) -> Option<Self::Item>479 fn next(&mut self) -> Option<Self::Item> { 480 self.0.next() 481 } 482 } 483 484 /// An iterator that limits the record signatures by SupportedAlgorithms 485 #[cfg(feature = "dnssec")] 486 #[derive(Debug)] 487 pub struct RrsigsByAlgorithms<'r> { 488 rrsigs: Iter<'r, Record>, 489 supported_algorithms: SupportedAlgorithms, 490 } 491 492 #[cfg(feature = "dnssec")] 493 impl<'r> Iterator for RrsigsByAlgorithms<'r> { 494 type Item = &'r Record; 495 next(&mut self) -> Option<Self::Item>496 fn next(&mut self) -> Option<Self::Item> { 497 use crate::rr::dnssec::rdata::DNSSECRData; 498 use crate::rr::dnssec::Algorithm; 499 500 let supported_algorithms = self.supported_algorithms; 501 502 // disable rfc 6975 when no supported_algorithms specified 503 if supported_algorithms.is_empty() { 504 self.rrsigs.next() 505 } else { 506 self.rrsigs 507 .by_ref() 508 .filter(|record| { 509 if let RData::DNSSEC(DNSSECRData::SIG(ref rrsig)) = *record.rdata() { 510 supported_algorithms.has(rrsig.algorithm()) 511 } else { 512 false 513 } 514 }) 515 .max_by_key(|record| { 516 if let RData::DNSSEC(DNSSECRData::SIG(ref rrsig)) = *record.rdata() { 517 rrsig.algorithm() 518 } else { 519 Algorithm::RSASHA1 520 } 521 }) 522 } 523 } 524 } 525 526 /// An iterator over the RecordSet data 527 #[derive(Debug)] 528 pub enum RrsetRecords<'r> { 529 /// There are no records in the record set 530 Empty, 531 /// The records associated with the record set 532 RecordsOnly(Iter<'r, Record>), 533 /// The records along with their signatures in the record set 534 #[cfg(feature = "dnssec")] 535 RecordsAndRrsigs(RecordsAndRrsigsIter<'r>), 536 } 537 538 impl<'r> RrsetRecords<'r> { 539 /// This is a best effort emptyness check is_empty(&self) -> bool540 pub fn is_empty(&self) -> bool { 541 match *self { 542 RrsetRecords::Empty => true, 543 _ => false, 544 } 545 } 546 } 547 548 impl<'r> Iterator for RrsetRecords<'r> { 549 type Item = &'r Record; 550 next(&mut self) -> Option<Self::Item>551 fn next(&mut self) -> Option<Self::Item> { 552 match self { 553 RrsetRecords::Empty => None, 554 RrsetRecords::RecordsOnly(i) => i.next(), 555 #[cfg(feature = "dnssec")] 556 RrsetRecords::RecordsAndRrsigs(i) => i.next(), 557 } 558 } 559 } 560 561 #[cfg(test)] 562 mod test { 563 use std::net::Ipv4Addr; 564 use std::str::FromStr; 565 566 use crate::rr::rdata::SOA; 567 use crate::rr::*; 568 569 #[test] test_insert()570 fn test_insert() { 571 let name = Name::from_str("www.example.com.").unwrap(); 572 let record_type = RecordType::A; 573 let mut rr_set = RecordSet::new(&name, record_type, 0); 574 575 let insert = Record::new() 576 .set_name(name.clone()) 577 .set_ttl(86400) 578 .set_rr_type(record_type) 579 .set_dns_class(DNSClass::IN) 580 .set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 24))) 581 .clone(); 582 583 assert!(rr_set.insert(insert.clone(), 0)); 584 assert_eq!(rr_set.records_without_rrsigs().count(), 1); 585 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 586 587 // dups ignored 588 assert!(!rr_set.insert(insert.clone(), 0)); 589 assert_eq!(rr_set.records_without_rrsigs().count(), 1); 590 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 591 592 // add one 593 let insert1 = Record::new() 594 .set_name(name) 595 .set_ttl(86400) 596 .set_rr_type(record_type) 597 .set_dns_class(DNSClass::IN) 598 .set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 25))) 599 .clone(); 600 assert!(rr_set.insert(insert1.clone(), 0)); 601 assert_eq!(rr_set.records_without_rrsigs().count(), 2); 602 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 603 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert1)); 604 } 605 606 #[test] 607 #[allow(clippy::unreadable_literal)] test_insert_soa()608 fn test_insert_soa() { 609 let name = Name::from_str("example.com.").unwrap(); 610 let record_type = RecordType::SOA; 611 let mut rr_set = RecordSet::new(&name, record_type, 0); 612 613 let insert = Record::new() 614 .set_name(name.clone()) 615 .set_ttl(3600) 616 .set_rr_type(RecordType::SOA) 617 .set_dns_class(DNSClass::IN) 618 .set_rdata(RData::SOA(SOA::new( 619 Name::from_str("sns.dns.icann.org.").unwrap(), 620 Name::from_str("noc.dns.icann.org.").unwrap(), 621 2015082403, 622 7200, 623 3600, 624 1209600, 625 3600, 626 ))) 627 .clone(); 628 let same_serial = Record::new() 629 .set_name(name.clone()) 630 .set_ttl(3600) 631 .set_rr_type(RecordType::SOA) 632 .set_dns_class(DNSClass::IN) 633 .set_rdata(RData::SOA(SOA::new( 634 Name::from_str("sns.dns.icann.net.").unwrap(), 635 Name::from_str("noc.dns.icann.net.").unwrap(), 636 2015082403, 637 7200, 638 3600, 639 1209600, 640 3600, 641 ))) 642 .clone(); 643 let new_serial = Record::new() 644 .set_name(name) 645 .set_ttl(3600) 646 .set_rr_type(RecordType::SOA) 647 .set_dns_class(DNSClass::IN) 648 .set_rdata(RData::SOA(SOA::new( 649 Name::from_str("sns.dns.icann.net.").unwrap(), 650 Name::from_str("noc.dns.icann.net.").unwrap(), 651 2015082404, 652 7200, 653 3600, 654 1209600, 655 3600, 656 ))) 657 .clone(); 658 659 assert!(rr_set.insert(insert.clone(), 0)); 660 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 661 // same serial number 662 assert!(!rr_set.insert(same_serial.clone(), 0)); 663 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 664 assert!(!rr_set 665 .records_without_rrsigs() 666 .any(|ref x| x == &&same_serial)); 667 668 assert!(rr_set.insert(new_serial.clone(), 0)); 669 assert!(!rr_set.insert(same_serial.clone(), 0)); 670 assert!(!rr_set.insert(insert.clone(), 0)); 671 672 assert!(rr_set 673 .records_without_rrsigs() 674 .any(|ref x| x == &&new_serial)); 675 assert!(!rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 676 assert!(!rr_set 677 .records_without_rrsigs() 678 .any(|ref x| x == &&same_serial)); 679 } 680 681 #[test] test_insert_cname()682 fn test_insert_cname() { 683 let name = Name::from_str("web.example.com.").unwrap(); 684 let cname = Name::from_str("www.example.com.").unwrap(); 685 let new_cname = Name::from_str("w2.example.com.").unwrap(); 686 687 let record_type = RecordType::CNAME; 688 let mut rr_set = RecordSet::new(&name, record_type, 0); 689 690 let insert = Record::new() 691 .set_name(name.clone()) 692 .set_ttl(3600) 693 .set_rr_type(RecordType::CNAME) 694 .set_dns_class(DNSClass::IN) 695 .set_rdata(RData::CNAME(cname)) 696 .clone(); 697 let new_record = Record::new() 698 .set_name(name) 699 .set_ttl(3600) 700 .set_rr_type(RecordType::CNAME) 701 .set_dns_class(DNSClass::IN) 702 .set_rdata(RData::CNAME(new_cname)) 703 .clone(); 704 705 assert!(rr_set.insert(insert.clone(), 0)); 706 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 707 708 // update the record 709 assert!(rr_set.insert(new_record.clone(), 0)); 710 assert!(!rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 711 assert!(rr_set 712 .records_without_rrsigs() 713 .any(|ref x| x == &&new_record)); 714 } 715 716 #[test] test_remove()717 fn test_remove() { 718 let name = Name::from_str("www.example.com.").unwrap(); 719 let record_type = RecordType::A; 720 let mut rr_set = RecordSet::new(&name, record_type, 0); 721 722 let insert = Record::new() 723 .set_name(name.clone()) 724 .set_ttl(86400) 725 .set_rr_type(record_type) 726 .set_dns_class(DNSClass::IN) 727 .set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 24))) 728 .clone(); 729 let insert1 = Record::new() 730 .set_name(name) 731 .set_ttl(86400) 732 .set_rr_type(record_type) 733 .set_dns_class(DNSClass::IN) 734 .set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 25))) 735 .clone(); 736 737 assert!(rr_set.insert(insert.clone(), 0)); 738 assert!(rr_set.insert(insert1.clone(), 0)); 739 740 assert!(rr_set.remove(&insert, 0)); 741 assert!(!rr_set.remove(&insert, 0)); 742 assert!(rr_set.remove(&insert1, 0)); 743 assert!(!rr_set.remove(&insert1, 0)); 744 } 745 746 #[test] 747 #[allow(clippy::unreadable_literal)] test_remove_soa()748 fn test_remove_soa() { 749 let name = Name::from_str("www.example.com.").unwrap(); 750 let record_type = RecordType::SOA; 751 let mut rr_set = RecordSet::new(&name, record_type, 0); 752 753 let insert = Record::new() 754 .set_name(name) 755 .set_ttl(3600) 756 .set_rr_type(RecordType::SOA) 757 .set_dns_class(DNSClass::IN) 758 .set_rdata(RData::SOA(SOA::new( 759 Name::from_str("sns.dns.icann.org.").unwrap(), 760 Name::from_str("noc.dns.icann.org.").unwrap(), 761 2015082403, 762 7200, 763 3600, 764 1209600, 765 3600, 766 ))) 767 .clone(); 768 769 assert!(rr_set.insert(insert.clone(), 0)); 770 assert!(!rr_set.remove(&insert, 0)); 771 assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert)); 772 } 773 774 #[test] test_remove_ns()775 fn test_remove_ns() { 776 let name = Name::from_str("example.com.").unwrap(); 777 let record_type = RecordType::NS; 778 let mut rr_set = RecordSet::new(&name, record_type, 0); 779 780 let ns1 = Record::new() 781 .set_name(name.clone()) 782 .set_ttl(86400) 783 .set_rr_type(RecordType::NS) 784 .set_dns_class(DNSClass::IN) 785 .set_rdata(RData::NS(Name::from_str("a.iana-servers.net.").unwrap())) 786 .clone(); 787 let ns2 = Record::new() 788 .set_name(name) 789 .set_ttl(86400) 790 .set_rr_type(RecordType::NS) 791 .set_dns_class(DNSClass::IN) 792 .set_rdata(RData::NS(Name::from_str("b.iana-servers.net.").unwrap())) 793 .clone(); 794 795 assert!(rr_set.insert(ns1.clone(), 0)); 796 assert!(rr_set.insert(ns2.clone(), 0)); 797 798 // ok to remove one, but not two... 799 assert!(rr_set.remove(&ns1, 0)); 800 assert!(!rr_set.remove(&ns2, 0)); 801 802 // check that we can swap which ones are removed 803 assert!(rr_set.insert(ns1.clone(), 0)); 804 805 assert!(rr_set.remove(&ns2, 0)); 806 assert!(!rr_set.remove(&ns1, 0)); 807 } 808 809 #[test] 810 #[cfg(feature = "dnssec")] // This tests RFC 6975, a DNSSEC-specific feature. 811 #[allow(clippy::block_in_if_condition_stmt)] test_get_filter()812 fn test_get_filter() { 813 use crate::rr::dnssec::rdata::SIG; 814 use crate::rr::dnssec::rdata::{DNSSECRData, DNSSECRecordType}; 815 use crate::rr::dnssec::{Algorithm, SupportedAlgorithms}; 816 817 let name = Name::root(); 818 let rsasha256 = SIG::new( 819 RecordType::A, 820 Algorithm::RSASHA256, 821 0, 822 0, 823 0, 824 0, 825 0, 826 Name::root(), 827 vec![], 828 ); 829 let ecp256 = SIG::new( 830 RecordType::A, 831 Algorithm::ECDSAP256SHA256, 832 0, 833 0, 834 0, 835 0, 836 0, 837 Name::root(), 838 vec![], 839 ); 840 let ecp384 = SIG::new( 841 RecordType::A, 842 Algorithm::ECDSAP384SHA384, 843 0, 844 0, 845 0, 846 0, 847 0, 848 Name::root(), 849 vec![], 850 ); 851 let ed25519 = SIG::new( 852 RecordType::A, 853 Algorithm::ED25519, 854 0, 855 0, 856 0, 857 0, 858 0, 859 Name::root(), 860 vec![], 861 ); 862 863 let rrsig_rsa = Record::new() 864 .set_name(name.clone()) 865 .set_ttl(3600) 866 .set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG)) 867 .set_dns_class(DNSClass::IN) 868 .set_rdata(RData::DNSSEC(DNSSECRData::SIG(rsasha256))) 869 .clone(); 870 let rrsig_ecp256 = Record::new() 871 .set_name(name.clone()) 872 .set_ttl(3600) 873 .set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG)) 874 .set_dns_class(DNSClass::IN) 875 .set_rdata(RData::DNSSEC(DNSSECRData::SIG(ecp256))) 876 .clone(); 877 let rrsig_ecp384 = Record::new() 878 .set_name(name.clone()) 879 .set_ttl(3600) 880 .set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG)) 881 .set_dns_class(DNSClass::IN) 882 .set_rdata(RData::DNSSEC(DNSSECRData::SIG(ecp384))) 883 .clone(); 884 let rrsig_ed25519 = Record::new() 885 .set_name(name.clone()) 886 .set_ttl(3600) 887 .set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG)) 888 .set_dns_class(DNSClass::IN) 889 .set_rdata(RData::DNSSEC(DNSSECRData::SIG(ed25519))) 890 .clone(); 891 892 let a = Record::new() 893 .set_name(name) 894 .set_ttl(3600) 895 .set_rr_type(RecordType::A) 896 .set_dns_class(DNSClass::IN) 897 .set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 24))) 898 .clone(); 899 900 let mut rrset = RecordSet::from(a); 901 rrset.insert_rrsig(rrsig_rsa); 902 rrset.insert_rrsig(rrsig_ecp256); 903 rrset.insert_rrsig(rrsig_ecp384); 904 rrset.insert_rrsig(rrsig_ed25519); 905 906 assert!(rrset 907 .records_with_rrsigs(SupportedAlgorithms::all(),) 908 .any( 909 |r| if let RData::DNSSEC(DNSSECRData::SIG(ref sig)) = *r.rdata() { 910 sig.algorithm() == Algorithm::ED25519 911 } else { 912 false 913 }, 914 )); 915 916 let mut supported_algorithms = SupportedAlgorithms::new(); 917 supported_algorithms.set(Algorithm::ECDSAP384SHA384); 918 assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| { 919 if let RData::DNSSEC(DNSSECRData::SIG(ref sig)) = *r.rdata() { 920 sig.algorithm() == Algorithm::ECDSAP384SHA384 921 } else { 922 false 923 } 924 })); 925 926 let mut supported_algorithms = SupportedAlgorithms::new(); 927 supported_algorithms.set(Algorithm::ED25519); 928 assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| { 929 if let RData::DNSSEC(DNSSECRData::SIG(ref sig)) = *r.rdata() { 930 sig.algorithm() == Algorithm::ED25519 931 } else { 932 false 933 } 934 })); 935 } 936 } 937