1
/******************************************************************************
2
* Copyright (c) 2004, 2008 IBM Corporation
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
10
* IBM Corporation - initial implementation
11
*****************************************************************************/
13
/*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/
19
#include <sys/socket.h>
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 */
33
#define DNS_QTYPE_A 1 /**< A 32-bit IP record type */
34
#define DNS_QTYPE_CNAME 5 /**< Canonical name record type */
36
#define DNS_QCLASS_IN 1 /**< Query class for internet msgs */
39
* A header for DNS-messages (see RFC 1035, paragraph 4.1.1).
41
* DNS-message consist of DNS-header and 4 optional sections,
42
* arranged in the following order:<ul>
44
* <li> question section
46
* <li> authority section
47
* <li> additional section
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
55
uint16_t ancount; /**< specifies the number of entries in the
57
uint16_t nscount; /**< specifies the number of entries in the
59
uint16_t arcount; /**< specifies the number of entries in the
64
/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
67
dns_send_query(int8_t * domain_name);
70
fill_dnshdr(uint8_t * packet, int8_t * domain_name);
73
dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name);
76
urltohost(char * url, char * host_name);
79
hosttodomain(char * host_name, char * domain_name);
81
/*>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<<<*/
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 */
90
/*>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
93
* DNS: Initialize the environment for DNS client.
94
* To perfrom DNS-queries use the function dns_get_ip.
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).
103
dns_init(uint32_t _dns_server_ip) {
104
dns_server_ip = _dns_server_ip;
109
* DNS: For given URL retrieves IPv4 from DNS-server.
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:pass@www.host.org/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");
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.
127
dns_get_ip(int8_t * url, uint32_t * domain_ip) {
128
/* this counter is used so that we abort after 30 DNS request */
130
/* this buffer stores host name retrieved from url */
131
static int8_t host_name[0x100];
135
// Retrieve host name from URL
136
if (!urltohost((char *) url, (char *) host_name)) {
137
printf("\nERROR:\t\t\tBad URL!\n");
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");
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");
154
// Use DNS-server to obtain IP
157
strcpy((char *) dns_domain_cname, "");
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);
164
dns_send_query(dns_domain_name);
166
// setting up a timer with a timeout of one seconds
167
set_timer(TICKS_SEC);
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)
176
} while (get_timer() > 0);
179
printf("\nGiving up after %d DNS requests\n", i);
180
return 0; // FALSE - domain name wasn't retrieved
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.
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)
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];
205
// verify ID - is it response for our query?
206
if (dnsh -> id != htons(0x1234))
209
// Is it DNS response?
210
if ((dnsh -> flags & htons(DNS_FLAG_MSGTYPE)) != htons(DNS_FLAG_SRESPONSE))
213
// Is error condition occurs? (check error field in incoming packet)
214
if ((dnsh -> flags & htons(DNS_FLAG_RCODE)) != DNS_RCODE_NERROR) {
219
/* Pass all (qdcount) records in question section */
221
for (i = 0; i < htons(dnsh -> qdcount); i++) {
223
resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section,
225
if (resp_section == NULL) {
226
return -1; // incorrect domain name (bad packet)
228
// pass QTYPE & QCLASS
232
/* Handle all (ancount) records in answer section */
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,
239
if (resp_section == NULL) {
240
return -1; // incorrect domain name (bad packet)
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)) {
252
dns_result_ip = htonl(* (uint32_t *) (resp_section + 10));
253
return 0; // IP successfully obtained
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)
265
// continue with next record in answer section
266
resp_section += htons(* (uint16_t *) (resp_section + 8)) + 10;
269
return 0; // Packet succesfuly handled but IP wasn't obtained
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.
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")
283
dns_send_query(int8_t * domain_name) {
284
int qry_len = strlen((char *) domain_name) + 5;
286
uint32_t packetsize = sizeof(struct iphdr) +
287
sizeof(struct udphdr) + sizeof(struct dnshdr) +
290
memset(ether_packet, 0, packetsize);
291
fill_dnshdr(ðer_packet[
292
sizeof(struct iphdr) + sizeof(struct udphdr)],
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);
303
send_ipv4(ether_packet, packetsize);
307
* DNS: Creates standard DNS-query package. Places DNS-header
308
* and question section in a packet and fills it with
309
* corresponding information.
311
* Use this function with similar functions for other network layers
312
* (fill_udphdr, fill_iphdr, fill_ethhdr).
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")
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);
327
dnsh -> id = htons(0x1234);
328
dnsh -> flags = htons(DNS_FLAG_SQUERY) | htons(DNS_FLAG_RD);
329
dnsh -> qdcount = htons(1);
331
strcpy((char *) qry_section, (char *) domain_name);
332
qry_section += strlen((char *) domain_name) + 1;
334
// fill QTYPE (ask for IP)
335
* (uint16_t *) qry_section = htons(DNS_QTYPE_A);
337
// fill QCLASS (IN is a standard class for Internet)
338
* (uint16_t *) qry_section = htons(DNS_QCLASS_IN);
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).
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.
356
dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name) {
357
int8_t * tail = domain_name;
359
int8_t * next_section = NULL;
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);
369
// message termination
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, "");
379
memcpy(tail, ptr, ptr[0] + 1);
384
if (next_section == NULL)
387
return (uint8_t *) next_section;
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:pass@www.host.org/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");
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.
408
urltohost(char * url, char * host_name) {
412
strcpy(host_name, "");
414
if (strstr(url, "://") != NULL)
415
url = strstr(url, "//") + 2; // URL
417
if (strstr(url, "@") != NULL) // truncate user & password
418
url = strstr(url, "@") + 1;
420
if (strstr(url, "/") != NULL) // truncate url path
421
length1 = strstr(url, "/") - url;
423
length1 = strlen(url);
425
if (strstr(url, ":") != NULL) // truncate port path
426
length2 = strstr(url, ":") - url;
428
length2 = strlen(url);
430
if(length1 > length2)
434
return 0; // string is empty
436
return 0; // host name is too big
438
strncpy(host_name, url, length1);
439
host_name[length1] = 0;
441
return 1; // Host name is retrieved
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"
449
* This format is used in DNS-messages.
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.
458
hosttodomain(char * host_name, char * domain_name) {
459
char * domain_iter = domain_name;
460
char * host_iter = host_name;
462
strcpy(domain_name, "");
464
if(strlen(host_name) > 255)
465
return 0; // invalid host name (refer to RFC 1035)
467
for(; 1; ++host_iter) {
468
if(*host_iter != '.' && *host_iter != 0)
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)
476
strncpy(domain_iter, host_name, host_iter - host_name);
477
domain_iter += (host_iter - host_name);
478
if(*host_iter == 0) {
482
host_name = host_iter + 1;