Back to index

opendkim  2.6.6
dkim-report.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2012, The OpenDKIM Project.  All rights reserved.
00003 */
00004 
00005 #ifndef lint
00006 static char dkim_report_c_id[] = "@(#)$Id$";
00007 #endif /* !lint */
00008 
00009 #include "build-config.h"
00010 
00011 /* system includes */
00012 #include <sys/types.h>
00013 #include <sys/param.h>
00014 #include <netinet/in.h>
00015 #include <arpa/nameser.h>
00016 #include <resolv.h>
00017 #include <assert.h>
00018 #include <stdio.h>
00019 #include <string.h>
00020 #include <ctype.h>
00021 #include <netdb.h>
00022 
00023 /* libopendkim includes */
00024 #include "dkim.h"
00025 #include "dkim-report.h"
00026 #include "dkim-internal.h"
00027 #include "dkim-types.h"
00028 #include "dkim-tables.h"
00029 #include "util.h"
00030 
00031 /* prototypes */
00032 extern void dkim_error __P((DKIM *, const char *, ...));
00033 
00034 /* local definitions needed for DNS queries */
00035 #define MAXPACKET           8192
00036 #if defined(__RES) && (__RES >= 19940415)
00037 # define RES_UNC_T          char *
00038 #else /* __RES && __RES >= 19940415 */
00039 # define RES_UNC_T          unsigned char *
00040 #endif /* __RES && __RES >= 19940415 */
00041 #ifndef T_RRSIG
00042 # define T_RRSIG            46
00043 #endif /* ! T_RRSIG */
00044 #ifndef MAX
00045 # define MAX(x,y)           ((x) > (y) ? (x) : (y))
00046 #endif /* ! MAX */
00047 
00048 /*
00049 **  DKIM_REPINFO -- retrieve reporting information from DNS
00050 **
00051 **  Parameters:
00052 **     dkim -- DKIM message handle
00053 **     sig -- signature information handle
00054 **     timeout -- timeout (can be NULL)
00055 **     buf -- buffer into which to place the answer
00056 **     buflen -- bytes available at "buf"
00057 **
00058 **  Return value:
00059 **     A DKIM_STAT_* constant.
00060 */
00061 
00062 DKIM_STAT
00063 dkim_repinfo(DKIM *dkim, DKIM_SIGINFO *sig, struct timeval *timeout,
00064              unsigned char *buf, size_t buflen)
00065 {
00066        int status;
00067        int qdcount;
00068        int ancount;
00069        int class;
00070        int type;
00071        int error;
00072        int n;
00073        unsigned int c;
00074 #ifdef QUERY_CACHE
00075        uint32_t ttl;
00076 #endif /* QUERY_CACHE */
00077        size_t anslen;
00078        DKIM_LIB *lib;
00079        u_char *sdomain;
00080        u_char *txtfound = NULL;
00081        void *qh;
00082        u_char *p;
00083        u_char *cp;
00084        u_char *eom;
00085        struct timeval to;
00086        HEADER hdr;
00087        u_char ansbuf[MAXPACKET];
00088        u_char query[DKIM_MAXHOSTNAMELEN + 1];
00089 
00090        assert(dkim != NULL);
00091        assert(sig != NULL);
00092        assert(buf != NULL);
00093 
00094        lib = dkim->dkim_libhandle;
00095        sdomain = dkim_sig_getdomain(sig);
00096 
00097        if (sdomain == NULL)
00098               return DKIM_STAT_INVALID;
00099 
00100        snprintf(query, sizeof query, "%s.%s", DKIM_REPORT_PREFIX, sdomain);
00101 
00102        /* XXX -- add QUERY_CACHE support here */
00103 
00104        /* send it */
00105        anslen = sizeof ansbuf;
00106        status = lib->dkiml_dns_start(lib->dkiml_dns_service, T_TXT,
00107                                      query, ansbuf, anslen, &qh);
00108        if (status != DKIM_DNS_SUCCESS)
00109               return DKIM_STAT_CANTVRFY;
00110 
00111        /* wait for the reply */
00112        to.tv_sec = dkim->dkim_timeout;
00113        to.tv_usec = 0;
00114        status = lib->dkiml_dns_waitreply(lib->dkiml_dns_service, qh,
00115                                          timeout == NULL ? &to : timeout,
00116                                          &anslen, &error, NULL);
00117        (void) lib->dkiml_dns_cancel(lib->dkiml_dns_service, qh);
00118 
00119        if (status != DKIM_DNS_SUCCESS)
00120               return DKIM_STAT_CANTVRFY;
00121 
00122        /* decode the reply */
00123        memcpy(&hdr, ansbuf, sizeof hdr);
00124        cp = (u_char *) &ansbuf + HFIXEDSZ;
00125        eom = (u_char *) &ansbuf + anslen;
00126 
00127        /* skip over the name at the front of the answer */
00128        for (qdcount = ntohs((unsigned short) hdr.qdcount);
00129             qdcount > 0;
00130             qdcount--)
00131        {
00132               /* copy it first */
00133               (void) dn_expand((unsigned char *) &ansbuf, eom, cp,
00134                                (char *) query, sizeof query);
00135  
00136               if ((n = dn_skipname(cp, eom)) < 0)
00137               {
00138                      dkim_error(dkim, "'%s' reply corrupt", query);
00139                      return DKIM_STAT_CANTVRFY;
00140               }
00141               cp += n;
00142 
00143               /* extract the type and class */
00144               if (cp + INT16SZ + INT16SZ > eom)
00145               {
00146                      dkim_error(dkim, "'%s' reply corrupt", query);
00147                      return DKIM_STAT_CANTVRFY;
00148               }
00149               GETSHORT(type, cp);
00150               GETSHORT(class, cp);
00151        }
00152 
00153        if (type != T_TXT || class != C_IN)
00154        {
00155               dkim_error(dkim, "'%s' unexpected reply type/class", query);
00156               return DKIM_STAT_CANTVRFY;
00157        }
00158 
00159        if (hdr.rcode == NXDOMAIN)
00160        {
00161               buf[0] = '\0';
00162               return DKIM_STAT_OK;
00163        }
00164 
00165        /* if truncated, we can't do it */
00166        if (dkim_check_dns_reply(ansbuf, anslen, C_IN, T_TXT) == 1)
00167        {
00168               dkim_error(dkim, "'%s' reply truncated", query);
00169               return DKIM_STAT_CANTVRFY;
00170        }
00171 
00172        /* get the answer count */
00173        ancount = ntohs((unsigned short) hdr.ancount);
00174        if (ancount == 0)
00175        {
00176               buf[0] = '\0';
00177               return DKIM_STAT_OK;
00178        }
00179 
00180        /*
00181        **  Extract the data from the first TXT answer.
00182        */
00183 
00184        while (--ancount >= 0 && cp < eom)
00185        {
00186               /* grab the label, even though we know what we asked... */
00187               if ((n = dn_expand((unsigned char *) &ansbuf, eom, cp,
00188                                  (RES_UNC_T) query, sizeof query)) < 0)
00189               {
00190                      dkim_error(dkim, "'%s' reply corrupt", query);
00191                      return DKIM_STAT_CANTVRFY;
00192               }
00193               /* ...and move past it */
00194               cp += n;
00195 
00196               /* extract the type and class */
00197               if (cp + INT16SZ + INT16SZ > eom)
00198               {
00199                      dkim_error(dkim, "'%s' reply corrupt", query);
00200                      return DKIM_STAT_CANTVRFY;
00201               }
00202 
00203               GETSHORT(type, cp);
00204               GETSHORT(class, cp);
00205 
00206 #ifdef QUERY_CACHE
00207               /* get the TTL */
00208               GETLONG(ttl, cp);
00209 #else /* QUERY_CACHE */
00210               /* skip the TTL */
00211               cp += INT32SZ;
00212 #endif /* QUERY_CACHE */
00213 
00214               /* skip CNAME if found; assume it was resolved */
00215               if (type == T_CNAME)
00216               {
00217                      char chost[DKIM_MAXHOSTNAMELEN + 1];
00218 
00219                      n = dn_expand((u_char *) &ansbuf, eom, cp,
00220                                    chost, DKIM_MAXHOSTNAMELEN);
00221                      cp += n;
00222                      continue;
00223               }
00224               else if (type == T_RRSIG)
00225               {
00226                      /* get payload length */
00227                      if (cp + INT16SZ > eom)
00228                      {
00229                             dkim_error(dkim, "'%s' reply corrupt", query);
00230                             return DKIM_STAT_CANTVRFY;
00231                      }
00232                      GETSHORT(n, cp);
00233 
00234                      cp += n;
00235 
00236                      continue;
00237               }
00238               else if (type != T_TXT)
00239               {
00240                      dkim_error(dkim, "'%s' reply was unexpected type %d",
00241                                 query, type);
00242                      return DKIM_STAT_CANTVRFY;
00243               }
00244 
00245               if (txtfound != NULL)
00246               {
00247                      dkim_error(dkim, "multiple DNS replies for '%s'",
00248                                 query);
00249                      return DKIM_STAT_MULTIDNSREPLY;
00250               }
00251 
00252               /* remember where this one started */
00253               txtfound = cp;
00254 
00255               /* get payload length */
00256               if (cp + INT16SZ > eom)
00257               {
00258                      dkim_error(dkim, "'%s' reply corrupt", query);
00259                      return DKIM_STAT_CANTVRFY;
00260               }
00261               GETSHORT(n, cp);
00262 
00263               /* move forward for now */
00264               cp += n;
00265        }
00266 
00267        /* if ancount went below 0, there were no good records */
00268        if (txtfound == NULL)
00269        {
00270               dkim_error(dkim, "'%s' reply was unresolved CNAME", query);
00271               return DKIM_STAT_CANTVRFY;
00272        }
00273 
00274        /* come back to the one we found */
00275        cp = txtfound;
00276 
00277        /* get payload length */
00278        if (cp + INT16SZ > eom)
00279        {
00280               dkim_error(dkim, "'%s' reply corrupt", query);
00281               return DKIM_STAT_CANTVRFY;
00282        }
00283        GETSHORT(n, cp);
00284 
00285        if (cp + n > eom)
00286        {
00287               dkim_error(dkim, "'%s' reply corrupt", query);
00288               return DKIM_STAT_CANTVRFY;
00289        }
00290 
00291        /* extract the payload */
00292        memset(buf, '\0', buflen);
00293        p = buf;
00294        eom = buf + buflen - 1;
00295        while (n > 0 && p < eom)
00296        {
00297               c = *cp++;
00298               n--;
00299               while (c > 0 && p < eom)
00300               {
00301                      *p++ = *cp++;
00302                      c--;
00303                      n--;
00304               }
00305        }
00306 
00307        return DKIM_STAT_OK;
00308 }