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.14  2008/08/11 07:00:35  haraldkipp
00036  * BSD types replaced by stdint types (feature request #1282721).
00037  *
00038  * Revision 1.13  2008/02/15 17:07:09  haraldkipp
00039  * Added routine to query DNS IP settings.
00040  *
00041  * Revision 1.12  2006/10/08 16:48:22  haraldkipp
00042  * Documentation fixed
00043  *
00044  * Revision 1.11  2006/03/16 15:25:39  haraldkipp
00045  * Changed human readable strings from u_char to char to stop GCC 4 from
00046  * nagging about signedness.
00047  *
00048  * Revision 1.10  2006/01/23 19:52:10  haraldkipp
00049  * Added required typecasts before left shift.
00050  *
00051  * Revision 1.9  2006/01/23 17:33:47  haraldkipp
00052  * Avoid memory alignment errors.
00053  *
00054  * Revision 1.8  2005/04/30 16:42:42  chaac
00055  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00056  * is defined in NutConf, it will make effect where it is used.
00057  *
00058  * Revision 1.7  2004/10/14 16:43:07  drsung
00059  * Fixed compiler warning "comparison between signed and unsigned"
00060  *
00061  * Revision 1.6  2004/07/28 19:23:15  drsung
00062  * call to DumpDnsResource commented out.
00063  *
00064  * Revision 1.5  2004/04/15 18:38:58  drsung
00065  * Bugfix if the DNS server sends more than one answer.
00066  *
00067  * Revision 1.4  2004/03/18 13:39:05  haraldkipp
00068  * Deprecated header file replaced
00069  *
00070  * Revision 1.3  2004/02/28 20:14:38  drsung
00071  * Merge from nut-3_4-release b/c of bugfixes.
00072  *
00073  * Revision 1.2.2.1  2004/02/28 19:15:07  drsung
00074  * Memory leak fixed in CreateDnsQuestion.
00075  * Thanks to Jean Pierre Gauthier.
00076  *
00077  * Revision 1.2  2003/07/20 18:25:40  haraldkipp
00078  * Support secondary DNS.
00079  *
00080  * Revision 1.1.1.1  2003/05/09 14:41:59  haraldkipp
00081  * Initial using 3.2.1
00082  *
00083  * Revision 1.5  2003/02/04 18:17:32  harald
00084  * Version 3 released
00085  *
00086  * Revision 1.4  2002/06/26 17:29:50  harald
00087  * First pre-release with 2.4 stack
00088  *
00089  */
00090 
00091 #include <cfg/os.h>
00092 #include <string.h>
00093 
00094 #include <sys/device.h>
00095 #include <sys/timer.h>
00096 #include <sys/heap.h>
00097 
00098 #include <arpa/inet.h>
00099 #include <net/if_var.h>
00100 #include <sys/socket.h>
00101 
00102 #ifdef NUTDEBUG
00103 #include <stdio.h>
00104 #endif
00105 
00110 
00111 typedef struct {
00112     uint8_t *doc_hostname;
00113     uint8_t *doc_domain;
00114     uint32_t doc_ip1;
00115     uint32_t doc_ip2;
00116 } DNSCONFIG;
00117 
00118 static DNSCONFIG doc;
00119 
00120 typedef struct {
00121     uint16_t doh_id;
00122     uint16_t doh_flags;
00123     uint16_t doh_quests;
00124     uint16_t doh_answers;
00125     uint16_t doh_authrr;
00126     uint16_t doh_addrr;
00127 } DNSHEADER;
00128 
00129 typedef struct {
00130     uint8_t *doq_name;
00131     uint16_t doq_type;
00132     uint16_t doq_class;
00133 } DNSQUESTION;
00134 
00135 typedef struct {
00136     uint8_t *dor_name;
00137     uint16_t dor_type;
00138     uint16_t dor_class;
00139     uint32_t dor_ttl;
00140     uint16_t dor_len;
00141     uint8_t *dor_data;
00142 } DNSRESOURCE;
00143 
00144 #ifdef NUTDEBUG
00145 void DumpDnsHeader(FILE * stream, DNSHEADER * doh)
00146 {
00147     fprintf(stream,
00148             "HEADER: id=%u flg=%04X #q=%u #an=%u #au=%u #ad=%u\r\n",
00149             doh->doh_id, doh->doh_flags, doh->doh_quests, doh->doh_answers, doh->doh_authrr, doh->doh_addrr);
00150 }
00151 
00152 void DumpDnsQuestion(FILE * stream, DNSQUESTION * doq)
00153 {
00154     fprintf(stream, "QUESTION: name='%s' type=%u class=%u\r\n", doq->doq_name, doq->doq_type, doq->doq_class);
00155 }
00156 
00157 void DumpDnsResource(FILE * stream, DNSRESOURCE * dor)
00158 {
00159     uint16_t i;
00160 
00161     fprintf(stream, "RESOURCE: name='%s' type=%u class=%u ttl=%lu len=%u ",
00162             dor->dor_name, dor->dor_type, dor->dor_class, dor->dor_ttl, dor->dor_len);
00163     for (i = 0; i < dor->dor_len; i++)
00164         fprintf(stream, "%02X ", dor->dor_data[i]);
00165     fputc('\n', stream);
00166 }
00167 #endif
00168 
00169 static uint16_t AddShort(uint8_t * cp, uint16_t val)
00170 {
00171     *cp++ = (uint8_t) (val >> 8);
00172     *cp++ = (uint8_t) val;
00173 
00174     return 2;
00175 }
00176 
00177 static uint16_t AddName(uint8_t * cp, CONST uint8_t * name)
00178 {
00179     uint8_t *lcp;
00180     uint16_t rc = strlen((char *)name) + 2;
00181 
00182     lcp = cp++;
00183     *lcp = 0;
00184     while (*name) {
00185         if (*name == '.') {
00186             lcp = cp++;
00187             *lcp = 0;
00188             name++;
00189         } else {
00190             *cp++ = *name++;
00191             (*lcp)++;
00192         }
00193     }
00194     *cp = 0;
00195 
00196     return rc;
00197 }
00198 
00199 static uint16_t ScanShort(uint8_t * cp, uint16_t * val)
00200 {
00201     *val = (uint16_t)(*cp++) << 8;
00202     *val |= *cp;
00203 
00204     return 2;
00205 }
00206 
00207 static uint16_t ScanLong(uint8_t * cp, uint32_t * val)
00208 {
00209     *val = *cp++;
00210     *val <<= 8;
00211     *val |= *cp++;
00212     *val <<= 8;
00213     *val |= *cp++;
00214     *val <<= 8;
00215     *val |= *cp;
00216 
00217     return 4;
00218 }
00219 
00220 static uint16_t ScanName(uint8_t * cp, uint8_t ** npp)
00221 {
00222     uint8_t len;
00223     uint16_t rc;
00224     uint8_t *np;
00225 
00226     if (*npp) {
00227         NutHeapFree(*npp);
00228         *npp = 0;
00229     }
00230 
00231     if ((*cp & 0xC0) == 0xC0)
00232         return 2;
00233 
00234     rc = strlen((char *)cp) + 1;
00235     np = *npp = NutHeapAlloc(rc);
00236     len = *cp++;
00237     while (len) {
00238         while (len--)
00239             *np++ = *cp++;
00240         if ((len = *cp++) != 0)
00241             *np++ = '.';
00242     }
00243     *np = 0;
00244 
00245     return rc;
00246 }
00247 
00248 static uint16_t ScanBinary(uint8_t * cp, uint8_t ** npp, uint16_t len)
00249 {
00250     if (*npp)
00251         NutHeapFree(*npp);
00252     *npp = NutHeapAlloc(len);
00253     memcpy(*npp, cp, len);
00254 
00255     return len;
00256 }
00257 
00258 static DNSHEADER *CreateDnsHeader(DNSHEADER * doh, uint16_t id)
00259 {
00260     if (doh == 0)
00261         doh = NutHeapAllocClear(sizeof(DNSHEADER));
00262     if (doh) {
00263         doh->doh_id = id;
00264         doh->doh_flags = 0x0100;
00265         doh->doh_quests = 1;
00266     }
00267     return doh;
00268 }
00269 
00270 static void ReleaseDnsHeader(DNSHEADER * doh)
00271 {
00272     if (doh)
00273         NutHeapFree(doh);
00274 }
00275 
00276 static uint16_t EncodeDnsHeader(uint8_t * buf, DNSHEADER * doh)
00277 {
00278     uint16_t rc;
00279 
00280     rc = AddShort(buf, doh->doh_id);
00281     rc += AddShort(buf + rc, doh->doh_flags);
00282     rc += AddShort(buf + rc, doh->doh_quests);
00283     rc += AddShort(buf + rc, doh->doh_answers);
00284     rc += AddShort(buf + rc, doh->doh_authrr);
00285     rc += AddShort(buf + rc, doh->doh_addrr);
00286 
00287     return rc;
00288 }
00289 
00290 static uint16_t DecodeDnsHeader(DNSHEADER * doh, uint8_t * buf)
00291 {
00292     uint16_t rc;
00293 
00294     rc = ScanShort(buf, &doh->doh_id);
00295     rc += ScanShort(buf + rc, &doh->doh_flags);
00296     rc += ScanShort(buf + rc, &doh->doh_quests);
00297     rc += ScanShort(buf + rc, &doh->doh_answers);
00298     rc += ScanShort(buf + rc, &doh->doh_authrr);
00299     rc += ScanShort(buf + rc, &doh->doh_addrr);
00300 
00301     return rc;
00302 }
00303 
00304 static DNSQUESTION *CreateDnsQuestion(DNSQUESTION * doq, CONST uint8_t * name, uint16_t type)
00305 {
00306     if (doq == 0)
00307         doq = NutHeapAllocClear(sizeof(DNSQUESTION));
00308     if (doq) {
00309         if (doq->doq_name)
00310             NutHeapFree(doq->doq_name);
00311         doq->doq_name = NutHeapAlloc(strlen((char *)name) + 1);
00312         strcpy((char *)doq->doq_name, (char *)name);
00313         doq->doq_type = type;
00314         doq->doq_class = 1;
00315     }
00316     return doq;
00317 }
00318 
00319 static void ReleaseDnsQuestion(DNSQUESTION * doq)
00320 {
00321     if (doq) {
00322         if (doq->doq_name)
00323             NutHeapFree(doq->doq_name);
00324         NutHeapFree(doq);
00325     }
00326 }
00327 
00328 static uint16_t EncodeDnsQuestion(uint8_t * buf, DNSQUESTION * doq)
00329 {
00330     uint16_t rc;
00331 
00332     rc = AddName(buf, doq->doq_name);
00333     rc += AddShort(buf + rc, doq->doq_type);
00334     rc += AddShort(buf + rc, doq->doq_class);
00335 
00336     return rc;
00337 }
00338 
00339 static uint16_t DecodeDnsQuestion(DNSQUESTION * doq, uint8_t * buf)
00340 {
00341     uint16_t rc;
00342 
00343     rc = ScanName(buf, &doq->doq_name);
00344     rc += ScanShort(buf + rc, &doq->doq_type);
00345     rc += ScanShort(buf + rc, &doq->doq_class);
00346 
00347     return rc;
00348 }
00349 
00350 static DNSRESOURCE *CreateDnsResource(DNSRESOURCE * dor)
00351 {
00352     if (dor == 0)
00353         dor = NutHeapAllocClear(sizeof(DNSRESOURCE));
00354     return dor;
00355 }
00356 
00357 static void ReleaseDnsResource(DNSRESOURCE * dor)
00358 {
00359     if (dor) {
00360         if (dor->dor_name)
00361             NutHeapFree(dor->dor_name);
00362         if (dor->dor_data)
00363             NutHeapFree(dor->dor_data);
00364         NutHeapFree(dor);
00365     }
00366 }
00367 
00368 static uint16_t DecodeDnsResource(DNSRESOURCE * dor, uint8_t * buf)
00369 {
00370     uint16_t rc;
00371 
00372     rc = ScanName(buf, &dor->dor_name);
00373     rc += ScanShort(buf + rc, &dor->dor_type);
00374     rc += ScanShort(buf + rc, &dor->dor_class);
00375     rc += ScanLong(buf + rc, &dor->dor_ttl);
00376     rc += ScanShort(buf + rc, &dor->dor_len);
00377     rc += ScanBinary(buf + rc, &dor->dor_data, dor->dor_len);
00378 
00379     return rc;
00380 }
00381 
00390 void NutDnsConfig2(uint8_t * hostname, uint8_t * domain, uint32_t pdnsip, uint32_t sdnsip)
00391 {
00392     if (doc.doc_hostname) {
00393         NutHeapFree(doc.doc_hostname);
00394         doc.doc_hostname = 0;
00395     }
00396     if (doc.doc_domain) {
00397         NutHeapFree(doc.doc_domain);
00398         doc.doc_domain = 0;
00399     }
00400     if (hostname) {
00401         doc.doc_hostname = NutHeapAlloc(strlen((char *)hostname) + 1);
00402         strcpy((char *)doc.doc_hostname, (char *)hostname);
00403     }
00404     if (domain) {
00405         doc.doc_domain = NutHeapAlloc(strlen((char *)domain) + 1);
00406         strcpy((char *)doc.doc_domain, (char *)domain);
00407     }
00408     doc.doc_ip1 = pdnsip;
00409     doc.doc_ip2 = sdnsip;
00410 }
00411 
00421 void NutDnsConfig(uint8_t * hostname, uint8_t * domain, uint32_t dnsip)
00422 {
00423     NutDnsConfig2(hostname, domain, dnsip, 0);
00424 }
00425 
00426 void NutDnsGetConfig2(char ** hostname, char ** domain, uint32_t *pdnsip, uint32_t *sdnsip)
00427 {
00428     if (hostname) {
00429         *hostname = (char *)doc.doc_hostname;
00430     }
00431     if (domain) {
00432         *domain = (char *)doc.doc_domain;
00433     }
00434     if (pdnsip) {
00435         *pdnsip = doc.doc_ip1;
00436     }
00437     if (sdnsip) {
00438         *sdnsip = doc.doc_ip2;
00439     }
00440 }
00441 
00454 uint32_t NutDnsGetResource(CONST uint8_t * hostname, CONST uint16_t type);
00455 
00456 uint32_t NutDnsGetHostByName(CONST uint8_t * hostname)
00457 {
00458     return NutDnsGetResource(hostname, 1);
00459 }
00460 
00461 uint32_t NutDnsGetMxByDomain(CONST uint8_t * hostname)
00462 {
00463     return NutDnsGetResource(hostname, 0x0F);
00464 }
00465 
00466 uint32_t NutDnsGetResource(CONST uint8_t * hostname, CONST uint16_t type)
00467 {
00468     uint32_t ip = 0;
00469     uint8_t *pkt;
00470     uint16_t len;
00471     uint16_t id = 0;
00472     UDPSOCKET *sock;
00473     DNSHEADER *doh = 0;
00474     DNSQUESTION *doq = 0;
00475     DNSRESOURCE *dor = 0;
00476     int n;
00477     int retries;
00478     uint32_t raddr;
00479     uint16_t rport;
00480 
00481     /*
00482      * We need a configured DNS address.
00483      */
00484     if (doc.doc_ip1 == 0 && doc.doc_ip2 == 0)
00485         return 0;
00486 
00487     /*
00488      * Create client socket and allocate
00489      * a buffer for the UDP packet.
00490      */
00491     if ((sock = NutUdpCreateSocket(0)) == 0)
00492         return 0;
00493     pkt = NutHeapAlloc(512);
00494 
00495     for (retries = 0; retries < 6; retries++) {
00496 
00497         /*
00498          * Create standard header info structures.
00499          */
00500         doh = CreateDnsHeader(doh, ++id);
00501         doq = CreateDnsQuestion(doq, hostname, type);
00502 
00503 #ifdef NUTDEBUG
00504         //DumpDnsHeader(doh);
00505         //DumpDnsQuestion(doq);
00506 #endif
00507 
00508         /*
00509          * Encode the header info into the packet buffer
00510          * and send it to the DNS server.
00511          */
00512         len = EncodeDnsHeader(pkt, doh);
00513         len += EncodeDnsQuestion(pkt + len, doq);
00514 
00515         if ((retries & 1) == 0 || doc.doc_ip2 == 0) {
00516             if (NutUdpSendTo(sock, doc.doc_ip1, 53, pkt, len) < 0)
00517                 break;
00518         } else {
00519             if (NutUdpSendTo(sock, doc.doc_ip2, 53, pkt, len) < 0)
00520                 break;
00521         }
00522 
00523         /*
00524          * Loop until we receive a response with the
00525          * expected id or until timeout.
00526          */
00527         for (;;) {
00528             len = 0;
00529             n = NutUdpReceiveFrom(sock, &raddr, &rport, pkt, 512, 1000);
00530             if (n <= 0)
00531                 break;
00532             if (n > 12) {
00533                 len = DecodeDnsHeader(doh, pkt);
00534 #ifdef NUTDEBUG
00535                 //DumpDnsHeader(doh);
00536 #endif
00537                 if (doh->doh_id == id)
00538                     break;
00539             }
00540         }
00541 
00542         /*
00543          * Decode the answer.
00544          */
00545         if (len && doh->doh_quests == 1) {
00546             len += DecodeDnsQuestion(doq, pkt + len);
00547 #ifdef NUTDEBUG
00548             //DumpDnsQuestion(doq);
00549 #endif
00550             if (doh->doh_answers < 1)
00551                 break;
00552             else {
00553                 for (n = 1; n <= (int) doh->doh_answers; n++) {
00554                     dor = CreateDnsResource(dor);
00555                     len += DecodeDnsResource(dor, pkt + len);
00556 #ifdef NUTDEBUG
00557                     //DumpDnsResource(dor);
00558 #endif
00559                     if (dor->dor_type == 1)
00560                         break;
00561                 }
00562                 if (dor->dor_len == 4) {
00563                     ip = *dor->dor_data;
00564                     ip += (uint32_t)(*(dor->dor_data + 1)) << 8;
00565                     ip += (uint32_t)(*(dor->dor_data + 2)) << 16;
00566                     ip += (uint32_t)(*(dor->dor_data + 3)) << 24;
00567                     break;
00568                 }
00569                 /* TBD: 18.3.2004 - for MX requests authoritative rrs should be skipped + additional rrs should be searched for IP address */
00570             }
00571         }
00572     }
00573 
00574     /*
00575      * Clean up.
00576      */
00577     ReleaseDnsHeader(doh);
00578     ReleaseDnsQuestion(doq);
00579     ReleaseDnsResource(dor);
00580 
00581     NutHeapFree(pkt);
00582     NutUdpDestroySocket(sock);
00583 
00584     return ip;
00585 }
00586 

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