Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
dns_client.c
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
Generated on Tue Jul 12 2022 17:10:13 by
1.7.2