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