Example #1
0
void nbnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params)
{
   size_t length;
   NbnsHeader *message;

   //Make sure the NBNS message was received from an IPv4 peer
   if(pseudoHeader->length != sizeof(Ipv4PseudoHeader))
      return;

   //Retrieve the length of the NBNS message
   length = netBufferGetLength(buffer) - offset;

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

   //Point to the NBNS message header
   message = netBufferAt(buffer, offset);
   //Sanity check
   if(message == NULL)
      return;

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

   //NBNS messages received with an opcode other than zero must be silently ignored
   if(message->opcode != DNS_OPCODE_QUERY)
      return;
   //NBNS messages received with non-zero response codes must be silently ignored
   if(message->rcode != DNS_RCODE_NO_ERROR)
      return;

   //NBNS query received?
   if(!message->qr)
   {
#if (NBNS_RESPONDER_SUPPORT == ENABLED)
      //Process incoming NBNS query message
      nbnsProcessQuery(interface, &pseudoHeader->ipv4Data,
         udpHeader, message, length);
#endif
   }
   //NBNS response received?
   else
   {
#if (NBNS_CLIENT_SUPPORT == ENABLED)
      //Process incoming NBNS response message
      nbnsProcessResponse(interface, &pseudoHeader->ipv4Data,
         udpHeader, message, length);
#endif
   }
}
Example #2
0
void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, const ChunkedBuffer *buffer, size_t offset, void *params)
{
   size_t length;
   DnsHeader *message;

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

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

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

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

   //mDNS messages received with an opcode other than zero must be silently ignored
   if(message->opcode != DNS_OPCODE_QUERY)
      return;
   //mDNS messages received with non-zero response codes must be silently ignored
   if(message->rcode != DNS_RCODE_NO_ERROR)
      return;

#if (MDNS_RESPONDER_SUPPORT == ENABLED)
   //mDNS query received?
   if(!message->qr)
   {
      //Process incoming mDNS query message
      mdnsProcessQuery(interface, pseudoHeader, udpHeader, message, length);
   }
#endif
#if (MDNS_CLIENT_SUPPORT == ENABLED)
   //mDNS response received?
   if(message->qr)
   {
      //Process incoming mDNS response message
      mdnsProcessResponse(interface, pseudoHeader, udpHeader, message, length);
   }
#endif
}
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;
}
Example #4
0
error_t nbnsSendResponse(NetInterface *interface,
   const IpAddr *destIpAddr, uint16_t destPort, uint16_t id)
{
   error_t error;
   size_t length;
   size_t offset;
   ChunkedBuffer *buffer;
   NbnsHeader *message;
   NbnsAddrEntry *addrEntry;
   DnsResourceRecord *resourceRecord;

   //Allocate a memory buffer to hold the NBNS response 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 = chunkedBufferAt(buffer, offset);

   //Take the identifier from the query message
   message->id = id;

   //Format NBNS response header
   message->qr = 1;
   message->opcode = DNS_OPCODE_QUERY;
   message->aa = 1;
   message->tc = 0;
   message->rd = 1;
   message->ra = 1;
   message->z = 0;
   message->b = 0;
   message->rcode = DNS_RCODE_NO_ERROR;

   //The NBNS response contains 1 answer resource record
   message->qdcount = 0;
   message->ancount = HTONS(1);
   message->nscount = 0;
   message->arcount = 0;

   //NBNS response message length
   length = sizeof(DnsHeader);

   //Encode the host name using the NBNS name notation
   length += nbnsEncodeName(interface->hostname, (uint8_t *) message + length);

   //Point to the corresponding resource record
   resourceRecord = DNS_GET_RESOURCE_RECORD(message, length);
   //Fill in resource record
   resourceRecord->rtype = HTONS(DNS_RR_TYPE_NB);
   resourceRecord->rclass = HTONS(DNS_RR_CLASS_IN);
   resourceRecord->ttl = HTONL(NBNS_DEFAULT_RESOURCE_RECORD_TTL);
   resourceRecord->rdlength = HTONS(sizeof(NbnsAddrEntry));

   //Point to the address entry array
   addrEntry = (NbnsAddrEntry *) resourceRecord->rdata;
   //Fill in address entry
   addrEntry->flags = HTONS(NBNS_G_UNIQUE | NBNS_ONT_BNODE);
   addrEntry->addr = interface->ipv4Config.addr;

   //Update the length of the NBNS response message
   length += sizeof(DnsResourceRecord) + sizeof(NbnsAddrEntry);

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

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

   //A response packet is always sent to the source UDP port and
   //source IP address of the request packet
   error = udpSendDatagramEx(interface, NBNS_PORT, destIpAddr,
      destPort, buffer, offset, IPV4_DEFAULT_TTL);

   //Free previously allocated memory
   chunkedBufferFree(buffer);
   //Return status code
   return error;
}
Example #5
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);
}
Example #6
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;
}
Example #7
0
error_t mdnsSendResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
   const UdpHeader *udpHeader, NetBuffer *buffer, size_t offset, size_t length)
{
   uint16_t destPort;
   IpAddr destIpAddr;
   DnsHeader *response;

#if (IPV4_SUPPORT == ENABLED)
   //Check whether the mDNS query was received from an IPv4 peer
   if(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(udpHeader->srcPort != HTONS(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 = pseudoHeader->ipv4Data.srcAddr;
      }
      else
      {
         //Use mDNS IPv4 multicast address
         destIpAddr.length = sizeof(Ipv4Addr);
         destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR;
      }
   }
#endif
#if (IPV6_SUPPORT == ENABLED)
   //Check whether the mDNS query was received from an IPv6 peer
   if(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(udpHeader->srcPort != HTONS(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 = pseudoHeader->ipv6Data.srcAddr;
      }
      else
      {
         //Use mDNS IPv6 multicast address
         destIpAddr.length = sizeof(Ipv6Addr);
         destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR;
      }
   }
#endif

   //Destination port
   destPort = ntohs(udpHeader->srcPort);

   //Point to the mDNS response header
   response = netBufferAt(buffer, offset);
   //Convert 16-bit value to network byte order
   response->ancount = htons(response->ancount);

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

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

   //All multicast DNS responses should be sent with an IP TTL set to 255
   return udpSendDatagramEx(interface, MDNS_PORT, &destIpAddr,
      destPort, buffer, offset, MDNS_DEFAULT_IP_TTL);
}
Example #8
0
error_t mdnsSendAnnouncement(NetInterface *interface)
{
   error_t error;
   size_t length;
   size_t offset;
   NetBuffer *buffer;
   DnsHeader *message;
   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 = 1;
   message->opcode = DNS_OPCODE_QUERY;
   message->aa = 1;
   message->tc = 0;
   message->rd = 0;
   message->ra = 0;
   message->z = 0;
   message->rcode = DNS_RCODE_NO_ERROR;

   //Multicast DNS responses must not contain any questions
   message->qdcount = 0;
   message->ancount = 0;
   message->nscount = 0;
   message->arcount = 0;

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

   //Start of exception handling block
   do
   {
      //Format A resource record
      error = mdnsAddIpv4ResourceRecord(interface, message, &length, TRUE);
      //Any error to report?
      if(error) break;

      //Format AAAA resource record
      error = mdnsAddIpv6ResourceRecord(interface, message, &length, TRUE);
      //Any error to report?
      if(error) break;

      //Convert 16-bit value to network byte order
      message->ancount = htons(message->ancount);

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

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

#if (IPV4_SUPPORT == ENABLED)
      //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);
      //Any error to report?
      if(error)break;
#endif

#if (IPV6_SUPPORT == ENABLED)
      //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);
      //Any error to report?
      if(error)break;
#endif

      //End of exception handling block
   } while(0);

   //Free previously allocated memory
   netBufferFree(buffer);
   //Return status code
   return error;
}
Example #9
0
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;
}