Webserver+3d print
cyclone_tcp/mdns/mdns_responder.c
- Committer:
- Sergunb
- Date:
- 2017-02-04
- Revision:
- 0:8918a71cdbe9
File content as of revision 0:8918a71cdbe9:
/** * @file mdns_responder.c * @brief mDNS responder (Multicast DNS) * * @section License * * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. * * This file is part of CycloneTCP Open. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @author Oryx Embedded SARL (www.oryx-embedded.com) * @version 1.7.6 **/ //Switch to the appropriate trace level #define TRACE_LEVEL MDNS_TRACE_LEVEL //Dependencies #include <stdlib.h> #include <ctype.h> #include "core/net.h" #include "mdns/mdns_responder.h" #include "mdns/mdns_common.h" #include "dns/dns_debug.h" #include "dns_sd/dns_sd.h" #include "str.h" #include "debug.h" //Check TCP/IP stack configuration #if (MDNS_RESPONDER_SUPPORT == ENABLED) //Tick counter to handle periodic operations systime_t mdnsResponderTickCounter; /** * @brief Initialize settings with default values * @param[out] settings Structure that contains mDNS responder settings **/ void mdnsResponderGetDefaultSettings(MdnsResponderSettings *settings) { //Use default interface settings->interface = netGetDefaultInterface(); //Number of announcement packets settings->numAnnouncements = MDNS_ANNOUNCE_NUM; //TTL resource record settings->ttl = MDNS_DEFAULT_RR_TTL; //FSM state change event settings->stateChangeEvent = NULL; } /** * @brief mDNS responder initialization * @param[in] context Pointer to the mDNS responder context * @param[in] settings mDNS responder specific settings * @return Error code **/ error_t mdnsResponderInit(MdnsResponderContext *context, const MdnsResponderSettings *settings) { NetInterface *interface; //Debug message TRACE_INFO("Initializing mDNS responder...\r\n"); //Ensure the parameters are valid if(context == NULL || settings == NULL) return ERROR_INVALID_PARAMETER; //Invalid network interface? if(settings->interface == NULL) return ERROR_INVALID_PARAMETER; //Point to the underlying network interface interface = settings->interface; //Clear the mDNS responder context memset(context, 0, sizeof(MdnsResponderContext)); //Save user settings context->settings = *settings; //mDNS responder is currently suspended context->running = FALSE; //Initialize state machine context->state = MDNS_STATE_INIT; //Attach the mDNS responder context to the network interface interface->mdnsResponderContext = context; //Successful initialization return NO_ERROR; } /** * @brief Start mDNS responder * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderStart(MdnsResponderContext *context) { //Check parameter if(context == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_INFO("Starting mDNS responder...\r\n"); //Get exclusive access osAcquireMutex(&netMutex); //Start mDNS responder context->running = TRUE; //Initialize state machine context->state = MDNS_STATE_INIT; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Stop mDNS responder * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderStop(MdnsResponderContext *context) { //Check parameter if(context == NULL) return ERROR_INVALID_PARAMETER; //Debug message TRACE_INFO("Stopping mDNS responder...\r\n"); //Get exclusive access osAcquireMutex(&netMutex); //Suspend mDNS responder context->running = FALSE; //Reinitialize state machine context->state = MDNS_STATE_INIT; //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Retrieve current state * @param[in] context Pointer to the mDNS responder context * @return Current mDNS responder state **/ MdnsState mdnsResponderGetState(MdnsResponderContext *context) { MdnsState state; //Get exclusive access osAcquireMutex(&netMutex); //Get current state state = context->state; //Release exclusive access osReleaseMutex(&netMutex); //Return current state return state; } /** * @brief Set hostname * @param[in] context Pointer to the mDNS responder context * @param[in] hostname NULL-terminated string that contains the hostname * @return Error code **/ error_t mdnsResponderSetHostname(MdnsResponderContext *context, const char_t *hostname) { NetInterface *interface; //Check parameters if(context == NULL || hostname == NULL) return ERROR_INVALID_PARAMETER; //Get exclusive access osAcquireMutex(&netMutex); //Point to the underlying network interface interface = context->settings.interface; //Check whether a hostname is already assigned if(context->hostname[0] != '\0') { //Check whether the link is up if(interface->linkState) { //Send a goodbye packet mdnsResponderSendGoodbye(context); } } //Set hostname strSafeCopy(context->hostname, hostname, MDNS_RESPONDER_MAX_HOSTNAME_LEN); //Restart probing process (hostname) mdnsResponderStartProbing(interface->mdnsResponderContext); #if (DNS_SD_SUPPORT == ENABLED) //Restart probing process (service instance name) dnsSdStartProbing(interface->dnsSdContext); #endif //Release exclusive access osReleaseMutex(&netMutex); //Successful processing return NO_ERROR; } /** * @brief Generate domain name for reverse DNS lookup (IPv4) * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderSetIpv4ReverseName(MdnsResponderContext *context) { #if (IPV4_SUPPORT == ENABLED) uint8_t *addr; NetInterface *interface; //Check whether the mDNS responder has been properly instantiated if(context == NULL) return ERROR_INVALID_PARAMETER; //Point to the underlying network interface interface = context->settings.interface; //Check whether the host address is valid if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID) { //Cast the IPv4 address as byte array addr = (uint8_t *) &interface->ipv4Context.addr; //Generate the domain name for reverse DNS lookup sprintf(context->ipv4ReverseName, "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, addr[3], addr[2], addr[1], addr[0]); } else { //The host address is not valid context->ipv4ReverseName[0] = '\0'; } #endif //Successful processing return NO_ERROR; } /** * @brief Generate domain name for reverse DNS lookup (IPv6) * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderSetIpv6ReverseName(MdnsResponderContext *context) { #if (IPV6_SUPPORT == ENABLED) uint_t i; uint_t m; uint_t n; char_t *p; uint8_t *addr; NetInterface *interface; //Check whether the mDNS responder has been properly instantiated if(context == NULL) return ERROR_INVALID_PARAMETER; //Point to the underlying network interface interface = context->settings.interface; //Point to the buffer where to format the reverse name p = context->ipv6ReverseName; //Check whether the link-local address is valid if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) { //Cast the IPv4 address as byte array addr = interface->ipv6Context.addrList[0].addr.b; //Generate the domain name for reverse DNS lookup for(i = 0; i < 32; i++) { //Calculate the shift count n = (31 - i) / 2; m = (i % 2) * 4; //Format the current digit p += sprintf(p, "%" PRIx8, (addr[n] >> m) & 0x0F); //Add a delimiter character if(i != 31) p += sprintf(p, "."); } } else { //The link-local address is not valid p[0] = '\0'; } #endif //Successful processing return NO_ERROR; } /** * @brief Restart probing process * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderStartProbing(MdnsResponderContext *context) { //Check whether the mDNS responder has been properly instantiated if(context == NULL) return ERROR_INVALID_PARAMETER; //Generate domain names for reverse DNS lookup mdnsResponderSetIpv4ReverseName(context); mdnsResponderSetIpv6ReverseName(context); //Force mDNS responder to start probing again context->state = MDNS_STATE_INIT; //Successful processing return NO_ERROR; } /** * @brief mDNS responder timer handler * * This routine must be periodically called by the TCP/IP stack to * manage mDNS operation * * @param[in] context Pointer to the mDNS responder context **/ void mdnsResponderTick(MdnsResponderContext *context) { bool_t valid; systime_t time; systime_t delay; NetInterface *interface; IpAddr destIpAddr; //Make sure the mDNS responder has been properly instantiated if(context == NULL) return; //Point to the underlying network interface interface = context->settings.interface; //Get current time time = osGetSystemTime(); //Check current state if(context->state == MDNS_STATE_INIT) { //Check whether a hostname has been assigned if(context->hostname[0] != '\0') { //Make sure that the link is up if(interface->linkState) { //Clear flag valid = FALSE; #if (IPV4_SUPPORT == ENABLED) //Check whether the IPv4 host address is valid if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID) valid = TRUE; #endif #if (IPV6_SUPPORT == ENABLED) //Check whether the IPv6 link-local address is valid if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) valid = TRUE; #endif //Any valid IP address assigned to the network interface? if(valid) { //Wait until both IPv4 and IPv6 addresses are valid mdnsResponderChangeState(context, MDNS_STATE_WAITING, 0); } } } } else if(context->state == MDNS_STATE_WAITING) { //Set flag valid = TRUE; //Check current time if(timeCompare(time, context->timestamp + MDNS_MAX_WAITING_DELAY) < 0) { #if (IPV4_SUPPORT == ENABLED) //Check whether the IPv4 host address is valid if(interface->ipv4Context.addrState != IPV4_ADDR_STATE_VALID) valid = FALSE; #endif #if (IPV6_SUPPORT == ENABLED) //Check whether the IPv6 link-local address is valid if(ipv6GetLinkLocalAddrState(interface) != IPV6_ADDR_STATE_PREFERRED) valid = FALSE; #endif } //Start probing? if(valid) { //Initial random delay delay = netGetRandRange(MDNS_RAND_DELAY_MIN, MDNS_RAND_DELAY_MAX); //Perform probing mdnsResponderChangeState(context, MDNS_STATE_PROBING, delay); } } else if(context->state == MDNS_STATE_PROBING) { //Probing failed? if(context->conflict && context->retransmitCount > 0) { //Programmatically change the host name mdnsResponderChangeHostname(context); //Probe again, and repeat as necessary until a unique name is found mdnsResponderChangeState(context, MDNS_STATE_PROBING, 0); } //Tie-break lost? else if(context->tieBreakLost && context->retransmitCount > 0) { //The host defers to the winning host by waiting one second, and //then begins probing for this record again mdnsResponderChangeState(context, MDNS_STATE_PROBING, MDNS_PROBE_DEFER); } else { //Check current time if(timeCompare(time, context->timestamp + context->timeout) >= 0) { //Probing is on-going? if(context->retransmitCount < MDNS_PROBE_NUM) { //First probe? if(context->retransmitCount == 0) { //Apparently conflicting mDNS responses received before the //first probe packet is sent must be silently ignored context->conflict = FALSE; context->tieBreakLost = FALSE; } //Send probe packet mdnsResponderSendProbe(context); //Save the time at which the packet was sent context->timestamp = time; //Time interval between subsequent probe packets context->timeout = MDNS_PROBE_DELAY; //Increment retransmission counter context->retransmitCount++; } //Probing is complete? else { //The mDNS responder must send unsolicited mDNS responses //containing all of its newly registered resource records if(context->settings.numAnnouncements > 0) mdnsResponderChangeState(context, MDNS_STATE_ANNOUNCING, 0); else mdnsResponderChangeState(context, MDNS_STATE_IDLE, 0); } } } } else if(context->state == MDNS_STATE_ANNOUNCING) { //Whenever a mDNS responder receives any mDNS response (solicited or //otherwise) containing a conflicting resource record, the conflict //must be resolved if(context->conflict) { //Probe again, and repeat as necessary until a unique name is found mdnsResponderChangeState(context, MDNS_STATE_PROBING, 0); } else { //Check current time if(timeCompare(time, context->timestamp + context->timeout) >= 0) { //Send announcement packet mdnsResponderSendAnnouncement(context); //Save the time at which the packet was sent context->timestamp = time; //Increment retransmission counter context->retransmitCount++; //First announcement packet? if(context->retransmitCount == 1) { //The mDNS responder must send at least two unsolicited //responses, one second apart context->timeout = MDNS_ANNOUNCE_DELAY; } else { //To provide increased robustness against packet loss, a mDNS //responder may send up to eight unsolicited responses, provided //that the interval between unsolicited responses increases by //at least a factor of two with every response sent context->timeout *= 2; } //Last announcement packet? if(context->retransmitCount >= context->settings.numAnnouncements) { //A mDNS responder must not send regular periodic announcements mdnsResponderChangeState(context, MDNS_STATE_IDLE, 0); } } } } else if(context->state == MDNS_STATE_IDLE) { //Whenever a mDNS responder receives any mDNS response (solicited or //otherwise) containing a conflicting resource record, the conflict //must be resolved if(context->conflict) { //Probe again, and repeat as necessary until a unique name is found mdnsResponderChangeState(context, MDNS_STATE_PROBING, 0); } } #if (IPV4_SUPPORT == ENABLED) //Any response message pending to be sent? if(context->ipv4Response.buffer != NULL) { //Check whether the time delay has elapsed if(timeCompare(time, context->ipv4Response.timestamp + context->ipv4Response.timeout) >= 0) { #if (DNS_SD_SUPPORT == ENABLED) //Additional record generation (DNS-SD) dnsSdGenerateAdditionalRecords(interface, &context->ipv4Response, FALSE); #endif //Additional record generation (mDNS) mdnsResponderGenerateAdditionalRecords(interface, &context->ipv4Response, FALSE); //Use mDNS IPv4 multicast address destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; //Send mDNS response message mdnsSendMessage(interface, &context->ipv4Response, &destIpAddr, MDNS_PORT); //Free previously allocated memory mdnsDeleteMessage(&context->ipv4Response); } } #endif #if (IPV6_SUPPORT == ENABLED) //Any response message pending to be sent? if(context->ipv6Response.buffer != NULL) { //Check whether the time delay has elapsed if(timeCompare(time, context->ipv6Response.timestamp + context->ipv6Response.timeout) >= 0) { #if (DNS_SD_SUPPORT == ENABLED) //Additional record generation (DNS-SD) dnsSdGenerateAdditionalRecords(interface, &context->ipv6Response, FALSE); #endif //Additional record generation (mDNS) mdnsResponderGenerateAdditionalRecords(interface, &context->ipv6Response, FALSE); //Use mDNS IPv6 multicast address destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; //Send mDNS response message mdnsSendMessage(interface, &context->ipv6Response, &destIpAddr, MDNS_PORT); //Free previously allocated memory mdnsDeleteMessage(&context->ipv6Response); } } #endif } /** * @brief Callback function for link change event * @param[in] context Pointer to the mDNS responder context **/ void mdnsResponderLinkChangeEvent(MdnsResponderContext *context) { //Make sure the mDNS responder has been properly instantiated if(context == NULL) return; #if (IPV4_SUPPORT == ENABLED) //Free any response message pending to be sent mdnsDeleteMessage(&context->ipv4Response); #endif #if (IPV6_SUPPORT == ENABLED) //Free any response message pending to be sent mdnsDeleteMessage(&context->ipv6Response); #endif //Whenever a mDNS responder receives an indication of a link //change event, it must perform probing and announcing mdnsResponderChangeState(context, MDNS_STATE_INIT, 0); } /** * @brief Update FSM state * @param[in] context Pointer to the mDNS responder context * @param[in] newState New state to switch to * @param[in] delay Initial delay **/ void mdnsResponderChangeState(MdnsResponderContext *context, MdnsState newState, systime_t delay) { NetInterface *interface; //Point to the underlying network interface interface = context->settings.interface; //Set time stamp context->timestamp = osGetSystemTime(); //Set initial delay context->timeout = delay; //Reset retransmission counter context->retransmitCount = 0; //Switch to the new state context->state = newState; //Any registered callback? if(context->settings.stateChangeEvent != NULL) { //Release exclusive access osReleaseMutex(&netMutex); //Invoke user callback function context->settings.stateChangeEvent(context, interface, newState); //Get exclusive access osAcquireMutex(&netMutex); } } /** * @brief Programmatically change the host name * @param[in] context Pointer to the mDNS responder context **/ void mdnsResponderChangeHostname(MdnsResponderContext *context) { size_t i; size_t m; size_t n; uint32_t index; char_t s[16]; //Retrieve the length of the string n = strlen(context->hostname); //Parse the string backwards for(i = n; i > 0; i--) { //Check whether the current character is a digit if(!isdigit((uint8_t) context->hostname[i - 1])) break; } //Any number following the host name? if(context->hostname[i] != '\0') { //Retrieve the number at the end of the name index = atoi(context->hostname + i); //Increment the value index++; //Strip the digits context->hostname[i] = '\0'; } else { //Append the digit "2" to the name index = 2; } //Convert the number to a string of characters m = sprintf(s, "%" PRIu32, index); //Sanity check if((i + m) <= NET_MAX_HOSTNAME_LEN) { //Add padding if necessary while((i + m) < n) context->hostname[i++] = '0'; //Properly terminate the string context->hostname[i] = '\0'; //Programmatically change the host name strcat(context->hostname, s); } } /** * @brief Send probe packet * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderSendProbe(MdnsResponderContext *context) { error_t error; NetInterface *interface; DnsQuestion *dnsQuestion; MdnsMessage message; //Point to the underlying network interface interface = context->settings.interface; //Create an empty mDNS query message error = mdnsCreateMessage(&message, FALSE); //Any error to report? if(error) return error; //Start of exception handling block do { //Encode the host name using the DNS name notation message.length += mdnsEncodeName(context->hostname, "", ".local", message.dnsHeader->questions); //Point to the corresponding question structure dnsQuestion = DNS_GET_QUESTION(message.dnsHeader, message.length); //The probes should be sent as QU questions with the unicast-response //bit set, to allow a defending host to respond immediately via unicast dnsQuestion->qtype = HTONS(DNS_RR_TYPE_ANY); dnsQuestion->qclass = HTONS(MDNS_QCLASS_QU | DNS_RR_CLASS_IN); //Update the length of the mDNS query message message.length += sizeof(DnsQuestion); //Format A resource record error = mdnsResponderAddIpv4AddrRecord(interface, &message, FALSE, MDNS_DEFAULT_RR_TTL); //Any error to report? if(error) break; //Format AAAA resource record error = mdnsResponderAddIpv6AddrRecord(interface, &message, FALSE, MDNS_DEFAULT_RR_TTL); //Any error to report? if(error) break; //Number of questions in the Question Section message.dnsHeader->qdcount = 1; //Number of resource records in the Authority Section message.dnsHeader->nscount = message.dnsHeader->ancount; //Number of resource records in the Answer Section message.dnsHeader->ancount = 0; //Send mDNS message error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT); //End of exception handling block } while(0); //Free previously allocated memory mdnsDeleteMessage(&message); //Return status code return error; } /** * @brief Send announcement packet * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderSendAnnouncement(MdnsResponderContext *context) { error_t error; NetInterface *interface; MdnsMessage message; //Point to the underlying network interface interface = context->settings.interface; //Create an empty mDNS response message error = mdnsCreateMessage(&message, TRUE); //Any error to report? if(error) return error; //Start of exception handling block do { //Format A resource record error = mdnsResponderAddIpv4AddrRecord(interface, &message, TRUE, MDNS_DEFAULT_RR_TTL); //Any error to report? if(error) break; //Format reverse address mapping PTR record (IPv4) error = mdnsResponderAddIpv4ReversePtrRecord(interface, &message, TRUE, MDNS_DEFAULT_RR_TTL); //Any error to report? if(error) break; //Format AAAA resource record error = mdnsResponderAddIpv6AddrRecord(interface, &message, TRUE, MDNS_DEFAULT_RR_TTL); //Any error to report? if(error) break; //Format reverse address mapping PTR record (IPv6) error = mdnsResponderAddIpv6ReversePtrRecord(interface, &message, TRUE, MDNS_DEFAULT_RR_TTL); //Any error to report? if(error) break; //Send mDNS message error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT); //End of exception handling block } while(0); //Free previously allocated memory mdnsDeleteMessage(&message); //Return status code return error; } /** * @brief Send goodbye packet * @param[in] context Pointer to the mDNS responder context * @return Error code **/ error_t mdnsResponderSendGoodbye(MdnsResponderContext *context) { error_t error; NetInterface *interface; MdnsMessage message; //Point to the underlying network interface interface = context->settings.interface; //Create an empty mDNS response message error = mdnsCreateMessage(&message, TRUE); //Any error to report? if(error) return error; //Start of exception handling block do { //Format A resource record error = mdnsResponderAddIpv4AddrRecord(interface, &message, TRUE, 0); //Any error to report? if(error) break; //Format reverse address mapping PTR record (IPv4) error = mdnsResponderAddIpv4ReversePtrRecord(interface, &message, TRUE, 0); //Any error to report? if(error) break; //Format AAAA resource record error = mdnsResponderAddIpv6AddrRecord(interface, &message, TRUE, 0); //Any error to report? if(error) break; //Format reverse address mapping PTR record (IPv6) error = mdnsResponderAddIpv6ReversePtrRecord(interface, &message, TRUE, 0); //Any error to report? if(error) break; //Send mDNS message error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT); //End of exception handling block } while(0); //Free previously allocated memory mdnsDeleteMessage(&message); //Return status code return error; } /** * @brief Process mDNS query message * @param[in] interface Underlying network interface * @param[in] query Incoming mDNS query message **/ void mdnsResponderProcessQuery(NetInterface *interface, MdnsMessage *query) { error_t error; uint_t i; size_t n; size_t offset; DnsQuestion *question; DnsResourceRecord *record; MdnsResponderContext *context; MdnsMessage *response; uint16_t destPort; IpAddr destIpAddr; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Make sure the mDNS responder has been properly instantiated if(context == NULL) return; #if (IPV4_SUPPORT == ENABLED) //IPv4 query received? if(query->pseudoHeader->length == sizeof(Ipv4PseudoHeader)) { //If the source UDP port in a received Multicast DNS query is not port 5353, //this indicates that the querier originating the query is a simple resolver if(ntohs(query->udpHeader->srcPort) != MDNS_PORT) { //The mDNS responder must send a UDP response directly back to the querier, //via unicast, to the query packet's source IP address and port destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = query->pseudoHeader->ipv4Data.srcAddr; } else { //Use mDNS IPv4 multicast address destIpAddr.length = sizeof(Ipv4Addr); destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR; } //Point to the mDNS response message response = &context->ipv4Response; } else #endif #if (IPV6_SUPPORT == ENABLED) //IPv6 query received? if(query->pseudoHeader->length == sizeof(Ipv6PseudoHeader)) { //If the source UDP port in a received Multicast DNS query is not port 5353, //this indicates that the querier originating the query is a simple resolver if(ntohs(query->udpHeader->srcPort) != MDNS_PORT) { //The mDNS responder must send a UDP response directly back to the querier, //via unicast, to the query packet's source IP address and port destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = query->pseudoHeader->ipv6Data.srcAddr; } else { //Use mDNS IPv6 multicast address destIpAddr.length = sizeof(Ipv6Addr); destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR; } //Point to the mDNS response message response = &context->ipv6Response; } else #endif //Invalid query received? { //Discard the mDNS query message return; } //When possible, a responder should, for the sake of network //efficiency, aggregate as many responses as possible into a //single mDNS response message if(response->buffer == NULL) { //Create an empty mDNS response message error = mdnsCreateMessage(response, TRUE); //Any error to report? if(error) return; } //Take the identifier from the query message response->dnsHeader->id = query->dnsHeader->id; //Point to the first question offset = sizeof(DnsHeader); //Start of exception handling block do { //Parse the Question Section for(i = 0; i < ntohs(query->dnsHeader->qdcount); i++) { //Parse resource record name n = dnsParseName(query->dnsHeader, query->length, offset, NULL, 0); //Invalid name? if(!n) break; //Malformed mDNS message? if((n + sizeof(DnsQuestion)) > query->length) break; //Point to the corresponding entry question = DNS_GET_QUESTION(query->dnsHeader, n); //Parse question error = mdnsResponderParseQuestion(interface, query, offset, question, response); //Any error to report? if(error) break; #if (DNS_SD_SUPPORT == ENABLED) //Parse resource record error = dnsSdParseQuestion(interface, query, offset, question, response); //Any error to report? if(error) break; #endif //Point to the next question offset = n + sizeof(DnsQuestion); } //Any error while parsing the Question Section? if(i != ntohs(query->dnsHeader->qdcount)) break; //Parse the Known-Answer Section for(i = 0; i < ntohs(query->dnsHeader->ancount); i++) { //Parse resource record name n = dnsParseName(query->dnsHeader, query->length, offset, NULL, 0); //Invalid name? if(!n) break; //Point to the associated resource record record = DNS_GET_RESOURCE_RECORD(query->dnsHeader, n); //Point to the resource data n += sizeof(DnsResourceRecord); //Make sure the resource record is valid if(n > query->length) break; if((n + ntohs(record->rdlength)) > query->length) break; //Parse resource record mdnsResponderParseKnownAnRecord(interface, query, offset, record, response); //Point to the next resource record offset = n + ntohs(record->rdlength); } //Any error while parsing the Answer Section? if(i != ntohs(query->dnsHeader->ancount)) break; //Parse Authority Section for(i = 0; i < ntohs(query->dnsHeader->nscount); i++) { //Parse resource record name n = dnsParseName(query->dnsHeader, query->length, offset, NULL, 0); //Invalid name? if(!n) break; //Point to the associated resource record record = DNS_GET_RESOURCE_RECORD(query->dnsHeader, n); //Point to the resource data n += sizeof(DnsResourceRecord); //Make sure the resource record is valid if(n > query->length) break; if((n + ntohs(record->rdlength)) > query->length) break; //Check for host name conflict mdnsResponderParseNsRecord(interface, query, offset, record); #if (DNS_SD_SUPPORT == ENABLED) //Check for service instance name conflict dnsSdParseNsRecord(interface, query, offset, record); #endif //Point to the next resource record offset = n + ntohs(record->rdlength); } //Any error while parsing the Authority Section? if(i != ntohs(query->dnsHeader->nscount)) break; //End of exception handling block } while(0); //Should a mDNS message be send in response to the query? if(response->dnsHeader->ancount > 0) { //If the source UDP port in a received Multicast DNS query is not port 5353, //this indicates that the querier originating the query is a simple resolver if(ntohs(query->udpHeader->srcPort) != MDNS_PORT) { #if (DNS_SD_SUPPORT == ENABLED) //Additional record generation (DNS-SD) dnsSdGenerateAdditionalRecords(interface, response, TRUE); #endif //Additional record generation (mDNS) mdnsResponderGenerateAdditionalRecords(interface, response, TRUE); //Destination port destPort = ntohs(query->udpHeader->srcPort); //Send mDNS response message mdnsSendMessage(interface, response, &destIpAddr, destPort); //Free previously allocated memory mdnsDeleteMessage(response); } else { //Check whether the answer should be delayed if(query->dnsHeader->tc) { //In the case where the query has the TC (truncated) bit set, indicating //that subsequent Known-Answer packets will follow, responders should //delay their responses by a random amount of time selected with uniform //random distribution in the range 400-500 ms response->timeout = netGetRandRange(400, 500); //Save current time response->timestamp = osGetSystemTime(); } else if(response->sharedRecordCount > 0) { //In any case where there may be multiple responses, such as queries //where the answer is a member of a shared resource record set, each //responder should delay its response by a random amount of time //selected with uniform random distribution in the range 20-120 ms response->timeout = netGetRandRange(20, 120); //Save current time response->timestamp = osGetSystemTime(); } else { #if (DNS_SD_SUPPORT == ENABLED) //Additional record generation (refer to RFC 6763 section 12) dnsSdGenerateAdditionalRecords(interface, response, FALSE); #endif //Additional record generation (mDNS) mdnsResponderGenerateAdditionalRecords(interface, response, FALSE); //Send mDNS response message mdnsSendMessage(interface, response, &destIpAddr, MDNS_PORT); //Free previously allocated memory mdnsDeleteMessage(response); } } } else { //Free mDNS response message mdnsDeleteMessage(response); } } /** * @brief Parse a question * @param[in] interface Underlying network interface * @param[in] query Incoming mDNS query message * @param[in] offset Offset to first byte of the question * @param[in] question Pointer to the question * @param[in,out] response mDNS response message * @return Error code **/ error_t mdnsResponderParseQuestion(NetInterface *interface, const MdnsMessage *query, size_t offset, const DnsQuestion *question, MdnsMessage *response) { error_t error; uint16_t qclass; uint16_t qtype; uint32_t ttl; bool_t cacheFlush; MdnsResponderContext *context; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Check the state of the mDNS responder if(context->state != MDNS_STATE_ANNOUNCING && context->state != MDNS_STATE_IDLE) { //Do not respond to mDNS queries during probing return NO_ERROR; } //Convert the query class to host byte order qclass = ntohs(question->qclass); //Discard QU flag qclass &= ~MDNS_QCLASS_QU; //Convert the query type to host byte order qtype = ntohs(question->qtype); //Get the TTL resource record ttl = context->settings.ttl; //Check whether the querier originating the query is a simple resolver if(ntohs(query->udpHeader->srcPort) != MDNS_PORT) { //The resource record TTL given in a legacy unicast response should //not be greater than ten seconds, even if the true TTL of the mDNS //resource record is higher ttl = MIN(ttl, MDNS_LEGACY_UNICAST_RR_TTL); //The cache-flush bit must not be set in legacy unicast responses cacheFlush = FALSE; } else { //The cache-bit should be set for unique resource records cacheFlush = TRUE; } //Check the class of the query if(qclass == DNS_RR_CLASS_IN || qclass == DNS_RR_CLASS_ANY) { //Compare domain name if(!mdnsCompareName(query->dnsHeader, query->length, offset, context->hostname, "", ".local", 0)) { #if (IPV4_SUPPORT == ENABLED) //A query? if(qtype == DNS_RR_TYPE_A) { //Format A resource record error = mdnsResponderAddIpv4AddrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; } else #endif #if (IPV6_SUPPORT == ENABLED) //AAAA query? if(qtype == DNS_RR_TYPE_AAAA) { //Format AAAA resource record error = mdnsResponderAddIpv6AddrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; } else #endif //ANY query? if(qtype == DNS_RR_TYPE_ANY) { //Format A resource record error = mdnsResponderAddIpv4AddrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; //Format AAAA resource record error = mdnsResponderAddIpv6AddrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; //Format NSEC resource record error = mdnsResponderAddNsecRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; } else { //Format NSEC resource record error = mdnsResponderAddNsecRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; } } #if (IPV4_SUPPORT == ENABLED) //Reverse DNS lookup? if(!mdnsCompareName(query->dnsHeader, query->length, offset, context->ipv4ReverseName, "in-addr", ".arpa", 0)) { //PTR query? if(qtype == DNS_RR_TYPE_PTR || qtype == DNS_RR_TYPE_ANY) { //Format reverse address mapping PTR record (IPv4) error = mdnsResponderAddIpv4ReversePtrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; } } #endif #if (IPV6_SUPPORT == ENABLED) //Reverse DNS lookup? if(!mdnsCompareName(query->dnsHeader, query->length, offset, context->ipv6ReverseName, "ip6", ".arpa", 0)) { //PTR query? if(qtype == DNS_RR_TYPE_PTR || qtype == DNS_RR_TYPE_ANY) { //Format reverse address mapping PTR record (IPv6) error = mdnsResponderAddIpv6ReversePtrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return error; } } #endif } //Successful processing return NO_ERROR; } /** * @brief Parse a resource record from the Known-Answer Section * @param[in] interface Underlying network interface * @param[in] query Incoming mDNS query message * @param[in] queryOffset Offset to first byte of the resource record * @param[in] queryRecord Pointer to the resource record * @param[in,out] response mDNS response message **/ void mdnsResponderParseKnownAnRecord(NetInterface *interface, const MdnsMessage *query, size_t queryOffset, const DnsResourceRecord *queryRecord, MdnsMessage *response) { size_t i; size_t n; size_t responseOffset; DnsResourceRecord *responseRecord; //mDNS responses must not contain any questions in the Question Section if(response->dnsHeader->qdcount == 0) { //Point to the first resource record responseOffset = sizeof(DnsHeader); //Parse the Answer Section of the response for(i = 0; i < response->dnsHeader->ancount; i++) { //Parse resource record name n = dnsParseName(response->dnsHeader, response->length, responseOffset, NULL, 0); //Invalid name? if(!n) break; //Point to the associated resource record responseRecord = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n); //Point to the resource data n += sizeof(DnsResourceRecord); //Make sure the resource record is valid if(n > response->length) break; //Point to the end of the resource record n += ntohs(responseRecord->rdlength); //Make sure the resource record is valid if(n > response->length) break; //Compare resource record names if(!dnsCompareEncodedName(query->dnsHeader, query->length, queryOffset, response->dnsHeader, response->length, responseOffset, 0)) { //Compare the contents of the resource records if(!mdnsCompareRecord(query, queryOffset, queryRecord, response, responseOffset, responseRecord)) { //A mDNS responder must not answer a mDNS query if the answer //it would give is already included in the Answer Section with //an RR TTL at least half the correct value if(ntohl(queryRecord->ttl) >= (ntohl(responseRecord->ttl) / 2)) { //Perform Known-Answer Suppression memmove((uint8_t *) response->dnsHeader + responseOffset, (uint8_t *) response->dnsHeader + n, response->length - n); //Update the length of the mDNS response message response->length -= (n - responseOffset); //Update the number of resource records in the Answer Section response->dnsHeader->ancount--; //Keep at the same position n = responseOffset; i--; } } } //Point to the next resource record responseOffset = n; } } } /** * @brief Parse a resource record from the Authority Section * @param[in] interface Underlying network interface * @param[in] query Incoming mDNS query message * @param[in] offset Offset to first byte of the resource record * @param[in] record Pointer to the resource record **/ void mdnsResponderParseNsRecord(NetInterface *interface, const MdnsMessage *query, size_t offset, const DnsResourceRecord *record) { bool_t tieBreakLost; uint16_t rclass; MdnsResponderContext *context; //Point to the mDNS responder context context = interface->mdnsResponderContext; //When a host that is probing for a record sees another host issue a query //for the same record, it consults the Authority Section of that query. //If it finds any resource record there which answers the query, then it //compares the data of that resource record with its own tentative data if(!mdnsCompareName(query->dnsHeader, query->length, offset, context->hostname, "", ".local", 0)) { //Convert the class to host byte order rclass = ntohs(record->rclass); //Discard Cache Flush flag rclass &= ~MDNS_RCLASS_CACHE_FLUSH; //Check the class of the resource record if(rclass == DNS_RR_CLASS_IN) { #if (IPV4_SUPPORT == ENABLED) //A resource record found? if(ntohs(record->rtype) == DNS_RR_TYPE_A) { //Apply tie-breaking rules tieBreakLost = TRUE; //Verify the length of the data field if(ntohs(record->rdlength) == sizeof(Ipv4Addr)) { //Valid host address? if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID) { //The two records are compared and the lexicographically //later data wins if(memcmp(&interface->ipv4Context.addr, record->rdata, sizeof(Ipv4Addr)) >= 0) { tieBreakLost = FALSE; } } } //Check whether the host has lost the tie-break if(tieBreakLost) context->tieBreakLost = TRUE; } #endif #if (IPV6_SUPPORT == ENABLED) //AAAA resource record found? if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA) { //Apply tie-breaking rules tieBreakLost = TRUE; //Verify the length of the data field if(ntohs(record->rdlength) == sizeof(Ipv6Addr)) { uint_t i; Ipv6AddrEntry *entry; //Loop through the list of IPv6 addresses assigned to the interface for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) { //Point to the current entry entry = &interface->ipv6Context.addrList[i]; //Valid IPv6 address if(entry->state != IPV6_ADDR_STATE_INVALID) { //The two records are compared and the lexicographically //later data wins if(memcmp(&interface->ipv6Context.addrList[i].addr, record->rdata, sizeof(Ipv6Addr)) >= 0) { tieBreakLost = FALSE; } } } } //Check whether the host has lost the tie-break if(tieBreakLost) context->tieBreakLost = TRUE; } #endif } } } /** * @brief Parse a resource record from the Answer Section * @param[in] interface Underlying network interface * @param[in] response Incoming mDNS response message * @param[in] offset Offset to first byte of the resource record to be checked * @param[in] record Pointer to the resource record **/ void mdnsResponderParseAnRecord(NetInterface *interface, const MdnsMessage *response, size_t offset, const DnsResourceRecord *record) { bool_t conflict; uint16_t rclass; MdnsResponderContext *context; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Check for conflicts if(!mdnsCompareName(response->dnsHeader, response->length, offset, context->hostname, "", ".local", 0)) { //Convert the class to host byte order rclass = ntohs(record->rclass); //Discard Cache Flush flag rclass &= ~MDNS_RCLASS_CACHE_FLUSH; //Check the class of the resource record if(rclass == DNS_RR_CLASS_IN) { #if (IPV4_SUPPORT == ENABLED) //A resource record found? if(ntohs(record->rtype) == DNS_RR_TYPE_A) { //A conflict occurs when a mDNS responder has a unique record for //which it is currently authoritative, and it receives a mDNS //response message containing a record with the same name, rrtype //and rrclass, but inconsistent rdata conflict = TRUE; //Verify the length of the data field if(ntohs(record->rdlength) == sizeof(Ipv4Addr)) { //Valid host address? if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID) { //Check whether the rdata field is consistent if(ipv4CompAddr(&interface->ipv4Context.addr, record->rdata)) context->conflict = FALSE; } } //Check whether the hostname is already in use by some other host if(conflict) context->conflict = TRUE; } #endif #if (IPV6_SUPPORT == ENABLED) //AAAA resource record found? if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA) { //A conflict occurs when a mDNS responder has a unique record for //which it is currently authoritative, and it receives a mDNS //response message containing a record with the same name, rrtype //and rrclass, but inconsistent rdata conflict = TRUE; //Verify the length of the data field if(ntohs(record->rdlength) == sizeof(Ipv6Addr)) { uint_t i; Ipv6AddrEntry *entry; //Loop through the list of IPv6 addresses assigned to the interface for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++) { //Point to the current entry entry = &interface->ipv6Context.addrList[i]; //Valid IPv6 address if(entry->state != IPV6_ADDR_STATE_INVALID) { //Check whether the rdata field is consistent if(ipv6CompAddr(&interface->ipv6Context.addrList[i].addr, record->rdata)) conflict = FALSE; } } } //Check whether the hostname is already in use by some other host if(conflict) context->conflict = TRUE; } #endif } } } /** * @brief Additional record generation * @param[in] interface Underlying network interface * @param[in,out] response mDNS response message * @param[in] legacyUnicast This flag is set for legacy unicast responses **/ void mdnsResponderGenerateAdditionalRecords(NetInterface *interface, MdnsMessage *response, bool_t legacyUnicast) { error_t error; uint_t i; uint_t k; size_t n; size_t offset; uint_t ancount; uint16_t rclass; uint32_t ttl; bool_t cacheFlush; MdnsResponderContext *context; DnsResourceRecord *record; //Point to the mDNS responder context context = interface->mdnsResponderContext; //mDNS responses must not contain any questions in the Question Section if(response->dnsHeader->qdcount != 0) return; //Get the TTL resource record ttl = context->settings.ttl; //Check whether the querier originating the query is a simple resolver if(legacyUnicast) { //The resource record TTL given in a legacy unicast response should //not be greater than ten seconds, even if the true TTL of the mDNS //resource record is higher ttl = MIN(ttl, MDNS_LEGACY_UNICAST_RR_TTL); //The cache-flush bit must not be set in legacy unicast responses cacheFlush = FALSE; } else { //The cache-bit should be set for unique resource records cacheFlush = TRUE; } //Point to the first resource record offset = sizeof(DnsHeader); //Save the number of resource records in the Answer Section ancount = response->dnsHeader->ancount; //Compute the total number of resource records k = response->dnsHeader->ancount + response->dnsHeader->nscount + response->dnsHeader->arcount; //Loop through the resource records for(i = 0; i < k; i++) { //Parse resource record name n = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0); //Invalid name? if(!n) break; //Point to the associated resource record record = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n); //Point to the resource data n += sizeof(DnsResourceRecord); //Make sure the resource record is valid if(n > response->length) break; if((n + ntohs(record->rdlength)) > response->length) break; //Convert the record class to host byte order rclass = ntohs(record->rclass); //Discard the cache-flush bit rclass &= ~MDNS_RCLASS_CACHE_FLUSH; //Check the class of the resource record if(rclass == DNS_RR_CLASS_IN) { //A record? if(ntohs(record->rtype) == DNS_RR_TYPE_A) { #if (IPV6_SUPPORT == ENABLED) //When a mDNS responder places an IPv4 address record into a //response message, it should also place any IPv6 address records //with the same name into the Additional Section error = mdnsResponderAddIpv6AddrRecord(interface, response, cacheFlush, ttl); #else //In the event that a device has only IPv4 addresses but no IPv6 //addresses, then the appropriate NSEC record should be placed //into the Additional Section error = mdnsResponderAddNsecRecord(interface, response, cacheFlush, ttl); #endif //Any error to report? if(error) return; } //AAAA record? else if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA) { #if (IPV4_SUPPORT == ENABLED) //When a mDNS responder places an IPv6 address record into a //response message, it should also place any IPv4 address records //with the same name into the Additional Section error = mdnsResponderAddIpv4AddrRecord(interface, response, cacheFlush, ttl); #else //In the event that a device has only IPv6 addresses but no IPv4 //addresses, then the appropriate NSEC record should be placed //into the Additional Section error = mdnsResponderAddNsecRecord(interface, response, cacheFlush, ttl); #endif //Any error to report? if(error) return; } //SRV record? else if(ntohs(record->rtype) == DNS_RR_TYPE_SRV) { //Format A resource record error = mdnsResponderAddIpv4AddrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return; //Format AAAA resource record error = mdnsResponderAddIpv6AddrRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return; #if (IPV4_SUPPORT == DISABLED || IPV6_SUPPORT == DISABLED) //In the event that a device has only IPv4 addresses but no IPv6 //addresses, or vice versa, then the appropriate NSEC record should //be placed into the additional section, so that queriers can know //with certainty that the device has no addresses of that kind error = mdnsResponderAddNsecRecord(interface, response, cacheFlush, ttl); //Any error to report? if(error) return; #endif } } //Point to the next resource record offset = n + ntohs(record->rdlength); } //Number of resource records in the Additional Section response->dnsHeader->arcount += response->dnsHeader->ancount - ancount; //Number of resource records in the Answer Section response->dnsHeader->ancount = ancount; } /** * @brief Add A record to a mDNS message * @param[in] interface Underlying network interface * @param[in,out] message Pointer to the mDNS message * @param[in] cacheFlush Cache-flush bit * @param[in] ttl Resource record TTL (cache lifetime) * @return Error code **/ error_t mdnsResponderAddIpv4AddrRecord(NetInterface *interface, MdnsMessage *message, bool_t cacheFlush, uint32_t ttl) { #if (IPV4_SUPPORT == ENABLED) size_t n; size_t offset; bool_t duplicate; MdnsResponderContext *context; DnsResourceRecord *record; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Valid IPv4 host address? if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID) { //Check whether the resource record is already present in the Answer //Section of the message duplicate = mdnsCheckDuplicateRecord(message, context->hostname, "", ".local", DNS_RR_TYPE_A); //The duplicates should be suppressed and the resource record should //appear only once in the list if(!duplicate) { //Set the position to the end of the buffer offset = message->length; //The first pass calculates the length of the DNS encoded host name n = mdnsEncodeName(context->hostname, "", ".local", NULL); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The second pass encodes the host name using the DNS name notation offset += mdnsEncodeName(context->hostname, "", ".local", (uint8_t *) message->dnsHeader + offset); //Consider the length of the resource record itself n = sizeof(DnsResourceRecord) + sizeof(Ipv4Addr); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //Point to the corresponding resource record record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset); //Fill in resource record record->rtype = HTONS(DNS_RR_TYPE_A); record->rclass = HTONS(DNS_RR_CLASS_IN); record->ttl = htonl(ttl); record->rdlength = HTONS(sizeof(Ipv4Addr)); //Check whether the cache-flush bit should be set if(cacheFlush) record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH); //Copy IPv4 address ipv4CopyAddr(record->rdata, &interface->ipv4Context.addr); //Number of resource records in the answer section message->dnsHeader->ancount++; //Update the length of the mDNS response message message->length = offset + n; } } #endif //Successful processing return NO_ERROR; } /** * @brief Add AAAA record to a mDNS message * @param[in] interface Underlying network interface * @param[in,out] message Pointer to the mDNS message * @param[in] cacheFlush Cache-flush bit * @param[in] ttl Resource record TTL (cache lifetime) * @return Error code **/ error_t mdnsResponderAddIpv6AddrRecord(NetInterface *interface, MdnsMessage *message, bool_t cacheFlush, uint32_t ttl) { #if (IPV6_SUPPORT == ENABLED) size_t n; size_t offset; bool_t duplicate; MdnsResponderContext *context; DnsResourceRecord *record; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Valid IPv6 link-local address? if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) { //Check whether the resource record is already present in the Answer //Section of the message duplicate = mdnsCheckDuplicateRecord(message, context->hostname, "", ".local", DNS_RR_TYPE_AAAA); //The duplicates should be suppressed and the resource record should //appear only once in the list if(!duplicate) { //Set the position to the end of the buffer offset = message->length; //The first pass calculates the length of the DNS encoded host name n = mdnsEncodeName(context->hostname, "", ".local", NULL); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The second pass encodes the host name using the DNS name notation offset += mdnsEncodeName(context->hostname, "", ".local", (uint8_t *) message->dnsHeader + offset); //Consider the length of the resource record itself n = sizeof(DnsResourceRecord) + sizeof(Ipv6Addr); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //Point to the corresponding resource record record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset); //Fill in resource record record->rtype = HTONS(DNS_RR_TYPE_AAAA); record->rclass = HTONS(DNS_RR_CLASS_IN); record->ttl = htonl(ttl); record->rdlength = HTONS(sizeof(Ipv6Addr)); //Check whether the cache-flush bit should be set if(cacheFlush) record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH); //Copy IPv6 address ipv6CopyAddr(record->rdata, &interface->ipv6Context.addrList[0].addr); //Number of resource records in the answer section message->dnsHeader->ancount++; //Update the length of the mDNS response message message->length = offset + n; } } #endif //Successful processing return NO_ERROR; } /** * @brief Add reverse address mapping PTR record (IPv4) * @param[in] interface Underlying network interface * @param[in,out] message Pointer to the mDNS message * @param[in] cacheFlush Cache-flush bit * @param[in] ttl Resource record TTL (cache lifetime) * @return Error code **/ error_t mdnsResponderAddIpv4ReversePtrRecord(NetInterface *interface, MdnsMessage *message, bool_t cacheFlush, uint32_t ttl) { #if (IPV4_SUPPORT == ENABLED) size_t n; size_t offset; bool_t duplicate; MdnsResponderContext *context; DnsResourceRecord *record; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Valid reverse name? if(context->ipv4ReverseName[0] != '\0') { //Check whether the resource record is already present in the Answer //Section of the message duplicate = mdnsCheckDuplicateRecord(message, context->ipv4ReverseName, "in-addr", ".arpa", DNS_RR_TYPE_PTR); //The duplicates should be suppressed and the resource record should //appear only once in the list if(!duplicate) { //Set the position to the end of the buffer offset = message->length; //The first pass calculates the length of the DNS encoded reverse name n = mdnsEncodeName(context->ipv4ReverseName, "in-addr", ".arpa", NULL); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The second pass encodes the reverse name using the DNS name notation offset += mdnsEncodeName(context->ipv4ReverseName, "in-addr", ".arpa", (uint8_t *) message->dnsHeader + offset); //Consider the length of the resource record itself n = sizeof(DnsResourceRecord) + sizeof(Ipv4Addr); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //Point to the corresponding resource record record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset); //Fill in resource record record->rtype = HTONS(DNS_RR_TYPE_PTR); record->rclass = HTONS(DNS_RR_CLASS_IN); record->ttl = htonl(ttl); //Check whether the cache-flush bit should be set if(cacheFlush) record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH); //Advance write index offset += sizeof(DnsResourceRecord); //The first pass calculates the length of the DNS encoded host name n = mdnsEncodeName("", context->hostname, ".local", NULL); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The second pass encodes the host name using DNS notation n = mdnsEncodeName("", context->hostname, ".local", record->rdata); //Convert length field to network byte order record->rdlength = htons(n); //Number of resource records in the answer section message->dnsHeader->ancount++; //Update the length of the mDNS response message message->length = offset + n; } } #endif //Successful processing return NO_ERROR; } /** * @brief Add reverse address mapping PTR record (IPv6) * @param[in] interface Underlying network interface * @param[in,out] message Pointer to the mDNS message * @param[in] cacheFlush Cache-flush bit * @param[in] ttl Resource record TTL (cache lifetime) * @return Error code **/ error_t mdnsResponderAddIpv6ReversePtrRecord(NetInterface *interface, MdnsMessage *message, bool_t cacheFlush, uint32_t ttl) { #if (IPV6_SUPPORT == ENABLED) size_t n; size_t offset; bool_t duplicate; MdnsResponderContext *context; DnsResourceRecord *record; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Valid reverse name? if(context->ipv6ReverseName[0] != '\0') { //Check whether the resource record is already present in the Answer //Section of the message duplicate = mdnsCheckDuplicateRecord(message, context->ipv6ReverseName, "ip6", ".arpa", DNS_RR_TYPE_PTR); //The duplicates should be suppressed and the resource record should //appear only once in the list if(!duplicate) { //Set the position to the end of the buffer offset = message->length; //The first pass calculates the length of the DNS encoded reverse name n = mdnsEncodeName(context->ipv6ReverseName, "ip6", ".arpa", NULL); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The second pass encodes the reverse name using the DNS name notation offset += mdnsEncodeName(context->ipv6ReverseName, "ip6", ".arpa", (uint8_t *) message->dnsHeader + offset); //Consider the length of the resource record itself n = sizeof(DnsResourceRecord) + sizeof(Ipv4Addr); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //Point to the corresponding resource record record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset); //Fill in resource record record->rtype = HTONS(DNS_RR_TYPE_PTR); record->rclass = HTONS(DNS_RR_CLASS_IN); record->ttl = htonl(ttl); //Check whether the cache-flush bit should be set if(cacheFlush) record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH); //Advance write index offset += sizeof(DnsResourceRecord); //The first pass calculates the length of the DNS encoded host name n = mdnsEncodeName("", context->hostname, ".local", NULL); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The second pass encodes the host name using DNS notation n = mdnsEncodeName("", context->hostname, ".local", record->rdata); //Convert length field to network byte order record->rdlength = htons(n); //Number of resource records in the answer section message->dnsHeader->ancount++; //Update the length of the mDNS response message message->length = offset + n; } } #endif //Successful processing return NO_ERROR; } /** * @brief Add NSEC record to a mDNS message * @param[in] interface Underlying network interface * @param[in,out] message Pointer to the mDNS message * @param[in] cacheFlush Cache-flush bit * @param[in] ttl Resource record TTL (cache lifetime) * @return Error code **/ error_t mdnsResponderAddNsecRecord(NetInterface *interface, MdnsMessage *message, bool_t cacheFlush, uint32_t ttl) { size_t n; size_t offset; bool_t duplicate; size_t bitmapLength; uint8_t bitmap[8]; MdnsResponderContext *context; DnsResourceRecord *record; //Point to the mDNS responder context context = interface->mdnsResponderContext; //Check whether the resource record is already present in the Answer //Section of the message duplicate = mdnsCheckDuplicateRecord(message, context->hostname, "", ".local", DNS_RR_TYPE_NSEC); //The duplicates should be suppressed and the resource record should //appear only once in the list if(!duplicate) { //The bitmap identifies the resource record types that exist memset(bitmap, 0, sizeof(bitmap)); #if (IPV4_SUPPORT == ENABLED) //A resource record is supported DNS_SET_NSEC_BITMAP(bitmap, DNS_RR_TYPE_A); #endif #if (IPV6_SUPPORT == ENABLED) //A resource record is supported DNS_SET_NSEC_BITMAP(bitmap, DNS_RR_TYPE_AAAA); #endif //Compute the length of the bitmap for(bitmapLength = sizeof(bitmap); bitmapLength > 0; bitmapLength--) { //Trailing zero octets in the bitmap must be omitted... if(bitmap[bitmapLength - 1] != 0x00) break; } //Set the position to the end of the buffer offset = message->length; //The first pass calculates the length of the DNS encoded host name n = mdnsEncodeName(context->hostname, "", ".local", NULL); //Check the length of the resulting mDNS message if((offset + n) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The second pass encodes the host name using the DNS name notation offset += mdnsEncodeName(context->hostname, "", ".local", (uint8_t *) message->dnsHeader + offset); //Consider the length of the resource record itself if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //Point to the corresponding resource record record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset); //Fill in resource record record->rtype = HTONS(DNS_RR_TYPE_NSEC); record->rclass = HTONS(DNS_RR_CLASS_IN); record->ttl = htonl(ttl); //Check whether the cache-flush bit should be set if(cacheFlush) record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH); //Advance write index offset += sizeof(DnsResourceRecord); //Check the length of the resulting mDNS message if((offset + n + 2 + bitmapLength) > MDNS_MESSAGE_MAX_SIZE) return ERROR_MESSAGE_TOO_LONG; //The Next Domain Name field contains the record's own name mdnsEncodeName(context->hostname, "", ".local", record->rdata); //DNS NSEC record is limited to Window Block number zero record->rdata[n++] = 0; //The Bitmap Length is a value in the range 1-32 record->rdata[n++] = bitmapLength; //The Bitmap data identifies the resource record types that exist memcpy(record->rdata + n, bitmap, bitmapLength); //Convert length field to network byte order record->rdlength = htons(n + bitmapLength); //Number of resource records in the answer section message->dnsHeader->ancount++; //Update the length of the DNS message message->length = offset + n + bitmapLength; } //Successful processing return NO_ERROR; } #endif