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.
DnsClient.cpp
00001 // Arduino DNS client for Enc28J60-based Ethernet shield 00002 // (c) Copyright 2009-2010 MCQN Ltd. 00003 // Released under Apache License, version 2.0 00004 #include "UdpSocket.h" 00005 #include "utility/util.h" 00006 00007 #include "DnsClient.h" 00008 #include <string.h> 00009 #include "mbed.h" 00010 #include "mbed_version.h" 00011 00012 #define SOCKET_NONE 255 00013 00014 // Various flags and header field values for a DNS message 00015 00016 #define UDP_HEADER_SIZE 8 00017 #define DNS_HEADER_SIZE 12 00018 #define TTL_SIZE 4 00019 #define QUERY_FLAG (0) 00020 #define RESPONSE_FLAG (1 << 15) 00021 #define QUERY_RESPONSE_MASK (1 << 15) 00022 #define OPCODE_STANDARD_QUERY (0) 00023 #define OPCODE_INVERSE_QUERY (1 << 11) 00024 #define OPCODE_STATUS_REQUEST (2 << 11) 00025 #define OPCODE_MASK (15 << 11) 00026 #define AUTHORITATIVE_FLAG (1 << 10) 00027 #define TRUNCATION_FLAG (1 << 9) 00028 #define RECURSION_DESIRED_FLAG (1 << 8) 00029 #define RECURSION_AVAILABLE_FLAG (1 << 7) 00030 #define RESP_NO_ERROR (0) 00031 #define RESP_FORMAT_ERROR (1) 00032 #define RESP_SERVER_FAILURE (2) 00033 #define RESP_NAME_ERROR (3) 00034 #define RESP_NOT_IMPLEMENTED (4) 00035 #define RESP_REFUSED (5) 00036 #define RESP_MASK (15) 00037 #define TYPE_A (0x0001) 00038 #define CLASS_IN (0x0001) 00039 #define LABEL_COMPRESSION_MASK (0xC0) 00040 00041 // Port number that DNS servers listen on 00042 00043 #define DNS_PORT 53 00044 00045 // Possible return codes from ProcessResponse 00046 00047 #define SUCCESS 1 00048 #define TIMED_OUT - 1 00049 #define INVALID_SERVER - 2 00050 #define TRUNCATED - 3 00051 #define INVALID_RESPONSE - 4 00052 00053 /** 00054 * @brief 00055 * @note 00056 * @param 00057 * @retval 00058 */ 00059 void DnsClient::begin(const IpAddress& aDNSServer) 00060 { 00061 iDNSServer = aDNSServer; 00062 iRequestId = 0; 00063 } 00064 00065 /** 00066 * @brief 00067 * @note 00068 * @param 00069 * @retval 00070 */ 00071 int DnsClient::inet_aton(const char* aIPAddrString, IpAddress& aResult) 00072 { 00073 // See if we've been given a valid IP address 00074 const char* p = aIPAddrString; 00075 while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { 00076 p++; 00077 } 00078 00079 if (*p == '\0') { 00080 // It's looking promising, we haven't found any invalid characters 00081 p = aIPAddrString; 00082 00083 int segment = 0; 00084 int segmentValue = 0; 00085 while (*p && (segment < 4)) { 00086 if (*p == '.') { 00087 // We've reached the end of a segment 00088 if (segmentValue > 255) { 00089 // You can't have IP address segments that don't fit in a byte 00090 return 0; 00091 } 00092 else { 00093 aResult[segment] = (uint8_t) segmentValue; 00094 segment++; 00095 segmentValue = 0; 00096 } 00097 } 00098 else { 00099 // Next digit 00100 segmentValue = (segmentValue * 10) + (*p - '0'); 00101 } 00102 00103 p++; 00104 } 00105 00106 // We've reached the end of address, but there'll still be the last 00107 // segment to deal with 00108 if ((segmentValue > 255) || (segment > 3)) { 00109 // You can't have IP address segments that don't fit in a byte, 00110 // or more than four segments 00111 return 0; 00112 } 00113 else { 00114 aResult[segment] = (uint8_t) segmentValue; 00115 return 1; 00116 } 00117 } 00118 else { 00119 return 0; 00120 } 00121 } 00122 00123 /** 00124 * @brief 00125 * @note 00126 * @param 00127 * @retval 00128 */ 00129 int DnsClient::getHostByName(const char* aHostname, IpAddress& aResult) 00130 { 00131 int ret = 0; 00132 Timer timer; 00133 00134 timer.start(); 00135 00136 // See if it's a numeric IP address 00137 if (inet_aton(aHostname, aResult)) { 00138 // It is, our work here is done 00139 return 1; 00140 } 00141 00142 // Check we've got a valid DNS server to use 00143 if (iDNSServer == INADDR_NONE) { 00144 return INVALID_SERVER; 00145 } 00146 00147 // Find a socket to use 00148 if (iUdp.begin(1024 + ((timer.read_ms() / 1000) & 0xF)) == 1) { 00149 // Try up to three times 00150 int retries = 0; 00151 // while ((retries < 3) && (ret <= 0)) 00152 { 00153 // Send DNS request 00154 ret = iUdp.beginPacket(iDNSServer, DNS_PORT); 00155 if (ret != 0) { 00156 // Now output the request data 00157 ret = buildRequest(aHostname); 00158 if (ret != 0) { 00159 // And finally send the request 00160 ret = iUdp.endPacket(); 00161 if (ret != 0) { 00162 // Now wait for a response 00163 int wait_retries = 0; 00164 ret = TIMED_OUT; 00165 while ((wait_retries < 3) && (ret == TIMED_OUT)) { 00166 ret = processResponse(5000, aResult); 00167 wait_retries++; 00168 } 00169 } 00170 } 00171 } 00172 00173 retries++; 00174 } 00175 00176 // We're done with the socket now 00177 iUdp.stop(); 00178 } 00179 00180 return ret; 00181 } 00182 00183 /** 00184 * @brief 00185 * @note 00186 * @param 00187 * @retval 00188 */ 00189 uint16_t DnsClient::buildRequest(const char* aName) 00190 { 00191 // Build header 00192 // 1 1 1 1 1 1 00193 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 00194 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 00195 // | ID | 00196 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 00197 // |QR| Opcode |AA|TC|RD|RA| Z | RCODE | 00198 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 00199 // | QDCOUNT | 00200 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 00201 // | ANCOUNT | 00202 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 00203 // | NSCOUNT | 00204 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 00205 // | ARCOUNT | 00206 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 00207 // As we only support one request at a time at present, we can simplify 00208 // some of this header 00209 srand(time(NULL) + 2); 00210 iRequestId = rand() % 0xFFFF + 1; // generate a random ID 00211 uint16_t twoByteBuffer; 00212 00213 // FIXME We should also check that there's enough space available to write to, rather 00214 // FIXME than assume there's enough space (as the code does at present) 00215 iUdp.write((uint8_t*) &iRequestId, sizeof(iRequestId)); 00216 00217 twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG); 00218 iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer)); 00219 00220 twoByteBuffer = htons(1); // One question record 00221 iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer)); 00222 00223 twoByteBuffer = 0; // Zero answer records 00224 iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer)); 00225 00226 iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer)); 00227 00228 // and zero additional records 00229 iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer)); 00230 00231 // Build question 00232 const char* start = aName; 00233 const char* end = start; 00234 uint8_t len; 00235 // Run through the name being requested 00236 while (*end) { 00237 // Find out how long this section of the name is 00238 end = start; 00239 while (*end && (*end != '.')) { 00240 end++; 00241 } 00242 00243 if (end - start > 0) { 00244 // Write out the size of this section 00245 len = end - start; 00246 iUdp.write(&len, sizeof(len)); 00247 00248 // And then write out the section 00249 iUdp.write((uint8_t*)start, end - start); 00250 } 00251 00252 start = end + 1; 00253 } 00254 00255 // We've got to the end of the question name, so 00256 // terminate it with a zero-length section 00257 len = 0; 00258 iUdp.write(&len, sizeof(len)); 00259 00260 // Finally the type and class of question 00261 twoByteBuffer = htons(TYPE_A); 00262 iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer)); 00263 00264 twoByteBuffer = htons(CLASS_IN); // Internet class of question 00265 iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer)); 00266 00267 // Success! Everything buffered okay 00268 return 1; 00269 } 00270 00271 /** 00272 * @brief 00273 * @note 00274 * @param 00275 * @retval 00276 */ 00277 int16_t DnsClient::processResponse(time_t aTimeout, IpAddress& aAddress) 00278 { 00279 time_t startTime = time(NULL); 00280 Timer timer; 00281 00282 timer.start(); 00283 00284 // Wait for a response packet 00285 while (iUdp.parsePacket() <= 0) { 00286 if ((time_t)timer.read_ms() > aTimeout) 00287 return TIMED_OUT; 00288 #if MBED_MAJOR_VERSION == 2 00289 wait_ms(50); 00290 #else 00291 wait_us(50000); 00292 #endif 00293 } 00294 00295 // We've had a reply! 00296 // Read the UDP header 00297 uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header 00298 00299 // Check that it's a response from the right server and the right port 00300 if ((iDNSServer != iUdp.remoteIP()) || (iUdp.remotePort() != DNS_PORT)) { 00301 // It's not from who we expected 00302 return INVALID_SERVER; 00303 } 00304 00305 // Read through the rest of the response 00306 if (iUdp.available() < DNS_HEADER_SIZE) { 00307 return TRUNCATED; 00308 } 00309 00310 iUdp.read(header, DNS_HEADER_SIZE); 00311 00312 uint16_t header_flags = htons(*((uint16_t*) &header[2])); 00313 // Check that it's a response to this request 00314 if 00315 ( 00316 (iRequestId != (*((uint16_t*) &header[0]))) || 00317 ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t) RESPONSE_FLAG) 00318 ) { 00319 // Mark the entire packet as read 00320 iUdp.flush(); 00321 return INVALID_RESPONSE; 00322 } 00323 00324 // Check for any errors in the response (or in our request) 00325 // although we don't do anything to get round these 00326 if ((header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK)) { 00327 // Mark the entire packet as read 00328 iUdp.flush(); 00329 return -5; //INVALID_RESPONSE; 00330 } 00331 00332 // And make sure we've got (at least) one answer 00333 uint16_t answerCount = htons(*((uint16_t*) &header[6])); 00334 if (answerCount == 0) { 00335 // Mark the entire packet as read 00336 iUdp.flush(); 00337 return -6; //INVALID_RESPONSE; 00338 } 00339 00340 // Skip over any questions 00341 for (uint16_t i = 0; i < htons(*((uint16_t*) &header[4])); i++) { 00342 // Skip over the name 00343 uint8_t len; 00344 do { 00345 iUdp.read(&len, sizeof(len)); 00346 if (len > 0) { 00347 // Don't need to actually read the data out for the string, just 00348 // advance ptr to beyond it 00349 while (len--) { 00350 iUdp.read(); // we don't care about the returned byte 00351 } 00352 } 00353 } while (len != 0); 00354 00355 // Now jump over the type and class 00356 for (int i = 0; i < 4; i++) { 00357 iUdp.read(); // we don't care about the returned byte 00358 } 00359 } 00360 00361 // Now we're up to the bit we're interested in, the answer 00362 // There might be more than one answer (although we'll just use the first 00363 // type A answer) and some authority and additional resource records but 00364 // we're going to ignore all of them. 00365 for (uint16_t i = 0; i < answerCount; i++) { 00366 // Skip the name 00367 uint8_t len; 00368 do { 00369 iUdp.read(&len, sizeof(len)); 00370 if ((len & LABEL_COMPRESSION_MASK) == 0) { 00371 // It's just a normal label 00372 if (len > 0) { 00373 // And it's got a length 00374 // Don't need to actually read the data out for the string, 00375 // just advance ptr to beyond it 00376 while (len--) { 00377 iUdp.read(); // we don't care about the returned byte 00378 } 00379 } 00380 } 00381 else { 00382 // This is a pointer to a somewhere else in the message for the 00383 // rest of the name. We don't care about the name, and RFC1035 00384 // says that a name is either a sequence of labels ended with a 00385 // 0 length octet or a pointer or a sequence of labels ending in 00386 // a pointer. Either way, when we get here we're at the end of 00387 // the name 00388 // Skip over the pointer 00389 iUdp.read(); // we don't care about the returned byte 00390 // And set len so that we drop out of the name loop 00391 len = 0; 00392 } 00393 } while (len != 0); 00394 00395 // Check the type and class 00396 uint16_t answerType; 00397 uint16_t answerClass; 00398 iUdp.read((uint8_t*) &answerType, sizeof(answerType)); 00399 iUdp.read((uint8_t*) &answerClass, sizeof(answerClass)); 00400 00401 // Ignore the Time-To-Live as we don't do any caching 00402 for (int i = 0; i < TTL_SIZE; i++) { 00403 iUdp.read(); // we don't care about the returned byte 00404 } 00405 00406 // And read out the length of this answer 00407 // Don't need header_flags anymore, so we can reuse it here 00408 iUdp.read((uint8_t*) &header_flags, sizeof(header_flags)); 00409 00410 if ((htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN)) { 00411 if (htons(header_flags) != 4) { 00412 // It's a weird size 00413 // Mark the entire packet as read 00414 iUdp.flush(); 00415 return -9; //INVALID_RESPONSE; 00416 } 00417 00418 iUdp.read(aAddress.rawAddress(), 4); 00419 return SUCCESS; 00420 } 00421 else { 00422 // This isn't an answer type we're after, move onto the next one 00423 for (uint16_t i = 0; i < htons(header_flags); i++) { 00424 iUdp.read(); // we don't care about the returned byte 00425 } 00426 } 00427 } 00428 00429 // Mark the entire packet as read 00430 iUdp.flush(); 00431 00432 // If we get here then we haven't found an answer 00433 return -10; //INVALID_RESPONSE; 00434 }
Generated on Sun Feb 26 2023 10:14:23 by
1.7.2