Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mdns_common.c Source File

mdns_common.c

Go to the documentation of this file.
00001 /**
00002  * @file mdns_common.c
00003  * @brief Functions common to mDNS client and mDNS responder
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * Multicast DNS and its companion technology DNS-Based Service Discovery
00028  * were created to provide ease-of-use and autoconfiguration to IP networks.
00029  * Refer to the following RFCs for complete details:
00030  * - RFC 6762: Multicast DNS
00031  * - RFC 6763: DNS-Based Service Discovery
00032  *
00033  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00034  * @version 1.7.6
00035  **/
00036 
00037 //Switch to the appropriate trace level
00038 #define TRACE_LEVEL MDNS_TRACE_LEVEL
00039 
00040 //Dependencies
00041 #include <stdlib.h>
00042 #include <string.h>
00043 #include <ctype.h>
00044 #include "core/net.h"
00045 #include "ipv6/ipv6_misc.h"
00046 #include "mdns/mdns_client.h"
00047 #include "mdns/mdns_responder.h"
00048 #include "mdns/mdns_common.h"
00049 #include "dns/dns_debug.h"
00050 #include "debug.h"
00051 
00052 //Check TCP/IP stack configuration
00053 #if (MDNS_CLIENT_SUPPORT == ENABLED || MDNS_RESPONDER_SUPPORT == ENABLED)
00054 
00055 #if (IPV6_SUPPORT == ENABLED)
00056 
00057 //mDNS IPv6 multicast group (ff02::fb)
00058 const Ipv6Addr MDNS_IPV6_MULTICAST_ADDR =
00059    IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00FB);
00060 
00061 #endif
00062 
00063 
00064 /**
00065  * @brief mDNS related initialization
00066  * @param[in] interface Underlying network interface
00067  * @return Error code
00068  **/
00069 
00070 error_t mdnsInit(NetInterface *interface)
00071 {
00072    error_t error;
00073 
00074 #if (IPV4_SUPPORT == ENABLED)
00075    //Join the mDNS IPv4 multicast group
00076    error = ipv4JoinMulticastGroup(interface, MDNS_IPV4_MULTICAST_ADDR);
00077    //Any error to report?
00078    if(error)
00079       return error;
00080 #endif
00081 
00082 #if (IPV6_SUPPORT == ENABLED)
00083    //Join the mDNS IPv6 multicast group
00084    error = ipv6JoinMulticastGroup(interface, &MDNS_IPV6_MULTICAST_ADDR);
00085    //Any error to report?
00086    if(error)
00087       return error;
00088 #endif
00089 
00090    //Callback function to be called when a mDNS message is received
00091    error = udpAttachRxCallback(interface, MDNS_PORT, mdnsProcessMessage, NULL);
00092    //Any error to report?
00093    if(error)
00094       return error;
00095 
00096    //Successful initialization
00097    return NO_ERROR;
00098 }
00099 
00100 
00101 /**
00102  * @brief Process incoming mDNS message
00103  * @param[in] interface Underlying network interface
00104  * @param[in] pseudoHeader UDP pseudo header
00105  * @param[in] udpHeader UDP header
00106  * @param[in] buffer Multi-part buffer containing the incoming mDNS message
00107  * @param[in] offset Offset to the first byte of the mDNS message
00108  * @param[in] params Callback function parameter (not used)
00109  **/
00110 
00111 void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
00112    const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params)
00113 {
00114    size_t length;
00115    DnsHeader *dnsHeader;
00116    MdnsMessage message;
00117 
00118    //Retrieve the length of the mDNS message
00119    length = netBufferGetLength(buffer) - offset;
00120 
00121    //Ensure the mDNS message is valid
00122    if(length < sizeof(DnsHeader))
00123       return;
00124    if(length > MDNS_MESSAGE_MAX_SIZE)
00125       return;
00126 
00127    //Point to the mDNS message header
00128    dnsHeader = netBufferAt(buffer, offset);
00129    //Sanity check
00130    if(dnsHeader == NULL)
00131       return;
00132 
00133    //Debug message
00134    TRACE_INFO("mDNS message received (%" PRIuSIZE " bytes)...\r\n", length);
00135    //Dump message
00136    dnsDumpMessage(dnsHeader, length);
00137 
00138    //mDNS messages received with an opcode other than zero must be silently ignored
00139    if(dnsHeader->opcode != DNS_OPCODE_QUERY)
00140       return;
00141    //mDNS messages received with non-zero response codes must be silently ignored
00142    if(dnsHeader->rcode != DNS_RCODE_NO_ERROR)
00143       return;
00144 
00145    //Save mDNS message
00146    message.buffer = (NetBuffer *) buffer;
00147    message.offset = offset;
00148    message.length = length;
00149    message.pseudoHeader = pseudoHeader;
00150    message.udpHeader = udpHeader;
00151    message.dnsHeader = dnsHeader;
00152 
00153    //mDNS query received?
00154    if(!dnsHeader->qr)
00155    {
00156 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
00157       //Process incoming mDNS query message
00158       mdnsResponderProcessQuery(interface, &message);
00159 #endif
00160    }
00161    //mDNS response received?
00162    else
00163    {
00164       //Process incoming mDNS response message
00165       mdnsProcessResponse(interface, &message);
00166    }
00167 }
00168 
00169 
00170 /**
00171  * @brief Process mDNS response message
00172  * @param[in] interface Underlying network interface
00173  * @param[in] response Incoming mDNS response message
00174  **/
00175 
00176 void mdnsProcessResponse(NetInterface *interface, MdnsMessage *response)
00177 {
00178    uint_t i;
00179    uint_t k;
00180    size_t n;
00181    size_t offset;
00182    DnsResourceRecord *record;
00183 
00184    //Source address check (refer to RFC 6762 section 11)
00185    if(!mdnsCheckSourceAddr(interface, response->pseudoHeader))
00186       return;
00187 
00188    //mDNS implementations must silently ignore any mDNS responses they
00189    //receive where the source UDP port is not 5353
00190    if(ntohs(response->udpHeader->srcPort) != MDNS_PORT)
00191       return;
00192 
00193    //Point to the question section
00194    offset = sizeof(DnsHeader);
00195 
00196    //Any questions in the question section of a received mDNS response
00197    //must be silently ignored
00198    for(i = 0; i < ntohs(response->dnsHeader->qdcount); i++)
00199    {
00200       //Parse domain name
00201       offset = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
00202       //Invalid name?
00203       if(!offset)
00204          break;
00205 
00206       //Point to the next question
00207       offset += sizeof(DnsQuestion);
00208       //Make sure the mDNS message is valid
00209       if(offset > response->length)
00210          break;
00211    }
00212 
00213    //Malformed mDNS message?
00214    if(i != ntohs(response->dnsHeader->qdcount))
00215       return;
00216 
00217    //Compute the total number of resource records
00218    k = ntohs(response->dnsHeader->ancount) +
00219       ntohs(response->dnsHeader->nscount) +
00220       ntohs(response->dnsHeader->arcount);
00221 
00222    //Loop through the resource records
00223    for(i = 0; i < k; i++)
00224    {
00225       //Parse resource record name
00226       n = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
00227       //Invalid name?
00228       if(!n)
00229          break;
00230 
00231       //Point to the associated resource record
00232       record = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n);
00233       //Point to the resource data
00234       n += sizeof(DnsResourceRecord);
00235 
00236       //Make sure the resource record is valid
00237       if(n > response->length)
00238          break;
00239       if((n + ntohs(record->rdlength)) > response->length)
00240          break;
00241 
00242 #if (MDNS_CLIENT_SUPPORT == ENABLED)
00243       //Parse the resource record
00244       mdnsClientParseAnRecord(interface, response, offset, record);
00245 #endif
00246 
00247 #if (MDNS_RESPONDER_SUPPORT == ENABLED)
00248       //Parse the resource record
00249       mdnsResponderParseAnRecord(interface, response, offset, record);
00250 #endif
00251 
00252 #if (DNS_SD_SUPPORT == ENABLED)
00253       //Parse the resource record
00254       dnsSdParseAnRecord(interface, response, offset, record);
00255 #endif
00256 
00257       //Point to the next resource record
00258       offset = n + ntohs(record->rdlength);
00259    }
00260 }
00261 
00262 
00263 /**
00264  * @brief Source address check
00265  * @param[in] interface Underlying network interface
00266  * @param[in] pseudoHeader UDP pseudo header
00267  * @return TRUE if the source address is valid, else FALSE
00268  **/
00269 
00270 bool_t mdnsCheckSourceAddr(NetInterface *interface,
00271    const IpPseudoHeader *pseudoHeader)
00272 {
00273    bool_t valid;
00274 
00275 #if (IPV4_SUPPORT == ENABLED)
00276    //IPv4 packet received?
00277    if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
00278    {
00279       //Perform source address check (refer to RFC 6762 section 11)
00280       if(pseudoHeader->ipv4Data.destAddr == MDNS_IPV4_MULTICAST_ADDR)
00281       {
00282          //All responses received with the destination address 224.0.0.251
00283          //are necessarily deemed to have originated on the local link,
00284          //regardless of source IP address
00285          valid = TRUE;
00286       }
00287       else if(ipv4IsLinkLocalAddr(pseudoHeader->ipv4Data.srcAddr) ||
00288          ipv4IsLinkLocalAddr(pseudoHeader->ipv4Data.destAddr))
00289       {
00290          //Packets with a link-local source or destination address
00291          //originate from the local link
00292          valid = TRUE;
00293       }
00294       else if(ipv4IsOnLocalSubnet(interface, pseudoHeader->ipv4Data.srcAddr))
00295       {
00296          //The source IP address is on the local subnet
00297          valid = TRUE;
00298       }
00299       else
00300       {
00301          //Only accept responses that originate from the local link, and
00302          //silently discard any other response packets
00303          valid = FALSE;
00304       }
00305    }
00306    else
00307 #endif
00308 #if (IPV6_SUPPORT == ENABLED)
00309    //IPv6 packet received?
00310    if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
00311    {
00312       //Perform source address check (refer to RFC 6762 section 11)
00313       if(ipv6CompAddr(&pseudoHeader->ipv6Data.destAddr, &MDNS_IPV6_MULTICAST_ADDR))
00314       {
00315          //All responses received with the destination address ff02::fb
00316          //are necessarily deemed to have originated on the local link,
00317          //regardless of source IP address
00318          valid = TRUE;
00319       }
00320       else if(ipv6IsOnLink(interface, &pseudoHeader->ipv6Data.srcAddr))
00321       {
00322          //The source IP address is on the local link
00323          valid = TRUE;
00324       }
00325       else
00326       {
00327          //Only accept responses that originate from the local link, and
00328          //silently discard any other response packets
00329          valid = FALSE;
00330       }
00331    }
00332    else
00333 #endif
00334    //Invalid packet received?
00335    {
00336       //Discard the response packet
00337       valid = FALSE;
00338    }
00339 
00340    //Return flag value
00341    return valid;
00342 }
00343 
00344 
00345 /**
00346  * @brief Create an empty mDNS message
00347  * @param[in,out] message Newly created mDNS message
00348  * @param[in] queryResponse This flag specifies whether the message is a query or a response
00349  * @return Error code
00350  **/
00351 
00352 error_t mdnsCreateMessage(MdnsMessage *message, bool_t queryResponse)
00353 {
00354    error_t error;
00355 
00356    //Allocate a memory buffer to hold the mDNS message
00357    message->buffer = udpAllocBuffer(MDNS_MESSAGE_MAX_SIZE, &message->offset);
00358 
00359    //Successful memory allocation?
00360    if(message->buffer != NULL)
00361    {
00362       //Point to the mDNS message header
00363       message->dnsHeader = netBufferAt(message->buffer, message->offset);
00364 
00365       //Sanity check
00366       if(message->dnsHeader != NULL)
00367       {
00368          //Format mDNS message header
00369          message->dnsHeader->id = 0;
00370          message->dnsHeader->opcode = DNS_OPCODE_QUERY;
00371          message->dnsHeader->tc = 0;
00372          message->dnsHeader->rd = 0;
00373          message->dnsHeader->ra = 0;
00374          message->dnsHeader->z = 0;
00375          message->dnsHeader->rcode = DNS_RCODE_NO_ERROR;
00376          message->dnsHeader->qdcount = 0;
00377          message->dnsHeader->ancount = 0;
00378          message->dnsHeader->nscount = 0;
00379          message->dnsHeader->arcount = 0;
00380 
00381          //Query or response mDNS message?
00382          if(!queryResponse)
00383          {
00384             //In query messages, QR and AA bits must be set to zero
00385             message->dnsHeader->qr = 0;
00386             message->dnsHeader->aa = 0;
00387          }
00388          else
00389          {
00390             //In response messages, QR and AA bits must be set to one
00391             message->dnsHeader->qr = 1;
00392             message->dnsHeader->aa = 1;
00393          }
00394 
00395          //Number of shared resource records
00396          message->sharedRecordCount = 0;
00397          //Length of the mDNS message
00398          message->length = sizeof(DnsHeader);
00399 
00400          //Successful processing
00401          error = NO_ERROR;
00402       }
00403       else
00404       {
00405          //Clean up side effects
00406          mdnsDeleteMessage(message);
00407 
00408          //Report an error
00409          error = ERROR_FAILURE;
00410       }
00411    }
00412    else
00413    {
00414       //Failed to allocate memory
00415       error = ERROR_OUT_OF_RESOURCES;
00416    }
00417 
00418    //Return status code
00419    return error;
00420 }
00421 
00422 
00423 /**
00424  * @brief release a mDNS message
00425  * @param[in] message mDNS message to be released
00426  **/
00427 
00428 void mdnsDeleteMessage(MdnsMessage *message)
00429 {
00430    //Valid mDNS message?
00431    if(message->buffer != NULL)
00432    {
00433       //Free previously allocated memory
00434       netBufferFree(message->buffer);
00435 
00436       //The mDNS message is no more valid
00437       message->buffer = NULL;
00438       message->length = 0;
00439    }
00440 }
00441 
00442 
00443 /**
00444  * @brief Send mDNS message
00445  * @param[in] interface Underlying network interface
00446  * @param[in] message mDNS message to be sent
00447  * @param[in] destIpAddr Destination IP address (optional parameter)
00448  * @param[in] destPort Destination port
00449  * @return Error code
00450  **/
00451 
00452 error_t mdnsSendMessage(NetInterface *interface, const MdnsMessage *message,
00453    const IpAddr *destIpAddr, uint_t destPort)
00454 {
00455    error_t error;
00456    IpAddr ipAddr;
00457 
00458    //Make sure the mDNS message is valid
00459    if(message->buffer == NULL)
00460       return ERROR_FAILURE;
00461 
00462    //Convert 16-bit values to network byte order
00463    message->dnsHeader->qdcount = htons(message->dnsHeader->qdcount);
00464    message->dnsHeader->nscount = htons(message->dnsHeader->nscount);
00465    message->dnsHeader->ancount = htons(message->dnsHeader->ancount);
00466    message->dnsHeader->arcount = htons(message->dnsHeader->arcount);
00467 
00468    //Start of exception handling block
00469    do
00470    {
00471       //Adjust the length of the multi-part buffer
00472       error = netBufferSetLength(message->buffer, message->offset + message->length);
00473       //Any error to report?
00474       if(error)
00475          break;
00476 
00477       //Debug message
00478       TRACE_INFO("Sending mDNS message (%" PRIuSIZE " bytes)...\r\n", message->length);
00479       //Dump message
00480       dnsDumpMessage(message->dnsHeader, message->length);
00481 
00482       //Check whether the message should be sent to a specific IP address
00483       if(destIpAddr != NULL)
00484       {
00485          //All multicast DNS responses should be sent with an IP TTL set to 255
00486          error = udpSendDatagramEx(interface, MDNS_PORT, destIpAddr,
00487             destPort, message->buffer, message->offset, MDNS_DEFAULT_IP_TTL);
00488          //Any error to report?
00489          if(error)
00490             break;
00491       }
00492       else
00493       {
00494 #if (IPV4_SUPPORT == ENABLED)
00495          //Select the relevant multicast address (224.0.0.251)
00496          ipAddr.length = sizeof(Ipv4Addr);
00497          ipAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR;
00498 
00499          //All multicast DNS queries should be sent with an IP TTL set to 255
00500          error = udpSendDatagramEx(interface, MDNS_PORT, &ipAddr,
00501             MDNS_PORT, message->buffer, message->offset, MDNS_DEFAULT_IP_TTL);
00502          //Any error to report?
00503          if(error)
00504             break;
00505 #endif
00506 
00507 #if (IPV6_SUPPORT == ENABLED)
00508          //Select the relevant multicast address (ff02::fb)
00509          ipAddr.length = sizeof(Ipv6Addr);
00510          ipAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR;
00511 
00512          //All multicast DNS queries should be sent with an IP TTL set to 255
00513          error = udpSendDatagramEx(interface, MDNS_PORT, &ipAddr,
00514             MDNS_PORT, message->buffer, message->offset, MDNS_DEFAULT_IP_TTL);
00515          //Any error to report?
00516          if(error)
00517             break;
00518 #endif
00519       }
00520 
00521       //End of exception handling block
00522    } while(0);
00523 
00524    //Return status code
00525    return error;
00526 }
00527 
00528 
00529 /**
00530  * @brief Encode instance, service and domain names using the DNS name notation
00531  * @param[in] instance Instance name
00532  * @param[in] service Service name
00533  * @param[in] domain Domain name
00534  * @param[out] dest Pointer to the encoded name (optional parameter)
00535  * @return Length of the encoded domain name
00536  **/
00537 
00538 size_t mdnsEncodeName(const char_t *instance, const char_t *service,
00539    const char_t *domain, uint8_t *dest)
00540 {
00541    size_t n;
00542    size_t length;
00543 
00544    //Total length of the encoded name
00545    length = 0;
00546 
00547    //Any instance name?
00548    if(*instance != '\0')
00549    {
00550       //Encode instance name
00551       n = dnsEncodeName(instance, dest);
00552 
00553       //Failed to encode instance name?
00554       if(!n)
00555          return 0;
00556 
00557       //Update the length of the encoded name
00558       length += n;
00559    }
00560 
00561    //Any service name?
00562    if(*service != '\0')
00563    {
00564       //If an instance name precedes the service name, then
00565       //remove the null label
00566       if(length > 0)
00567          length--;
00568 
00569       //Encode service name
00570       if(dest != NULL)
00571          n = dnsEncodeName(service, dest + length);
00572       else
00573          n = dnsEncodeName(service, NULL);
00574 
00575       //Failed to encode instance name?
00576       if(!n)
00577          return 0;
00578 
00579       //Update the length of the encoded name
00580       length += n;
00581    }
00582 
00583    //Skip the separator that may precede the domain name
00584    if(*domain == '.')
00585       domain++;
00586 
00587    //Any domain name to encode?
00588    if(*domain != '\0')
00589    {
00590       //If an instance or a service name precedes the domain name, then
00591       //remove the null label
00592       if(length > 0)
00593          length--;
00594 
00595       //Encode domain name
00596       if(dest != NULL)
00597          n = dnsEncodeName(domain, dest + length);
00598       else
00599          n = dnsEncodeName(domain, NULL);
00600 
00601       //Failed to encode instance name?
00602       if(!n)
00603          return 0;
00604 
00605       //Update the length of the encoded name
00606       length += n;
00607    }
00608 
00609    //Return the length of the encoded string
00610    return length;
00611 }
00612 
00613 
00614 /**
00615  * @brief Compare instance, service and domain names
00616  * @param[in] message Pointer to the DNS message
00617  * @param[in] length Length of the DNS message
00618  * @param[in] pos Offset of the encoded name
00619  * @param[in] instance Instance name
00620  * @param[in] service Service name
00621  * @param[in] domain Domain name
00622  * @param[in] level Current level of recursion
00623  * @return The function returns 0 if the domain names match, -1 if the first
00624  *   domain name lexicographically precedes the second name, or 1 if the
00625  *   second domain name lexicographically precedes the first name
00626  **/
00627 
00628 int_t mdnsCompareName(const DnsHeader *message, size_t length, size_t pos,
00629    const char_t *instance, const char_t *service, const char_t *domain, uint_t level)
00630 {
00631    int_t res;
00632    size_t n;
00633    size_t pointer;
00634    uint8_t *p;
00635 
00636    //Check parameters
00637    if(instance == NULL || service == NULL || domain == NULL)
00638       return -2;
00639 
00640    //Recursion limit exceeded?
00641    if(level >= DNS_NAME_MAX_RECURSION)
00642       return -2;
00643 
00644    //Cast the DNS message to byte array
00645    p = (uint8_t *) message;
00646 
00647    //Skip the separator that may precede the domain name
00648    if(*domain == '.')
00649       domain++;
00650 
00651    //Parse encoded domain name
00652    while(pos < length)
00653    {
00654       //Retrieve the length of the current label
00655       n = p[pos];
00656 
00657       //End marker found?
00658       if(n == 0)
00659       {
00660          //The domain name which still has remaining data is deemed
00661          //lexicographically later
00662          if(*instance != '\0' || *service != '\0' || *domain != '\0')
00663             return -1;
00664 
00665          //The domain names match each other
00666          return 0;
00667       }
00668       //Compression tag found?
00669       if(n >= DNS_COMPRESSION_TAG)
00670       {
00671          //Malformed DNS message?
00672          if((pos + 1) >= length)
00673             return -2;
00674 
00675          //Read the most significant byte of the pointer
00676          pointer = (p[pos] & ~DNS_COMPRESSION_TAG) << 8;
00677          //Read the least significant byte of the pointer
00678          pointer |= p[pos + 1];
00679 
00680          //Compare the remaining part
00681          res = mdnsCompareName(message, length, pointer,
00682             instance, service, domain, level + 1);
00683 
00684          //Return comparison result
00685          return res;
00686       }
00687       else
00688       {
00689          //Advance data pointer
00690          pos++;
00691 
00692          //Malformed DNS message?
00693          if((pos + n) > length)
00694             return -2;
00695 
00696          //Compare current label
00697          if(*instance != '\0')
00698          {
00699             //Compare instance name
00700             res = strncasecmp((char_t *) p + pos, instance, n);
00701             //Any mismatch?
00702             if(res)
00703                return res;
00704 
00705             //Advance data pointer
00706             instance += n;
00707 
00708             //The instance name which still has remaining data is deemed
00709             //lexicographically later
00710             if(*instance != '\0' && *instance != '.')
00711                return -1;
00712 
00713             //Skip the separator character, if any
00714             if(*instance == '.')
00715                instance++;
00716          }
00717          else if(*service != '\0')
00718          {
00719             //Compare service name
00720             res = strncasecmp((char_t *) p + pos, service, n);
00721             //Any mismatch?
00722             if(res)
00723                return res;
00724 
00725             //Advance data pointer
00726             service += n;
00727 
00728             //The service name which still has remaining data is deemed
00729             //lexicographically later
00730             if(*service != '\0' && *service != '.')
00731                return -1;
00732 
00733             //Any separator in service name?
00734             if(*service == '.')
00735                service++;
00736          }
00737          else
00738          {
00739             //Compare domain name
00740             res = strncasecmp((char_t *) p + pos, domain, n);
00741             //Any mismatch?
00742             if(res)
00743                return res;
00744 
00745             //Advance data pointer
00746             domain += n;
00747 
00748             //The domain name which still has remaining data is deemed
00749             //lexicographically later
00750             if(*domain != '\0' && *domain != '.')
00751                return -1;
00752 
00753             //Any separator in domain name?
00754             if(*domain == '.')
00755                domain++;
00756          }
00757 
00758          //Advance data pointer
00759          pos += n;
00760       }
00761    }
00762 
00763    //Malformed DNS message
00764    return -2;
00765 }
00766 
00767 
00768 /**
00769  * @brief Compare resource records
00770  * @param[in] message1 Pointer to the first mDNS message
00771  * @param[in] offset1 Offset of the first but of the resource record
00772  * @param[in] record1 Pointer the first resource record
00773  * @param[in] message2 Pointer to the second mDNS message
00774  * @param[in] offset2 Offset of the first but of the resource record
00775  * @param[in] record2 Pointer the second resource record
00776  * @return The function returns 0 if the resource records match, -1 if the first
00777  *   resource record lexicographically precedes the second one, or 1 if the
00778  *   second resource record lexicographically precedes the first one
00779  **/
00780 
00781 int_t mdnsCompareRecord(const MdnsMessage *message1, size_t offset1,
00782    const DnsResourceRecord *record1, const MdnsMessage *message2,
00783    size_t offset2, const DnsResourceRecord *record2)
00784 {
00785    int_t res;
00786    size_t n1;
00787    size_t n2;
00788    uint16_t value1;
00789    uint16_t value2;
00790 
00791    //Convert the record class to host byte order
00792    value1 = ntohs(record1->rclass);
00793    value2 = ntohs(record2->rclass);
00794 
00795    //Discard cache-flush bit
00796    value1 &= ~MDNS_RCLASS_CACHE_FLUSH;
00797    value2 &= ~MDNS_RCLASS_CACHE_FLUSH;
00798 
00799    //The determination of lexicographically later record is performed by
00800    //first comparing the record class (excluding the cache-flush bit)
00801    if(value1 < value2)
00802       return -1;
00803    else if(value1 > value2)
00804       return 1;
00805 
00806    //Convert the record type to host byte order
00807    value1 = ntohs(record1->rtype);
00808    value2 = ntohs(record2->rtype);
00809 
00810    //Then compare the record type
00811    if(value1 < value2)
00812       return -1;
00813    else if(value1 > value2)
00814       return 1;
00815 
00816    //If the rrtype and rrclass both match, then the rdata is compared
00817    if(value1 == DNS_RR_TYPE_NS || value1 == DNS_RR_TYPE_SOA ||
00818       value1 == DNS_RR_TYPE_CNAME || value1 == DNS_RR_TYPE_PTR)
00819    {
00820       //Compute the offset of the first byte of the rdata
00821       n1 = record1->rdata - (uint8_t *) message1->dnsHeader;
00822       n2 = record2->rdata - (uint8_t *) message2->dnsHeader;
00823 
00824       //The names must be uncompressed before comparison
00825       res = dnsCompareEncodedName(message1->dnsHeader, message1->length,
00826          n1, message2->dnsHeader, message2->length, n2, 0);
00827    }
00828    else
00829    {
00830       //Retrieve the length of the rdata fields
00831       n1 = htons(record1->rdlength);
00832       n2 = htons(record2->rdlength);
00833 
00834       //The bytes of the raw uncompressed rdata are compared in turn, interpreting
00835       //the bytes as eight-bit unsigned values, until a byte is found whose value
00836       //is greater than that of its counterpart (in which case, the rdata whose
00837       //byte has the greater value is deemed lexicographically later) or one of the
00838       //resource records runs out of rdata (in which case, the resource record which
00839       //still has remaining data first is deemed lexicographically later)
00840       if(n1 < n2)
00841       {
00842          //Raw comparison of the binary content of the rdata
00843          res = memcmp(record1->rdata, record2->rdata, n1);
00844 
00845          //Check comparison result
00846          if(!res)
00847          {
00848             //The first resource records runs out of rdata
00849             res = -1;
00850          }
00851       }
00852       else if(n1 > n2)
00853       {
00854          //Raw comparison of the binary content of the rdata
00855          res = memcmp(record1->rdata, record2->rdata, n2);
00856 
00857          //Check comparison result
00858          if(!res)
00859          {
00860             //The second resource records runs out of rdata
00861             res = 1;
00862          }
00863       }
00864       else
00865       {
00866          //Raw comparison of the binary content of the rdata
00867          res = memcmp(record1->rdata, record2->rdata, n1);
00868       }
00869    }
00870 
00871    //Return comparison result
00872    return res;
00873 }
00874 
00875 
00876 /**
00877  * @brief Check for duplicate resource records
00878  * @param[in] message Pointer to the mDNS message
00879  * @param[in] instance Instance name
00880  * @param[in] service Service name
00881  * @param[in] domain Domain name
00882  * @param[in] rtype Resource record type
00883  * @return The function returns TRUE is the specified resource record is a
00884  *   duplicate. Otherwise FALSE is returned
00885  **/
00886 
00887 bool_t mdnsCheckDuplicateRecord(const MdnsMessage *message, const char_t *instance,
00888    const char_t *service, const char_t *domain, uint16_t rtype)
00889 {
00890    uint_t i;
00891    uint_t k;
00892    size_t n;
00893    size_t offset;
00894    uint16_t rclass;
00895    bool_t duplicate;
00896    DnsResourceRecord *record;
00897 
00898    //Clear flag
00899    duplicate = FALSE;
00900 
00901    //Point to the first question
00902    offset = sizeof(DnsHeader);
00903 
00904    //Parse the Question Section
00905    for(i = 0; i < message->dnsHeader->qdcount; i++)
00906    {
00907       //Parse domain name
00908       offset = dnsParseName(message->dnsHeader, message->length, offset, NULL, 0);
00909       //Invalid name?
00910       if(!offset)
00911          break;
00912 
00913       //Point to the next question
00914       offset += sizeof(DnsQuestion);
00915       //Make sure the mDNS message is valid
00916       if(offset > message->length)
00917          break;
00918    }
00919 
00920    //Successful processing?
00921    if(i == message->dnsHeader->qdcount)
00922    {
00923       //Compute the total number of resource records
00924       k = message->dnsHeader->ancount + message->dnsHeader->nscount +
00925          message->dnsHeader->arcount;
00926 
00927       //Loop through the resource records
00928       for(i = 0; i < k; i++)
00929       {
00930          //Parse resource record name
00931          n = dnsParseName(message->dnsHeader, message->length, offset, NULL, 0);
00932          //Invalid name?
00933          if(!n)
00934             break;
00935 
00936          //Point to the associated resource record
00937          record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, n);
00938          //Point to the resource data
00939          n += sizeof(DnsResourceRecord);
00940 
00941          //Make sure the resource record is valid
00942          if(n > message->length)
00943             break;
00944          if((n + ntohs(record->rdlength)) > message->length)
00945             break;
00946 
00947          //Convert the record class to host byte order
00948          rclass = ntohs(record->rclass);
00949          //Discard cache-flush bit
00950          rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
00951 
00952          //Check the class and the type of the resource record
00953          if(rclass == DNS_RR_CLASS_IN && ntohs(record->rtype) == rtype)
00954          {
00955             //Compare resource record name
00956             if(!mdnsCompareName(message->dnsHeader, message->length,
00957                offset, instance, service, domain, 0))
00958             {
00959                //The resource record is already present in the Answer Section
00960                duplicate = TRUE;
00961                //We are done
00962                break;
00963             }
00964          }
00965 
00966          //Point to the next resource record
00967          offset = n + ntohs(record->rdlength);
00968       }
00969    }
00970 
00971    //The function returns TRUE is the specified resource record is a duplicate
00972    return duplicate;
00973 }
00974 
00975 #endif
00976