Nut/OS  4.10.3
API Reference
resolv.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 by egnite GmbH
00003  * Copyright (C) 2001-2003 by egnite Software GmbH
00004  *
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  */
00035 
00036 /*
00037  * $Id: resolv.c 2786 2009-11-06 18:14:36Z haraldkipp $
00038  */
00039 
00040 #include <cfg/os.h>
00041 
00042 #include <sys/device.h>
00043 #include <sys/timer.h>
00044 #include <sys/heap.h>
00045 
00046 #include <arpa/inet.h>
00047 #include <net/if_var.h>
00048 #include <sys/socket.h>
00049 
00050 #include <stdlib.h>
00051 #include <string.h>
00052 #include <memdebug.h>
00053 
00054 #include <netdb.h>
00055 
00056 #ifdef NUTDEBUG
00057 #include <stdio.h>
00058 #endif
00059 
00064 
00065 extern DNSCONFIG confdns;
00066 
00067 typedef struct {
00068     uint16_t doh_id;
00069     uint16_t doh_flags;
00070     uint16_t doh_quests;
00071     uint16_t doh_answers;
00072     uint16_t doh_authrr;
00073     uint16_t doh_addrr;
00074 } DNSHEADER;
00075 
00076 typedef struct {
00077     uint8_t *doq_name;
00078     uint16_t doq_type;
00079     uint16_t doq_class;
00080 } DNSQUESTION;
00081 
00082 typedef struct {
00083     uint8_t *dor_name;
00084     uint16_t dor_type;
00085     uint16_t dor_class;
00086     uint32_t dor_ttl;
00087     uint16_t dor_len;
00088     uint8_t *dor_data;
00089 } DNSRESOURCE;
00090 
00091 #ifdef NUTDEBUG
00092 void DumpDnsHeader(FILE * stream, DNSHEADER * doh)
00093 {
00094     fprintf(stream, "HEADER: id=%u flg=%04X #q=%u #an=%u #au=%u #ad=%u\r\n",
00095             doh->doh_id, doh->doh_flags, doh->doh_quests, doh->doh_answers, 
00096             doh->doh_authrr, doh->doh_addrr);
00097 }
00098 
00099 void DumpDnsQuestion(FILE * stream, DNSQUESTION * doq)
00100 {
00101     fprintf(stream, "QUESTION: name='%s' type=%u class=%u\r\n", 
00102         doq->doq_name, doq->doq_type, doq->doq_class);
00103 }
00104 
00105 void DumpDnsResource(FILE * stream, DNSRESOURCE * dor)
00106 {
00107     uint16_t i;
00108 
00109     fprintf(stream, "RESOURCE: name='%s' type=%u class=%u ttl=%lu len=%u ",
00110             dor->dor_name, dor->dor_type, dor->dor_class, dor->dor_ttl, dor->dor_len);
00111     for (i = 0; i < dor->dor_len; i++)
00112         fprintf(stream, "%02X ", dor->dor_data[i]);
00113     fputc('\n', stream);
00114 }
00115 #endif
00116 
00117 static uint16_t AddShort(uint8_t * cp, uint16_t val)
00118 {
00119     *cp++ = (uint8_t) (val >> 8);
00120     *cp++ = (uint8_t) val;
00121 
00122     return 2;
00123 }
00124 
00125 static uint16_t AddName(uint8_t * cp, CONST uint8_t * name)
00126 {
00127     uint8_t *lcp;
00128     uint16_t rc = strlen((char *) name) + 2;
00129 
00130     lcp = cp++;
00131     *lcp = 0;
00132     while (*name) {
00133         if (*name == '.') {
00134             lcp = cp++;
00135             *lcp = 0;
00136             name++;
00137         } else {
00138             *cp++ = *name++;
00139             (*lcp)++;
00140         }
00141     }
00142     *cp = 0;
00143 
00144     return rc;
00145 }
00146 
00147 static uint16_t ScanShort(uint8_t * cp, uint16_t * val)
00148 {
00149     *val = (uint16_t) (*cp++) << 8;
00150     *val |= *cp;
00151 
00152     return 2;
00153 }
00154 
00155 static uint16_t ScanLong(uint8_t * cp, uint32_t * val)
00156 {
00157     *val = *cp++;
00158     *val <<= 8;
00159     *val |= *cp++;
00160     *val <<= 8;
00161     *val |= *cp++;
00162     *val <<= 8;
00163     *val |= *cp;
00164 
00165     return 4;
00166 }
00167 
00168 static uint16_t ScanName(uint8_t * cp, uint8_t ** npp)
00169 {
00170     uint8_t len;
00171     uint16_t rc;
00172     uint8_t *np;
00173 
00174     if (*npp) {
00175         free(*npp);
00176         *npp = 0;
00177     }
00178 
00179     if ((*cp & 0xC0) == 0xC0)
00180         return 2;
00181 
00182     rc = strlen((char *) cp) + 1;
00183     np = *npp = malloc(rc);
00184     len = *cp++;
00185     while (len) {
00186         while (len--)
00187             *np++ = *cp++;
00188         if ((len = *cp++) != 0)
00189             *np++ = '.';
00190     }
00191     *np = 0;
00192 
00193     return rc;
00194 }
00195 
00196 static uint16_t ScanBinary(uint8_t * cp, uint8_t ** npp, uint16_t len)
00197 {
00198     if (*npp)
00199         free(*npp);
00200     *npp = malloc(len);
00201     memcpy(*npp, cp, len);
00202 
00203     return len;
00204 }
00205 
00206 static DNSHEADER *CreateDnsHeader(DNSHEADER * doh, uint16_t id)
00207 {
00208     if (doh == NULL)
00209         doh = calloc(1, sizeof(DNSHEADER));
00210     if (doh) {
00211         doh->doh_id = id;
00212         doh->doh_flags = 0x0100;
00213         doh->doh_quests = 1;
00214     }
00215     return doh;
00216 }
00217 
00218 static void ReleaseDnsHeader(DNSHEADER * doh)
00219 {
00220     if (doh)
00221         free(doh);
00222 }
00223 
00224 static uint16_t EncodeDnsHeader(uint8_t * buf, DNSHEADER * doh)
00225 {
00226     uint16_t rc;
00227 
00228     rc = AddShort(buf, doh->doh_id);
00229     rc += AddShort(buf + rc, doh->doh_flags);
00230     rc += AddShort(buf + rc, doh->doh_quests);
00231     rc += AddShort(buf + rc, doh->doh_answers);
00232     rc += AddShort(buf + rc, doh->doh_authrr);
00233     rc += AddShort(buf + rc, doh->doh_addrr);
00234 
00235     return rc;
00236 }
00237 
00238 static uint16_t DecodeDnsHeader(DNSHEADER * doh, uint8_t * buf)
00239 {
00240     uint16_t rc;
00241 
00242     rc = ScanShort(buf, &doh->doh_id);
00243     rc += ScanShort(buf + rc, &doh->doh_flags);
00244     rc += ScanShort(buf + rc, &doh->doh_quests);
00245     rc += ScanShort(buf + rc, &doh->doh_answers);
00246     rc += ScanShort(buf + rc, &doh->doh_authrr);
00247     rc += ScanShort(buf + rc, &doh->doh_addrr);
00248 
00249     return rc;
00250 }
00251 
00252 static DNSQUESTION *CreateDnsQuestion(DNSQUESTION * doq, CONST uint8_t * name, uint16_t type)
00253 {
00254     if (doq == NULL)
00255         doq = calloc(1, sizeof(DNSQUESTION));
00256     if (doq) {
00257         if (doq->doq_name)
00258             free(doq->doq_name);
00259         doq->doq_name = (uint8_t *) strdup((char *) name);
00260         doq->doq_type = type;
00261         doq->doq_class = 1;
00262     }
00263     return doq;
00264 }
00265 
00266 static void ReleaseDnsQuestion(DNSQUESTION * doq)
00267 {
00268     if (doq) {
00269         if (doq->doq_name)
00270             free(doq->doq_name);
00271         free(doq);
00272     }
00273 }
00274 
00275 static uint16_t EncodeDnsQuestion(uint8_t * buf, DNSQUESTION * doq)
00276 {
00277     uint16_t rc;
00278 
00279     rc = AddName(buf, doq->doq_name);
00280     rc += AddShort(buf + rc, doq->doq_type);
00281     rc += AddShort(buf + rc, doq->doq_class);
00282 
00283     return rc;
00284 }
00285 
00286 static uint16_t DecodeDnsQuestion(DNSQUESTION * doq, uint8_t * buf)
00287 {
00288     uint16_t rc;
00289 
00290     rc = ScanName(buf, &doq->doq_name);
00291     rc += ScanShort(buf + rc, &doq->doq_type);
00292     rc += ScanShort(buf + rc, &doq->doq_class);
00293 
00294     return rc;
00295 }
00296 
00297 static DNSRESOURCE *CreateDnsResource(DNSRESOURCE * dor)
00298 {
00299     if (dor == NULL)
00300         dor = calloc(1, sizeof(DNSRESOURCE));
00301     return dor;
00302 }
00303 
00304 static void ReleaseDnsResource(DNSRESOURCE * dor)
00305 {
00306     if (dor) {
00307         if (dor->dor_name)
00308             free(dor->dor_name);
00309         if (dor->dor_data)
00310             free(dor->dor_data);
00311         free(dor);
00312     }
00313 }
00314 
00315 static uint16_t DecodeDnsResource(DNSRESOURCE * dor, uint8_t * buf)
00316 {
00317     uint16_t rc;
00318 
00319     rc = ScanName(buf, &dor->dor_name);
00320     rc += ScanShort(buf + rc, &dor->dor_type);
00321     rc += ScanShort(buf + rc, &dor->dor_class);
00322     rc += ScanLong(buf + rc, &dor->dor_ttl);
00323     rc += ScanShort(buf + rc, &dor->dor_len);
00324     rc += ScanBinary(buf + rc, &dor->dor_data, dor->dor_len);
00325 
00326     return rc;
00327 }
00328 
00338 void NutDnsConfig(CONST uint8_t * hostname, CONST uint8_t * domain, uint32_t dnsip)
00339 {
00340     NutDnsConfig2(hostname, domain, dnsip, 0);
00341 }
00342 
00343 void NutDnsGetConfig2(char **hostname, char **domain, uint32_t * pdnsip, uint32_t * sdnsip)
00344 {
00345     if (hostname) {
00346         *hostname = (char *) confdns.doc_hostname;
00347     }
00348     if (domain) {
00349         *domain = (char *) confdns.doc_domain;
00350     }
00351     if (pdnsip) {
00352         *pdnsip = confdns.doc_ip1;
00353     }
00354     if (sdnsip) {
00355         *sdnsip = confdns.doc_ip2;
00356     }
00357 }
00358 
00371 uint32_t NutDnsGetResource(CONST uint8_t * hostname, CONST uint16_t type);
00372 
00373 uint32_t NutDnsGetHostByName(CONST uint8_t * hostname)
00374 {
00375     return NutDnsGetResource(hostname, 1);
00376 }
00377 
00391 uint8_t NutDnsGetResourceAll(CONST uint8_t * hostname, CONST uint16_t type, uint32_t * ip_all);
00392 
00393 uint8_t NutDnsGetHostsByName(CONST uint8_t * hostname, uint32_t * ip_all)
00394 {
00395     return NutDnsGetResourceAll(hostname, 1, ip_all);
00396 }
00397 
00398 uint32_t NutDnsGetMxByDomain(CONST uint8_t * hostname)
00399 {
00400     return NutDnsGetResource(hostname, 0x0F);
00401 }
00402 
00403 uint32_t NutDnsGetResource(CONST uint8_t * hostname, CONST uint16_t type)
00404 {
00405     uint32_t ip = 0;
00406     uint8_t *pkt;
00407     uint16_t len;
00408     uint16_t id = 0;
00409     UDPSOCKET *sock;
00410     DNSHEADER *doh = 0;
00411     DNSQUESTION *doq = 0;
00412     DNSRESOURCE *dor = 0;
00413     int n;
00414     int retries;
00415     uint32_t raddr;
00416     uint16_t rport;
00417 
00418     /*
00419      * We need a configured DNS address.
00420      */
00421     if (confdns.doc_ip1 == 0 && confdns.doc_ip2 == 0)
00422         return 0;
00423 
00424     /*
00425      * Create client socket and allocate
00426      * a buffer for the UDP packet.
00427      */
00428     if ((sock = NutUdpCreateSocket(0)) == 0)
00429         return 0;
00430     pkt = malloc(512);
00431 
00432     for (retries = 0; retries < 6; retries++) {
00433 
00434         /*
00435          * Create standard header info structures.
00436          */
00437         doh = CreateDnsHeader(doh, ++id);
00438         doq = CreateDnsQuestion(doq, hostname, type);
00439 
00440 #ifdef NUTDEBUG
00441         //DumpDnsHeader(doh);
00442         //DumpDnsQuestion(doq);
00443 #endif
00444 
00445         /*
00446          * Encode the header info into the packet buffer
00447          * and send it to the DNS server.
00448          */
00449         len = EncodeDnsHeader(pkt, doh);
00450         len += EncodeDnsQuestion(pkt + len, doq);
00451 
00452         if ((retries & 1) == 0 || confdns.doc_ip2 == 0) {
00453             if (NutUdpSendTo(sock, confdns.doc_ip1, 53, pkt, len) < 0)
00454                 break;
00455         } else {
00456             if (NutUdpSendTo(sock, confdns.doc_ip2, 53, pkt, len) < 0)
00457                 break;
00458         }
00459 
00460         /*
00461          * Loop until we receive a response with the
00462          * expected id or until timeout.
00463          */
00464         for (;;) {
00465             len = 0;
00466             n = NutUdpReceiveFrom(sock, &raddr, &rport, pkt, 512, 1000);
00467             if (n <= 0)
00468                 break;
00469             if (n > 12) {
00470                 len = DecodeDnsHeader(doh, pkt);
00471 #ifdef NUTDEBUG
00472                 //DumpDnsHeader(doh);
00473 #endif
00474                 if (doh->doh_id == id)
00475                     break;
00476             }
00477         }
00478 
00479         /*
00480          * Decode the answer.
00481          */
00482         if (len && doh->doh_quests == 1) {
00483             len += DecodeDnsQuestion(doq, pkt + len);
00484 #ifdef NUTDEBUG
00485             //DumpDnsQuestion(doq);
00486 #endif
00487             if (doh->doh_answers < 1)
00488                 break;
00489             else {
00490                 for (n = 1; n <= (int) doh->doh_answers; n++) {
00491                     dor = CreateDnsResource(dor);
00492                     len += DecodeDnsResource(dor, pkt + len);
00493 #ifdef NUTDEBUG
00494                     //DumpDnsResource(dor);
00495 #endif
00496                     if (dor->dor_type == 1)
00497                         break;
00498                 }
00499                 if (dor->dor_len == 4) {
00500                     ip = *dor->dor_data;
00501                     ip += (uint32_t) (*(dor->dor_data + 1)) << 8;
00502                     ip += (uint32_t) (*(dor->dor_data + 2)) << 16;
00503                     ip += (uint32_t) (*(dor->dor_data + 3)) << 24;
00504                     break;
00505                 }
00506                 /* TBD: 18.3.2004 - for MX requests authoritative rrs should be skipped + additional rrs should be searched for IP address */
00507             }
00508         }
00509     }
00510 
00511     /*
00512      * Clean up.
00513      */
00514     ReleaseDnsHeader(doh);
00515     ReleaseDnsQuestion(doq);
00516     ReleaseDnsResource(dor);
00517 
00518     free(pkt);
00519     NutUdpDestroySocket(sock);
00520 
00521     return ip;
00522 }
00523 
00524 uint8_t NutDnsGetResourceAll(CONST uint8_t * hostname, CONST uint16_t type, uint32_t * ip_all)
00525 {
00526     uint8_t n_ip;
00527     uint8_t *pkt;
00528     uint16_t len;
00529     uint16_t id = 0;
00530     UDPSOCKET *sock;
00531     DNSHEADER *doh = 0;
00532     DNSQUESTION *doq = 0;
00533     DNSRESOURCE *dor = 0;
00534     int n;
00535     int retries;
00536     uint32_t raddr;
00537     uint16_t rport;
00538 
00539 
00540     for (n_ip = 0; n_ip < 8; n_ip++)
00541         ip_all[n_ip] = 0;
00542 
00543     /*
00544      * We need a configured DNS address.
00545      */
00546     if (confdns.doc_ip1 == 0 && confdns.doc_ip2 == 0)
00547         return 0;
00548 
00549     /*
00550      * Create client socket and allocate
00551      * a buffer for the UDP packet.
00552      */
00553     if ((sock = NutUdpCreateSocket(0)) == 0)
00554         return 0;
00555     pkt = NutHeapAlloc(512);
00556 
00557     for (retries = 0; retries < 6; retries++) {
00558 
00559         /*
00560          * Create standard header info structures.
00561          */
00562         doh = CreateDnsHeader(doh, ++id);
00563         doq = CreateDnsQuestion(doq, hostname, type);
00564 
00565 #ifdef NUTDEBUG
00566         //DumpDnsHeader(doh);
00567         //DumpDnsQuestion(doq);
00568 #endif
00569 
00570         /*
00571          * Encode the header info into the packet buffer
00572          * and send it to the DNS server.
00573          */
00574         len = EncodeDnsHeader(pkt, doh);
00575         len += EncodeDnsQuestion(pkt + len, doq);
00576 
00577         if ((retries & 1) == 0 || confdns.doc_ip2 == 0) {
00578             if (NutUdpSendTo(sock, confdns.doc_ip1, 53, pkt, len) < 0)
00579                 break;
00580         } else {
00581             if (NutUdpSendTo(sock, confdns.doc_ip2, 53, pkt, len) < 0)
00582                 break;
00583         }
00584 
00585         /*
00586          * Loop until we receive a response with the
00587          * expected id or until timeout.
00588          */
00589         for (;;) {
00590             len = 0;
00591             n = NutUdpReceiveFrom(sock, &raddr, &rport, pkt, 512, 1000);
00592             if (n <= 0)
00593                 break;
00594             if (n > 12) {
00595                 len = DecodeDnsHeader(doh, pkt);
00596 #ifdef NUTDEBUG
00597                 //DumpDnsHeader(doh);
00598 #endif
00599                 if (doh->doh_id == id)
00600                     break;
00601             }
00602         }
00603 
00604         /*
00605          * Decode the answer.
00606          */
00607         if (len && doh->doh_quests == 1) {
00608             len += DecodeDnsQuestion(doq, pkt + len);
00609 #ifdef NUTDEBUG
00610             //DumpDnsQuestion(doq);
00611 #endif
00612             if (doh->doh_answers < 1)
00613                 break;
00614             else {
00615                 n_ip = 0;
00616                 for (n = 1; n <= (int) doh->doh_answers; n++) {
00617                     dor = CreateDnsResource(dor);
00618                     len += DecodeDnsResource(dor, pkt + len);
00619 #ifdef NUTDEBUG
00620                     //DumpDnsResource(dor);
00621 #endif
00622                     if (dor->dor_type == 1) {
00623                         if (dor->dor_len == 4) {
00624                             ip_all[n_ip] = *dor->dor_data;
00625                             ip_all[n_ip] += (uint32_t) (*(dor->dor_data + 1)) << 8;
00626                             ip_all[n_ip] += (uint32_t) (*(dor->dor_data + 2)) << 16;
00627                             ip_all[n_ip] += (uint32_t) (*(dor->dor_data + 3)) << 24;
00628                             n_ip++;
00629                         }
00630                     }
00631                 }
00632             }
00633         }
00634     }
00635 
00636     /*
00637      * Clean up.
00638      */
00639     ReleaseDnsHeader(doh);
00640     ReleaseDnsQuestion(doq);
00641     ReleaseDnsResource(dor);
00642 
00643     NutHeapFree(pkt);
00644     NutUdpDestroySocket(sock);
00645 
00646     return n_ip;
00647 }
00648