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