Exemple #1
0
void nbnsProcessQuery(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, const NbnsHeader *message, size_t length)
{
   size_t pos;
   DnsQuestion *question;

   //The NBNS query shall contain one question
   if(ntohs(message->qdcount) != 1)
      return;

   //Parse NetBIOS name
   pos = nbnsParseName(message, length, sizeof(DnsHeader), NULL);

   //Invalid name?
   if(!pos)
      return;
   //Malformed mDNS message?
   if((pos + sizeof(DnsQuestion)) > length)
      return;

   //Point to the corresponding entry
   question = DNS_GET_QUESTION(message, pos);

   //Check the class and the type of the request
   if(ntohs(question->qclass) != DNS_RR_CLASS_IN)
      return;
   if(ntohs(question->qtype) != DNS_RR_TYPE_NB)
      return;

   //Compare NetBIOS names
   if(nbnsCompareName(message, length, sizeof(DnsHeader), interface->hostname))
   {
      uint16_t destPort;
      IpAddr destIpAddr;

      //A response packet is always sent to the source UDP port and
      //source IP address of the request packet
      destIpAddr.length = sizeof(Ipv4Addr);
      destIpAddr.ipv4Addr = pseudoHeader->srcAddr;

      //Convert the port number to host byte order
      destPort = ntohs(udpHeader->srcPort);

      //Send NBNS response
      nbnsSendResponse(interface, &destIpAddr, destPort, message->id);
   }
}
error_t nbnsSendQuery(DnsCacheEntry *entry)
{
   error_t error;
   size_t length;
   size_t offset;
   NetBuffer *buffer;
   NbnsHeader *message;
   DnsQuestion *dnsQuestion;
   IpAddr destIpAddr;

   //Allocate a memory buffer to hold the NBNS query message
   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
   //Failed to allocate buffer?
   if(!buffer) return ERROR_OUT_OF_MEMORY;

   //Point to the NBNS header
   message = netBufferAt(buffer, offset);

   //Format NBNS query message
   message->id = htons(entry->id);
   message->qr = 0;
   message->opcode = DNS_OPCODE_QUERY;
   message->aa = 0;
   message->tc = 0;
   message->rd = 0;
   message->ra = 0;
   message->z = 0;
   message->b = 1;
   message->rcode = DNS_RCODE_NO_ERROR;

   //The NBNS query contains one question
   message->qdcount = HTONS(1);
   message->ancount = 0;
   message->nscount = 0;
   message->arcount = 0;

   //Length of the NBNS query message
   length = sizeof(DnsHeader);

   //Encode the NetBIOS name
   length += nbnsEncodeName(entry->name, message->questions);

   //Point to the corresponding question structure
   dnsQuestion = DNS_GET_QUESTION(message, length);
   //Fill in question structure
   dnsQuestion->qtype = HTONS(DNS_RR_TYPE_NB);
   dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);

   //Update the length of the NBNS query message
   length += sizeof(DnsQuestion);

   //Adjust the length of the multi-part buffer
   netBufferSetLength(buffer, offset + length);

   //Debug message
   TRACE_INFO("Sending NBNS message (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message
   dnsDumpMessage((DnsHeader *) message, length);

   //The destination address is the broadcast address
   destIpAddr.length = sizeof(Ipv4Addr);
   ipv4GetBroadcastAddr(entry->interface, &destIpAddr.ipv4Addr);

   //A request packet is always sent to the well known port 137
   error = udpSendDatagramEx(entry->interface, NBNS_PORT,
      &destIpAddr, NBNS_PORT, buffer, offset, IPV4_DEFAULT_TTL);

   //Free previously allocated memory
   netBufferFree(buffer);
   //Return status code
   return error;
}
Exemple #3
0
void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params)
{
   uint_t i;
   uint_t j;
   size_t n;
   size_t pos;
   size_t length;
   DnsHeader *message;
   DnsQuestion *question;
   DnsResourceRecord *resourceRecord;
   DnsCacheEntry *entry;

   //Retrieve the length of the DNS message
   length = chunkedBufferGetLength(buffer) - offset;

   //Ensure the DNS message is valid
   if(length < sizeof(DnsHeader))
      return;
   if(length > DNS_MESSAGE_MAX_SIZE)
      return;

   //Point to the DNS message header
   message = chunkedBufferAt(buffer, offset);
   //Sanity check
   if(!message) return;

   //Debug message
   TRACE_INFO("DNS message received (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message
   dnsDumpMessage(message, length);

   //Acquire exclusive access to the DNS cache
   osMutexAcquire(dnsCacheMutex);

   //Loop through DNS cache entries
   for(i = 0; i < DNS_CACHE_SIZE; i++)
   {
      //Point to the current entry
      entry = &dnsCache[i];

      //DNS name resolution in progress?
      if(entry->state == DNS_STATE_IN_PROGRESS &&
         entry->protocol == HOST_NAME_RESOLVER_DNS)
      {
         //Check destination port number
         if(entry->port == ntohs(udpHeader->destPort))
         {
            //Compare identifier against expected one
            if(ntohs(message->id) != entry->id)
               break;
            //Check message type
            if(!message->qr)
               break;
            //The DNS message shall contain one question
            if(ntohs(message->qdcount) != 1)
               break;

            //Point to the first question
            pos = sizeof(DnsHeader);

            //Parse domain name
            n = dnsParseName(message, length, pos, NULL, 0);

            //Invalid name?
            if(!n)
               break;
            //Malformed mDNS message?
            if((n + sizeof(DnsQuestion)) > length)
               break;

            //Compare domain name
            if(!dnsCompareName(message, length, pos, entry->name, 0))
               break;

            //Point to the corresponding entry
            question = DNS_GET_QUESTION(message, n);

            //Check the class of the query
            if(ntohs(question->qclass) != DNS_RR_CLASS_IN)
               break;
            //Check the type of the query
            if(entry->type == HOST_TYPE_IPV4 && ntohs(question->qtype) != DNS_RR_TYPE_A)
               break;
            if(entry->type == HOST_TYPE_IPV6 && ntohs(question->qtype) != DNS_RR_TYPE_AAAA)
               break;

            //Make sure recursion is available
            if(!message->ra)
            {
               //The entry should be deleted since name resolution has failed
               dnsDeleteEntry(entry);
               //Exit immediately
               break;
            }

            //Check return code
            if(message->rcode != DNS_RCODE_NO_ERROR)
            {
               //The entry should be deleted since name resolution has failed
               dnsDeleteEntry(entry);
               //Exit immediately
               break;
            }

            //Point to the first answer
            pos = n + sizeof(DnsQuestion);

            //Parse answer resource records
            for(j = 0; j < ntohs(message->ancount); j++)
            {
               //Parse domain name
               pos = dnsParseName(message, length, pos, NULL, 0);
               //Invalid name?
               if(!pos) break;

               //Point to the associated resource record
               resourceRecord = DNS_GET_RESOURCE_RECORD(message, pos);
               //Point to the resource data
               pos += sizeof(DnsResourceRecord);

               //Make sure the resource record is valid
               if(pos >= length)
                  break;
               if((pos + ntohs(resourceRecord->rdlength)) > length)
                  break;

#if (IPV4_SUPPORT == ENABLED)
               //IPv4 address expected?
               if(entry->type == HOST_TYPE_IPV4)
               {
                  //A resource record found?
                  if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_A)
                  {
                     //Verify the length of the data field
                     if(ntohs(resourceRecord->rdlength) == sizeof(Ipv4Addr))
                     {
                        //Copy the IPv4 address
                        entry->ipAddr.length = sizeof(Ipv4Addr);
                        ipv4CopyAddr(&entry->ipAddr.ipv4Addr, resourceRecord->rdata);

                        //Save current time
                        entry->timestamp = osGetTickCount();
                        //Save TTL value
                        entry->timeout = ntohl(resourceRecord->ttl) * 1000;
                        //Limit the lifetime of the DNS cache entries
                        entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME);

                        //Unregister UDP callback function
                        udpDetachRxCallback(interface, entry->port);
                        //Host name successfully resolved
                        entry->state = DNS_STATE_RESOLVED;
                        //Exit immediately
                        break;
                     }
                  }
               }
#endif
#if (IPV6_SUPPORT == ENABLED)
               //IPv6 address expected?
               if(entry->type == HOST_TYPE_IPV6)
               {
                  //AAAA resource record found?
                  if(ntohs(resourceRecord->rtype) == DNS_RR_TYPE_AAAA)
                  {
                     //Verify the length of the data field
                     if(ntohs(resourceRecord->rdlength) == sizeof(Ipv6Addr))
                     {
                        //Copy the IPv6 address
                        entry->ipAddr.length = sizeof(Ipv6Addr);
                        ipv6CopyAddr(&entry->ipAddr.ipv6Addr, resourceRecord->rdata);

                        //Save current time
                        entry->timestamp = osGetTickCount();
                        //Save TTL value
                        entry->timeout = ntohl(resourceRecord->ttl) * 1000;
                        //Limit the lifetime of the DNS cache entries
                        entry->timeout = min(entry->timeout, DNS_MAX_LIFETIME);

                        //Unregister UDP callback function
                        udpDetachRxCallback(interface, entry->port);
                        //Host name successfully resolved
                        entry->state = DNS_STATE_RESOLVED;
                        //Exit immediately
                        break;
                     }
                  }
               }
#endif
               //Point to the next resource record
               pos += ntohs(resourceRecord->rdlength);
            }

            //We are done
            break;
         }
      }
   }

   //Release exclusive access to the DNS cache
   osMutexRelease(dnsCacheMutex);
}
Exemple #4
0
error_t dnsSendQuery(DnsCacheEntry *entry)
{
   error_t error;
   size_t length;
   size_t offset;
   ChunkedBuffer *buffer;
   DnsHeader *message;
   DnsQuestion *dnsQuestion;
   IpAddr destIpAddr;

#if (IPV4_SUPPORT == ENABLED)
   //An IPv4 address is expected?
   if(entry->type == HOST_TYPE_IPV4)
   {
      //Select the relevant DNS server
      destIpAddr.length = sizeof(Ipv4Addr);
      ipv4GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv4Addr);

      //Make sure the IP address is valid
      if(destIpAddr.ipv4Addr == IPV4_UNSPECIFIED_ADDR)
         return ERROR_NO_DNS_SERVER;
   }
   else
#endif
#if (IPV6_SUPPORT == ENABLED)
   //An IPv6 address is expected?
   if(entry->type == HOST_TYPE_IPV6)
   {
      //Select the relevant DNS server
      destIpAddr.length = sizeof(Ipv6Addr);
      ipv6GetDnsServer(entry->interface, entry->dnsServerNum, &destIpAddr.ipv6Addr);

      //Make sure the IP address is valid
      if(ipv6CompAddr(&destIpAddr.ipv6Addr, &IPV6_UNSPECIFIED_ADDR))
         return ERROR_NO_DNS_SERVER;
   }
   else
#endif
   //Invalid host type?
   {
      //Report an error
      return ERROR_INVALID_PARAMETER;
   }

   //Allocate a memory buffer to hold the DNS query message
   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
   //Failed to allocate buffer?
   if(!buffer) return ERROR_OUT_OF_MEMORY;

   //Point to the DNS header
   message = chunkedBufferAt(buffer, offset);

   //Format DNS query message
   message->id = htons(entry->id);
   message->qr = 0;
   message->opcode = DNS_OPCODE_QUERY;
   message->aa = 0;
   message->tc = 0;
   message->rd = 1;
   message->ra = 0;
   message->z = 0;
   message->rcode = DNS_RCODE_NO_ERROR;

   //The DNS query contains one question
   message->qdcount = HTONS(1);
   message->ancount = 0;
   message->nscount = 0;
   message->arcount = 0;

   //Length of the DNS query message
   length = sizeof(DnsHeader);

   //Encode the host name using the DNS name notation
   length += dnsEncodeName(entry->name, message->questions);

   //Point to the corresponding question structure
   dnsQuestion = DNS_GET_QUESTION(message, length);

#if (IPV4_SUPPORT == ENABLED)
   //An IPv4 address is expected?
   if(entry->type == HOST_TYPE_IPV4)
   {
      //Fill in question structure
      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A);
      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
   }
#endif
#if (IPV6_SUPPORT == ENABLED)
   //An IPv6 address is expected?
   if(entry->type == HOST_TYPE_IPV6)
   {
      //Fill in question structure
      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA);
      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
   }
#endif

   //Update the length of the DNS query message
   length += sizeof(DnsQuestion);

   //Adjust the length of the multi-part buffer
   chunkedBufferSetLength(buffer, offset + length);

   //Debug message
   TRACE_INFO("Sending DNS message (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message
   dnsDumpMessage(message, length);

   //Send DNS query message
   error = udpSendDatagramEx(entry->interface, entry->port,
      &destIpAddr, DNS_PORT, buffer, offset, 0);

   //Free previously allocated memory
   chunkedBufferFree(buffer);
   //Return status code
   return error;
}
error_t mdnsSendProbe(NetInterface *interface)
{
   error_t error;
   size_t length;
   size_t offset;
   NetBuffer *buffer;
   DnsHeader *message;
   DnsQuestion *dnsQuestion;
   IpAddr destIpAddr;

   //Initialize error code
   error = NO_ERROR;

   //Allocate a memory buffer to hold the mDNS query message
   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
   //Failed to allocate buffer?
   if(!buffer) return ERROR_OUT_OF_MEMORY;

   //Point to the mDNS header
   message = netBufferAt(buffer, offset);

   //Format mDNS query message
   message->id = 0;
   message->qr = 0;
   message->opcode = DNS_OPCODE_QUERY;
   message->aa = 0;
   message->tc = 0;
   message->rd = 0;
   message->ra = 0;
   message->z = 0;
   message->rcode = DNS_RCODE_NO_ERROR;

   //The mDNS query contains one question
   message->qdcount = HTONS(1);
   message->ancount = 0;
   message->nscount = 0;
   message->arcount = 0;

   //Length of the mDNS query message
   length = sizeof(DnsHeader);

   //Encode the host name using the DNS name notation
   length += mdnsEncodeName(interface->hostname, "",
      ".local", (uint8_t *) message + length);

   //Point to the corresponding question structure
   dnsQuestion = DNS_GET_QUESTION(message, length);
   //Fill in question structure
   dnsQuestion->qtype = HTONS(DNS_RR_TYPE_ANY);
   dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);

   //Update the length of the mDNS query message
   length += sizeof(DnsQuestion);

   //Adjust the length of the multi-part buffer
   netBufferSetLength(buffer, offset + length);

   //Debug message
   TRACE_INFO("Sending mDNS probe (%" PRIuSIZE " bytes)...\r\n", length);
   //Dump message
   dnsDumpMessage(message, length);

#if (IPV4_SUPPORT == ENABLED)
   //Check status code
   if(!error)
   {
      //Select the relevant multicast address (224.0.0.251)
      destIpAddr.length = sizeof(Ipv4Addr);
      destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR;

      //All multicast DNS queries should be sent with an IP TTL set to 255
      error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr,
         MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL);
   }
#endif

#if (IPV6_SUPPORT == ENABLED)
   //Check status code
   if(!error)
   {
      //Select the relevant multicast address (ff02::fb)
      destIpAddr.length = sizeof(Ipv6Addr);
      destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR;

      //All multicast DNS queries should be sent with an IP TTL set to 255
      error = udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr,
         MDNS_PORT, buffer, offset, MDNS_DEFAULT_IP_TTL);
   }
#endif

   //Free previously allocated memory
   netBufferFree(buffer);
   //Return status code
   return error;
}
void mdnsProcessQuery(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, const DnsHeader *query, size_t queryLen)
{
   error_t error;
   uint_t i;
   size_t pos;
   size_t offset;
   size_t responseLen;
   DnsHeader *response;
   NetBuffer *buffer;

   //Check the state of the mDNS responder
   if(interface->mdnsContext.state != MDNS_STATE_DONE)
      return;

   //Initialize error code
   error = NO_ERROR;

   //This buffer will hold the mDNS response, if any
   buffer = NULL;
   //Point to the first question
   pos = sizeof(DnsHeader);

   //Parse question section
   for(i = 0; i < ntohs(query->qdcount); i++)
   {
      size_t n;
      uint16_t qclass;
      uint16_t qtype;
      DnsQuestion *question;

      //Parse domain name
      n = dnsParseName(query, queryLen, pos, NULL, 0);

      //Invalid name?
      if(!n)
         break;
      //Malformed mDNS message?
      if((n + sizeof(DnsQuestion)) > queryLen)
         break;

      //Point to the corresponding entry
      question = DNS_GET_QUESTION(query, n);
      //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);

      //Check the class of the query
      if(qclass == DNS_RR_CLASS_IN || qclass == DNS_RR_CLASS_ANY)
      {
#if (DNS_SD_SUPPORT == ENABLED)
         //PTR query?
         if(qtype == DNS_RR_TYPE_PTR || qtype == DNS_RR_TYPE_ANY)
         {
            uint_t j;

            //Service type enumeration meta-query received?
            if(mdnsCompareName(query, queryLen, pos, "", "_services._dns-sd._udp", ".local", 0))
            {
               //Any registered services?
               if(interface->numServices > 0 && interface->instanceName != NULL)
               {
                  //Create a mDNS response message only if necessary
                  if(!buffer)
                  {
                     //Allocate the mDNS response message
                     buffer = mdnsCreateResponse(query->id, &offset, &response, &responseLen);
                     //Failed to allocate memory?
                     if(!buffer) break;
                  }

                  //Loop through registered services
                  for(j = 0; j < interface->numServices; j++)
                  {
                     //Format PTR resource record
                     error = dnsSdAddPtrResourceRecord(interface,
                        &interface->services[j], TRUE, response, &responseLen);
                     //Any error to report?
                     if(error) break;
                  }

                  //Propagate exception if necessary...
                  if(error) break;
               }
            }
            else
            {
               //Loop through registered services
               for(j = 0; j < interface->numServices; j++)
               {
                  //Compare service name
                  if(mdnsCompareName(query, queryLen, pos, "",
                     interface->services[j].name, ".local", 0))
                  {
                     //The current service name matches a registered service
                     break;
                  }
               }

               //Any matching service name?
               if(j < interface->numServices && interface->instanceName != NULL)
               {
                  //Create a mDNS response message only if necessary
                  if(!buffer)
                  {
                     //Allocate the mDNS response message
                     buffer = mdnsCreateResponse(query->id, &offset, &response, &responseLen);
                     //Failed to allocate memory?
                     if(!buffer) break;
                  }

                  //Format PTR resource record
                  error = dnsSdAddPtrResourceRecord(interface,
                     &interface->services[j], FALSE, response, &responseLen);
                  //Any error to report?
                  if(error) break;

                  //Format TXT resource record
                  error = dnsSdAddTxtResourceRecord(interface,
                     &interface->services[j], response, &responseLen);
                  //Any error to report?
                  if(error) break;

                  //Format SRV resource record
                  error = dnsSdAddSrvResourceRecord(interface,
                     &interface->services[j], response, &responseLen);
                  //Any error to report?
                  if(error) break;

                  //Format A resource record
                  error = mdnsAddIpv4ResourceRecord(interface,
                     response, &responseLen, FALSE);
                  //Any error to report?
                  if(error) break;

                  //Format AAAA resource record
                  error = mdnsAddIpv6ResourceRecord(interface,
                     response, &responseLen, FALSE);
                  //Any error to report?
                  if(error) break;
               }
            }
         }
         //SRV or TXT query?
         else if(qtype == DNS_RR_TYPE_SRV || qtype == DNS_RR_TYPE_TXT)
         {
            uint_t j;

            //Loop through registered services
            for(j = 0; j < interface->numServices; j++)
            {
               //Compare service name
               if(mdnsCompareName(query, queryLen, pos, interface->instanceName,
                  interface->services[j].name, ".local", 0))
               {
                  //The current service name matches a registered service
                  break;
               }
            }

            //Any matching service name?
            if(j < interface->numServices && interface->instanceName != NULL)
            {
               //Create a mDNS response message only if necessary
               if(!buffer)
               {
                  //Allocate the mDNS response message
                  buffer = mdnsCreateResponse(query->id, &offset, &response, &responseLen);
                  //Failed to allocate memory?
                  if(!buffer) break;
               }

               //Check query type?
               if(qtype == DNS_RR_TYPE_TXT)
               {
                  //Format TXT resource record
                  error = dnsSdAddTxtResourceRecord(interface,
                     &interface->services[j], response, &responseLen);
                  //Any error to report?
                  if(error) break;
               }
               else if(qtype == DNS_RR_TYPE_SRV)
               {
                  //Format SRV resource record
                  error = dnsSdAddSrvResourceRecord(interface,
                     &interface->services[j], response, &responseLen);
                  //Any error to report?
                  if(error) break;

                  //Format A resource record
                  error = mdnsAddIpv4ResourceRecord(interface,
                     response, &responseLen, FALSE);
                  //Any error to report?
                  if(error) break;

                  //Format AAAA resource record
                  error = mdnsAddIpv6ResourceRecord(interface,
                     response, &responseLen, FALSE);
                  //Any error to report?
                  if(error) break;
               }
            }
         }
#endif
#if (IPV4_SUPPORT == ENABLED)
         //A query?
#if (DNS_SD_SUPPORT == ENABLED)
         if(qtype == DNS_RR_TYPE_A)
#else
         if(qtype == DNS_RR_TYPE_A || qtype == DNS_RR_TYPE_ANY)
#endif
         {
            //Compare domain name
            if(mdnsCompareName(query, queryLen, pos, interface->hostname, "", ".local", 0))
            {
               //Create a mDNS response message only if necessary
               if(!buffer)
               {
                  //Allocate the mDNS response message
                  buffer = mdnsCreateResponse(query->id, &offset, &response, &responseLen);
                  //Failed to allocate memory?
                  if(!buffer) break;
               }

               //Format A resource record
               error = mdnsAddIpv4ResourceRecord(interface, response, &responseLen, FALSE);
               //Any error to report?
               if(error) break;
            }
         }
#endif
#if (IPV6_SUPPORT == ENABLED)
         //AAAA query?
#if (DNS_SD_SUPPORT == ENABLED)
         if(qtype == DNS_RR_TYPE_AAAA)
#else
         if(qtype == DNS_RR_TYPE_AAAA || qtype == DNS_RR_TYPE_ANY)
#endif
         {
            //Compare domain name
            if(mdnsCompareName(query, queryLen, pos, interface->hostname, "", ".local", 0))
            {
               //Create a mDNS response message only if necessary
               if(!buffer)
               {
                  //Allocate the mDNS response message
                  buffer = mdnsCreateResponse(query->id, &offset, &response, &responseLen);
                  //Failed to allocate memory?
                  if(!buffer) break;
               }

               //Format AAAA resource record
               error = mdnsAddIpv6ResourceRecord(interface, response, &responseLen, FALSE);
               //Any error to report?
               if(error) break;
            }
         }
#endif
      }

      //Point to the next question
      pos = n + sizeof(DnsQuestion);
   }

   //Any response to send?
   if(buffer != NULL)
   {
      //Check status code
      if(!error)
      {
         //Send mDNS response message
         mdnsSendResponse(interface, pseudoHeader, udpHeader,
            buffer, offset, responseLen);
      }

      //Free previously allocated memory
      netBufferFree(buffer);
   }
}