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

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