### Example: Create Start of Authority Record (SOA) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Demonstrates the creation of an SOA record with essential zone authority and timing parameters. ```rust use simple_dns::{Name, rdata::SOA}; let soa = SOA { primary_nameserver: Name::new_unchecked("ns1.example.com"), responsible_person: Name::new_unchecked("admin.example.com"), serial: 2024010101, refresh: 3600, retry: 1800, expire: 604800, minimum: 86400, }; ``` -------------------------------- ### Example: Create CNAME Record Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Demonstrates creating a CNAME record, which represents an alias for a domain name. ```rust use simple_dns::{Name, rdata::CNAME}; let cname = CNAME(Name::new_unchecked("alias.example.com")); ``` -------------------------------- ### Example: Create and Iterate TXT Record Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Demonstrates creating a TXT record by adding strings and then iterating through its parsed key-value attributes. ```rust use simple_dns::rdata::TXT; let mut txt = TXT::new(); txt.add_string("version=1.0").unwrap(); txt.add_string("name=myservice").unwrap(); for (key, value) in txt.iter_raw() { println!("{:?}={:?}", key, value); } ``` -------------------------------- ### Example: Create IPv4 Address Record (A) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Demonstrates how to create an A record from an Ipv4Addr. The address is stored in network byte order. ```rust use simple_dns::rdata::A; use std::net::Ipv4Addr; let a_record = A { address: Ipv4Addr::LOCALHOST.into() }; // address = 2130706433 (0x7F000001 = 127.0.0.1) ``` -------------------------------- ### Discovering Services with IPV6 Source: https://github.com/balliegojr/simple-dns/blob/main/simple-mdns/README.md Demonstrates how to initialize ServiceDiscovery for IPV6 using NetworkScope::V6. This example requires the 'sync' feature to be enabled. ```rust # // This is test is marked as no_run because IPV6 is not available in github actions. # #[cfg(feature = "sync")] { use simple_mdns::sync_discovery::ServiceDiscovery; use simple_mdns::{NetworkScope, InstanceInformation}; use std::str::FromStr; let mut discovery = ServiceDiscovery::new_with_scope( InstanceInformation::new("a".into()), "_mysrv._tcp.local", 60, None, NetworkScope::V6, ).expect(" Service Name"); # } ``` -------------------------------- ### Example: Create Service Record (SRV) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Illustrates how to create an SRV record for service discovery, including priority, weight, port, and target. ```rust use simple_dns::{Name, rdata::SRV}; let srv = SRV { priority: 10, weight: 100, port: 5060, target: Name::new_unchecked("sipserver.example.com"), }; ``` -------------------------------- ### Create a New SimpleMdnsResponder (Sync) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Initializes a new synchronous mDNS responder. Use this to start advertising services. ```rust use simple_mdns::sync_discovery::SimpleMdnsResponder; let mut responder = SimpleMdnsResponder::new(300); ``` -------------------------------- ### Example: Create Mail Exchange Record (MX) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Shows how to construct an MX record with a preference value and the mail server's domain name. ```rust use simple_dns::{Name, rdata::MX}; let mx = MX { preference: 10, exchange: Name::new_unchecked("mail.example.com"), }; ``` -------------------------------- ### Complete Sync Service Discovery and Query Example Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Demonstrates a full usage scenario: advertising a service using `ServiceDiscovery` and then querying for another service's address and port using `OneShotMdnsResolver`. ```rust use simple_mdns::sync_discovery::{ServiceDiscovery, OneShotMdnsResolver}; use simple_mdns::InstanceInformation; fn main() -> Result<(), Box> { // Advertise a service let instance = InstanceInformation::new("MyWebServer".into()) .with_socket_address("192.168.1.100:8080".parse()?) .with_attribute("path".into(), Some("/api".into())); let mut discovery = ServiceDiscovery::new( instance, "_http._tcp.local", 300, )?; println!("Advertising _http._tcp.local"); std::thread::sleep(std::time::Duration::from_secs(10)); discovery.remove_service_from_discovery(); // Query for a different service let resolver = OneShotMdnsResolver::new()?; match resolver.query_service_address_and_port("_printer._tcp.local") { Ok(addr) => println!("Found printer at: {}", addr), Err(e) => println!("Printer not found: {:?}", e), } Ok(()) } ``` -------------------------------- ### Build a DNS Query in a no_std Environment Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md An example of building a DNS query packet within a no_std environment using the 'alloc' feature. It demonstrates creating a packet, adding a question, and serializing it. ```rust #![no_std] extern crate alloc; use alloc::{vec::Vec, string::String}; use simple_dns::{Packet, Question, Name, TYPE, CLASS}; fn build_query() -> Result, simple_dns::SimpleDnsError> { let mut packet = Packet::new_query(42); packet.questions.push(Question::new( Name::new("example.com")?, TYPE::A.into(), CLASS::IN.into(), false, )); packet.build_bytes_vec() } ``` -------------------------------- ### Complete DNS Header Inspection Example Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Demonstrates quick header inspection of a DNS response packet without full parsing, checking ID, flags, RCODE, and record counts. ```Rust use simple_dns::header_buffer; use simple_dns::{PacketFlag, RCODE, OPCODE}; let buffer = b"\x00\x03\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00"; // [ID] [FLAGS] [QDCOUNT] [ANCOUNT] [NSCOUNT] [ARCOUNT] // Quick header inspection without full parsing assert_eq!(header_buffer::id(&buffer[..]).unwrap(), 3); assert!(header_buffer::has_flags(&buffer[..], PacketFlag::RESPONSE).unwrap()); assert_eq!(header_buffer::rcode(&buffer[..]).unwrap(), RCODE::NoError); assert_eq!(header_buffer::questions(&buffer[..]).unwrap(), 1); assert_eq!(header_buffer::answers(&buffer[..]).unwrap(), 1); println!("Valid DNS response with 1 question and 1 answer"); ``` -------------------------------- ### Documentation Structure Overview Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/MANIFEST.md Illustrates the hierarchical organization of the documentation, starting from README.md and branching into detailed API references, types, errors, and advanced usage sections. ```markdown README.md ↓ index.md (quick start) ├→ api-reference/ (detailed APIs) │ ├→ packet.md │ ├→ name.md │ ├→ question-and-resource.md │ ├→ character-string.md │ ├→ header-buffer.md │ └→ service-discovery.md │ ├→ types.md (all enums and types) ├→ errors.md (error handling) └→ advanced-usage.md (optimization, edge cases) ``` -------------------------------- ### NetworkScope Enum Examples Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Demonstrates the usage of the NetworkScope enum to control IPv4 or IPv6 multicast operation for service discovery. ```rust use simple_mdns::NetworkScope; let scope = NetworkScope::V4; // 224.0.0.251:5353 let scope = NetworkScope::V6; // [ff02::fb]:5353 ``` -------------------------------- ### InstanceInformation Constructor Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Creates a new InstanceInformation struct with a given service instance name. This is the starting point for defining a service instance. ```rust use simple_mdns::InstanceInformation; let info = InstanceInformation::new("my-service".into()); ``` -------------------------------- ### Create Async Responder with Scope and Signal Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Initializes an asynchronous mDNS responder with a specified network scope (IPv4 or IPv6) and an optional shutdown signal. The responder starts listening in the background. ```rust use simple_mdns::async_discovery::SimpleMdnsResponder; use simple_mdns::NetworkScope; #[tokio::main] async fn main() { let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>(); let mut responder = SimpleMdnsResponder::new_with_scope(300, NetworkScope::V4, Some(shutdown_rx)); // Start listening (returns immediately in background task) // When ready to stop: shutdown_tx.send(()).expect("Failed to send shutdown signal"); } ``` -------------------------------- ### Define Start of Authority Record (SOA) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Provides authoritative information about a DNS zone, including primary name server, responsible person, and timing details. ```rust pub struct SOA<'a> { pub primary_nameserver: Name<'a>, pub responsible_person: Name<'a>, pub serial: u32, pub refresh: u32, // Seconds pub retry: u32, // Seconds pub expire: u32, // Seconds pub minimum: u32, // TTL in seconds } ``` -------------------------------- ### Graceful DNS Packet Parsing Error Handling Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Provides an example of handling potential errors during DNS packet parsing using a match statement. It shows how to gracefully degrade by retrying on insufficient data or logging and skipping corrupted or other erroneous packets. ```rust use simple_dns::Packet; match Packet::parse(&buffer[..]) { Ok(packet) => process_packet(packet), Err(SimpleDnsError::InsufficientData) => { // Incomplete — buffer and retry buffer.extend_from_slice(&read_more_data()?); } Err(SimpleDnsError::InvalidDnsPacket) => { // Corrupted packet — log and skip eprintln!("Corrupted packet, dropping"); } Err(e) => { // Other errors — log and continue eprintln!("DNS error: {:?}", e); } } ``` -------------------------------- ### Handle DNS Parse Errors Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/README.md Handles potential errors during DNS packet parsing. This example shows how to specifically catch insufficient data errors, which indicate a need to read more bytes. ```rust use simple_dns::{Packet, SimpleDnsError}; match Packet::parse(&buffer[..]) { Ok(packet) => { /* success */ } Err(SimpleDnsError::InsufficientData) => { // Buffer too short, read more data } Err(e) => { eprintln!("Parse failed: {}", e); } } ``` -------------------------------- ### Get Packet ID Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/packet.md Retrieves the DNS transaction ID from the packet. ```rust pub fn id(&self) -> u16 ``` -------------------------------- ### Get Question Count Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Extracts the 16-bit question count from bytes 4-5 of a packet buffer. ```Rust use simple_dns::header_buffer; let buffer = b"\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00"; assert_eq!(header_buffer::questions(&buffer[..]).unwrap(), 2); ``` -------------------------------- ### Get Packet RCODE Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/packet.md Retrieves the response code of the DNS packet. This can be a 12-bit value if EDNS is used. ```rust use simple_dns::{Packet, RCODE}; let packet = Packet::new_reply(1); assert_eq!(packet.rcode(), RCODE::NoError); ``` -------------------------------- ### Get DNS Operation Code (OPCODE) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Retrieves the 4-bit operation code from bytes 2-3 of the packet buffer. ```Rust use simple_dns::{header_buffer, OPCODE}; // Byte [2-3] = 0x0500 (Update opcode) let buffer = b"\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00"; assert_eq!(header_buffer::opcode(&buffer[..]).unwrap(), OPCODE::Update); ``` -------------------------------- ### Get Additional Record Count Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Extracts the 16-bit count of additional records from bytes 10-11 of a packet buffer. ```Rust use simple_dns::header_buffer; let buffer = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03"; assert_eq!(header_buffer::additional_records(&buffer[..]).unwrap(), 3); ``` -------------------------------- ### Get Answer Record Count Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Extracts the 16-bit count of answer records from bytes 6-7 of a packet buffer. ```Rust use simple_dns::header_buffer; let buffer = b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"; assert_eq!(header_buffer::answers(&buffer[..]).unwrap(), 1); ``` -------------------------------- ### ResourceRecord Constructor: new Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/question-and-resource.md Initializes a new ResourceRecord. Specify the domain name, class, time-to-live, and the actual resource data. ```rust pub fn new(name: Name<'a>, class: CLASS, ttl: u32, rdata: RData<'a>) -> Self ``` ```rust use simple_dns::{ResourceRecord, Name, CLASS, rdata::{RData, A}}; let record = ResourceRecord::new( Name::new("example.com").unwrap(), CLASS::IN, 300, RData::A(A { address: 93_142_176_1 }), ); ``` -------------------------------- ### Get DNS Transaction ID Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Extracts the 16-bit DNS transaction ID from the first two bytes of a packet buffer. ```Rust use simple_dns::header_buffer; let buffer = b"\x00\x42\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00"; assert_eq!(header_buffer::id(&buffer[..]).unwrap(), 0x0042); ``` -------------------------------- ### Build Cargo for simple-mdns with Features Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/index.md Shows how to build the simple-mdns crate with specific features enabled, such as both synchronous and asynchronous discovery. ```bash cargo build --features sync,async-tokio # For simple-mdns ``` -------------------------------- ### Question Constructor: new Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/question-and-resource.md Creates a new DNS Question. Use this to specify the domain name, record type, class, and unicast response preference for a DNS query. ```rust pub fn new( qname: Name<'a>, qtype: QTYPE, qclass: QCLASS, unicast_response: bool, ) -> Self ``` ```rust use simple_dns::{Question, Name, TYPE, CLASS, QTYPE, QCLASS}; let question = Question::new( Name::new("example.com").unwrap(), TYPE::A.into(), CLASS::IN.into(), false, ); ``` -------------------------------- ### Efficiently Build Packets in Batches Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Illustrates the efficient method of building a DNS packet once and adding multiple records, contrasting it with the inefficient approach of creating and building a new packet for each record addition. ```rust use simple_dns::Packet; // ✓ Efficient: Build once let mut packet = Packet::new_reply(1); for i in 0..100 { packet.answers.push(/* record */); } let bytes = packet.build_bytes_vec_compressed()?; // ❌ Inefficient: Rebuild on each addition let mut bytes = Vec::new(); for i in 0..100 { let mut p = Packet::new_reply(1); p.answers.push(/* record */); bytes.extend(p.build_bytes_vec()?); } ``` -------------------------------- ### Get OPT (EDNS) Record Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/packet.md Retrieves a reference to the OPT (EDNS) resource record if it exists in the packet, otherwise returns None. ```rust use simple_dns::Packet; let packet = Packet::new_query(1); assert_eq!(packet.opt(), None); ``` -------------------------------- ### Configure simple-dns for no_std with Alloc Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Shows the TOML configuration needed to build the simple-dns library for a no_std environment, requiring the 'alloc' feature. ```toml [dependencies] simple-dns = { version = "0.11", default-features = false, features = ["alloc"] } ``` -------------------------------- ### Get Name Server Record Count Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Extracts the 16-bit count of name server records from bytes 8-9 of a packet buffer. ```Rust use simple_dns::header_buffer; let buffer = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00"; assert_eq!(header_buffer::name_servers(&buffer[..]).unwrap(), 2); ``` -------------------------------- ### Advertise a Service with Simple-MDNS Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/index.md Illustrates how to advertise a local service using Simple-MDNS. This requires creating an InstanceInformation object and a ServiceDiscovery instance. ```rust use simple_mdns::sync_discovery::ServiceDiscovery; use simple_mdns::InstanceInformation; let instance = InstanceInformation::new("web".into()) .with_socket_address("192.168.1.100:8080".parse()?); let discovery = ServiceDiscovery::new( instance, "_http._tcp.local", 300, )?; // Service is now advertised ``` -------------------------------- ### ServiceDiscovery::new Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Initializes a ServiceDiscovery instance to advertise a local service on the network. ```APIDOC ## ServiceDiscovery::new ### Description Initializes a `ServiceDiscovery` instance, which is used to advertise a local service on the network using mDNS. It requires information about the service to be advertised. ### Method Signature `pub fn new(instance: InstanceInformation, service_type: &str, ttl: u32) -> Result>` ### Parameters #### Path Parameters - **instance** (InstanceInformation) - Required - Information about the service instance to advertise. - **service_type** (string) - Required - The type of service being advertised (e.g., `"_http._tcp.local"`). - **ttl** (u32) - Required - The time-to-live for the advertised service records. ### Returns A `Result` containing the initialized `ServiceDiscovery` instance or an error if initialization fails. ### Example ```rust use simple_mdns::sync_discovery::ServiceDiscovery; use simple_mdns::InstanceInformation; let instance = InstanceInformation::new("MyWebServer".into()) .with_socket_address("192.168.1.100:8080".parse()?); let mut discovery = ServiceDiscovery::new( instance, "_http._tcp.local", 300, )?; ``` ``` -------------------------------- ### Validate and Parse DNS Names Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/index.md Shows how to create, validate, and compare DNS names. Use `Name::new` for validated parsing and `Name::new_unchecked` for performance-critical or trusted inputs. ```rust use simple_dns::Name; // Validated parsing let name = Name::new("example.com")?; // Unchecked (for trusted input or special cases) let name = Name::new_unchecked("example.com"); // Check domain relationships let sub = Name::new_unchecked("api.example.com"); let domain = Name::new_unchecked("example.com"); assert!(sub.is_subdomain_of(&domain)); // Extract subdomain let subdomain = sub.without(&domain).unwrap(); assert_eq!(subdomain.to_string(), "api"); ``` -------------------------------- ### InstanceInformation Escaped Name Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Gets the instance name with special characters (dots and backslashes) escaped according to DNS conventions. Use this for proper mDNS naming. ```rust use simple_mdns::InstanceInformation; let info = InstanceInformation::new("example.com".into()); assert_eq!(info.escaped_instance_name(), "example\.com"); ``` -------------------------------- ### Create and Write DNS Packet Source: https://github.com/balliegojr/simple-dns/blob/main/simple-dns/README.md Construct a DNS query packet, add questions and additional records, and write it to a buffer or a Vec. Ensure correct imports for Packet, Name, TYPE, CLASS, and RData. ```rust use simple_dns::*; use simple_dns::rdata::*; let mut packet = Packet::new_query(1); let question = Question::new(Name::new_unchecked("_srv._udp.local"), TYPE::TXT.into(), CLASS::IN.into(), false); packet.questions.push(question); let resource = ResourceRecord::new(Name::new_unchecked("_srv._udp.local"), CLASS::IN, 10, RData::A(A { address: 10 })); packet.additional_records.push(resource); // Write the packet in the provided buffer; let mut bytes = [0u8; 200]; assert!(packet.write_to(&mut &mut bytes[..]).is_ok()); // Same as above, but allocates and returns a Vec let bytes = packet.build_bytes_vec(); assert!(bytes.is_ok()); // Same as above, but Names are compressed let bytes = packet.build_bytes_vec_compressed(); assert!(bytes.is_ok()); ``` -------------------------------- ### Service Discovery with Sync Discovery Source: https://github.com/balliegojr/simple-dns/blob/main/simple-mdns/README.md Use ServiceDiscovery to register and manage service instances. It requires instance information and service name. Services can be removed from discovery. ```rust # #[cfg(feature = "sync")] { use simple_mdns::sync_discovery::ServiceDiscovery; use simple_mdns::InstanceInformation; use std::str::FromStr; let mut discovery = ServiceDiscovery::new( InstanceInformation::new("a".into()).with_socket_address("192.168.1.22:8090".parse().expect("Invalid socket address")), "_mysrv._tcp.local", 60 ).expect("Failed to start service discovery"); // Removing service from discovery discovery.remove_service_from_discovery(); # } ``` -------------------------------- ### Adding EDNS Options Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Shows how to add EDNS Options, such as padding, to an OPT record within a DNS packet. ```rust use simple_dns::{Packet, rdata::{OPT, OPTCode}}; let mut packet = Packet::new_reply(1); let mut opt = OPT::new(); // Add options (example: padding) opt.options.push(( OPTCode::Padding, vec![0u8; 16], // Padding data )); *packet.opt_mut() = Some(opt); ``` -------------------------------- ### Define Custom Record from RData::NULL Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Provides an example of creating a `CustomRecord` struct that can wrap data from an `RData::NULL` variant, allowing for handling of proprietary or unknown record types. ```rust use simple_dns::{Packet, rdata::RData}; struct CustomRecord { type_code: u16, data: Vec, } impl CustomRecord { fn from_rdata(rdata: &RData) -> Option { match rdata { RData::NULL(type_code, null_data) => { Some(CustomRecord { type_code: *type_code, data: null_data.data.to_vec(), }) } _ => None, } } } ``` -------------------------------- ### Create ServiceDiscovery with Scope and DNS Records Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Use `ServiceDiscovery::new_with_scope` to create a service discovery instance with additional options like DNS records and network scope (IPv4/IPv6). ```rust pub fn new_with_scope( instance: InstanceInformation, service_name: &str, ttl: u32, dns_records: Option>>, scope: NetworkScope, ) -> Result ``` ```rust use simple_mdns::{ sync_discovery::ServiceDiscovery, InstanceInformation, NetworkScope }; let discovery = ServiceDiscovery::new_with_scope( InstanceInformation::new("ipv6service".into()), "_service._tcp.local", 60, None, NetworkScope::V6, ).expect("Failed to create IPv6 service"); ``` -------------------------------- ### Create and Manipulate DNS Reply Packet Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Demonstrates creating a DNS reply packet, setting flags, adding an A record answer, and inspecting record types. ```rust use simple_dns::{ Packet, Question, ResourceRecord, Name, CLASS, TYPE, QTYPE, RCODE, PacketFlag, rdata::{RData, A, TXT}, }; // Create a response packet let mut packet = Packet::new_reply(123); packet.set_flags(PacketFlag::RECURSION_AVAILABLE); // Add answer packet.answers.push(ResourceRecord::new( Name::new("example.com").unwrap(), CLASS::IN, 3600, RData::A(A { address: 93_142_176_1 }), )); // Check types let first_answer = &packet.answers[0]; match &first_answer.rdata { RData::A(a) => println!("IPv4: {}", a.address), _ => {} } ``` -------------------------------- ### Build and Test Cargo Project Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/index.md Basic commands for building and testing the Rust project, including testing with all features enabled. ```bash cargo build cargo test cargo test --all-features ``` -------------------------------- ### Header vs Full RCODE Inspection with EDNS Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Illustrates the difference between inspecting the 4-bit RCODE directly from the header buffer versus parsing the full packet to get the 12-bit EDNS RCODE. ```rust use simple_dns::{header_buffer, Packet, RCODE}; let bytes = /* EDNS packet with RCODE=16 */; // ❌ Incomplete (shows only 4-bit value) let header_rcode = header_buffer::rcode(&bytes).unwrap(); // ✓ Complete (parses OPT record) let packet = Packet::parse(&bytes).unwrap(); let full_rcode = packet.rcode(); // Full 12-bit value ``` -------------------------------- ### Build Jumbo DNS Packets Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Demonstrates how to build a DNS packet that may exceed the standard 512-byte UDP limit. If the packet size exceeds 512 bytes, the TRUNCATION flag is set. ```rust use simple_dns::Packet; let mut packet = Packet::new_reply(1); // Add many records... for i in 0..100 { // Add records... } let bytes = packet.build_bytes_vec_compressed()?; if bytes.len() > 512 { // Set truncation flag for UDP let mut packet = Packet::new_reply(1); packet.set_flags(PacketFlag::TRUNCATION); } ``` -------------------------------- ### ServiceDiscovery::new Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Creates a new ServiceDiscovery instance for registering a service on the mDNS network. ```APIDOC ## ServiceDiscovery::new ### Description Creates a new ServiceDiscovery instance for registering a service on the mDNS network. ### Method `new(instance: InstanceInformation, service_name: &str, ttl: u32) -> Result` ### Parameters #### Path Parameters - **instance** (InstanceInformation) - Required - Service instance details - **service_name** (&str) - Required - Service type (e.g., "_http._tcp.local") - **ttl** (u32) - Required - Time to live in seconds ### Response #### Success Response - Returns `Active ServiceDiscovery` on success. #### Errors - `NetworkError` - `InvalidServiceName` ### Request Example ```rust use simple_mdns::sync_discovery::ServiceDiscovery; use simple_mdns::InstanceInformation; let instance = InstanceInformation::new("web".into()) .with_socket_address("192.168.1.100:8080".parse().unwrap()); let discovery = ServiceDiscovery::new( instance, "_http._tcp.local", 300, ).expect("Failed to create service discovery"); ``` ``` -------------------------------- ### Query for a Service Address with Simple-MDNS Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/index.md Demonstrates how to query for the address and port of a specific service on the local network using Simple-MDNS. This uses the OneShotMdnsResolver for a single query. ```rust use simple_mdns::sync_discovery::OneShotMdnsResolver; let resolver = OneShotMdnsResolver::new()?; let addr = resolver.query_service_address_and_port("_http._tcp.local")?; println!("Found service at: {}", addr); ``` -------------------------------- ### Handle InvalidUtf8String Error During CharacterString Conversion Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/errors.md This example illustrates handling the InvalidUtf8String error, which arises when attempting to convert a CharacterString containing invalid UTF-8 bytes into a String. The conversion is expected to fail. ```rust use simple_dns::CharacterString; use std::convert::TryInto; let invalid_utf8 = CharacterString::new(&[0xFF, 0xFE]).unwrap(); let result: Result = invalid_utf8.try_into(); assert!(matches!(result, Err(SimpleDnsError::InvalidUtf8String(_)))); ``` -------------------------------- ### InstanceInformation::new Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Creates a new InstanceInformation struct with a given instance name. ```APIDOC ## InstanceInformation::new ### Description Creates a new `InstanceInformation` struct with a given instance name. ### Method Associated function (constructor) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **instance_name** (String) - Required - Service instance name ### Response Returns a new `InstanceInformation`. ### Example ```rust use simple_mdns::InstanceInformation; let info = InstanceInformation::new("my-service".into()); ``` ``` -------------------------------- ### Handle FailedToWrite Error with Limited Buffer Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/errors.md Demonstrates handling the FailedToWrite error, which occurs when a write operation fails, such as when using a buffer that is too small. The example asserts that writing to a limited buffer results in this error. ```rust use simple_dns::Packet; let mut packet = Packet::new_query(1); let mut limited_buffer = [0u8; 5]; // Too small for any real packet let result = packet.write_to(&mut &mut limited_buffer[..]); assert!(matches!(result, Err(SimpleDnsError::FailedToWrite))); ``` -------------------------------- ### Build and Serialize DNS Response Packet Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/question-and-resource.md Demonstrates building a DNS response packet, adding a question and an answer, and then serializing it into bytes. This is useful for creating custom DNS responses. ```rust use simple_dns::{ Packet, Question, ResourceRecord, Name, CLASS, TYPE, QTYPE, rdata::{RData, A}, }; // Build a response packet let mut packet = Packet::new_reply(42); // Add a questionpacket.questions.push(Question::new( Name::new("example.com").unwrap(), TYPE::A.into(), CLASS::IN.into(), false, )); // Add an answerpacket.answers.push(ResourceRecord::new( Name::new("example.com").unwrap(), CLASS::IN, 300, RData::A(A { address: 93_142_176_1 }), )); // Serialize let bytes = packet.build_bytes_vec().unwrap(); ``` -------------------------------- ### Get DNS Response Code (RCODE) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/header-buffer.md Retrieves the lower 4 bits of the response code from the packet header. For EDNS packets, the full RCODE might be 12 bits and requires full packet parsing. ```Rust use simple_dns::{header_buffer, RCODE}; // Byte [2-3] = 0x8003 (RESPONSE flag + NameError RCODE) let buffer = b"\x00\x00\x80\x03\x00\x00\x00\x00\x00\x00\x00\x00"; assert_eq!(header_buffer::rcode(&buffer[..]).unwrap(), RCODE::NameError); ``` -------------------------------- ### Generate DNS Packet Wire Formats Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/index.md Demonstrates different methods for serializing a DNS packet into byte format. Use `build_bytes_vec_compressed` for efficiency. ```rust use simple_dns::Packet; let packet = Packet::new_query(1); // Without compression let bytes = packet.build_bytes_vec()?; // With compression (smaller, faster) let compressed = packet.build_bytes_vec_compressed()?; // To custom writer let mut writer = Vec::new(); packet.write_to(&mut writer)?; ``` -------------------------------- ### Handle Invalid Class Type Conversion Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/errors.md This example demonstrates how to handle errors during type conversion from a raw class value (u16) to the `CLASS` enum. It specifically catches the `InvalidClass` error, indicating an unknown class value. ```rust use simple_dns::{CLASS, SimpleDnsError}; use std::convert::TryFrom; let class_value: u16 = 42; // Invalid class match CLASS::try_from(class_value) { Ok(class) => println!("Valid class: {:?}", class), Err(SimpleDnsError::InvalidClass(v)) => println!("Unknown class: {}", v), Err(e) => println!("Unexpected error: {:?}", e), } ``` -------------------------------- ### Check DNS Packet Flags Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/index.md Demonstrates how to set and check for specific flags within a DNS packet. Useful for controlling query behavior or verifying received packet properties. ```rust use simple_dns::{Packet, PacketFlag}; let mut packet = Packet::new_query(1); packet.set_flags(PacketFlag::RECURSION_DESIRED); assert!(packet.has_flags(PacketFlag::RECURSION_DESIRED)); ``` -------------------------------- ### Create and Use CharacterString in TXT Record Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/character-string.md Demonstrates how to create a TXT record and add multiple CharacterStrings to it, representing key-value attributes and flags. Includes parsing these attributes using the 'std' feature. ```rust use simple_dns::{CharacterString, rdata::TXT}; use std::convert::TryInto; // Create TXT record with attributes let mut txt = TXT::new(); txt.add_char_string(CharacterString::new(b"version=1.0").unwrap()); txt.add_char_string(CharacterString::new(b"path=/api").unwrap()); txt.add_char_string(CharacterString::new(b"secure").unwrap()); // flag // Parse attributes (std feature only) #[cfg(feature = "std")] { let attrs = txt.attributes(); assert_eq!(attrs.get("version"), Some(&Some("1.0".to_string()))); assert_eq!(attrs.get("path"), Some(&Some("/api".to_string()))); assert_eq!(attrs.get("secure"), Some(&None)); // flag attribute } // Iterate raw for (key, value) in txt.iter_raw() { match value { Some(v) => println!("{}={:?}", String::from_utf8_lossy(key), v), None => println!("{}", String::from_utf8_lossy(key)), } } ``` -------------------------------- ### Define Name Records (CNAME, NS, PTR, etc.) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Wrapper types for domain name-based records like CNAME, NS, and PTR. These store a single domain name reference. ```rust pub struct CNAME<'a>(pub Name<'a>); pub struct NS<'a>(pub Name<'a>); pub struct PTR<'a>(pub Name<'a>); // ... etc ``` -------------------------------- ### Create ServiceDiscovery Instance Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Use `ServiceDiscovery::new` to create a new service discovery instance. This is suitable for registering a service with its instance details, service name, and TTL. ```rust pub fn new( instance: InstanceInformation, service_name: &str, ttl: u32, ) -> Result ``` ```rust use simple_mdns::sync_discovery::ServiceDiscovery; use simple_mdns::InstanceInformation; let instance = InstanceInformation::new("web".into()) .with_socket_address("192.168.1.100:8080".parse().unwrap()); let discovery = ServiceDiscovery::new( instance, "_http._tcp.local", 300, ).expect("Failed to create service discovery"); ``` -------------------------------- ### SimpleMdnsResponder::new Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Creates a new SimpleMdnsResponder instance for synchronous service discovery. This responder is not yet listening and needs to be configured further. ```APIDOC ## SimpleMdnsResponder::new ### Description Creates a new `SimpleMdnsResponder` instance. This responder is initialized but not yet actively listening for or advertising services. ### Method Signature `pub fn new(ttl: u32) -> Self` ### Parameters #### Path Parameters - **ttl** (u32) - Required - Default time to live for resources. ### Returns A new `SimpleMdnsResponder` instance. ### Example ```rust use simple_mdns::sync_discovery::SimpleMdnsResponder; let mut responder = SimpleMdnsResponder::new(300); ``` ``` -------------------------------- ### Question::new Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/question-and-resource.md Creates a new DNS Question. A Question represents a DNS query and is used in the Questions section of a DNS packet. ```APIDOC ## Question::new ### Description Creates a new DNS Question. A Question represents a DNS query and is used in the Questions section of a DNS packet. ### Signature ```rust pub fn new( qname: Name<'a>, qtype: QTYPE, qclass: QCLASS, unicast_response: bool, ) -> Self ``` ### Parameters #### Path Parameters - **qname** (Name) - Required - Domain name to query - **qtype** (QTYPE) - Required - Record type to query for (e.g., TYPE::A) - **qclass** (QCLASS) - Required - Class to query (e.g., CLASS::IN) - **unicast_response** (bool) - Required - If true, responder should send unicast (mDNS RFC 6762) ### Returns A new `Question`. ### Example ```rust use simple_dns::{Question, Name, TYPE, CLASS, QTYPE, QCLASS}; let question = Question::new( Name::new("example.com").unwrap(), TYPE::A.into(), CLASS::IN.into(), false, ); ``` ``` -------------------------------- ### Add Resource Record to Responder (Sync) Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Adds a DNS resource record to be advertised by the responder. This allows the responder to reply to queries for the specified record. ```rust use simple_mdns::sync_discovery::SimpleMdnsResponder; use simple_mdns::simple_dns::{Name, CLASS, ResourceRecord, rdata::{RData, SRV, A}}; use std::net::Ipv4Addr; let mut responder = SimpleMdnsResponder::new(300); let srv_name = Name::new_unchecked("_myservice._tcp.local"); responder.add_resource(ResourceRecord::new( srv_name.clone(), CLASS::IN, 300, RData::SRV(SRV { priority: 0, weight: 0, port: 8080, target: srv_name, }), )); responder.add_resource(ResourceRecord::new( srv_name.clone(), CLASS::IN, 300, RData::A(A { address: Ipv4Addr::new(127, 0, 0, 1).into() }), )); ``` -------------------------------- ### QTYPE Enumeration for DNS Questions Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Defines query types for DNS questions, including standard TYPEs and special query types like ANY and AXFR. Supports conversions from TYPE and numeric values. ```rust #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum QTYPE { TYPE(TYPE), IXFR, // 251 - Incremental zone transfer AXFR, // 252 - Zone transfer MAILB, // 253 - Mailbox records MAILA, // 254 - Mail agent records ANY, // 255 - Any type } ``` -------------------------------- ### QCLASS Enumeration for DNS Questions Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/types.md Represents the class in DNS questions, including a wildcard 'ANY' option. Supports conversions from CLASS and numeric values. ```rust #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum QCLASS { CLASS(CLASS), ANY, // Match any class (255) } ``` -------------------------------- ### Handle Unknown and Implemented RData Types Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Shows how to iterate through DNS packet answers and match on different `RData` variants, including `RData::NULL` for unknown types, `RData::Empty`, and implemented types like `RData::A`. ```rust use simple_dns::{Packet, rdata::RData, TYPE}; let packet = Packet::parse(&bytes)?; for record in &packet.answers { match &record.rdata { RData::NULL(type_code, data) => { // Unimplemented type println!("Unknown type {}: {:?}", type_code, data); } RData::Empty(ty) => { // Empty data (valid but no payload) println!("Empty record of type {:?}", ty); } RData::A(a) => { // Implemented type println!("A record: {}", a.address); } _ => {} // Handle other implemented types or ignore } } ``` -------------------------------- ### Create New Query Packet Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/packet.md Constructs a new DNS packet initialized for a query. Sets the RESPONSE flag to false and OPCODE to StandardQuery. ```rust use simple_dns::Packet; let packet = Packet::new_query(42); assert_eq!(packet.id(), 42); assert!(!packet.has_flags(PacketFlag::RESPONSE)); ``` -------------------------------- ### Handle DNS Packet Buffer Size Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Provides constants for minimum and maximum DNS packet sizes and demonstrates a basic check for sufficient data to parse a DNS header from a buffer. ```rust const MIN_DNS_SIZE: usize = 512; // Standard DNS UDP limit const MAX_DNS_SIZE: usize = 9_000; // Jumbo packet limit const HEADER_SIZE: usize = 12; let mut buffer = vec![0u8; MIN_DNS_SIZE]; // Read data into buffer let bytes_read = socket.recv(&mut buffer)?; if bytes_read < HEADER_SIZE { return Err("Packet too short for header"); } let packet = Packet::parse(&buffer[..bytes_read])?; ``` -------------------------------- ### Create New Reply Packet Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/packet.md Constructs a new DNS packet initialized for a reply. Sets the RESPONSE flag to true and OPCODE to StandardQuery. ```rust use simple_dns::Packet; let packet = Packet::new_reply(42); assert_eq!(packet.id(), 42); assert!(packet.has_flags(PacketFlag::RESPONSE)); ``` -------------------------------- ### Build Compressed DNS Packet Bytes Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/packet.md Builds a Vec containing the DNS packet in wire format with DNS name compression enabled. Name compression reduces packet size by using pointers for repeated domain names. ```rust use simple_dns::{Packet, Question, Name, CLASS, TYPE}; let mut packet = Packet::new_query(1); packet.questions.push(Question::new( Name::new_unchecked("sub.example.com"), TYPE::A.into(), CLASS::IN.into(), false, )); let compressed = packet.build_bytes_vec_compressed().unwrap(); let uncompressed = packet.build_bytes_vec().unwrap(); // Compressed should be smaller or equal assert!(compressed.len() <= uncompressed.len()); ``` -------------------------------- ### Create a DNS Question with Unicast Response Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Demonstrates how to create a DNS question that requests a unicast response from the responder, as per RFC 6762 §5.4. Set the `unicast_response` flag to true. ```rust use simple_dns::{Question, Name, TYPE, CLASS}; let question = Question::new( Name::new_unchecked("_service._tcp.local"), TYPE::A.into(), CLASS::IN.into(), true, // unicast_response = true ); // Responder should reply via unicast instead of multicast ``` -------------------------------- ### InstanceInformation::with_ip_address Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/service-discovery.md Adds an IP address to the InstanceInformation. ```APIDOC ## InstanceInformation::with_ip_address ### Description Adds an IP address to the `InstanceInformation`. ### Method Builder method ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **ip_address** (IpAddr) - Required - The IP address to add ### Response Returns `Self` with the IP address added. ### Example ```rust use simple_mdns::InstanceInformation; use std::net::IpAddr; let info = InstanceInformation::new("service".into()) .with_ip_address("192.168.1.100".parse::().unwrap()) .with_ip_address("::1".parse::().unwrap()); // IPv6 ``` ``` -------------------------------- ### Create a new Name with validation Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/name.md Constructs a new Name from a string slice, performing validation checks. Use this for standard domain names. ```rust use simple_dns::Name; let name = Name::new("example.com").unwrap(); let invalid = Name::new("_service._tcp.example.com").unwrap(); ``` -------------------------------- ### Iterate TXT Record Raw Key-Value Pairs Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Demonstrates iterating through the raw key-value components of a TXT record, useful for parsing custom attribute formats. The values are optional. ```rust use simple_dns::rdata::TXT; let txt = /* TXT record from DNS */; // Iterate as key-value pairs for (key, value) in txt.iter_raw() { let key_str = String::from_utf8_lossy(key); let value_str = value.map(|v| String::from_utf8_lossy(v)); println!("{}={:?}", key_str, value_str); } ``` -------------------------------- ### Marking mDNS Resource Records with Cache Flush Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Shows how to create an mDNS resource record and set the cache flush flag, which instructs receiving hosts to invalidate existing cached records of the same name, type, and class. ```rust use simple_dns::{ResourceRecord, Name, CLASS, rdata::{RData, A}, Cache}; let mut record = ResourceRecord::new( Name::new_unchecked("service.local"), CLASS::IN, 120, RData::A(A { address: 127_0_0_1 }), ); // Mark as cache flush record = record.with_cache_flush(true); // When serialized, class field has high bit set (0x8000 | CLASS::IN) let bytes = record.build_bytes_vec().unwrap(); ``` -------------------------------- ### EDNS with Extended RCODE Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/advanced-usage.md Demonstrates setting an extended RCODE (16 or higher) which requires EDNS, by setting the RCODE in the header and adding an OPT record. ```rust use simple_dns::{Packet, RCODE, rdata::OPT}; let mut packet = Packet::new_reply(1); *packet.rcode_mut() = RCODE::BADVERS; // Code 16 (requires EDNS) *packet.opt_mut() = Some(OPT::new()); // Lower 4 bits in header, upper 8 bits in OPT let serialized = packet.build_bytes_vec().unwrap(); ``` -------------------------------- ### Build Uncompressed DNS Packet Bytes Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/api-reference/packet.md Builds a Vec containing the DNS packet in wire format without compression. This method allocates sufficient space for a jumbo UDP packet. ```rust use simple_dns::{Packet, Question, Name, CLASS, QTYPE, TYPE}; let mut packet = Packet::new_query(1); packet.questions.push(Question::new( Name::new_unchecked("example.com"), TYPE::A.into(), CLASS::IN.into(), false, )); let bytes = packet.build_bytes_vec().unwrap(); assert!(bytes.len() > 0); ``` -------------------------------- ### Build a DNS Query Packet Source: https://github.com/balliegojr/simple-dns/blob/main/_autodocs/README.md Constructs a DNS query packet. This is used to send requests to DNS servers. It requires specifying the query name, type, and class. ```rust use simple_dns::{Packet, Question, Name, TYPE, CLASS}; let mut packet = Packet::new_query(42); packet.questions.push(Question::new( Name::new("example.com")?, TYPE::A.into(), CLASS::IN.into(), false, )); let bytes = packet.build_bytes_vec_compressed()?; ```