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; }
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); }
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); } }