|
|
1.1 ! root 1: /****************************************************************************** ! 2: * Copyright (c) 2004, 2008 IBM Corporation ! 3: * All rights reserved. ! 4: * This program and the accompanying materials ! 5: * are made available under the terms of the BSD License ! 6: * which accompanies this distribution, and is available at ! 7: * http://www.opensource.org/licenses/bsd-license.php ! 8: * ! 9: * Contributors: ! 10: * IBM Corporation - initial implementation ! 11: *****************************************************************************/ ! 12: ! 13: /*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/ ! 14: ! 15: #include <dns.h> ! 16: #include <stdio.h> ! 17: #include <string.h> ! 18: #include <time.h> ! 19: #include <sys/socket.h> ! 20: ! 21: #include <ethernet.h> ! 22: #include <ipv4.h> ! 23: #include <udp.h> ! 24: ! 25: #define DNS_FLAG_MSGTYPE 0xF800 /**< Message type mask (opcode) */ ! 26: #define DNS_FLAG_SQUERY 0x0000 /**< Standard query type */ ! 27: #define DNS_FLAG_SRESPONSE 0x8000 /**< Standard response type */ ! 28: #define DNS_FLAG_RD 0x0100 /**< Recursion desired flag */ ! 29: #define DNS_FLAG_RCODE 0x000F /**< Response code mask ! 30: (stores err.cond.) code */ ! 31: #define DNS_RCODE_NERROR 0 /**< "No errors" code */ ! 32: ! 33: #define DNS_QTYPE_A 1 /**< A 32-bit IP record type */ ! 34: #define DNS_QTYPE_CNAME 5 /**< Canonical name record type */ ! 35: ! 36: #define DNS_QCLASS_IN 1 /**< Query class for internet msgs */ ! 37: ! 38: /** \struct dnshdr ! 39: * A header for DNS-messages (see RFC 1035, paragraph 4.1.1). ! 40: * <p> ! 41: * DNS-message consist of DNS-header and 4 optional sections, ! 42: * arranged in the following order:<ul> ! 43: * <li> DNS-header ! 44: * <li> question section ! 45: * <li> answer section ! 46: * <li> authority section ! 47: * <li> additional section ! 48: * </ul> ! 49: */ ! 50: struct dnshdr { ! 51: uint16_t id; /**< an identifier used to match up replies */ ! 52: uint16_t flags; /**< contains op_code, err_code, etc. */ ! 53: uint16_t qdcount; /**< specifies the number of entries in the ! 54: question section */ ! 55: uint16_t ancount; /**< specifies the number of entries in the ! 56: answer section */ ! 57: uint16_t nscount; /**< specifies the number of entries in the ! 58: authority section */ ! 59: uint16_t arcount; /**< specifies the number of entries in the ! 60: additional section */ ! 61: }; ! 62: ! 63: ! 64: /*>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ ! 65: ! 66: static void ! 67: dns_send_query(int8_t * domain_name); ! 68: ! 69: static void ! 70: fill_dnshdr(uint8_t * packet, int8_t * domain_name); ! 71: ! 72: static uint8_t * ! 73: dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name); ! 74: ! 75: static int8_t ! 76: urltohost(char * url, char * host_name); ! 77: ! 78: static int8_t ! 79: hosttodomain(char * host_name, char * domain_name); ! 80: ! 81: /*>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<<<*/ ! 82: ! 83: static uint8_t ether_packet[ETH_MTU_SIZE]; ! 84: static int32_t dns_server_ip = 0; ! 85: static int32_t dns_result_ip = 0; ! 86: static int8_t dns_error = 0; /**< Stores error code or 0 */ ! 87: static int8_t dns_domain_name[0x100]; /**< Raw domain name */ ! 88: static int8_t dns_domain_cname[0x100]; /**< Canonical domain name */ ! 89: ! 90: /*>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ ! 91: ! 92: /** ! 93: * DNS: Initialize the environment for DNS client. ! 94: * To perfrom DNS-queries use the function dns_get_ip. ! 95: * ! 96: * @param device_socket a socket number used to send and recieve packets ! 97: * @param server_ip DNS-server IPv4 address (e.g. 127.0.0.1) ! 98: * @return TRUE in case of successful initialization; ! 99: * FALSE in case of fault (e.g. can't obtain MAC). ! 100: * @see dns_get_ip ! 101: */ ! 102: int8_t ! 103: dns_init(uint32_t _dns_server_ip) { ! 104: dns_server_ip = _dns_server_ip; ! 105: return 0; ! 106: } ! 107: ! 108: /** ! 109: * DNS: For given URL retrieves IPv4 from DNS-server. ! 110: * <p> ! 111: * URL can be given in one of the following form: <ul> ! 112: * <li> scheme with full path with (without) user and password ! 113: * <br>(e.g. "http://user:[email protected]/url-path"); ! 114: * <li> host name with url-path ! 115: * <br>(e.g. "www.host.org/url-path"); ! 116: * <li> nothing but host name ! 117: * <br>(e.g. "www.host.org"); ! 118: * </ul> ! 119: * ! 120: * @param url the URL to be resolved ! 121: * @param domain_ip In case of SUCCESS stores extracted IP. ! 122: * In case of FAULT stores zeros (0.0.0.0). ! 123: * @return TRUE - IP successfuly retrieved; ! 124: * FALSE - error condition occurs. ! 125: */ ! 126: int8_t ! 127: dns_get_ip(int8_t * url, uint32_t * domain_ip) { ! 128: /* this counter is used so that we abort after 30 DNS request */ ! 129: int32_t i; ! 130: /* this buffer stores host name retrieved from url */ ! 131: static int8_t host_name[0x100]; ! 132: ! 133: (* domain_ip) = 0; ! 134: ! 135: // Retrieve host name from URL ! 136: if (!urltohost((char *) url, (char *) host_name)) { ! 137: printf("\nERROR:\t\t\tBad URL!\n"); ! 138: return 0; ! 139: } ! 140: ! 141: // Reformat host name into a series of labels ! 142: if (!hosttodomain((char *) host_name, (char *) dns_domain_name)) { ! 143: printf("\nERROR:\t\t\tBad host name!\n"); ! 144: return 0; ! 145: } ! 146: ! 147: // Check if DNS server is presented and accessable ! 148: if (dns_server_ip == 0) { ! 149: printf("\nERROR:\t\t\tCan't resolve domain name " ! 150: "(DNS server is not presented)!\n"); ! 151: return 0; ! 152: } ! 153: ! 154: // Use DNS-server to obtain IP ! 155: dns_result_ip = 0; ! 156: dns_error = 0; ! 157: strcpy((char *) dns_domain_cname, ""); ! 158: ! 159: for(i = 0; i < 30; ++i) { ! 160: // Use canonical name in case we obtained it ! 161: if (strlen((char *) dns_domain_cname)) ! 162: dns_send_query(dns_domain_cname); ! 163: else ! 164: dns_send_query(dns_domain_name); ! 165: ! 166: // setting up a timer with a timeout of one seconds ! 167: set_timer(TICKS_SEC); ! 168: do { ! 169: receive_ether(); ! 170: if (dns_error) ! 171: return 0; // FALSE - error ! 172: if (dns_result_ip != 0) { ! 173: (* domain_ip) = dns_result_ip; ! 174: return 1; // TRUE - success (domain IP retrieved) ! 175: } ! 176: } while (get_timer() > 0); ! 177: } ! 178: ! 179: printf("\nGiving up after %d DNS requests\n", i); ! 180: return 0; // FALSE - domain name wasn't retrieved ! 181: } ! 182: ! 183: /** ! 184: * DNS: Handles DNS-messages according to Receive-handle diagram. ! 185: * Sets dns_result_ip for given dns_domain_name (see dns_get_ip) ! 186: * or signals error condition occurs during DNS-resolving proccess ! 187: * by setting dns_error flag. ! 188: * ! 189: * @param packet DNS-packet to be handled ! 190: * @param packetsize length of the packet ! 191: * @return ZERO - packet handled successfully; ! 192: * NON ZERO - packet was not handled (e.g. bad format) ! 193: * @see dns_get_ip ! 194: * @see receive_ether ! 195: * @see dnshdr ! 196: */ ! 197: int32_t ! 198: handle_dns(uint8_t * packet, int32_t packetsize) { ! 199: struct dnshdr * dnsh = (struct dnshdr *) packet; ! 200: uint8_t * resp_section = packet + sizeof(struct dnshdr); ! 201: /* This string stores domain name from DNS-packets */ ! 202: static int8_t handle_domain_name[0x100]; ! 203: int i; ! 204: ! 205: // verify ID - is it response for our query? ! 206: if (dnsh -> id != htons(0x1234)) ! 207: return 0; ! 208: ! 209: // Is it DNS response? ! 210: if ((dnsh -> flags & htons(DNS_FLAG_MSGTYPE)) != htons(DNS_FLAG_SRESPONSE)) ! 211: return 0; ! 212: ! 213: // Is error condition occurs? (check error field in incoming packet) ! 214: if ((dnsh -> flags & htons(DNS_FLAG_RCODE)) != DNS_RCODE_NERROR) { ! 215: dns_error = 1; ! 216: return 0; ! 217: } ! 218: ! 219: /* Pass all (qdcount) records in question section */ ! 220: ! 221: for (i = 0; i < htons(dnsh -> qdcount); i++) { ! 222: // pass QNAME ! 223: resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, ! 224: handle_domain_name); ! 225: if (resp_section == NULL) { ! 226: return -1; // incorrect domain name (bad packet) ! 227: } ! 228: // pass QTYPE & QCLASS ! 229: resp_section += 4; ! 230: } ! 231: ! 232: /* Handle all (ancount) records in answer section */ ! 233: ! 234: for (i = 0; i < htons(dnsh -> ancount); i++) { ! 235: // retrieve domain name from the packet ! 236: resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, ! 237: handle_domain_name); ! 238: ! 239: if (resp_section == NULL) { ! 240: return -1; // incorrect domain name (bad packet) ! 241: } ! 242: ! 243: // Check the class of the query (should be IN for Internet) ! 244: if (* (uint16_t *) (resp_section + 2) == htons(DNS_QCLASS_IN)) { ! 245: // check if retrieved name fit raw or canonical domain name ! 246: if (!strcmp((char *) handle_domain_name, (char *) dns_domain_name) || ! 247: !strcmp((char *) handle_domain_name, (char *) dns_domain_cname)) { ! 248: switch (htons(* (uint16_t *) resp_section)) { ! 249: ! 250: case DNS_QTYPE_A : ! 251: // rdata contains IP ! 252: dns_result_ip = htonl(* (uint32_t *) (resp_section + 10)); ! 253: return 0; // IP successfully obtained ! 254: ! 255: case DNS_QTYPE_CNAME : ! 256: // rdata contains canonical name, store it for further requests ! 257: if (dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section + 10, ! 258: dns_domain_cname) == NULL) { ! 259: // incorrect domain name (bad packet) ! 260: return -1; ! 261: } ! 262: break; ! 263: } ! 264: } ! 265: // continue with next record in answer section ! 266: resp_section += htons(* (uint16_t *) (resp_section + 8)) + 10; ! 267: } ! 268: } ! 269: return 0; // Packet succesfuly handled but IP wasn't obtained ! 270: } ! 271: ! 272: /** ! 273: * DNS: Sends a standard DNS-query (read request package) to a DNS-server. ! 274: * DNS-server respones with host IP or signals some error condition. ! 275: * Responses from the server are handled by handle_dns function. ! 276: * ! 277: * @param domain_name the domain name given as series of labels preceded ! 278: * with length(label) and terminated with 0 ! 279: * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") ! 280: * @see handle_dns ! 281: */ ! 282: static void ! 283: dns_send_query(int8_t * domain_name) { ! 284: int qry_len = strlen((char *) domain_name) + 5; ! 285: ! 286: uint32_t packetsize = sizeof(struct iphdr) + ! 287: sizeof(struct udphdr) + sizeof(struct dnshdr) + ! 288: qry_len; ! 289: ! 290: memset(ether_packet, 0, packetsize); ! 291: fill_dnshdr(ðer_packet[ ! 292: sizeof(struct iphdr) + sizeof(struct udphdr)], ! 293: domain_name); ! 294: fill_udphdr(ðer_packet[ ! 295: sizeof(struct iphdr)], sizeof(struct dnshdr) + ! 296: sizeof(struct udphdr) + qry_len, ! 297: UDPPORT_DNSC, UDPPORT_DNSS); ! 298: fill_iphdr(ether_packet, ! 299: sizeof(struct dnshdr) + sizeof(struct udphdr) + ! 300: sizeof(struct iphdr) + qry_len, ! 301: IPTYPE_UDP, 0, dns_server_ip); ! 302: ! 303: send_ipv4(ether_packet, packetsize); ! 304: } ! 305: ! 306: /** ! 307: * DNS: Creates standard DNS-query package. Places DNS-header ! 308: * and question section in a packet and fills it with ! 309: * corresponding information. ! 310: * <p> ! 311: * Use this function with similar functions for other network layers ! 312: * (fill_udphdr, fill_iphdr, fill_ethhdr). ! 313: * ! 314: * @param packet Points to the place where ARP-header must be placed. ! 315: * @param domain_name the domain name given as series of labels preceded ! 316: * with length(label) and terminated with 0 ! 317: * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") ! 318: * @see fill_udphdr ! 319: * @see fill_iphdr ! 320: * @see fill_ethhdr ! 321: */ ! 322: static void ! 323: fill_dnshdr(uint8_t * packet, int8_t * domain_name) { ! 324: struct dnshdr * dnsh = (struct dnshdr *) packet; ! 325: uint8_t * qry_section = packet + sizeof(struct dnshdr); ! 326: ! 327: dnsh -> id = htons(0x1234); ! 328: dnsh -> flags = htons(DNS_FLAG_SQUERY) | htons(DNS_FLAG_RD); ! 329: dnsh -> qdcount = htons(1); ! 330: ! 331: strcpy((char *) qry_section, (char *) domain_name); ! 332: qry_section += strlen((char *) domain_name) + 1; ! 333: ! 334: // fill QTYPE (ask for IP) ! 335: * (uint16_t *) qry_section = htons(DNS_QTYPE_A); ! 336: qry_section += 2; ! 337: // fill QCLASS (IN is a standard class for Internet) ! 338: * (uint16_t *) qry_section = htons(DNS_QCLASS_IN); ! 339: } ! 340: ! 341: /** ! 342: * DNS: Extracts domain name from the question or answer section of ! 343: * the DNS-message. This function is need to support message ! 344: * compression requirement (see RFC 1035, paragraph 4.1.4). ! 345: * ! 346: * @param dnsh Points at the DNS-header. ! 347: * @param head Points at the beginning of the domain_name ! 348: * which has to be extracted. ! 349: * @param domain_name In case of SUCCESS this string stores extracted name. ! 350: * In case of FAULT this string is empty. ! 351: * @return NULL in case of FAULT (domain name > 255 octets); ! 352: * otherwise pointer to the data following the name. ! 353: * @see dnshdr ! 354: */ ! 355: static uint8_t * ! 356: dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name) { ! 357: int8_t * tail = domain_name; ! 358: int8_t * ptr = head; ! 359: int8_t * next_section = NULL; ! 360: ! 361: while (1) { ! 362: if ((ptr[0] & 0xC0) == 0xC0) { ! 363: // message compressed (reference is used) ! 364: next_section = ptr + 2; ! 365: ptr = (int8_t *) dnsh + (htons(* (uint16_t *) ptr) & 0x3FFF); ! 366: continue; ! 367: } ! 368: if (ptr[0] == 0) { ! 369: // message termination ! 370: tail[0] = 0; ! 371: ptr += 1; ! 372: break; ! 373: } ! 374: // maximum length for domain name is 255 octets w/o termination sym ! 375: if (tail - domain_name + ptr[0] + 1 > 255) { ! 376: strcpy((char *) domain_name, ""); ! 377: return NULL; ! 378: } ! 379: memcpy(tail, ptr, ptr[0] + 1); ! 380: tail += ptr[0] + 1; ! 381: ptr += ptr[0] + 1; ! 382: } ! 383: ! 384: if (next_section == NULL) ! 385: next_section = ptr; ! 386: ! 387: return (uint8_t *) next_section; ! 388: } ! 389: ! 390: /** ! 391: * DNS: Parses URL and returns host name. ! 392: * Input string can be given as: <ul> ! 393: * <li> scheme with full path with (without) user and password ! 394: * <br>(e.g. "http://user:[email protected]/url-path"); ! 395: * <li> host name with url-path ! 396: * <br>(e.g. "www.host.org/url-path"); ! 397: * <li> nothing but host name ! 398: * <br>(e.g. "www.host.org"); ! 399: * </ul> ! 400: * ! 401: * @param url string that stores incoming URL ! 402: * @param host_name In case of SUCCESS this string stores the host name, ! 403: * In case of FAULT this string is empty. ! 404: * @return TRUE - host name retrieved, ! 405: * FALSE - host name > 255 octets or empty. ! 406: */ ! 407: static int8_t ! 408: urltohost(char * url, char * host_name) { ! 409: uint16_t length1; ! 410: uint16_t length2; ! 411: ! 412: strcpy(host_name, ""); ! 413: ! 414: if (strstr(url, "://") != NULL) ! 415: url = strstr(url, "//") + 2; // URL ! 416: ! 417: if (strstr(url, "@") != NULL) // truncate user & password ! 418: url = strstr(url, "@") + 1; ! 419: ! 420: if (strstr(url, "/") != NULL) // truncate url path ! 421: length1 = strstr(url, "/") - url; ! 422: else ! 423: length1 = strlen(url); ! 424: ! 425: if (strstr(url, ":") != NULL) // truncate port path ! 426: length2 = strstr(url, ":") - url; ! 427: else ! 428: length2 = strlen(url); ! 429: ! 430: if(length1 > length2) ! 431: length1 = length2; ! 432: ! 433: if (length1 == 0) ! 434: return 0; // string is empty ! 435: if(length1 >= 256) ! 436: return 0; // host name is too big ! 437: ! 438: strncpy(host_name, url, length1); ! 439: host_name[length1] = 0; ! 440: ! 441: return 1; // Host name is retrieved ! 442: } ! 443: ! 444: /** ! 445: * DNS: Transforms host name string into a series of labels ! 446: * each of them preceded with length(label). 0 is a terminator. ! 447: * "www.domain.dom" -> "\3,w,w,w,\6,d,o,m,a,i,n,\3,c,o,m,\0" ! 448: * <p> ! 449: * This format is used in DNS-messages. ! 450: * ! 451: * @param host_name incoming string with the host name ! 452: * @param domain_name resulting string with series of labels ! 453: * or empty string in case of FAULT ! 454: * @return TRUE - host name transformed, ! 455: * FALSE - host name > 255 octets or label > 63 octets. ! 456: */ ! 457: static int8_t ! 458: hosttodomain(char * host_name, char * domain_name) { ! 459: char * domain_iter = domain_name; ! 460: char * host_iter = host_name; ! 461: ! 462: strcpy(domain_name, ""); ! 463: ! 464: if(strlen(host_name) > 255) ! 465: return 0; // invalid host name (refer to RFC 1035) ! 466: ! 467: for(; 1; ++host_iter) { ! 468: if(*host_iter != '.' && *host_iter != 0) ! 469: continue; ! 470: *domain_iter = host_iter - host_name; ! 471: if (*domain_iter > 63) { ! 472: strcpy(domain_name, ""); ! 473: return 0; // invalid host name (refer to RFC 1035) ! 474: } ! 475: ++domain_iter; ! 476: strncpy(domain_iter, host_name, host_iter - host_name); ! 477: domain_iter += (host_iter - host_name); ! 478: if(*host_iter == 0) { ! 479: *domain_iter = 0; ! 480: break; ! 481: } ! 482: host_name = host_iter + 1; ! 483: } ! 484: return 1; // ok ! 485: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.