|
|
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.