~ubuntu-branches/ubuntu/wily/slof/wily

« back to all changes in this revision

Viewing changes to clients/net-snk/app/netlib/dns.c

  • Committer: Package Import Robot
  • Author(s): Aurelien Jarno
  • Date: 2012-09-16 23:05:23 UTC
  • Revision ID: package-import@ubuntu.com-20120916230523-r2ynulqmp2tyu2e5
Tags: upstream-20120217+dfsg
ImportĀ upstreamĀ versionĀ 20120217+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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: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");
 
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(&ether_packet[
 
292
                    sizeof(struct iphdr) + sizeof(struct udphdr)],
 
293
                    domain_name);
 
294
        fill_udphdr(&ether_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: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");
 
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
}