Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.
Dependents: mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more
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 thread_sleep_for(50); 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 Tue Jul 12 2022 18:48:00 by 1.7.2