Back to index

opendkim  2.6.4
dkim-rep.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2008 Sendmail, Inc. and its suppliers.
00003 **     All rights reserved.
00004 **
00005 **  Copyright (c) 2009-2011, The OpenDKIM Project.  All rights reserved.
00006 */
00007 
00008 #ifndef lint
00009 static char dkim_rep_c_id[] = "@(#)$Id: dkim-rep.c,v 1.13 2010/10/04 04:37:26 cm-msk Exp $";
00010 #endif /* !lint */
00011 
00012 #include "build-config.h"
00013 
00014 /* system includes */
00015 #include <sys/types.h>
00016 #include <sys/param.h>
00017 #include <netinet/in.h>
00018 #include <arpa/nameser.h>
00019 #include <resolv.h>
00020 #include <assert.h>
00021 #include <stdio.h>
00022 #include <string.h>
00023 #include <stdlib.h>
00024 #include <ctype.h>
00025 #include <netdb.h>
00026 #include <errno.h>
00027 
00028 #ifdef USE_GNUTLS
00029 /* GnuTLS includes */
00030 # include <gnutls/gnutls.h>
00031 # include <gnutls/crypto.h>
00032 # ifndef MD5_DIGEST_LENGTH
00033 #  define MD5_DIGEST_LENGTH 16
00034 # endif /* ! MD5_DIGEST_LENGTH */
00035 #else /* USE_GNUTLS */
00036 /* openssl includes */
00037 # include <openssl/md5.h>
00038 #endif /* USE_GNUTLS */
00039 
00040 /* libopendkim includes */
00041 #include "dkim-rep.h"
00042 
00043 /* local definitions needed for DNS queries */
00044 #define MAXPACKET           8192
00045 #if defined(__RES) && (__RES >= 19940415)
00046 # define RES_UNC_T          char *
00047 #else /* __RES && __RES >= 19940415 */
00048 # define RES_UNC_T          unsigned char *
00049 #endif /* __RES && __RES >= 19940415 */
00050 #ifndef T_RRSIG
00051 # define T_RRSIG            46
00052 #endif /* ! T_RRSIG */
00053 
00054 /* other local definitions */
00055 #define       BUFRSZ               1024
00056 #define       DKIM_REP_DEFTIMEOUT  5
00057 #define       DKIM_REP_MAXERRORSTRING     256
00058 #define       DKIM_REP_MAXHOSTNAMELEN     256
00059 
00060 #ifndef FALSE
00061 # define FALSE                     0
00062 #endif /* ! FALSE */
00063 #ifndef TRUE
00064 # define TRUE               1
00065 #endif /* ! TRUE */
00066 
00067 /* struct dkim_rep_query -- an open DKIM_REP query */
00068 struct dkim_rep_query
00069 {
00070        void *               drq_qh;
00071        size_t               drq_anslen;
00072        u_char               drq_buf[HFIXEDSZ + MAXPACKET];
00073 };
00074 
00075 /* struct dkim_rep_handle -- a DKIM_REP library context */
00076 struct dkim_rep_handle
00077 {
00078        u_int         dkim_rep_timeout;
00079        u_int         dkim_rep_cbint;
00080        void *        dkim_rep_cbctx;
00081        void *        dkim_rep_closure;
00082        void *        (*dkim_rep_malloc) (void *closure, size_t nbytes);
00083        void          (*dkim_rep_free) (void *closure, void *p);
00084        void          (*dkim_rep_dns_callback) (const void *context);
00085        void *        dkim_rep_dns_service;
00086        int           (*dkim_rep_dns_start) (void *srv, int type,
00087                                        unsigned char *query,
00088                                        unsigned char *buf,
00089                                        size_t buflen,
00090                                        void **qh);
00091        int           (*dkim_rep_dns_cancel) (void *srv, void *qh);
00092        int           (*dkim_rep_dns_waitreply) (void *srv,
00093                                            void *qh,
00094                                            struct timeval *to,
00095                                            size_t *bytes,
00096                                            int *error,
00097                                            int *dnssec);
00098        u_char        dkim_rep_qroot[DKIM_REP_MAXHOSTNAMELEN + 1];
00099        u_char        dkim_rep_error[DKIM_REP_MAXERRORSTRING + 1];
00100 };
00101 
00102 /*
00103 **  Standard UNIX resolver stub functions
00104 */
00105 
00106 struct dkim_rep_res_qh
00107 {
00108        int           rq_error;
00109        size_t        rq_buflen;
00110 };
00111 
00112 /*
00113 **  DKIM_REP_RES_CANCEL -- cancel a pending resolver query
00114 **
00115 **  Parameters:
00116 **     srv -- query service handle (ignored)
00117 **     qh -- query handle (ignored)
00118 **
00119 **  Return value:
00120 **     0 on success, !0 on error
00121 **
00122 **  Notes:
00123 **     The standard UNIX resolver is synchronous, so in theory this can
00124 **     never get called.  We have not yet got any use cases for one thread
00125 **     canceling another thread's pending queries, so for now just return 0.
00126 */
00127 
00128 static int
00129 dkim_rep_res_cancel(void *srv, void *qh)
00130 {
00131        if (qh != NULL)
00132               free(qh);
00133 
00134        return 0;
00135 }
00136 
00137 /*
00138 **  DKIM_REP_RES_QUERY -- initiate a DNS query
00139 **
00140 **  Parameters:
00141 **     srv -- service handle (ignored)
00142 **     type -- RR type to query
00143 **     query -- the question to ask
00144 **     buf -- where to write the answer
00145 **     buflen -- bytes at "buf"
00146 **     qh -- query handle, used with rbl_res_waitreply
00147 **
00148 **  Return value:
00149 **     An DKIM_REP_DNS_* constant.
00150 **
00151 **  Notes:
00152 **     This is a stub for the stock UNIX resolver (res_) functions, which
00153 **     are synchronous so no handle needs to be created, so "qh" is set to
00154 **     "buf".  "buf" is actually populated before this returns (unless
00155 **     there's an error).
00156 */
00157 
00158 static int
00159 dkim_rep_res_query(void *srv, int type, unsigned char *query,
00160                    unsigned char *buf, size_t buflen, void **qh)
00161 {
00162        int n;
00163        int ret;
00164        struct dkim_rep_res_qh *rq;
00165        unsigned char qbuf[HFIXEDSZ + MAXPACKET];
00166 #ifdef HAVE_RES_NINIT
00167        struct __res_state statp;
00168 #endif /* HAVE_RES_NINIT */
00169 
00170 #ifdef HAVE_RES_NINIT
00171        memset(&statp, '\0', sizeof statp);
00172        res_ninit(&statp);
00173 #endif /* HAVE_RES_NINIT */
00174 
00175 #ifdef HAVE_RES_NINIT
00176        n = res_nmkquery(&statp, QUERY, (char *) query, C_IN, type, NULL, 0,
00177                         NULL, qbuf, sizeof qbuf);
00178 #else /* HAVE_RES_NINIT */
00179        n = res_mkquery(QUERY, (char *) query, C_IN, type, NULL, 0, NULL, qbuf,
00180                        sizeof qbuf);
00181 #endif /* HAVE_RES_NINIT */
00182        if (n == (size_t) -1)
00183        {
00184 #ifdef HAVE_RES_NINIT
00185               res_nclose(&statp);
00186 #endif /* HAVE_RES_NINIT */
00187               return DKIM_REP_DNS_ERROR;
00188        }
00189 
00190 #ifdef HAVE_RES_NINIT
00191        ret = res_nsend(&statp, qbuf, n, buf, buflen);
00192 #else /* HAVE_RES_NINIT */
00193        ret = res_send(qbuf, n, buf, buflen);
00194 #endif /* HAVE_RES_NINIT */
00195        if (ret == -1)
00196        {
00197 #ifdef HAVE_RES_NINIT
00198               res_nclose(&statp);
00199 #endif /* HAVE_RES_NINIT */
00200               return DKIM_REP_DNS_ERROR;
00201        }
00202 
00203 #ifdef HAVE_RES_NINIT
00204        res_nclose(&statp);
00205 #endif /* HAVE_RES_NINIT */
00206 
00207        rq = (struct dkim_rep_res_qh *) malloc(sizeof *rq);
00208        if (rq == NULL)
00209               return DKIM_REP_DNS_ERROR;
00210 
00211        if (ret == -1)
00212        {
00213               rq->rq_error = errno;
00214               rq->rq_buflen = 0;
00215        }
00216        else
00217        {
00218               rq->rq_error = 0;
00219               rq->rq_buflen = (size_t) ret;
00220        }
00221 
00222        *qh = (void *) rq;
00223 
00224        return DKIM_REP_DNS_SUCCESS;
00225 }
00226 
00227 /*
00228 **  DKIM_REP_RES_WAITREPLY -- wait for a reply to a pending query
00229 **
00230 **  Parameters:
00231 **     srv -- service handle
00232 **     qh -- query handle
00233 **     to -- timeout
00234 **     bytes -- number of bytes in the reply (returned)
00235 **     error -- error code (returned)
00236 **
00237 **  Return value:
00238 **     A DKIM_REP_DNS_* code.
00239 **
00240 **  Notes:
00241 **     Since the stock UNIX resolver is synchronous, the reply was completed
00242 **     before rbl_res_query() returned, and thus this is almost a no-op.
00243 */
00244 
00245 int
00246 dkim_rep_res_waitreply(void *srv, void *qh, struct timeval *to, size_t *bytes,
00247                        int *error, int *dnssec)
00248 {
00249        struct dkim_rep_res_qh *rq;
00250 
00251        assert(qh != NULL);
00252 
00253        rq = qh;
00254 
00255        if (bytes != NULL)
00256               *bytes = rq->rq_buflen;
00257        if (error != NULL)
00258               *error = rq->rq_error;
00259 
00260        return DKIM_REP_DNS_SUCCESS;
00261 }
00262 
00263 /*
00264 **  DKIM_REP_INIT -- initialize an RBL handle
00265 **
00266 **  Parameters:
00267 **     caller_mallocf -- caller-provided memory allocation function
00268 **     caller_freef -- caller-provided memory release function
00269 **     closure -- memory closure to pass to the above when used
00270 **
00271 **  Return value:
00272 **     A new RBL handle suitable for use with other RBL functions, or
00273 **     NULL on failure.
00274 **  
00275 **  Side effects:
00276 **     Sudden changes in local density altitude.
00277 */
00278 
00279 /*
00280 **  DKIM_REP_MD5_TO_STRING -- convert an MD5 digest to printable hex
00281 **
00282 **  Parameters:
00283 **     md5 -- MD5 digest
00284 **     str -- destination string
00285 **     len -- bytes available at "str"
00286 **
00287 **  Return value:
00288 **     -1 -- not enough room in "str" for output
00289 **     otherwise -- number of bytes written to "str", not including a
00290 **                  terminating NULL
00291 */
00292 
00293 static int
00294 dkim_rep_md5_to_string(void *md5, unsigned char *str, size_t len)
00295 {
00296        int c;
00297        int out = 0;
00298        unsigned char *cvt;
00299        unsigned char digest[MD5_DIGEST_LENGTH];
00300 
00301        assert(md5 != NULL);
00302        assert(str != NULL);
00303 
00304        if (len < 2 * MD5_DIGEST_LENGTH + 1)
00305               return -1;
00306 
00307 #ifdef USE_GNUTLS
00308        (void) gnutls_hash_deinit(md5, digest);
00309 #else /* USE_GNUTLS */
00310        MD5_Final(digest, md5);
00311 #endif /* USE_GNUTLS */
00312 
00313        for (cvt = str, c = 0; c < MD5_DIGEST_LENGTH; c++)
00314        {
00315               snprintf((char *) cvt, len, "%02x", digest[c]);
00316               cvt += 2;
00317               out += 2;
00318               len -= 2;
00319        }
00320 
00321        return out;
00322 }
00323 
00324 /*
00325 **  DKIM_REP_STRING_EMPTY -- determine if a string is empty or not
00326 **
00327 **  Parameters:
00328 **     str -- string to analyze
00329 **
00330 **  Return value:
00331 **     TRUE iff "str" contained no non-whitespace characters
00332 */
00333 
00334 static _Bool
00335 dkim_rep_string_empty(char *str)
00336 {
00337        char *p;
00338 
00339        assert(str != NULL);
00340 
00341        for (p = str; *p != '\0'; p++)
00342        {
00343               if (!isascii(*p) || !isspace(*p))
00344                      return FALSE;
00345        }
00346 
00347        return TRUE;
00348 }
00349 
00350 /*
00351 **  DKIM_REP_INIT -- initialize an DKIM_REP handle
00352 **
00353 **  Parameters:
00354 **     caller_mallocf -- caller-provided memory allocation function
00355 **     caller_freef -- caller-provided memory release function
00356 **     closure -- memory closure to pass to the above when used
00357 **
00358 **  Return value:
00359 **     A new DKIM_REP handle suitable for use with other DKIM_REP
00360 **     functions, or NULL on failure.
00361 **  
00362 **  Side effects:
00363 **     Small but detectable movement of the Indian subcontinent.
00364 */
00365 
00366 DKIM_REP
00367 dkim_rep_init(void *(*caller_mallocf)(void *closure, size_t nbytes),
00368               void (*caller_freef)(void *closure, void *p),
00369               void *closure)
00370 {
00371        DKIM_REP new;
00372 
00373        if (caller_mallocf == NULL)
00374        {
00375               new = (DKIM_REP) malloc(sizeof(struct dkim_rep_handle));
00376        }
00377        else
00378        {
00379               new = (DKIM_REP) caller_mallocf(closure,
00380                                               sizeof(struct dkim_rep_handle));
00381        }
00382 
00383        if (new == NULL)
00384               return NULL;
00385 
00386        memset(new, '\0', sizeof(struct dkim_rep_handle));
00387 
00388        new->dkim_rep_timeout = DKIM_REP_DEFTIMEOUT;
00389        new->dkim_rep_closure = closure;
00390        new->dkim_rep_malloc = caller_mallocf;
00391        new->dkim_rep_free = caller_freef;
00392        new->dkim_rep_dns_start = dkim_rep_res_query;
00393        new->dkim_rep_dns_waitreply = dkim_rep_res_waitreply;
00394        new->dkim_rep_dns_cancel = dkim_rep_res_cancel;
00395        dkim_rep_setdomain(new, DKIM_REP_DEFROOT);
00396 
00397        return new;
00398 }
00399 
00400 /*
00401 **  DKIM_REP_CLOSE -- shut down a DKIM_REP instance
00402 **
00403 **  Parameters:
00404 **     dkim_rep -- DKIM_REP handle to shut down
00405 **
00406 **  Return value:
00407 **     None.
00408 */
00409 
00410 void
00411 dkim_rep_close(DKIM_REP dr)
00412 {
00413        assert(dr != NULL);
00414 
00415        if (dr->dkim_rep_free != NULL)
00416               dr->dkim_rep_free(dr->dkim_rep_closure, dr);
00417        else
00418               free(dr);
00419 }
00420 
00421 /*
00422 **  DKIM_REP_GETERROR -- return any stored error string from within the
00423 **                       DKIM_REP context handle
00424 **
00425 **  Parameters:
00426 **     dkim_rep -- DKIM_REP handle from which to retrieve an error string
00427 **
00428 **  Return value:
00429 **     A pointer to the stored string, or NULL if none was stored.
00430 */
00431 
00432 const u_char *
00433 dkim_rep_geterror(DKIM_REP dr)
00434 {
00435        assert(dr != NULL);
00436 
00437        return dr->dkim_rep_error;
00438 }
00439 
00440 /*
00441 **  DKIM_REP_SETDOMAIN -- declare the DKIM_REP's domain (the query root)
00442 **
00443 **  Parameters:
00444 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00445 **     qroot -- query root
00446 **
00447 **  Return value:
00448 **     None (yet).
00449 */
00450 
00451 void
00452 dkim_rep_setdomain(DKIM_REP dr, u_char *qroot)
00453 {
00454        assert(dr != NULL);
00455        assert(qroot != NULL);
00456 
00457        strncpy(dr->dkim_rep_qroot, qroot, sizeof dr->dkim_rep_qroot);
00458        dr->dkim_rep_qroot[sizeof dr->dkim_rep_qroot - 1] = '\0';
00459 }
00460 
00461 /*
00462 **  DKIM_REP_QUERY_START -- initiate a query to the DKIM_REP for entries
00463 **
00464 **  Parameters:
00465 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00466 **     user -- local-part of From:
00467 **     domain -- domain part of From:
00468 **     signdomain -- signing domain
00469 **     qh -- query handle (returned)
00470 **
00471 **  Return value:
00472 **     DKIM_REP_STAT_INVALID -- dkim_rep_setdomain() was not called,
00473 **                               or "query" was NULL
00474 **     DKIM_REP_STAT_* -- as defined
00475 */
00476 
00477 DKIM_REP_STAT
00478 dkim_rep_query_start(DKIM_REP dr, u_char *user, u_char *domain,
00479                      u_char *signdomain, void **qh)
00480 {
00481        int out;
00482        size_t anslen;
00483        int qdcount;
00484        int ancount;
00485        int n;
00486        int type;
00487        int class;
00488        int status;
00489 #ifdef QUERY_CACHE
00490        uint32_t ttl;
00491 #endif /* QUERY_CACHE */
00492        int error;
00493        struct dkim_rep_query *q;
00494        void *rq;
00495        char *eq;
00496        char *e;
00497 #ifdef USE_GNUTLS
00498        gnutls_hash_hd_t md5_user;
00499        gnutls_hash_hd_t md5_domain;
00500        gnutls_hash_hd_t md5_signdomain;
00501 #else /* USE_GNUTLS */
00502        MD5_CTX md5_user;
00503        MD5_CTX md5_domain;
00504        MD5_CTX md5_signdomain;
00505 #endif /* USE_GNUTLS */
00506        struct timeval timeout;
00507        unsigned char md5_user_str[MD5_DIGEST_LENGTH * 2 + 1];
00508        unsigned char md5_domain_str[MD5_DIGEST_LENGTH * 2 + 1];
00509        unsigned char md5_signdomain_str[MD5_DIGEST_LENGTH * 2 + 1];
00510        unsigned char ansbuf[MAXPACKET];
00511        char query[DKIM_REP_MAXHOSTNAMELEN + 1];
00512        char qname[DKIM_REP_MAXHOSTNAMELEN + 1];
00513 
00514        assert(dr != NULL);
00515        assert(user != NULL);
00516        assert(domain != NULL);
00517        assert(signdomain != NULL);
00518        assert(qh != NULL);
00519 
00520        q = (struct dkim_rep_query *) malloc(sizeof(struct dkim_rep_query));
00521        if (q == NULL)
00522               return DKIM_REP_STAT_ERROR;
00523 
00524        /* hash the values */
00525        memset(md5_user_str, '\0', sizeof md5_user_str);
00526 #ifdef USE_GNUTLS
00527        if (gnutls_hash_init(&md5_user, GNUTLS_DIG_MD5) == 0)
00528               gnutls_hash(md5_user, (void *) user, strlen((char *) user));
00529 #else /* USE_GNUTLS */
00530        MD5_Init(&md5_user);
00531        MD5_Update(&md5_user, (void *) user, strlen((char *) user));
00532 #endif /* USE_GNUTLS */
00533        (void) dkim_rep_md5_to_string(&md5_user, md5_user_str,
00534                                      sizeof md5_user_str);
00535 
00536        memset(md5_domain_str, '\0', sizeof md5_domain_str);
00537 #ifdef USE_GNUTLS
00538        if (gnutls_hash_init(&md5_domain, GNUTLS_DIG_MD5) == 0)
00539        {
00540               gnutls_hash(md5_domain, (void *) domain,
00541                           strlen((char *) domain));
00542        }
00543 #else /* USE_GNUTLS */
00544        MD5_Init(&md5_domain);
00545        MD5_Update(&md5_domain, (void *) domain, strlen((char *) domain));
00546 #endif /* USE_GNUTLS */
00547        (void) dkim_rep_md5_to_string(&md5_domain, md5_domain_str,
00548                                      sizeof md5_domain_str);
00549 
00550        memset(md5_signdomain_str, '\0', sizeof md5_signdomain_str);
00551 #ifdef USE_GNUTLS
00552        if (gnutls_hash_init(&md5_signdomain, GNUTLS_DIG_MD5) == 0)
00553        {
00554               gnutls_hash(md5_signdomain, (void *) signdomain,
00555                           strlen((char *) signdomain));
00556        }
00557 #else /* USE_GNUTLS */
00558        MD5_Init(&md5_signdomain);
00559        MD5_Update(&md5_signdomain, (void *) signdomain, strlen(signdomain));
00560 #endif /* USE_GNUTLS */
00561        (void) dkim_rep_md5_to_string(&md5_signdomain, md5_signdomain_str,
00562                                      sizeof md5_signdomain_str);
00563 
00564        /* construct the query */
00565        snprintf(query, sizeof query, "%s.%s.%s.%s", md5_user_str,
00566                 md5_domain_str, md5_signdomain_str, dr->dkim_rep_qroot);
00567 
00568        /* start the query */
00569        timeout.tv_sec = dr->dkim_rep_timeout;
00570        timeout.tv_usec = 0;
00571 
00572        anslen = sizeof ansbuf;
00573 
00574        status = dr->dkim_rep_dns_start(dr->dkim_rep_dns_service, T_TXT,
00575                                        query, q->drq_buf, sizeof q->drq_buf,
00576                                        &rq);
00577 
00578        if (status != 0)
00579        {
00580               snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00581                        "DNS query for '%s' failed", query);
00582               return DKIM_REP_STAT_ERROR;
00583        }
00584 
00585        q->drq_qh = rq;
00586 
00587        *qh = q;
00588 
00589        return DKIM_REP_STAT_OK;
00590 }
00591 
00592 /*
00593 **  DKIM_REP_QUERY_CHECK -- check for a reply from an active query
00594 **
00595 **  Parameters:
00596 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00597 **     qh -- query handle (returned)
00598 **     timeout -- timeout
00599 **     res -- integer into which to write the result (can be NULL)
00600 **
00601 **  Return value:
00602 **     DKIM_REP_STAT_* -- as defined
00603 */
00604 
00605 DKIM_REP_STAT
00606 dkim_rep_query_check(DKIM_REP dr, void *qh, struct timeval *timeout,
00607                      int *res)
00608 {
00609        int out;
00610        int c;
00611        int dnserr;
00612        int status;
00613        int n;
00614        int type;
00615        int class;
00616        int qdcount;
00617        int ancount;
00618        struct dkim_rep_query *rq;
00619        char *e;
00620        char *eq;
00621        char *p;
00622        char *eob;
00623        char *ctx;
00624        u_char *cp;
00625        u_char *eom;
00626        u_char *found = NULL;
00627        HEADER hdr;
00628        u_char qname[DKIM_REP_MAXHOSTNAMELEN + 1];
00629        char buf[BUFRSZ + 1];
00630 
00631        assert(dr != NULL);
00632        assert(qh != NULL);
00633 
00634        rq = qh;
00635 
00636        status = dr->dkim_rep_dns_waitreply(dr->dkim_rep_dns_service,
00637                                            rq->drq_qh, timeout,
00638                                            &rq->drq_anslen, &dnserr, NULL);
00639 
00640        if (status == DKIM_REP_DNS_ERROR)
00641        {
00642               snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00643                        "error during query");
00644               return DKIM_REP_STAT_ERROR;
00645        }
00646        else if (status == DKIM_REP_DNS_NOREPLY)
00647        {
00648               return DKIM_REP_STAT_NOREPLY;
00649        }
00650        else if (status == DKIM_REP_DNS_EXPIRED)
00651        {
00652               return DKIM_REP_STAT_EXPIRED;
00653        }
00654 
00655        /* set up pointers */
00656        memcpy(&hdr, rq->drq_buf, sizeof hdr);
00657        cp = (u_char *) rq->drq_buf + HFIXEDSZ;
00658        eom = (u_char *) rq->drq_buf + rq->drq_anslen;
00659 
00660        /* skip over the name at the front of the answer */
00661        for (qdcount = ntohs((unsigned short) hdr.qdcount);
00662             qdcount > 0;
00663             qdcount--)
00664        {
00665               /* copy it first */
00666               (void) dn_expand((unsigned char *) rq->drq_buf, eom, cp,
00667                                (char *) qname, sizeof qname);
00668  
00669               if ((n = dn_skipname(cp, eom)) < 0)
00670               {
00671                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00672                               "'%s' reply corrupt", qname);
00673                      return DKIM_REP_STAT_ERROR;
00674               }
00675               cp += n;
00676 
00677               /* extract the type and class */
00678               if (cp + INT16SZ + INT16SZ > eom)
00679               {
00680                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00681                               "'%s' reply corrupt", qname);
00682                      return DKIM_REP_STAT_ERROR;
00683               }
00684               GETSHORT(type, cp);
00685               GETSHORT(class, cp);
00686        }
00687 
00688        if (type != T_TXT || class != C_IN)
00689        {
00690               snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00691                        "'%s' unexpected reply type/class", qname);
00692               return DKIM_REP_STAT_ERROR;
00693        }
00694 
00695        if (hdr.rcode == NXDOMAIN)
00696               return DKIM_REP_STAT_NOTFOUND;
00697 
00698        /* get the answer count */
00699        ancount = ntohs((unsigned short) hdr.ancount);
00700        if (ancount == 0)
00701               return DKIM_REP_STAT_NOTFOUND;
00702 
00703        /*
00704        **  Extract the data from the first TXT answer.
00705        */
00706 
00707        while (--ancount >= 0 && cp < eom)
00708        {
00709               /* grab the label, even though we know what we asked... */
00710               if ((n = dn_expand((unsigned char *) rq->drq_buf, eom, cp,
00711                                  (RES_UNC_T) qname, sizeof qname)) < 0)
00712               {
00713                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00714                               "'%s' reply corrupt", qname);
00715                      return DKIM_REP_STAT_ERROR;
00716               }
00717               /* ...and move past it */
00718               cp += n;
00719 
00720               /* extract the type and class */
00721               if (cp + INT16SZ + INT16SZ > eom)
00722               {
00723                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00724                               "'%s' reply corrupt", qname);
00725                      return DKIM_REP_STAT_ERROR;
00726               }
00727 
00728               GETSHORT(type, cp);
00729               GETSHORT(class, cp);
00730 
00731               /* skip the TTL */
00732               cp += INT32SZ;
00733 
00734               /* skip CNAME if found; assume it was resolved */
00735               if (type == T_CNAME)
00736               {
00737                      char chost[DKIM_REP_MAXHOSTNAMELEN + 1];
00738 
00739                      n = dn_expand((u_char *) rq->drq_buf, eom, cp,
00740                                    chost, DKIM_REP_MAXHOSTNAMELEN);
00741                      cp += n;
00742                      continue;
00743               }
00744               else if (type == T_RRSIG)
00745               {
00746                      /* get payload length */
00747                      if (cp + INT16SZ > eom)
00748                      {
00749                             snprintf(dr->dkim_rep_error,
00750                                      sizeof dr->dkim_rep_error,
00751                                      "'%s' reply corrupt", qname);
00752                             return DKIM_REP_STAT_ERROR;
00753                      }
00754                      GETSHORT(n, cp);
00755 
00756                      cp += n;
00757 
00758                      continue;
00759               }
00760               else if (type != T_TXT)
00761               {
00762                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00763                               "'%s' unexpected reply type/class", qname);
00764                      return DKIM_REP_STAT_ERROR;
00765               }
00766 
00767               if (found != NULL)
00768               {
00769                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00770                               "multiple replies for '%s'", qname);
00771                      return DKIM_REP_STAT_ERROR;
00772               }
00773 
00774               /* remember where this one started */
00775               found = cp;
00776 
00777               /* get payload length */
00778               if (cp + INT16SZ > eom)
00779               {
00780                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00781                               "'%s' reply corrupt", qname);
00782                      return DKIM_REP_STAT_ERROR;
00783               }
00784               GETSHORT(n, cp);
00785 
00786               /* move forward for now */
00787               cp += n;
00788        }
00789 
00790        /* if ancount went below 0, there were no good records */
00791        if (found == NULL)
00792        {
00793               snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00794                        "'%s' reply was unresolved CNAME", qname);
00795               return DKIM_REP_STAT_ERROR;
00796        }
00797 
00798        /* come back to the one we found */
00799        cp = found;
00800 
00801        /* get payload length */
00802        if (cp + INT16SZ > eom)
00803        {
00804               snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00805                        "'%s' reply corrupt", qname);
00806               return DKIM_REP_STAT_ERROR;
00807        }
00808 
00809        GETSHORT(n, cp);
00810 
00811        if (cp + n > eom)
00812        {
00813               snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00814                        "'%s' reply corrupt", qname);
00815               return DKIM_REP_STAT_ERROR;
00816        }
00817 
00818        /* extract the payload */
00819        memset(buf, '\0', sizeof buf);
00820        p = buf;
00821        eob = buf + sizeof buf - 1;
00822        while (n > 0 && p < eob)
00823        {
00824               c = *cp++;
00825               n--;
00826               while (c > 0 && p < eob)
00827               {
00828                      *p++ = *cp++;
00829                      c--;
00830                      n--;
00831               }
00832        }
00833 
00834        /* parse the result and return it */
00835        out = 0;
00836        for (p = strtok_r(buf, ";", &ctx);
00837             p != NULL;
00838             p = strtok_r(NULL, ";", &ctx))
00839        {
00840               eq = strchr(p, '=');
00841               if (eq == NULL)
00842                      continue;
00843 
00844               if (dkim_rep_string_empty(eq + 1))
00845                      continue;
00846 
00847               *eq = '\0';
00848 
00849               if (strcmp(p, "rep") != 0)
00850                      continue;            /* XXX -- other values? */
00851 
00852               errno = 0;
00853               out = (int) strtol(eq + 1, &e, 10);
00854               if (*e != '\0' || errno == EINVAL)
00855               {
00856                      snprintf(dr->dkim_rep_error, sizeof dr->dkim_rep_error,
00857                               "invalid reputation '%s'", eq + 1);
00858                      return DKIM_REP_STAT_SYNTAX;
00859               }
00860 
00861               *res = out;
00862               break;
00863        }
00864 
00865        return DKIM_REP_STAT_FOUND;
00866 }
00867 
00868 /*
00869 **  DKIM_REP_QUERY_CANCEL -- cancel an open query to the service
00870 **
00871 **  Parameters:
00872 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00873 **     qh -- query handle
00874 **
00875 **  Return value:
00876 **     DKIM_REP_STAT_* -- as defined
00877 */
00878 
00879 DKIM_REP_STAT
00880 dkim_rep_query_cancel(DKIM_REP dr, void *qh)
00881 {
00882        struct dkim_rep_query *rq;
00883 
00884        assert(dr != NULL);
00885        assert(qh != NULL);
00886 
00887        rq = qh;
00888 
00889        dr->dkim_rep_dns_cancel(dr->dkim_rep_dns_service, rq->drq_qh);
00890 
00891        if (dr->dkim_rep_free != NULL)
00892               dr->dkim_rep_free(dr->dkim_rep_closure, rq);
00893        else
00894               free(rq);
00895 
00896        return DKIM_REP_STAT_OK;
00897 }
00898 
00899 /*
00900 **  DKIM_REP_SETTIMEOUT -- set the DNS timeout
00901 **
00902 **  Parameters:
00903 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00904 **     timeout -- requested timeout (seconds)
00905 **
00906 **  Return value:
00907 **     None.
00908 */
00909 
00910 void
00911 dkim_rep_settimeout(DKIM_REP dr, u_int timeout)
00912 {
00913        assert(dr != NULL);
00914 
00915        dr->dkim_rep_timeout = timeout;
00916 }
00917 
00918 /*
00919 **  DKIM_REP_SETCALLBACKINT -- set the DNS callback interval
00920 **
00921 **  Parameters:
00922 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00923 **     cbint -- requested callback interval (seconds)
00924 **
00925 **  Return value:
00926 **     None.
00927 */
00928 
00929 void
00930 dkim_rep_setcallbackint(DKIM_REP dr, u_int cbint)
00931 {
00932        assert(dr != NULL);
00933 
00934        dr->dkim_rep_cbint = cbint;
00935 }
00936 
00937 /*
00938 **  DKIM_REP_SETCALLBACKCTX -- set the DNS callback context
00939 **
00940 **  Parameters:
00941 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00942 **     ctx -- context to pass to the DNS callback
00943 **
00944 **  Return value:
00945 **     None.
00946 */
00947 
00948 void
00949 dkim_rep_setcallbackctx(DKIM_REP dr, void *ctx)
00950 {
00951        assert(dr != NULL);
00952 
00953        dr->dkim_rep_cbctx = ctx;
00954 }
00955 
00956 /*
00957 **  DKIM_REP_SETDNSCALLBACK -- set the DNS wait callback
00958 **
00959 **  Parameters:
00960 **     dkim_rep -- DKIM_REP handle, created by dkim_rep_init()
00961 **     func -- function to call; should take an opaque context pointer
00962 **
00963 **  Return value:
00964 **     None.
00965 */
00966 
00967 void
00968 dkim_rep_setdnscallback(DKIM_REP dr, void (*func)(const void *))
00969 {
00970        assert(dr != NULL);
00971 
00972        dr->dkim_rep_dns_callback = func;
00973 }
00974 
00975 /*
00976 **  DKIM_REP_DNS_SET_QUERY_SERVICE -- stores a handle representing the DNS
00977 **                                    query service to be used, returning any
00978 **                                    previous handle
00979 **
00980 **  Parameters:
00981 **     dkim_rep -- DKIM_REP library handle
00982 **     h -- handle to be used
00983 **
00984 **  Return value:
00985 **     Previously stored handle, or NULL if none.
00986 */
00987 
00988 void *
00989 dkim_rep_dns_set_query_service(DKIM_REP dr, void *h)
00990 {
00991        void *old;
00992 
00993        assert(dr != NULL);
00994 
00995        old = dr->dkim_rep_dns_service;
00996 
00997        dr->dkim_rep_dns_service = h;
00998 
00999        return old;
01000 }
01001 
01002 /*
01003 **  DKIM_REP_DNS_SET_QUERY_START -- stores a pointer to a query start function
01004 **
01005 **  Parameters:
01006 **     dkim_rep -- DKIM_REP library handle
01007 **     func -- function to use to start queries
01008 **
01009 **  Return value:
01010 **     None.
01011 **
01012 **  Notes:
01013 **     "func" should match the following prototype:
01014 **            returns int (status)
01015 **            void *dns -- receives handle stored by
01016 **                         dkim_rep_dns_set_query_service()
01017 **            int type -- DNS RR query type (C_IN assumed)
01018 **            char *query -- question to ask
01019 **            char *buf -- buffer into which to write reply
01020 **            size_t buflen -- size of buf
01021 **            void **qh -- returned query handle
01022 */
01023 
01024 void
01025 dkim_rep_dns_set_query_start(DKIM_REP dr, int (*func)(void *, int,
01026                                                       unsigned char *,
01027                                                       unsigned char *,
01028                                                       size_t, void **))
01029 {
01030        assert(dr != NULL);
01031 
01032        dr->dkim_rep_dns_start = func;
01033 }
01034 
01035 /*
01036 **  DKIM_REP_DNS_SET_QUERY_CANCEL -- stores a pointer to a query cancel
01037 **                                   function
01038 **
01039 **  Parameters:
01040 **     dkim_rep -- DKIM_REP library handle
01041 **     func -- function to use to cancel running queries
01042 **
01043 **  Return value:
01044 **     None.
01045 **
01046 **  Notes:
01047 **     "func" should match the following prototype:
01048 **            returns int (status)
01049 **            void *dns -- DNS service handle
01050 **            void *qh -- query handle to be canceled
01051 */
01052 
01053 void
01054 dkim_rep_dns_set_query_cancel(DKIM_REP dr, int (*func)(void *, void *))
01055 {
01056        assert(dr != NULL);
01057 
01058        dr->dkim_rep_dns_cancel = func;
01059 }
01060 
01061 /*
01062 **  DKIM_REP_DNS_SET_QUERY_WAITREPLY -- stores a pointer to wait for a
01063 **                                      DNS reply
01064 **
01065 **  Parameters:
01066 **     dkim_rep -- DKIM_REP library handle
01067 **     func -- function to use to wait for a reply
01068 **
01069 **  Return value:
01070 **     None.
01071 **
01072 **  Notes:
01073 **     "func" should match the following prototype:
01074 **            returns int (status)
01075 **            void *dns -- DNS service handle
01076 **            void *qh -- handle of query that has completed
01077 **            struct timeval *timeout -- how long to wait
01078 **            size_t *bytes -- bytes returned
01079 **            int *error -- error code returned
01080 **            int *dnssec -- DNSSEC status returned
01081 */
01082 
01083 void
01084 dkim_rep_dns_set_query_waitreply(DKIM_REP dr, int (*func)(void *, void *,
01085                                                           struct timeval *,
01086                                                           size_t *, int *,
01087                                                           int *))
01088 {
01089        assert(dr != NULL);
01090 
01091        dr->dkim_rep_dns_waitreply = func;
01092 }