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