libSPF v1.0: dns.c Source File

Main Page | Data Structures | File List | Data Fields | Globals | Search for

# dns.c

Go to the documentation of this file.

    00001 /* libspf - Sender Policy Framework library
    00002 *
    00003 *  ANSI C implementation of spf-draft-200405.txt
    00004 *
    00005 *  Author: James Couzens <jcouzens@codeshare.ca>
    00006 *  Author: Sean Comeau <scomeau@obscurity.org>
    00007 *
    00008 *  File:   dns.c
    00009 *  Desc:   DNS related functions
    00010 *
    00011 *  License:
    00012 *
    00013 *  The libspf Software License, Version 1.0
    00014 *
    00015 *  Copyright (c) 2004 James Couzens & Sean Comeau  All rights
    00016 *  reserved.
    00017 *
    00018 *  Redistribution and use in source and binary forms, with or without
    00019 *  modification, are permitted provided that the following conditions
    00020 *  are met:
    00021 *
    00022 *  1. Redistributions of source code must retain the above copyright
    00023 *     notice, this list of conditions and the following disclaimer.
    00024 *
    00025 *  2. Redistributions in binary form must reproduce the above copyright
    00026 *     notice, this list of conditions and the following disclaimer in
    00027 *     the documentation and/or other materials provided with the
    00028 *     distribution.
    00029 *
    00030 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
    00031 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    00032 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    00033 *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS MAKING USE OF THIS LICESEN
    00034 *  OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    00035 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    00036 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    00037 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    00038 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    00039 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    00040 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    00041 *  SUCH DAMAGE.
    00042 *
    00043 */
    00044
    00045
    00046 #ifdef _WITH_PTHREADS
    00047 #include <pthread.h>          /* pthread_mutex_t */
    00048   #ifdef HAVE_GETHOSTBYNAME_R
    00049 #include <errno.h>            /* errno */
    00050   #endif /* HAVE_GETHOSTBYNAME_R */
    00051 #endif /* _WITH_PTHREADS */
    00052
    00053 #include "spf.h"              /* SPF */
    00054 #include "util.h"             /* Utility functions */
    00055 #include "dns.h"              /* our header */
    00056 #include "../../config.h"     /* autoconf */
    00057
    00058 #ifdef _WITH_PTHREADS
    00059
    00060 /* mutex for gethostbyname wrapper */
    00061 pthread_mutex_t dns_mutex = PTHREAD_MUTEX_INITIALIZER;
    00062
    00063   #ifdef HAVE_GETHOSTBYNAME_R
    00064
    00065 /* used by gethostbyname_r */
    00066 extern int errno;
    00067
    00068   #endif /* HAVE_GETHOSTBYNAME_R */
    00069 #else
    00070
    00071 /* mutex for gethostbyname dummy wrapper */
    00072 void *dns_mutex;
    00073
    00074 #endif /* _WITH_PTHREADS */
    00075
    00076
    00202 /* DNS_query
    00203 *
    00204 * Author: James Couzens <jcouzens@codeshare.ca>\n
    00205 * Author: Travis Anderson <tanderson@codeshare.ca>\n
    00206 *
    00207 * Date:   12/10/03
    00208 * Date:   02/20/04 - Added cache by Travis Anderson <tanderson@codeshare.ca>
    00209 *
    00210 * Desc:
    00211 *         Executes a DNS query of type T_TYPE and then calls the
    00212 * appropriate answer parsing function based on that type.  Returns
    00213 * a pointer to allocated memory (a string of space delimited
    00214 * records).  Upon failure returns NULL.
    00215 *
    00216 */
    00217 char *DNS_query(peer_info_t *p, const char *s, const int T_TYPE,
    00218   const char *mta)
    00219 {
    00220   HEADER *hdr;                  /* pointer to the header of the packet */
    00221
    00222   int8_t ancount;               /* number of answers */
    00223
    00224   int16_t r_len;                /* res_search return code & packet len */
    00225   int16_t rc;                   /* generic return code / length of */
    00226
    00227   int ttl;                      /* answer TTL */
    00228
    00229   char buf[SPF_MAXDNAME];       /* record extraction buffer */
    00230   char answer[SPF_PACKETSZ];    /* answer buffer */
    00231
    00232   char *rr_data = NULL;         /* record */
    00233
    00234   u_char *msg_ptr;              /* pointer to beginning of the message */
    00235   u_char *eom_ptr;              /* pointer to the end of the message */
    00236   u_char *rd_ptr;               /* pointer to uncompressed message */
    00237
    00238
    00239   if (s == NULL)
    00240   {
    00241     xepprintf("Passed a NULL char.  Aborting.\n");
    00242     return(NULL);
    00243   }
    00244
    00245   xprintf("Called with (%s) and type: %i\n", s, T_TYPE);
    00246
    00247   r_len = res_search(s, C_IN, T_TYPE, (u_char *)answer, SPF_PACKETSZ);
    00248
    00249   if (r_len <= 0)
    00250   {
    00251     switch (h_errno)
    00252     {
    00253       case HOST_NOT_FOUND:
    00254         snprintf(p->error, SPF_MAX_ERROR, "%s\n", hstrerror(h_errno));
    00255         UTIL_assoc_prefix(p, SPF_NONE, NULL);
    00256         xvprintf("%s\n", p->error);
    00257
    00258         return(NULL);
    00259
    00260       case TRY_AGAIN:
    00261         snprintf(p->error, SPF_MAX_ERROR, "%s\n", hstrerror(h_errno));
    00262         UTIL_assoc_prefix(p, SPF_NONE, NULL);
    00263         xvprintf("%s\n", p->error);
    00264
    00265         return(NULL);
    00266
    00267       case NO_RECOVERY:
    00268         snprintf(p->error, SPF_MAX_ERROR, "%s\n", hstrerror(h_errno));
    00269         UTIL_assoc_prefix(p, SPF_ERROR, NULL);
    00270         xvprintf("%s\n", p->error);
    00271
    00272         return(NULL);
    00273
    00274       case NO_DATA:
    00275         snprintf(p->error, SPF_MAX_ERROR, "%s\n", hstrerror(h_errno));
    00276         UTIL_assoc_prefix(p, SPF_NONE, NULL);
    00277         xvprintf("%s\n", p->error);
    00278
    00279         return(NULL);
    00280
    00281       default:
    00282         snprintf(p->error, SPF_MAX_ERROR, "%s\n", hstrerror(h_errno));
    00283         UTIL_assoc_prefix(p, SPF_ERROR, NULL);
    00284         xvprintf("%s\n", p->error);
    00285
    00286         return(NULL);
    00287     } /* switch */
    00288   }
    00289
    00290   hdr     = (HEADER *)&answer;
    00291   ancount = ntohs(hdr->ancount);
    00292
    00293   xvprintf("Received packet size of %i bytes which contains %i answers.\n",
    00294     r_len, ancount);
    00295
    00296   xvprintf("ANSWERS: %i\n", ancount);
    00297   xvprintf("QUESTIONS: %i\n", ntohs(hdr->qdcount));
    00298
    00299   if (ancount > 0)
    00300   {
    00301     msg_ptr = (u_char *)&answer;               /* point to start of message */
    00302     eom_ptr = (u_char *)&answer + r_len;       /* point to end of message */
    00303     rd_ptr  = (u_char *)&answer + HFIXEDSZ;    /* point to start of RDATA */
    00304
    00305     if ((rc = dn_skipname(rd_ptr, eom_ptr)) < 0)
    00306     {
    00307       xepprintf("Error obtaining QUESTION!\n");
    00308
    00309       return(NULL);
    00310     }
    00311
    00312     rd_ptr += rc + QFIXEDSZ;    /* jump to start of ANSWER */
    00313
    00314     switch (T_TYPE)
    00315     {
    00316        /*
    00317        * T_A: A 'a' or address record used to associate an IP address with any
    00318        *      given hostname.
    00319       */
    00320       case T_A:
    00321         return((char *)SPF_TRUE);
    00322
    00323        /*
    00324        * T_TXT: A 'txt' or TEXT record which is what is currently used to store
    00325        *        the SPF records within DNS.  Its unfortunate that this had to be done
    00326        *        because you'll discover that I had to make concessions to deal with
    00327        *        the fact that many people publish more than just SPF records using
    00328        *        the T_TXT record type, especially considering it was seeing use long
    00329        *        before SPF ever came on the scene.  Hopefully this will change in the
    00330        *        near future.
    00331       */
    00332       case T_TXT:
    00333         if ((rr_data = DNS_txt_answer(ancount, (u_char *)msg_ptr, (u_char *)eom_ptr,
    00334           (u_char *)rd_ptr, buf, &ttl)) == NULL)
    00335         {
    00336           return(NULL);
    00337         }
    00338         break;
    00339
    00340       /*
    00341        * T_MX: A 'mx' or Mail Exchanger (Server) record identifies an authorative
    00342        *       mail server (or in multiples server(s)) which may be used to deliver
    00343        *       e-mail to a given domain.
    00344       */
    00345       case T_MX:
    00346         if ((rr_data = DNS_mx_answer(ancount, (u_char *)msg_ptr, (u_char *)eom_ptr,
    00347           (u_char *)rd_ptr, buf, &ttl)) == NULL)
    00348         {
    00349           return(NULL);
    00350         }
    00351       break;
    00352
    00353       /*
    00354       * T_PTR: A 'ptr' DNS record contains the reverse address for a
    00355       *        given hostname in the format x.x.x.x-in-addr.arpa in the
    00356       *        case of IPv4.
    00357      */
    00358       case T_PTR:
    00359
    00360          /*
    00361          * Comment by: James Couzens <jcouzens@codeshare.ca>
    00362          * Date:       01/04/04
    00363          *
    00364          * DNS_ptr_answer doesn't allocate any memory and returns SPF_TRUE or
    00365          * SPF_FALSE, however this function returns a char so a the boolean
    00366          * value of SPF_TRUE/FALSE is cast to avoid writing a handler for
    00367          * this one specific instance.  I might change this in the future by
    00368          * either altering its return type or something else but for now this
    00369          * seems to do nicely.
    00370         */
    00371         if (DNS_ptr_answer(p, ancount, (u_char *)msg_ptr, (u_char *)eom_ptr,
    00372           (u_char *)rd_ptr, buf, mta, &ttl) == SPF_FALSE)
    00373         {
    00374           return((char *)SPF_FALSE);
    00375         }
    00376         else
    00377         {
    00378           return((char *)SPF_TRUE);
    00379         }
    00380         break;
    00381
    00382        /*
    00383        * T_CNAME: A 'cname' or an alias record for a DNS forward or reverse
    00384        *          record which has been assigned more than one value.
    00385       */
    00386       case T_CNAME:
    00387         if ((rr_data = DNS_cname_answer(ancount, (u_char *)msg_ptr, (u_char *)eom_ptr,
    00388           (u_char *)rd_ptr, buf, &ttl)) == NULL)
    00389         {
    00390           return(NULL);
    00391         }
    00392         break;
    00393       default:
    00394         break;
    00395     }
    00396     return(rr_data);
    00397   }
    00398
    00399   return(NULL);
    00400 }
    00401
    00402
    00403 /* DNS_txt_answer
    00404 *
    00405 *  Author: James Couzens <jcouzens@codeshare.ca>
    00406 *
    00407 *  Date:   01/02/04
    00408 *  Date:   02/23/04 - Bugfix from Albert Weichselbraun <albert@atnet.at>
    00409 *  Date:   02/20/04 - Added cache by Travis Anderson <tanderson@codeshare.ca>
    00410 *
    00411 *  Desc:
    00412 *          SPF_PACKETSZ bytes are allocated and then filled with \0 chars.
    00413 *  This buffer is then used in a TXT DNS query using data from the passed
    00414 *  peer_info_t structure.  Upon success this buffer is re-cast as a char *
    00415 *  and then a pointer to this memory is returned.  Upon failure a NULL
    00416 *  pointer is returned.
    00417 *
    00418 */
    00419 char *DNS_txt_answer(int16_t ancount, const u_char *msg_ptr,
    00420   const u_char *eom_ptr, u_char *rd_ptr, char *buf, int *ttl)
    00421 {
    00422   int16_t i;               /* utility */
    00423   int16_t j;               /* utility */
    00424   int16_t rc;              /* generic return code / length of */
    00425   int16_t rd_type;         /* answer type */
    00426   int16_t rd_len;          /* res_search return code & packet len */
    00427
    00428   int32_t rd_ttl;          /* TTL */
    00429
    00430   char *rr_data = NULL;    /* data pointer */
    00431   char *r_buf   = NULL;    /* return buffer */
    00432   char *pos     = NULL;    /* utility pointer */
    00433
    00434
    00435   if ((msg_ptr == NULL) || (eom_ptr == NULL) ||
    00436       (rd_ptr == NULL)  || (buf == NULL))
    00437   {
    00438     xepprintf("Called with NULL pointers\n");
    00439     return(NULL);
    00440   }
    00441
    00442   xpprintf("entering function\n");
    00443
    00444   i = 0;
    00445   j = ancount;
    00446   while ((ancount > 0) && (rd_ptr < eom_ptr))
    00447   {
    00448     if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    00449     {
    00450       xvprintf("Unable to expand T_TXT response packet!; Reason: %s\n",
    00451         hstrerror(h_errno));
    00452
    00453       if (rr_data != NULL)
    00454       {
    00455         xfree(rr_data);
    00456       }
    00457
    00458       return(NULL);
    00459     }
    00460
    00461     /* dname, type, class, TTL, rdata len, rdata */
    00462     rd_ptr += rc;                 /* jump to start of ANSWER data */
    00463     GETSHORT(rd_type, rd_ptr);    /* get response type */
    00464     rd_ptr += INT16SZ;            /* skip class */
    00465     GETLONG(rd_ttl, rd_ptr);      /* get TTL */
    00466     GETSHORT(rd_len, rd_ptr);     /* get data length */
    00467
    00468     *ttl = rd_ttl;                /* TTL working pointer */
    00469
    00470     if (rd_type != T_TXT)
    00471     {
    00472       xvprintf("Ignoring record not of T_TXT type. (%i)\n", rd_type);
    00473       rd_ptr += rd_len;
    00474       continue;
    00475     }
    00476
    00477     rd_ptr++;                    /* skip byte */
    00478
    00479     rd_ptr[rd_len]     = '\0';
    00480     rd_ptr[rd_len - 1] = ' ';
    00481
    00482     i += rd_len;
    00483
    00484     xvprintf("Answer %i has length %i. (%i)\n", ancount, rd_len, i);
    00485
    00486     /*
    00487      * Only received one answer, so this MUST start with v=spf1 or its
    00488      * not a valid SPFv1 record
    00489     */
    00490     if ((j == 1) && ((*rd_ptr != 'v') && (*(rd_ptr + 1) != '=')))
    00491     {
    00492       xvprintf("INVALID Answer Data: (%s) len: %i\n", rd_ptr, rd_len);
    00493
    00494       if (rr_data != NULL)
    00495       {
    00496         xfree(rr_data);
    00497       }
    00498
    00499       return(NULL);
    00500     }
    00501
    00502     xvprintf("Answer Data: (%s) len: %i\n", rd_ptr, rd_len);
    00503
    00504     if ((rd_len <= SPF_MAXDNAME) && (rd_len > 0))
    00505     {
    00506       if (rr_data == NULL)
    00507       {
    00508         rr_data = xmalloc(i + 1);
    00509       }
    00510       else
    00511       {
    00512         rr_data = xrealloc(rr_data, (i + 1));
    00513       }
    00514
    00515       xvprintf("REALLOCATE memory: %i bytes\n", i);
    00516
    00517       strncat(rr_data, (char *)rd_ptr, rd_len);
    00518       rr_data[i - 1] = ' ';
    00519       rr_data[i] = '\0';
    00520     }
    00521
    00522     rd_ptr += rd_len;
    00523     ancount--;
    00524   }
    00525
    00526   if (rr_data == NULL)
    00527   {
    00528     xpprintf("rr_data is NULL (no ANSWERS in packet(s)), returning\n");
    00529
    00530     return(NULL);
    00531   }
    00532
    00533   rr_data[i] = '\0';
    00534
    00535   xvprintf("RR_DATA: (%s)\n", rr_data);
    00536
    00537    /*
    00538    *  Comment: James Couzens <jcouzens@codeshare.ca>
    00539    *
    00540    *  Date: 09/08/04
    00541    *
    00542    *  if: see if the string STARTS with v=spf1
    00543    *  else: see if the string CONTAINS v=spf1
    00544    *
    00545    *  Whats all this?!  This is because of the way DNS packets return -- that is
    00546    *  in random order.  This is of consequence because of the wording in the RFC.
    00547    *  That wording is that the version identifier may only appear once withing any
    00548    *  given SPF record (this makes sense) and that should it appear twice, this
    00549    *  is considered a violation (also makes sense). The point here is that as a
    00550    *  result of records returning out of order (then how they were declared in
    00551    *  the server's configuration that they were requested from) and how this
    00552    *  particular implementation deals with the DNS packets (they are concatenated
    00553    *  in the order in which they were received) the following code facilitates an
    00554    *  early pre-parse check to see if its even a valid SPF record.
    00555    *
    00556   */
    00557   if ((*rr_data == 'v') &&
    00558       (*(rr_data + 1) == '=') &&
    00559       (*(rr_data + 2) == 's') &&
    00560       (*(rr_data + 3) == 'p') &&
    00561       (*(rr_data + 4) == 'f') &&
    00562       (*(rr_data + 5) == '1'))
    00563   {
    00564     xvprintf("Returning with valid SPFv1 record: (%s)\n", rr_data);
    00565
    00566     return(rr_data);
    00567   }
    00568   else
    00569   {
    00570     /* search the buffer for a valid SPFv1 version mechanism */
    00571     if ((pos = strstr(rr_data, "v=spf1")) != NULL)
    00572     {
    00573       xvprintf("Found SPFv1 version mechanism: (%s)\n", pos);
    00574
    00575       r_buf = xstrndup(pos, SPF_MAX_STR);
    00576       r_buf[strlen(pos)] = '\0';
    00577
    00578       xvprintf("Old buffer: (%s)\n", rr_data);
    00579       xvprintf("New buffer: (%s)\n", r_buf);
    00580
    00581       pos = NULL;
    00582     }
    00583
    00584     /* no valid SPFv1 record here */
    00585     xfree(rr_data);
    00586
    00587     return(r_buf);
    00588   }
    00589
    00590   xpprintf("Returning NULL (not a valid SPF TXT record)\n");
    00591
    00592   return(NULL);
    00593 }
    00594
    00595
    00596 /* DNS_mx_answer
    00597 *
    00598 *  Author: James Couzens <jcouzens@codeshare.ca>
    00599 *
    00600 *  Date:   01/02/04
    00601 *  Date:   02/20/04 - Added cache by Travis Anderson <tanderson@codeshare.ca>
    00602 *
    00603 *  Desc:
    00604 *          SPF_PACKETSZ bytes are allocated and then filled with \0 chars.
    00605 *  This buffer is then used in an MX DNS query using data from the passed
    00606 *  peer_info_t structure.  Upon success this buffer is re-cast as a char *
    00607 *  and then a pointer to this memory is returned.  Upon failure a NULL
    00608 *  pointer is returned.
    00609 *
    00610 */
    00611 char *DNS_mx_answer(int16_t ancount, const u_char *msg_ptr,
    00612   const u_char *eom_ptr, u_char *rd_ptr, char *buf, int *ttl)
    00613 {
    00614   size_t buf_len;          /* buffer length */
    00615
    00616   int16_t i;               /* utility */
    00617   int16_t rc;              /* generic return code / length of */
    00618   int16_t rd_pref;         /* MX preference */
    00619   int16_t rd_type;         /* answer type */
    00620   int16_t rd_len = 0;      /* res_search return code & packet len */
    00621
    00622   int32_t rd_ttl;          /* TTL */
    00623
    00624   char *rr_data = NULL;    /* data pointer */
    00625
    00626
    00627   i = 0;
    00628   while ((ancount > 0) && (rd_ptr < eom_ptr))
    00629   {
    00630     if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    00631     {
    00632       xvprintf("Error expanding ANSWER packet at count %i; Reason: %s \n",
    00633         ancount, hstrerror(h_errno));
    00634
    00635       return(NULL);
    00636     }
    00637
    00638     /* dname, type, class, TTL, rdata len, rdata */
    00639     rd_ptr += rc;                 /* jump to start of ANSWER data */
    00640     GETSHORT(rd_type, rd_ptr);    /* get response type */
    00641     rd_ptr += INT16SZ;            /* skip class */
    00642     GETLONG(rd_ttl, rd_ptr);      /* get TTL */
    00643     GETSHORT(rd_len, rd_ptr);     /* get data length */
    00644
    00645     *ttl = rd_ttl;
    00646
    00647     if (rd_type != T_MX)
    00648     {
    00649       xprintf("Forged packet?!  We requested T_MX (15) but got %i\n", rd_type);
    00650       rd_ptr += rd_len;
    00651       continue;
    00652     }
    00653
    00654     GETSHORT(rd_pref, rd_ptr);  /* get MX preference */
    00655
    00656     if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    00657     {
    00658       xvprintf("Error expanding ANSWER packet at count %i; Reason: %s \n",
    00659         ancount, hstrerror(h_errno));
    00660
    00661       return(NULL);
    00662     }
    00663
    00664     xvprintf("MX: %s Preference: %i\n", buf, rd_pref);
    00665
    00666     buf_len = strlen(buf);
    00667     i += (buf_len + 1);
    00668
    00669     if ((rd_len <= SPF_MAXDNAME) && (rd_len > 0))
    00670     {
    00671       if (rr_data == NULL)
    00672       {
    00673         rr_data = xmalloc(i + 1);
    00674         /*memset(rr_data, '\0', (i + 1));*/
    00675       }
    00676       else
    00677       {
    00678         rr_data = xrealloc(rr_data, (i + 1));
    00679       }
    00680
    00681       xvprintf("REALLOCATE memory: %i bytes\n", (i + 1));
    00682
    00683       strncat(rr_data, buf, buf_len);
    00684       rr_data[i - 1] = ' ';
    00685       rr_data[i] = '\0';
    00686     }
    00687
    00688     rd_ptr += rc;
    00689     ancount--;
    00690   }
    00691
    00692   if (rr_data != NULL)
    00693   {
    00694     rr_data[i - 1] = '\0';
    00695   }
    00696
    00697   return(rr_data);
    00698 }
    00699
    00700
    00701 /* DNS_ptr_answer
    00702 *
    00703 *  Author: James Couzens <jcouzens@codeshare.ca>
    00704 *
    00705 *  Date:   01/02/04
    00706 *  Date:   02/20/04 - Added cache by Travis Anderson <tanderson@codeshare.ca>
    00707 *
    00708 *  Desc:
    00709 *          A reverse lookup on an IP address leads to a lookup per returned
    00710 *  PTR answer to see if the returned answer matches.  The forward lookups are
    00711 *  handled by a separate function which calls gethostbyname.  Upon a single
    00712 *  successful match of a forward lookup with a reverse lookup, returns SPF_TRUE.
    00713 *  Returns SPF_FALSE upon failure.
    00714 *
    00715 */
    00716 SPF_BOOL DNS_ptr_answer(peer_info_t *p, int16_t ancount,
    00717 const u_char *msg_ptr, const u_char *eom_ptr, u_char *rd_ptr, char *buf,
    00718   const char *mta, int *ttl)
    00719 {
    00720   int16_t rc;               /* generic return code / length of */
    00721   int16_t rd_type;          /* answer type */
    00722   int16_t rd_len = 0;       /* res_search return code & packet len */
    00723
    00724   int32_t rd_ttl;           /* TTL */
    00725
    00726   char *buf_cmp  = NULL;    /* stores domain from buf's (ptr res) hostname */
    00727   char *mta_cmp  = NULL;    /* stores domain from rpeer's hostname */
    00728
    00729
    00730   while ((ancount > 0) && (rd_ptr < eom_ptr))
    00731   {
    00732     if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    00733     {
    00734       xvprintf("Error expanding ANSWER packet at count %i; Reason: %s \n",
    00735         ancount, hstrerror(h_errno));
    00736
    00737       return(SPF_FALSE);
    00738     }
    00739
    00740     /* dname, type, class, TTL, rdata len, rdata */
    00741     rd_ptr += rc;                 /* jump to start of ANSWER data */
    00742     GETSHORT(rd_type, rd_ptr);    /* get response type */
    00743     rd_ptr += INT16SZ;            /* skip class */
    00744     GETLONG(rd_ttl, rd_ptr);      /* get TTL */
    00745     GETSHORT(rd_len, rd_ptr);     /* get data length */
    00746
    00747     *ttl = rd_ttl;
    00748
    00749     if (rd_type != T_PTR)
    00750     {
    00751       rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME);
    00752
    00753       xvprintf("Error expanding ANSWER packet at count %i; Reason: %s \n",
    00754         ancount, hstrerror(h_errno));
    00755
    00756       xvprintf("Got answer to type %i (%s) when we asked for T_PTR (%i)\n",
    00757         rd_type, buf, T_PTR);
    00758
    00759       rd_ptr += rd_len;
    00760       continue;
    00761     }
    00762
    00763     if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    00764     {
    00765       xvprintf("Error expanding ANSWER packet at count %i; Reason: %s \n",
    00766         ancount, hstrerror(h_errno));
    00767
    00768       xvprintf("Error expanding ANSWER packet at count %i. (%s)\n",
    00769         ancount, buf);
    00770
    00771       return(SPF_FALSE);
    00772     }
    00773
    00774     xvprintf("Answer %i has length %i.\n", ancount, rd_len);
    00775     xvprintf("Answer data (buffer): (%s); buffer length: %i\n",
    00776       buf, strlen(buf));
    00777
    00778     if ((rd_len <= SPF_MAXDNAME) && (rd_len > 0))
    00779     {
    00780       if (UTIL_validate_hostname(p, buf, 32) == SPF_FALSE)
    00781       {
    00782         xvprintf("Unable to validate hostname (%s) with (%s)\n",
    00783           buf, mta);
    00784
    00785         rd_ptr += rc;
    00786         ancount--;
    00787         continue;
    00788       }
    00789
    00790       /* buf MUST be a subdomain of mta */
    00791       if (strlen(buf) < strlen(mta))
    00792       {
    00793         /* not a chance */
    00794         rd_ptr += rc;
    00795         ancount--;
    00796         continue;
    00797       }
    00798       else if (strlen(buf) == strlen(mta))
    00799       {
    00800         if (strcasecmp(buf, mta) == 0)
    00801         {
    00802           return (SPF_TRUE);
    00803         }
    00804         else
    00805         {
    00806           rd_ptr += rc;
    00807           ancount--;
    00808           continue;
    00809         }
    00810       }
    00811       else
    00812       {
    00813         buf_cmp = &(buf[strlen(buf) - 1]);
    00814         mta_cmp = (char *)&(mta[strlen(mta) - 1]);
    00815
    00816         while (mta_cmp != mta - 1)
    00817         {
    00818           if (*mta_cmp-- != *buf_cmp--)
    00819           {
    00820             rd_ptr += rc;
    00821             ancount--;
    00822             continue;
    00823           }
    00824         }
    00825
    00826         if (*buf_cmp == '.')
    00827         {
    00828           return (SPF_TRUE);
    00829         }
    00830         else
    00831         {
    00832           rd_ptr += rc;
    00833           ancount--;
    00834           continue;
    00835         }
    00836       }
    00837     }
    00838     else
    00839     {
    00840       xepprintf("Answer length is too long!\n");
    00841     }
    00842
    00843     rd_ptr += rc;
    00844     ancount--;
    00845   }
    00846
    00847   return(SPF_FALSE);
    00848 }
    00849
    00850
    00851 /* DNS_cname_answer
    00852 *
    00853 *  Author: Teddy <teddy@teddy.ch>
    00854 *
    00855 *  Date:   29/04/04
    00856 *  Date:   02/20/04 - Added cache by Travis Anderson <tanderson@codeshare.ca>
    00857 *
    00858 *  Desc:
    00859 *          SPF_PACKETSZ bytes are allocated and then filled with \0 chars.
    00860 *  This buffer is then used in a TXT DNS query using data from the passed
    00861 *  peer_info_t structure.  Upon success this buffer is re-cast as a char *
    00862 *  and then a pointer to this memory is returned.  Upon failure a NULL
    00863 *  pointer is returned.
    00864 *
    00865 */
    00866 char *DNS_cname_answer(int16_t ancount, const u_char *msg_ptr,
    00867   const u_char *eom_ptr, u_char *rd_ptr, char *buf, int *ttl)
    00868 {
    00869   int16_t i;               /* utility */
    00870   int16_t rc;              /* generic return code / length of */
    00871   int16_t rd_type;         /* answer type */
    00872   int16_t rd_len;          /* res_search return code & packet len */
    00873
    00874   int32_t rd_ttl;          /* TTL */
    00875
    00876   size_t buf_len;          /* buffer length */
    00877
    00878   char *rr_data = NULL;    /* data pointer */
    00879
    00880
    00881   if ((msg_ptr == NULL) || (eom_ptr == NULL) ||
    00882       (rd_ptr == NULL)  || (buf == NULL))
    00883   {
    00884     xepprintf("Called with NULL pointers\n");
    00885
    00886     return(NULL);
    00887   }
    00888
    00889   xpprintf("entering function\n");
    00890
    00891   i = 0;
    00892   while ((ancount > 0) && (rd_ptr < eom_ptr))
    00893   {
    00894     if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    00895     {
    00896       xvprintf("Error expanding ANSWER packet at count %i; Reason: %s \n",
    00897         ancount, hstrerror(h_errno));
    00898
    00899       return(NULL);
    00900     }
    00901
    00902     /* dname, type, class, TTL, rdata len, rdata */
    00903     rd_ptr += rc;                 /* jump to start of ANSWER data */
    00904     GETSHORT(rd_type, rd_ptr);    /* get response type */
    00905     rd_ptr += INT16SZ;            /* skip class */
    00906     GETLONG(rd_ttl, rd_ptr);      /* get TTL */
    00907     GETSHORT(rd_len, rd_ptr);     /* get data length */
    00908
    00909     *ttl = rd_ttl;
    00910
    00911     if (rd_type != T_CNAME)
    00912     {
    00913       xvprintf("Ignoring record not of T_CNAME type. (%i)\n", rd_type);
    00914       rd_ptr += rd_len;
    00915       continue;
    00916     }
    00917
    00918     if (dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME) < 0)
    00919     {
    00920       xvprintf("Error expanding ANSWER packet at count %i; Reason: %s \n",
    00921         ancount, hstrerror(h_errno));
    00922
    00923       return(NULL);
    00924     }
    00925
    00926     buf_len = strlen(buf);
    00927     i += (buf_len + 1);
    00928
    00929     if ((rd_len <= SPF_MAXDNAME) && (rd_len > 0))
    00930     {
    00931       if (rr_data == NULL)
    00932       {
    00933         rr_data = xmalloc(i + 1);
    00934         /*memset(rr_data, '\0', (i + 1));*/
    00935       }
    00936       else
    00937       {
    00938         rr_data = xrealloc(rr_data, (i + 1));
    00939       }
    00940
    00941       xvprintf("REALLOCATE memory: %i bytes\n", (i + 1));
    00942
    00943       strncat(rr_data, buf, buf_len);
    00944       rr_data[i - 1] = ' ';
    00945       rr_data[i] = '\0';
    00946     }
    00947
    00948     rd_ptr += rc;
    00949     ancount--;
    00950   }
    00951
    00952   if (rr_data != NULL)
    00953   {
    00954     rr_data[i - 1] = '\0';
    00955   }
    00956
    00957   xpprintf("leaving function\n");
    00958   return(rr_data);
    00959 }
    00960
    00961
    00962 /* DNS_check_client_reverse
    00963 *
    00964 *  Author: Travis Anderson <tanderson@codeshare.ca>
    00965 *          Adapted from DNS_query
    00966 *
    00967 *  Date:   08/??/04
    00968 *
    00969 *  Desc:
    00970 *          First get addr's reverse then for each hostname returned,
    00971 *  resolve it and then see if it matches addr.  If a match is found return
    00972 *  SPF_TRUE if not return SPF_FALSE.
    00973 *
    00974 */
    00975 SPF_BOOL DNS_check_client_reverse(peer_info_t *p)
    00976 {
    00977
    00978   HEADER  *hdr;                 /* pointer to the header of the packet */
    00979
    00980   int8_t ancount;               /* number of answers */
    00981
    00982   int16_t rd_type;              /* answer type */
    00983   int16_t rd_len = 0;           /* res_search return code & packet len */
    00984   int16_t r_len;                /* res_search return code & packet len */
    00985   int16_t rc;                   /* generic return code / length of */
    00986
    00987   char buf[SPF_MAXDNAME];       /* record extraction buffer */
    00988   char answer[SPF_PACKETSZ];    /* answer buffer */
    00989
    00990   char *addr_arpa = NULL;       /* reverse address in dot-quad notation */
    00991
    00992   u_char *msg_ptr;              /* pointer to beginning of the message */
    00993   u_char *eom_ptr;              /* pointer to the end of the message */
    00994   u_char *rd_ptr;               /* pointer to uncompressed message */
    00995
    00996
    00997   if (p == NULL)
    00998   {
    00999     xepprintf("Unable to continue, peer info struct is NULL!\n");
    01000
    01001     return(SPF_FALSE);
    01002   }
    01003
    01004   xpprintf("entering function\n");
    01005
    01006   addr_arpa = UTIL_rev_addr(p->r_ip);
    01007
    01008   r_len = res_search(addr_arpa, C_IN, T_PTR, (u_char *)answer,
    01009     SPF_PACKETSZ);
    01010
    01011   xfree(addr_arpa);
    01012
    01013   hdr     = (HEADER *)&answer;
    01014   ancount = ntohs(hdr->ancount);
    01015
    01016   xvprintf("Received packet size of %i bytes which contains %i answers.\n",
    01017     r_len, ancount);
    01018   xvprintf("ANSWERS: %i\n",   ancount);
    01019   xvprintf("QUESTIONS: %i\n", ntohs(hdr->qdcount));
    01020
    01021   if (ancount > 0)
    01022   {
    01023     msg_ptr = (u_char *)&answer;                 /* point to start of message */
    01024     eom_ptr = ((u_char *)&answer + r_len);       /* point to end of message */
    01025     rd_ptr  = ((u_char *)&answer + HFIXEDSZ);    /* point to start of RDATA */
    01026
    01027     if ((rc = dn_skipname(rd_ptr, eom_ptr)) < 0)
    01028     {
    01029       xepprintf("Error obtaining QUESTION!\n");
    01030
    01031       return(SPF_FALSE);
    01032     }
    01033
    01034     rd_ptr += rc + QFIXEDSZ;    /* jump to start of ANSWER */
    01035
    01036     while ((ancount > 0) && (rd_ptr < eom_ptr))
    01037     {
    01038       if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    01039       {
    01040         xeprintf("Error expanding ANSWER packet at count %i. (%s)\n",
    01041           ancount, buf);
    01042
    01043         return(SPF_FALSE);
    01044       }
    01045
    01046        /* dname, type, class, TTL, rdata len, rdata */
    01047       rd_ptr += rc;                 /* jump to start of ANSWER data */
    01048       GETSHORT(rd_type, rd_ptr);    /* get response type */
    01049       rd_ptr += INT16SZ;            /* skip class */
    01050       rd_ptr += INT32SZ;            /* skip TTL */
    01051       GETSHORT(rd_len, rd_ptr);     /* get data length */
    01052
    01053       if (rd_type != T_PTR)
    01054       {
    01055         rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME);
    01056
    01057         xvprintf("Got answer to type %i (%s) when we asked for T_PTR (%i)\n",
    01058           rd_type, buf, T_PTR);
    01059
    01060         rd_ptr += rd_len;
    01061         continue;
    01062       }
    01063
    01064       if ((rc = dn_expand(msg_ptr, eom_ptr, rd_ptr, buf, SPF_MAXCDNAME)) < 0)
    01065       {
    01066         xvprintf("Error expanding ANSWER packet at count %i. (%s)\n",
    01067           ancount, buf);
    01068
    01069         return(SPF_FALSE);
    01070       }
    01071
    01072       xvprintf("Answer %i has length %i.\n", ancount, rd_len);
    01073       xvprintf("Answer data (buffer): (%s); data length: %i\n",
    01074         buf, strlen(buf));
    01075
    01076       if ((rd_len <= SPF_MAXDNAME) && (rd_len > 0))
    01077       {
    01078         if (UTIL_validate_hostname(p, buf, 32) == SPF_FALSE)
    01079         {
    01080           xvprintf("Unable to validate hostname (%s) with (%s)\n",
    01081             buf, p->r_ip);
    01082
    01083           rd_ptr += rc;
    01084           ancount--;
    01085           continue;
    01086         }
    01087         else
    01088         {
    01089           if (p->r_vhname != NULL)
    01090           {
    01091             xfree(p->r_vhname);
    01092           }
    01093
    01094           p->r_vhname = xstrndup(buf, strlen(buf) + 1);
    01095
    01096           return(SPF_TRUE);
    01097         } /* if (UTIL_validate.. */
    01098       }
    01099       else
    01100       {
    01101         xepprintf("Answer length is too long!\n");
    01102         continue;
    01103       }
    01104
    01105       rd_ptr += rc;
    01106       ancount--;
    01107
    01108     } /* if (rd_len */
    01109   } /* while (ancount */
    01110
    01111   return(SPF_FALSE);
    01112 }
    01113
    01114 #ifdef _WITH_PTHREADS
    01115   #ifdef HAVE_GETHOSTBYNAME_R
    01116
    01117 /* _DNS_gethostbyname_r
    01118 *
    01119 *  Author: James Couzens <jcouzens@codeshare.ca>
    01120 *  Date:   08/28/04
    01121 *
    01122 *  Desc:
    01123 *          A wrapper around the GNU gethostbyname_r extension function which
    01124 *  introduces reentrant (some) resolver functionality allbeit way too bloody
    01125 *  late.  Someone smack me with a tire iron and rewrite all of this using DJB.
    01126 *
    01127 *  This function is intented to be wrapped by a MACRO going by something along
    01128 *  the lines of 'xgethostbyname' or 'xgethostbyname_r'.
    01129 *
    01130 */
    01131 struct hostent *_DNS_GNU_gethostbyname_r(const char *name,
    01132   struct hostent *result, char *buf, int buf_len, int *h_errnop)
    01133 {
    01134   struct hostent *hp = NULL;    /* hostent structure */
    01135
    01136
    01137   if (!name)
    01138   {
    01139     xepprintf("ERROR: No hostname to resolve.\n");
    01140
    01141     return(NULL);
    01142   }
    01143
    01144   xpprintf("entering function\n");
    01145
    01146   if (buf_len >= SPF_MAX_STR)
    01147   {
    01148     xvprintf("buf_len (%i) is > max size (%i); Disregarded.\n",
    01149       buf_len, SPF_MAX_STR);
    01150
    01151     return(NULL);
    01152   }
    01153
    01154   xvprintf("called with hostname (%s)\n", name);
    01155   memset(buf, '\0', SPF_MAX_GHBNR_DBUF);
    01156   hp = xmalloc(SIZEOF(struct hostent));
    01157
    01158   gethostbyname_r(name, result, buf, (size_t)buf_len, &hp, h_errnop);
    01159
    01160   xpprintf("leaving function\n");
    01161
    01162   return(hp);
    01163 }
    01164
    01165   #else /* HAVE_GETHOSTBYNAME_R */
    01166
    01167 /* _DNS_gethostbyname_r
    01168 *
    01169 *  Author: James Couzens <jcouzens@codeshare.ca>
    01170 *  Date:   08/28/04
    01171 *
    01172 *  Desc:
    01173 *          Reentrant version of gethostbyname_r which is available as a
    01174 *  GNU extension but unavailable to everyone else, so I've opted to
    01175 *  implement it here.  This function is intented to be wrapped by a
    01176 *  MACRO going by something along the lines of 'xgethostbyname' or
    01177 *  'xgethostbyname_r'
    01178 *
    01179 *  Calling function is responsible to call _DNS_gethostbyname_r_free
    01180 *  to unlock the mutex.
    01181 *
    01182 */
    01183 struct hostent *_DNS_gethostbyname_r(const char *name,
    01184   struct hostent *result, char *buf, int buf_len, int *h_errnop)
    01185 {
    01186   struct hostent *hp;    /* hostent structure */
    01187
    01188   if (!name)
    01189   {
    01190     xepprintf("ERROR: No hostname to resolve!\n");
    01191
    01192     return(NULL);
    01193   }
    01194
    01195   xvprintf("called with hostname (%s)\n", name);
    01196
    01197   xpthread_mutex_lock(&dns_mutex);
    01198
    01199   hp = gethostbyname(name);
    01200   *h_errnop = h_errno;
    01201
    01202   xpprintf("leaving function\n");
    01203
    01204   return(hp);
    01205 }
    01206
    01207
    01208 /* _DNS_gethostbyname_r_free
    01209 *
    01210 *  Author: James Couzens <jcouzens@codeshare.ca>
    01211 *  Date:   08/28/04
    01212 *
    01213 *  Desc:
    01214 *          This function unlocks the mutex which was locked when
    01215 *  _DNS_gethostbyname_r was called as a reentrant wrapper around the
    01216 *  non-threadsafe 'gethostbyname' function call.  This function is
    01217 *  intended to be wrapped by a MACRO going by something along the lines
    01218 *  of 'xgethostbyname_free' or 'xgethostbyname_r_free'
    01219 *
    01220 *
    01221 */
    01222 void _DNS_gethostbyname_r_free(void)
    01223 {
    01224   xpthread_mutex_unlock(&dns_mutex);
    01225
    01226   return;
    01227 }
    01228
    01229   #endif /* HAVE_GETHOSTBYNAME_R */
    01230 #endif /* _WITH_PTHREADS */
    01231
    01232
    01233 /* end dns.c */