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

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