resolv.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  */
00032 
00033 /*
00034  * $Log: resolv.c,v $
00035  * Revision 1.16  2009/02/13 14:52:05  haraldkipp
00036  * Include memdebug.h for heap management debugging support.
00037  *
00038  * Revision 1.15  2009/02/06 15:40:29  haraldkipp
00039  * Using newly available strdup() and calloc().
00040  * Replaced NutHeap routines by standard malloc/free.
00041  * Replaced pointer value 0 by NULL.
00042  *
00043  * Revision 1.14  2008/08/11 07:00:35  haraldkipp
00044  * BSD types replaced by stdint types (feature request #1282721).
00045  *
00046  * Revision 1.13  2008/02/15 17:07:09  haraldkipp
00047  * Added routine to query DNS IP settings.
00048  *
00049  * Revision 1.12  2006/10/08 16:48:22  haraldkipp
00050  * Documentation fixed
00051  *
00052  * Revision 1.11  2006/03/16 15:25:39  haraldkipp
00053  * Changed human readable strings from u_char to char to stop GCC 4 from
00054  * nagging about signedness.
00055  *
00056  * Revision 1.10  2006/01/23 19:52:10  haraldkipp
00057  * Added required typecasts before left shift.
00058  *
00059  * Revision 1.9  2006/01/23 17:33:47  haraldkipp
00060  * Avoid memory alignment errors.
00061  *
00062  * Revision 1.8  2005/04/30 16:42:42  chaac
00063  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00064  * is defined in NutConf, it will make effect where it is used.
00065  *
00066  * Revision 1.7  2004/10/14 16:43:07  drsung
00067  * Fixed compiler warning "comparison between signed and unsigned"
00068  *
00069  * Revision 1.6  2004/07/28 19:23:15  drsung
00070  * call to DumpDnsResource commented out.
00071  *
00072  * Revision 1.5  2004/04/15 18:38:58  drsung
00073  * Bugfix if the DNS server sends more than one answer.
00074  *
00075  * Revision 1.4  2004/03/18 13:39:05  haraldkipp
00076  * Deprecated header file replaced
00077  *
00078  * Revision 1.3  2004/02/28 20:14:38  drsung
00079  * Merge from nut-3_4-release b/c of bugfixes.
00080  *
00081  * Revision 1.2.2.1  2004/02/28 19:15:07  drsung
00082  * Memory leak fixed in CreateDnsQuestion.
00083  * Thanks to Jean Pierre Gauthier.
00084  *
00085  * Revision 1.2  2003/07/20 18:25:40  haraldkipp
00086  * Support secondary DNS.
00087  *
00088  * Revision 1.1.1.1  2003/05/09 14:41:59  haraldkipp
00089  * Initial using 3.2.1
00090  *
00091  * Revision 1.5  2003/02/04 18:17:32  harald
00092  * Version 3 released
00093  *
00094  * Revision 1.4  2002/06/26 17:29:50  harald
00095  * First pre-release with 2.4 stack
00096  *
00097  */
00098 
00099 #include <cfg/os.h>
00100 
00101 #include <sys/device.h>
00102 #include <sys/timer.h>
00103 #include <sys/heap.h>
00104 
00105 #include <arpa/inet.h>
00106 #include <net/if_var.h>
00107 #include <sys/socket.h>
00108 
00109 #include <stdlib.h>
00110 #include <string.h>
00111 #include <memdebug.h>
00112 
00113 #ifdef NUTDEBUG
00114 #include <stdio.h>
00115 #endif
00116 
00121 
00122 typedef struct {
00123     uint8_t *doc_hostname;
00124     uint8_t *doc_domain;
00125     uint32_t doc_ip1;
00126     uint32_t doc_ip2;
00127 } DNSCONFIG;
00128 
00129 static DNSCONFIG doc;
00130 
00131 typedef struct {
00132     uint16_t doh_id;
00133     uint16_t doh_flags;
00134     uint16_t doh_quests;
00135     uint16_t doh_answers;
00136     uint16_t doh_authrr;
00137     uint16_t doh_addrr;
00138 } DNSHEADER;
00139 
00140 typedef struct {
00141     uint8_t *doq_name;
00142     uint16_t doq_type;
00143     uint16_t doq_class;
00144 } DNSQUESTION;
00145 
00146 typedef struct {
00147     uint8_t *dor_name;
00148     uint16_t dor_type;
00149     uint16_t dor_class;
00150     uint32_t dor_ttl;
00151     uint16_t dor_len;
00152     uint8_t *dor_data;
00153 } DNSRESOURCE;
00154 
00155 #ifdef NUTDEBUG
00156 void DumpDnsHeader(FILE * stream, DNSHEADER * doh)
00157 {
00158     fprintf(stream,
00159             "HEADER: id=%u flg=%04X #q=%u #an=%u #au=%u #ad=%u\r\n",
00160             doh->doh_id, doh->doh_flags, doh->doh_quests, doh->doh_answers, doh->doh_authrr, doh->doh_addrr);
00161 }
00162 
00163 void DumpDnsQuestion(FILE * stream, DNSQUESTION * doq)
00164 {
00165     fprintf(stream, "QUESTION: name='%s' type=%u class=%u\r\n", doq->doq_name, doq->doq_type, doq->doq_class);
00166 }
00167 
00168 void DumpDnsResource(FILE * stream, DNSRESOURCE * dor)
00169 {
00170     uint16_t i;
00171 
00172     fprintf(stream, "RESOURCE: name='%s' type=%u class=%u ttl=%lu len=%u ",
00173             dor->dor_name, dor->dor_type, dor->dor_class, dor->dor_ttl, dor->dor_len);
00174     for (i = 0; i < dor->dor_len; i++)
00175         fprintf(stream, "%02X ", dor->dor_data[i]);
00176     fputc('\n', stream);
00177 }
00178 #endif
00179 
00180 static uint16_t AddShort(uint8_t * cp, uint16_t val)
00181 {
00182     *cp++ = (uint8_t) (val >> 8);
00183     *cp++ = (uint8_t) val;
00184 
00185     return 2;
00186 }
00187 
00188 static uint16_t AddName(uint8_t * cp, CONST uint8_t * name)
00189 {
00190     uint8_t *lcp;
00191     uint16_t rc = strlen((char *)name) + 2;
00192 
00193     lcp = cp++;
00194     *lcp = 0;
00195     while (*name) {
00196         if (*name == '.') {
00197             lcp = cp++;
00198             *lcp = 0;
00199             name++;
00200         } else {
00201             *cp++ = *name++;
00202             (*lcp)++;
00203         }
00204     }
00205     *cp = 0;
00206 
00207     return rc;
00208 }
00209 
00210 static uint16_t ScanShort(uint8_t * cp, uint16_t * val)
00211 {
00212     *val = (uint16_t)(*cp++) << 8;
00213     *val |= *cp;
00214 
00215     return 2;
00216 }
00217 
00218 static uint16_t ScanLong(uint8_t * cp, uint32_t * val)
00219 {
00220     *val = *cp++;
00221     *val <<= 8;
00222     *val |= *cp++;
00223     *val <<= 8;
00224     *val |= *cp++;
00225     *val <<= 8;
00226     *val |= *cp;
00227 
00228     return 4;
00229 }
00230 
00231 static uint16_t ScanName(uint8_t * cp, uint8_t ** npp)
00232 {
00233     uint8_t len;
00234     uint16_t rc;
00235     uint8_t *np;
00236 
00237     if (*npp) {
00238         free(*npp);
00239         *npp = 0;
00240     }
00241 
00242     if ((*cp & 0xC0) == 0xC0)
00243         return 2;
00244 
00245     rc = strlen((char *)cp) + 1;
00246     np = *npp = malloc(rc);
00247     len = *cp++;
00248     while (len) {
00249         while (len--)
00250             *np++ = *cp++;
00251         if ((len = *cp++) != 0)
00252             *np++ = '.';
00253     }
00254     *np = 0;
00255 
00256     return rc;
00257 }
00258 
00259 static uint16_t ScanBinary(uint8_t * cp, uint8_t ** npp, uint16_t len)
00260 {
00261     if (*npp)
00262         free(*npp);
00263     *npp = malloc(len);
00264     memcpy(*npp, cp, len);
00265 
00266     return len;
00267 }
00268 
00269 static DNSHEADER *CreateDnsHeader(DNSHEADER * doh, uint16_t id)
00270 {
00271     if (doh == NULL)
00272         doh = calloc(1, sizeof(DNSHEADER));
00273     if (doh) {
00274         doh->doh_id = id;
00275         doh->doh_flags = 0x0100;
00276         doh->doh_quests = 1;
00277     }
00278     return doh;
00279 }
00280 
00281 static void ReleaseDnsHeader(DNSHEADER * doh)
00282 {
00283     if (doh)
00284         free(doh);
00285 }
00286 
00287 static uint16_t EncodeDnsHeader(uint8_t * buf, DNSHEADER * doh)
00288 {
00289     uint16_t rc;
00290 
00291     rc = AddShort(buf, doh->doh_id);
00292     rc += AddShort(buf + rc, doh->doh_flags);
00293     rc += AddShort(buf + rc, doh->doh_quests);
00294     rc += AddShort(buf + rc, doh->doh_answers);
00295     rc += AddShort(buf + rc, doh->doh_authrr);
00296     rc += AddShort(buf + rc, doh->doh_addrr);
00297 
00298     return rc;
00299 }
00300 
00301 static uint16_t DecodeDnsHeader(DNSHEADER * doh, uint8_t * buf)
00302 {
00303     uint16_t rc;
00304 
00305     rc = ScanShort(buf, &doh->doh_id);
00306     rc += ScanShort(buf + rc, &doh->doh_flags);
00307     rc += ScanShort(buf + rc, &doh->doh_quests);
00308     rc += ScanShort(buf + rc, &doh->doh_answers);
00309     rc += ScanShort(buf + rc, &doh->doh_authrr);
00310     rc += ScanShort(buf + rc, &doh->doh_addrr);
00311 
00312     return rc;
00313 }
00314 
00315 static DNSQUESTION *CreateDnsQuestion(DNSQUESTION * doq, CONST uint8_t * name, uint16_t type)
00316 {
00317     if (doq == NULL)
00318         doq = calloc(1, sizeof(DNSQUESTION));
00319     if (doq) {
00320         if (doq->doq_name)
00321             free(doq->doq_name);
00322         doq->doq_name = (uint8_t *)strdup((char *)name);
00323         doq->doq_type = type;
00324         doq->doq_class = 1;
00325     }
00326     return doq;
00327 }
00328 
00329 static void ReleaseDnsQuestion(DNSQUESTION * doq)
00330 {
00331     if (doq) {
00332         if (doq->doq_name)
00333             free(doq->doq_name);
00334         free(doq);
00335     }
00336 }
00337 
00338 static uint16_t EncodeDnsQuestion(uint8_t * buf, DNSQUESTION * doq)
00339 {
00340     uint16_t rc;
00341 
00342     rc = AddName(buf, doq->doq_name);
00343     rc += AddShort(buf + rc, doq->doq_type);
00344     rc += AddShort(buf + rc, doq->doq_class);
00345 
00346     return rc;
00347 }
00348 
00349 static uint16_t DecodeDnsQuestion(DNSQUESTION * doq, uint8_t * buf)
00350 {
00351     uint16_t rc;
00352 
00353     rc = ScanName(buf, &doq->doq_name);
00354     rc += ScanShort(buf + rc, &doq->doq_type);
00355     rc += ScanShort(buf + rc, &doq->doq_class);
00356 
00357     return rc;
00358 }
00359 
00360 static DNSRESOURCE *CreateDnsResource(DNSRESOURCE * dor)
00361 {
00362     if (dor == NULL)
00363         dor = calloc(1, sizeof(DNSRESOURCE));
00364     return dor;
00365 }
00366 
00367 static void ReleaseDnsResource(DNSRESOURCE * dor)
00368 {
00369     if (dor) {
00370         if (dor->dor_name)
00371             free(dor->dor_name);
00372         if (dor->dor_data)
00373             free(dor->dor_data);
00374         free(dor);
00375     }
00376 }
00377 
00378 static uint16_t DecodeDnsResource(DNSRESOURCE * dor, uint8_t * buf)
00379 {
00380     uint16_t rc;
00381 
00382     rc = ScanName(buf, &dor->dor_name);
00383     rc += ScanShort(buf + rc, &dor->dor_type);
00384     rc += ScanShort(buf + rc, &dor->dor_class);
00385     rc += ScanLong(buf + rc, &dor->dor_ttl);
00386     rc += ScanShort(buf + rc, &dor->dor_len);
00387     rc += ScanBinary(buf + rc, &dor->dor_data, dor->dor_len);
00388 
00389     return rc;
00390 }
00391 
00400 void NutDnsConfig2(uint8_t * hostname, uint8_t * domain, uint32_t pdnsip, uint32_t sdnsip)
00401 {
00402     if (doc.doc_hostname) {
00403         free(doc.doc_hostname);
00404         doc.doc_hostname = 0;
00405     }
00406     if (doc.doc_domain) {
00407         free(doc.doc_domain);
00408         doc.doc_domain = 0;
00409     }
00410     if (hostname) {
00411         doc.doc_hostname = (uint8_t *)strdup((char *)hostname);
00412     }
00413     if (domain) {
00414         doc.doc_domain = (uint8_t *)strdup((char *)domain);
00415     }
00416     doc.doc_ip1 = pdnsip;
00417     doc.doc_ip2 = sdnsip;
00418 }
00419 
00429 void NutDnsConfig(uint8_t * hostname, uint8_t * domain, uint32_t dnsip)
00430 {
00431     NutDnsConfig2(hostname, domain, dnsip, 0);
00432 }
00433 
00434 void NutDnsGetConfig2(char ** hostname, char ** domain, uint32_t *pdnsip, uint32_t *sdnsip)
00435 {
00436     if (hostname) {
00437         *hostname = (char *)doc.doc_hostname;
00438     }
00439     if (domain) {
00440         *domain = (char *)doc.doc_domain;
00441     }
00442     if (pdnsip) {
00443         *pdnsip = doc.doc_ip1;
00444     }
00445     if (sdnsip) {
00446         *sdnsip = doc.doc_ip2;
00447     }
00448 }
00449 
00462 uint32_t NutDnsGetResource(CONST uint8_t * hostname, CONST uint16_t type);
00463 
00464 uint32_t NutDnsGetHostByName(CONST uint8_t * hostname)
00465 {
00466     return NutDnsGetResource(hostname, 1);
00467 }
00468 
00469 uint32_t NutDnsGetMxByDomain(CONST uint8_t * hostname)
00470 {
00471     return NutDnsGetResource(hostname, 0x0F);
00472 }
00473 
00474 uint32_t NutDnsGetResource(CONST uint8_t * hostname, CONST uint16_t type)
00475 {
00476     uint32_t ip = 0;
00477     uint8_t *pkt;
00478     uint16_t len;
00479     uint16_t id = 0;
00480     UDPSOCKET *sock;
00481     DNSHEADER *doh = 0;
00482     DNSQUESTION *doq = 0;
00483     DNSRESOURCE *dor = 0;
00484     int n;
00485     int retries;
00486     uint32_t raddr;
00487     uint16_t rport;
00488 
00489     /*
00490      * We need a configured DNS address.
00491      */
00492     if (doc.doc_ip1 == 0 && doc.doc_ip2 == 0)
00493         return 0;
00494 
00495     /*
00496      * Create client socket and allocate
00497      * a buffer for the UDP packet.
00498      */
00499     if ((sock = NutUdpCreateSocket(0)) == 0)
00500         return 0;
00501     pkt = malloc(512);
00502 
00503     for (retries = 0; retries < 6; retries++) {
00504 
00505         /*
00506          * Create standard header info structures.
00507          */
00508         doh = CreateDnsHeader(doh, ++id);
00509         doq = CreateDnsQuestion(doq, hostname, type);
00510 
00511 #ifdef NUTDEBUG
00512         //DumpDnsHeader(doh);
00513         //DumpDnsQuestion(doq);
00514 #endif
00515 
00516         /*
00517          * Encode the header info into the packet buffer
00518          * and send it to the DNS server.
00519          */
00520         len = EncodeDnsHeader(pkt, doh);
00521         len += EncodeDnsQuestion(pkt + len, doq);
00522 
00523         if ((retries & 1) == 0 || doc.doc_ip2 == 0) {
00524             if (NutUdpSendTo(sock, doc.doc_ip1, 53, pkt, len) < 0)
00525                 break;
00526         } else {
00527             if (NutUdpSendTo(sock, doc.doc_ip2, 53, pkt, len) < 0)
00528                 break;
00529         }
00530 
00531         /*
00532          * Loop until we receive a response with the
00533          * expected id or until timeout.
00534          */
00535         for (;;) {
00536             len = 0;
00537             n = NutUdpReceiveFrom(sock, &raddr, &rport, pkt, 512, 1000);
00538             if (n <= 0)
00539                 break;
00540             if (n > 12) {
00541                 len = DecodeDnsHeader(doh, pkt);
00542 #ifdef NUTDEBUG
00543                 //DumpDnsHeader(doh);
00544 #endif
00545                 if (doh->doh_id == id)
00546                     break;
00547             }
00548         }
00549 
00550         /*
00551          * Decode the answer.
00552          */
00553         if (len && doh->doh_quests == 1) {
00554             len += DecodeDnsQuestion(doq, pkt + len);
00555 #ifdef NUTDEBUG
00556             //DumpDnsQuestion(doq);
00557 #endif
00558             if (doh->doh_answers < 1)
00559                 break;
00560             else {
00561                 for (n = 1; n <= (int) doh->doh_answers; n++) {
00562                     dor = CreateDnsResource(dor);
00563                     len += DecodeDnsResource(dor, pkt + len);
00564 #ifdef NUTDEBUG
00565                     //DumpDnsResource(dor);
00566 #endif
00567                     if (dor->dor_type == 1)
00568                         break;
00569                 }
00570                 if (dor->dor_len == 4) {
00571                     ip = *dor->dor_data;
00572                     ip += (uint32_t)(*(dor->dor_data + 1)) << 8;
00573                     ip += (uint32_t)(*(dor->dor_data + 2)) << 16;
00574                     ip += (uint32_t)(*(dor->dor_data + 3)) << 24;
00575                     break;
00576                 }
00577                 /* TBD: 18.3.2004 - for MX requests authoritative rrs should be skipped + additional rrs should be searched for IP address */
00578             }
00579         }
00580     }
00581 
00582     /*
00583      * Clean up.
00584      */
00585     ReleaseDnsHeader(doh);
00586     ReleaseDnsQuestion(doq);
00587     ReleaseDnsResource(dor);
00588 
00589     free(pkt);
00590     NutUdpDestroySocket(sock);
00591 
00592     return ip;
00593 }
00594 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/