Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers dns_client.c Source File

dns_client.c

Go to the documentation of this file.
00001 /**
00002  * @file dns_client.c
00003  * @brief DNS client (Domain Name System)
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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00026  * @version 1.7.6
00027  **/
00028 
00029 //Switch to the appropriate trace level
00030 #define TRACE_LEVEL DNS_TRACE_LEVEL
00031 
00032 //Dependencies
00033 #include "core/net.h"
00034 #include "dns/dns_cache.h"
00035 #include "dns/dns_client.h"
00036 #include "dns/dns_common.h"
00037 #include "dns/dns_debug.h"
00038 #include "debug.h"
00039 
00040 //Check TCP/IP stack configuration
00041 #if (DNS_CLIENT_SUPPORT == ENABLED)
00042 
00043 
00044 /**
00045  * @brief Resolve a host name using DNS
00046  * @param[in] interface Underlying network interface
00047  * @param[in] name Name of the host to be resolved
00048  * @param[in] type Host type (IPv4 or IPv6)
00049  * @param[out] ipAddr IP address corresponding to the specified host name
00050  **/
00051 
00052 error_t dnsResolve(NetInterface *interface,
00053    const char_t *name, HostType type, IpAddr *ipAddr)
00054 {
00055    error_t error;
00056    DnsCacheEntry *entry;
00057 
00058 #if (NET_RTOS_SUPPORT == ENABLED)
00059    systime_t delay;
00060 
00061    //Debug message
00062    TRACE_INFO("Resolving host name %s (DNS resolver)...\r\n", name);
00063 #endif
00064 
00065    //Get exclusive access
00066    osAcquireMutex(&netMutex);
00067 
00068    //Search the DNS cache for the specified host name
00069    entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_DNS);
00070 
00071    //Check whether a matching entry has been found
00072    if(entry)
00073    {
00074       //Host name already resolved?
00075       if(entry->state == DNS_STATE_RESOLVED ||
00076          entry->state == DNS_STATE_PERMANENT)
00077       {
00078          //Return the corresponding IP address
00079          *ipAddr = entry->ipAddr;
00080          //Successful host name resolution
00081          error = NO_ERROR;
00082       }
00083       else
00084       {
00085          //Host name resolution is in progress...
00086          error = ERROR_IN_PROGRESS;
00087       }
00088    }
00089    else
00090    {
00091       //If no entry exists, then create a new one
00092       entry = dnsCreateEntry();
00093 
00094       //Record the host name whose IP address is unknown
00095       strcpy(entry->name, name);
00096 
00097       //Initialize DNS cache entry
00098       entry->type = type;
00099       entry->protocol = HOST_NAME_RESOLVER_DNS;
00100       entry->interface = interface;
00101       //Select primary DNS server
00102       entry->dnsServerNum = 0;
00103       //Get an ephemeral port number
00104       entry->port = udpGetDynamicPort();
00105 
00106       //An identifier is used by the DNS client to match replies
00107       //with corresponding requests
00108       entry->id = netGetRand();
00109 
00110       //Callback function to be called when a DNS response is received
00111       error = udpAttachRxCallback(interface, entry->port, dnsProcessResponse, NULL);
00112 
00113       //Check status code
00114       if(!error)
00115       {
00116          //Initialize retransmission counter
00117          entry->retransmitCount = DNS_CLIENT_MAX_RETRIES;
00118          //Send DNS query
00119          error = dnsSendQuery(entry);
00120 
00121          //DNS message successfully sent?
00122          if(!error)
00123          {
00124             //Save the time at which the query message was sent
00125             entry->timestamp = osGetSystemTime();
00126             //Set timeout value
00127             entry->timeout = DNS_CLIENT_INIT_TIMEOUT;
00128             entry->maxTimeout = DNS_CLIENT_MAX_TIMEOUT;
00129             //Decrement retransmission counter
00130             entry->retransmitCount--;
00131 
00132             //Switch state
00133             entry->state = DNS_STATE_IN_PROGRESS;
00134             //Host name resolution is in progress
00135             error = ERROR_IN_PROGRESS;
00136          }
00137          else
00138          {
00139             //Unregister callback function
00140             udpDetachRxCallback(interface, entry->port);
00141          }
00142       }
00143    }
00144 
00145    //Release exclusive access
00146    osReleaseMutex(&netMutex);
00147 
00148 #if (NET_RTOS_SUPPORT == ENABLED)
00149    //Set default polling interval
00150    delay = DNS_CACHE_INIT_POLLING_INTERVAL;
00151 
00152    //Wait the host name resolution to complete
00153    while(error == ERROR_IN_PROGRESS)
00154    {
00155       //Wait until the next polling period
00156       osDelayTask(delay);
00157 
00158       //Get exclusive access
00159       osAcquireMutex(&netMutex);
00160 
00161       //Search the DNS cache for the specified host name
00162       entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_DNS);
00163 
00164       //Check whether a matching entry has been found
00165       if(entry)
00166       {
00167          //Host name successfully resolved?
00168          if(entry->state == DNS_STATE_RESOLVED)
00169          {
00170             //Return the corresponding IP address
00171             *ipAddr = entry->ipAddr;
00172             //Successful host name resolution
00173             error = NO_ERROR;
00174          }
00175       }
00176       else
00177       {
00178          //Host name resolution failed
00179          error = ERROR_FAILURE;
00180       }
00181 
00182       //Release exclusive access
00183       osReleaseMutex(&netMutex);
00184 
00185       //Backoff support for less aggressive polling
00186       delay = MIN(delay * 2, DNS_CACHE_MAX_POLLING_INTERVAL);
00187    }
00188 
00189    //Check status code
00190    if(error)
00191    {
00192       //Failed to resolve host name
00193       TRACE_INFO("Host name resolution failed!\r\n");
00194    }
00195    else
00196    {
00197       //Successful host name resolution
00198       TRACE_INFO("Host name resolved to %s...\r\n", ipAddrToString(ipAddr, NULL));
00199    }
00200 #endif
00201 
00202    //Return status code
00203    return error;
00204 }
00205 
00206 
00207 /**
00208  * @brief Send a DNS query message
00209  * @param[in] entry Pointer to a valid DNS cache entry
00210  * @return Error code
00211  **/
00212 
00213 error_t dnsSendQuery(DnsCacheEntry *entry)
00214 {
00215    error_t error;
00216    size_t length;
00217    size_t offset;
00218    NetBuffer *buffer;
00219    DnsHeader *message;
00220    DnsQuestion *dnsQuestion;
00221    IpAddr destIpAddr;
00222 
00223 #if (IPV4_SUPPORT == ENABLED)
00224    //An IPv4 address is expected?
00225    if(entry->type == HOST_TYPE_IPV4)
00226    {
00227       //Point to the IPv4 context
00228       Ipv4Context *ipv4Context = &entry->interface->ipv4Context;
00229 
00230       //Out of range index?
00231       if(entry->dnsServerNum >= IPV4_DNS_SERVER_LIST_SIZE)
00232          return ERROR_NO_DNS_SERVER;
00233 
00234       //Select the relevant DNS server
00235       destIpAddr.length = sizeof(Ipv4Addr);
00236       destIpAddr.ipv4Addr = ipv4Context->dnsServerList[entry->dnsServerNum];
00237 
00238       //Make sure the IP address is valid
00239       if(destIpAddr.ipv4Addr == IPV4_UNSPECIFIED_ADDR)
00240          return ERROR_NO_DNS_SERVER;
00241    }
00242    else
00243 #endif
00244 #if (IPV6_SUPPORT == ENABLED)
00245    //An IPv6 address is expected?
00246    if(entry->type == HOST_TYPE_IPV6)
00247    {
00248       //Point to the IPv6 context
00249       Ipv6Context *ipv6Context = &entry->interface->ipv6Context;
00250 
00251       //Out of range index?
00252       if(entry->dnsServerNum >= IPV6_DNS_SERVER_LIST_SIZE)
00253          return ERROR_NO_DNS_SERVER;
00254 
00255       //Select the relevant DNS server
00256       destIpAddr.length = sizeof(Ipv6Addr);
00257       destIpAddr.ipv6Addr = ipv6Context->dnsServerList[entry->dnsServerNum];
00258 
00259       //Make sure the IP address is valid
00260       if(ipv6CompAddr(&destIpAddr.ipv6Addr, &IPV6_UNSPECIFIED_ADDR))
00261          return ERROR_NO_DNS_SERVER;
00262    }
00263    else
00264 #endif
00265    //Invalid host type?
00266    {
00267       //Report an error
00268       return ERROR_INVALID_PARAMETER;
00269    }
00270 
00271    //Allocate a memory buffer to hold the DNS query message
00272    buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
00273    //Failed to allocate buffer?
00274    if(buffer == NULL)
00275       return ERROR_OUT_OF_MEMORY;
00276 
00277    //Point to the DNS header
00278    message = netBufferAt(buffer, offset);
00279 
00280    //Format DNS query message
00281    message->id = htons(entry->id);
00282    message->qr = 0;
00283    message->opcode = DNS_OPCODE_QUERY;
00284    message->aa = 0;
00285    message->tc = 0;
00286    message->rd = 1;
00287    message->ra = 0;
00288    message->z = 0;
00289    message->rcode = DNS_RCODE_NO_ERROR;
00290 
00291    //The DNS query contains one question
00292    message->qdcount = HTONS(1);
00293    message->ancount = 0;
00294    message->nscount = 0;
00295    message->arcount = 0;
00296 
00297    //Length of the DNS query message
00298    length = sizeof(DnsHeader);
00299 
00300    //Encode the host name using the DNS name notation
00301    length += dnsEncodeName(entry->name, message->questions);
00302 
00303    //Point to the corresponding question structure
00304    dnsQuestion = DNS_GET_QUESTION(message, length);
00305 
00306 #if (IPV4_SUPPORT == ENABLED)
00307    //An IPv4 address is expected?
00308    if(entry->type == HOST_TYPE_IPV4)
00309    {
00310       //Fill in question structure
00311       dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A);
00312       dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
00313    }
00314 #endif
00315 #if (IPV6_SUPPORT == ENABLED)
00316    //An IPv6 address is expected?
00317    if(entry->type == HOST_TYPE_IPV6)
00318    {
00319       //Fill in question structure
00320       dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA);
00321       dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
00322    }
00323 #endif
00324 
00325    //Update the length of the DNS query message
00326    length += sizeof(DnsQuestion);
00327 
00328    //Adjust the length of the multi-part buffer
00329    netBufferSetLength(buffer, offset + length);
00330 
00331    //Debug message
00332    TRACE_INFO("Sending DNS message (%" PRIuSIZE " bytes)...\r\n", length);
00333    //Dump message
00334    dnsDumpMessage(message, length);
00335 
00336    //Send DNS query message
00337    error = udpSendDatagramEx(entry->interface, entry->port,
00338       &destIpAddr, DNS_PORT, buffer, offset, 0);
00339 
00340    //Free previously allocated memory
00341    netBufferFree(buffer);
00342    //Return status code
00343    return error;
00344 }
00345 
00346 
00347 /**
00348  * @brief Process incoming DNS response message
00349  * @param[in] interface Underlying network interface
00350  * @param[in] pseudoHeader UDP pseudo header
00351  * @param[in] udpHeader UDP header
00352  * @param[in] buffer Multi-part buffer containing the incoming DNS message
00353  * @param[in] offset Offset to the first byte of the DNS message
00354  * @param[in] params Callback function parameter (not used)
00355  **/
00356 
00357 void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
00358    const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params)
00359 {
00360    uint_t i;
00361    uint_t j;
00362    size_t pos;
00363    size_t length;
00364    DnsHeader *message;
00365    DnsQuestion *question;
00366    DnsResourceRecord *record;
00367    DnsCacheEntry *entry;
00368 
00369    //Retrieve the length of the DNS message
00370    length = netBufferGetLength(buffer) - offset;
00371 
00372    //Ensure the DNS message is valid
00373    if(length < sizeof(DnsHeader))
00374       return;
00375    if(length > DNS_MESSAGE_MAX_SIZE)
00376       return;
00377 
00378    //Point to the DNS message header
00379    message = netBufferAt(buffer, offset);
00380    //Sanity check
00381    if(message == NULL)
00382       return;
00383 
00384    //Debug message
00385    TRACE_INFO("DNS message received (%" PRIuSIZE " bytes)...\r\n", length);
00386    //Dump message
00387    dnsDumpMessage(message, length);
00388 
00389    //Check message type
00390    if(!message->qr)
00391       return;
00392    //The DNS message shall contain one question
00393    if(ntohs(message->qdcount) != 1)
00394       return;
00395 
00396    //Loop through DNS cache entries
00397    for(i = 0; i < DNS_CACHE_SIZE; i++)
00398    {
00399       //Point to the current entry
00400       entry = &dnsCache[i];
00401 
00402       //DNS name resolution in progress?
00403       if(entry->state == DNS_STATE_IN_PROGRESS &&
00404          entry->protocol == HOST_NAME_RESOLVER_DNS)
00405       {
00406          //Check destination port number
00407          if(entry->port == ntohs(udpHeader->destPort))
00408          {
00409             //Compare identifier against the expected one
00410             if(ntohs(message->id) != entry->id)
00411                break;
00412 
00413             //Point to the first question
00414             pos = sizeof(DnsHeader);
00415             //Parse domain name
00416             pos = dnsParseName(message, length, pos, NULL, 0);
00417 
00418             //Invalid name?
00419             if(!pos)
00420                break;
00421             //Malformed DNS message?
00422             if((pos + sizeof(DnsQuestion)) > length)
00423                break;
00424 
00425             //Compare domain name
00426             if(dnsCompareName(message, length, sizeof(DnsHeader), entry->name, 0))
00427                break;
00428 
00429             //Point to the corresponding entry
00430             question = DNS_GET_QUESTION(message, pos);
00431 
00432             //Check the class of the query
00433             if(ntohs(question->qclass) != DNS_RR_CLASS_IN)
00434                break;
00435 
00436             //Check the type of the query
00437             if(entry->type == HOST_TYPE_IPV4 && ntohs(question->qtype) != DNS_RR_TYPE_A)
00438                break;
00439             if(entry->type == HOST_TYPE_IPV6 && ntohs(question->qtype) != DNS_RR_TYPE_AAAA)
00440                break;
00441 
00442             //Check return code
00443             if(message->rcode != DNS_RCODE_NO_ERROR)
00444             {
00445                //The entry should be deleted since name resolution has failed
00446                dnsDeleteEntry(entry);
00447                //Exit immediately
00448                break;
00449             }
00450 
00451             //Point to the first answer
00452             pos += sizeof(DnsQuestion);
00453 
00454             //Parse answer resource records
00455             for(j = 0; j < ntohs(message->ancount); j++)
00456             {
00457                //Parse domain name
00458                pos = dnsParseName(message, length, pos, NULL, 0);
00459                //Invalid name?
00460                if(!pos)
00461                   break;
00462 
00463                //Point to the associated resource record
00464                record = DNS_GET_RESOURCE_RECORD(message, pos);
00465                //Point to the resource data
00466                pos += sizeof(DnsResourceRecord);
00467 
00468                //Make sure the resource record is valid
00469                if(pos > length)
00470                   break;
00471                if((pos + ntohs(record->rdlength)) > length)
00472                   break;
00473 
00474 #if (IPV4_SUPPORT == ENABLED)
00475                //IPv4 address expected?
00476                if(entry->type == HOST_TYPE_IPV4)
00477                {
00478                   //A resource record found?
00479                   if(ntohs(record->rtype) == DNS_RR_TYPE_A &&
00480                      ntohs(record->rdlength) == sizeof(Ipv4Addr))
00481                   {
00482                      //Copy the IPv4 address
00483                      entry->ipAddr.length = sizeof(Ipv4Addr);
00484                      ipv4CopyAddr(&entry->ipAddr.ipv4Addr, record->rdata);
00485 
00486                      //Save current time
00487                      entry->timestamp = osGetSystemTime();
00488                      //Save TTL value
00489                      entry->timeout = ntohl(record->ttl) * 1000;
00490 
00491                      //Limit the lifetime of the DNS cache entries
00492                      if(entry->timeout >= DNS_MAX_LIFETIME)
00493                         entry->timeout = DNS_MAX_LIFETIME;
00494                      if(entry->timeout <= DNS_MIN_LIFETIME)
00495                         entry->timeout = DNS_MIN_LIFETIME;
00496 
00497                      //Unregister UDP callback function
00498                      udpDetachRxCallback(interface, entry->port);
00499                      //Host name successfully resolved
00500                      entry->state = DNS_STATE_RESOLVED;
00501                      //Exit immediately
00502                      break;
00503                   }
00504                }
00505 #endif
00506 #if (IPV6_SUPPORT == ENABLED)
00507                //IPv6 address expected?
00508                if(entry->type == HOST_TYPE_IPV6)
00509                {
00510                   //AAAA resource record found?
00511                   if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA &&
00512                      ntohs(record->rdlength) == sizeof(Ipv6Addr))
00513                   {
00514                      //Copy the IPv6 address
00515                      entry->ipAddr.length = sizeof(Ipv6Addr);
00516                      ipv6CopyAddr(&entry->ipAddr.ipv6Addr, record->rdata);
00517 
00518                      //Save current time
00519                      entry->timestamp = osGetSystemTime();
00520                      //Save TTL value
00521                      entry->timeout = ntohl(record->ttl) * 1000;
00522 
00523                      //Limit the lifetime of the DNS cache entries
00524                      if(entry->timeout >= DNS_MAX_LIFETIME)
00525                         entry->timeout = DNS_MAX_LIFETIME;
00526                      if(entry->timeout <= DNS_MIN_LIFETIME)
00527                         entry->timeout = DNS_MIN_LIFETIME;
00528 
00529                      //Unregister UDP callback function
00530                      udpDetachRxCallback(interface, entry->port);
00531                      //Host name successfully resolved
00532                      entry->state = DNS_STATE_RESOLVED;
00533                      //Exit immediately
00534                      break;
00535                   }
00536                }
00537 #endif
00538                //Point to the next resource record
00539                pos += ntohs(record->rdlength);
00540             }
00541 
00542             //We are done
00543             break;
00544          }
00545       }
00546    }
00547 }
00548 
00549 #endif
00550