Back to index

opendkim  2.6.4
rbl.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2010, 2011, The OpenDKIM Project.  All rights reserved.
00003 */
00004 
00005 #ifndef lint
00006 static char rbl_c_id[] = "$Id$";
00007 #endif /* !lint */
00008 
00009 #include "build-config.h"
00010 
00011 /* system includes */
00012 #include <sys/param.h>
00013 #include <sys/types.h>
00014 #include <netinet/in.h>
00015 #include <arpa/inet.h>
00016 #include <arpa/nameser.h>
00017 #include <netdb.h>
00018 #include <resolv.h>
00019 #include <stdlib.h>
00020 #include <string.h>
00021 #include <assert.h>
00022 #include <errno.h>
00023 
00024 /* librbl includes */
00025 #include "rbl.h"
00026 
00027 /* local definitions needed for DNS queries */
00028 #define MAXPACKET           8192
00029 #if defined(__RES) && (__RES >= 19940415)
00030 # define RES_UNC_T          char *
00031 #else /* __RES && __RES >= 19940415 */
00032 # define RES_UNC_T          unsigned char *
00033 #endif /* __RES && __RES >= 19940415 */
00034 #ifndef T_RRSIG
00035 # define T_RRSIG            46
00036 #endif /* ! T_RRSIG */
00037 
00038 /* struct rbl_query -- an open RBL query */
00039 struct rbl_query
00040 {
00041        void *               rq_qh;
00042        size_t               rq_anslen;
00043        u_char               rq_buf[HFIXEDSZ + MAXPACKET];
00044 };
00045 
00046 /* struct rbl_handle -- an RBL library context */
00047 struct rbl_handle
00048 {
00049        u_int                rbl_timeout;
00050        u_int                rbl_cbint;
00051        void *               rbl_cbctx;
00052        void *               rbl_closure;
00053        void *               (*rbl_malloc) (void *closure, size_t nbytes);
00054        void                 (*rbl_free) (void *closure, void *p);
00055        void                 (*rbl_dns_callback) (const void *context);
00056        void *               rbl_dns_service;
00057        int                  (*rbl_dns_start) (void *srv, int type,
00058                                               unsigned char *query,
00059                                               unsigned char *buf,
00060                                               size_t buflen,
00061                                               void **qh);
00062        int                  (*rbl_dns_cancel) (void *srv, void *qh);
00063        int                  (*rbl_dns_waitreply) (void *srv,
00064                                                   void *qh,
00065                                                   struct timeval *to,
00066                                                   size_t *bytes,
00067                                                   int *error,
00068                                                   int *dnssec);
00069        u_char               rbl_qroot[RBL_MAXHOSTNAMELEN + 1];
00070        u_char               rbl_error[RBL_MAXERRORSTRING + 1];
00071 };
00072 
00073 /*
00074 **  Standard UNIX resolver stub functions
00075 */
00076 
00077 struct rbl_res_qh
00078 {
00079        int           rq_error;
00080        size_t        rq_buflen;
00081 };
00082 
00083 /*
00084 **  RBL_RES_CANCEL -- cancel a pending resolver query
00085 **
00086 **  Parameters:
00087 **     srv -- query service handle (ignored)
00088 **     qh -- query handle (ignored)
00089 **
00090 **  Return value:
00091 **     0 on success, !0 on error
00092 **
00093 **  Notes:
00094 **     The standard UNIX resolver is synchronous, so in theory this can
00095 **     never get called.  We have not yet got any use cases for one thread
00096 **     canceling another thread's pending queries, so for now just return 0.
00097 */
00098 
00099 static int
00100 rbl_res_cancel(void *srv, void *qh)
00101 {
00102        if (qh != NULL)
00103               free(qh);
00104 
00105        return 0;
00106 }
00107 
00108 /*
00109 **  RBL_RES_QUERY -- initiate a DNS query
00110 **
00111 **  Parameters:
00112 **     srv -- service handle (ignored)
00113 **     type -- RR type to query
00114 **     query -- the question to ask
00115 **     buf -- where to write the answer
00116 **     buflen -- bytes at "buf"
00117 **     qh -- query handle, used with rbl_res_waitreply
00118 **
00119 **  Return value:
00120 **     An RBL_DNS_* constant.
00121 **
00122 **  Notes:
00123 **     This is a stub for the stock UNIX resolver (res_) functions, which
00124 **     are synchronous so no handle needs to be created, so "qh" is set to
00125 **     "buf".  "buf" is actually populated before this returns (unless
00126 **     there's an error).
00127 */
00128 
00129 static int
00130 rbl_res_query(void *srv, int type, unsigned char *query, unsigned char *buf,
00131               size_t buflen, void **qh)
00132 {
00133        int n;
00134        int ret;
00135        struct rbl_res_qh *rq;
00136        unsigned char qbuf[HFIXEDSZ + MAXPACKET];
00137 #ifdef HAVE_RES_NINIT
00138        struct __res_state statp;
00139 #endif /* HAVE_RES_NINIT */
00140 
00141 #ifdef HAVE_RES_NINIT
00142        memset(&statp, '\0', sizeof statp);
00143        res_ninit(&statp);
00144 #endif /* HAVE_RES_NINIT */
00145 
00146 #ifdef HAVE_RES_NINIT
00147        n = res_nmkquery(&statp, QUERY, (char *) query, C_IN, type, NULL, 0,
00148                         NULL, qbuf, sizeof qbuf);
00149 #else /* HAVE_RES_NINIT */
00150        n = res_mkquery(QUERY, (char *) query, C_IN, type, NULL, 0, NULL, qbuf,
00151                        sizeof qbuf);
00152 #endif /* HAVE_RES_NINIT */
00153        if (n == (size_t) -1)
00154        {
00155 #ifdef HAVE_RES_NINIT
00156               res_nclose(&statp);
00157 #endif /* HAVE_RES_NINIT */
00158               return RBL_DNS_ERROR;
00159        }
00160 
00161 #ifdef HAVE_RES_NINIT
00162        ret = res_nsend(&statp, qbuf, n, buf, buflen);
00163 #else /* HAVE_RES_NINIT */
00164        ret = res_send(qbuf, n, buf, buflen);
00165 #endif /* HAVE_RES_NINIT */
00166        if (ret == -1)
00167        {
00168 #ifdef HAVE_RES_NINIT
00169               res_nclose(&statp);
00170 #endif /* HAVE_RES_NINIT */
00171               return RBL_DNS_ERROR;
00172        }
00173 
00174 #ifdef HAVE_RES_NINIT
00175        res_nclose(&statp);
00176 #endif /* HAVE_RES_NINIT */
00177 
00178        rq = (struct rbl_res_qh *) malloc(sizeof *rq);
00179        if (rq == NULL)
00180               return RBL_DNS_ERROR;
00181 
00182        if (ret == -1)
00183        {
00184               rq->rq_error = errno;
00185               rq->rq_buflen = 0;
00186        }
00187        else
00188        {
00189               rq->rq_error = 0;
00190               rq->rq_buflen = (size_t) ret;
00191        }
00192 
00193        *qh = (void *) rq;
00194 
00195        return RBL_DNS_SUCCESS;
00196 }
00197 
00198 /*
00199 **  RBL_RES_WAITREPLY -- wait for a reply to a pending query
00200 **
00201 **  Parameters:
00202 **     srv -- service handle
00203 **     qh -- query handle
00204 **     to -- timeout
00205 **     bytes -- number of bytes in the reply (returned)
00206 **     error -- error code (returned)
00207 **
00208 **  Return value:
00209 **     A RBL_DNS_* code.
00210 **
00211 **  Notes:
00212 **     Since the stock UNIX resolver is synchronous, the reply was completed
00213 **     before rbl_res_query() returned, and thus this is almost a no-op.
00214 */
00215 
00216 int
00217 rbl_res_waitreply(void *srv, void *qh, struct timeval *to, size_t *bytes,
00218                   int *error, int *dnssec)
00219 {
00220        struct rbl_res_qh *rq;
00221 
00222        assert(qh != NULL);
00223 
00224        rq = qh;
00225 
00226        if (bytes != NULL)
00227               *bytes = rq->rq_buflen;
00228        if (error != NULL)
00229               *error = rq->rq_error;
00230 
00231        return RBL_DNS_SUCCESS;
00232 }
00233 
00234 /*
00235 **  RBL_INIT -- initialize an RBL handle
00236 **
00237 **  Parameters:
00238 **     caller_mallocf -- caller-provided memory allocation function
00239 **     caller_freef -- caller-provided memory release function
00240 **     closure -- memory closure to pass to the above when used
00241 **
00242 **  Return value:
00243 **     A new RBL handle suitable for use with other RBL functions, or
00244 **     NULL on failure.
00245 **  
00246 **  Side effects:
00247 **     Sudden changes in local density altitude.
00248 */
00249 
00250 RBL *
00251 rbl_init(void *(*caller_mallocf)(void *closure, size_t nbytes),
00252          void (*caller_freef)(void *closure, void *p),
00253          void *closure)
00254 {
00255        RBL *new;
00256 
00257        if (caller_mallocf == NULL)
00258               new = (RBL *) malloc(sizeof(struct rbl_handle));
00259        else
00260               new = caller_mallocf(closure, sizeof(struct rbl_handle));
00261 
00262        if (new == NULL)
00263               return NULL;
00264 
00265        memset(new, '\0', sizeof(struct rbl_handle));
00266 
00267        new->rbl_timeout = RBL_DEFTIMEOUT;
00268        new->rbl_closure = closure;
00269        new->rbl_malloc = caller_mallocf;
00270        new->rbl_free = caller_freef;
00271        new->rbl_dns_start = rbl_res_query;
00272        new->rbl_dns_waitreply = rbl_res_waitreply;
00273        new->rbl_dns_cancel = rbl_res_cancel;
00274 
00275        return new;
00276 }
00277 
00278 /*
00279 **  RBL_CLOSE -- shut down a RBL instance
00280 **
00281 **  Parameters:
00282 **     rbl -- RBL handle to shut down
00283 **
00284 **  Return value:
00285 **     None.
00286 */
00287 
00288 void
00289 rbl_close(RBL *rbl)
00290 {
00291        assert(rbl != NULL);
00292 
00293        if (rbl->rbl_free != NULL)
00294               rbl->rbl_free(rbl->rbl_closure, rbl);
00295        else
00296               free(rbl);
00297 }
00298 
00299 /*
00300 **  RBL_GETERROR -- return any stored error string from within the RBL
00301 **                  context handle
00302 **
00303 **  Parameters:
00304 **     rbl -- RBL handle from which to retrieve an error string
00305 **
00306 **  Return value:
00307 **     A pointer to the stored string, or NULL if none was stored.
00308 */
00309 
00310 const u_char *
00311 rbl_geterror(RBL *rbl)
00312 {
00313        assert(rbl != NULL);
00314 
00315        return rbl->rbl_error;
00316 }
00317 
00318 /*
00319 **  RBL_SETDOMAIN -- declare the RBL's domain (the query root)
00320 **
00321 **  Parameters:
00322 **     rbl -- RBL handle, created by rbl_init()
00323 **     qroot -- query root
00324 **
00325 **  Return value:
00326 **     None (yet).
00327 */
00328 
00329 void
00330 rbl_setdomain(RBL *rbl, u_char *qroot)
00331 {
00332        assert(rbl != NULL);
00333        assert(qroot != NULL);
00334 
00335        strncpy(rbl->rbl_qroot, qroot, sizeof rbl->rbl_qroot);
00336        rbl->rbl_qroot[sizeof rbl->rbl_qroot - 1] = '\0';
00337 }
00338 
00339 /*
00340 **  RBL_SETTIMEOUT -- set the DNS timeout
00341 **
00342 **  Parameters:
00343 **     rbl -- RBL handle, created by rbl_init()
00344 **     timeout -- requested timeout (seconds)
00345 **
00346 **  Return value:
00347 **     None.
00348 */
00349 
00350 void
00351 rbl_settimeout(RBL *rbl, u_int timeout)
00352 {
00353        assert(rbl != NULL);
00354 
00355        rbl->rbl_timeout = timeout;
00356 }
00357 
00358 /*
00359 **  RBL_SETCALLBACKINT -- set the DNS callback interval
00360 **
00361 **  Parameters:
00362 **     rbl -- RBL handle, created by rbl_init()
00363 **     cbint -- requested callback interval (seconds)
00364 **
00365 **  Return value:
00366 **     None.
00367 */
00368 
00369 void
00370 rbl_setcallbackint(RBL *rbl, u_int cbint)
00371 {
00372        assert(rbl != NULL);
00373 
00374        rbl->rbl_cbint = cbint;
00375 }
00376 
00377 /*
00378 **  RBL_SETCALLBACKCTX -- set the DNS callback context
00379 **
00380 **  Parameters:
00381 **     rbl -- RBL handle, created by rbl_init()
00382 **     ctx -- context to pass to the DNS callback
00383 **
00384 **  Return value:
00385 **     None.
00386 */
00387 
00388 void
00389 rbl_setcallbackctx(RBL *rbl, void *ctx)
00390 {
00391        assert(rbl != NULL);
00392 
00393        rbl->rbl_cbctx = ctx;
00394 }
00395 
00396 /*
00397 **  RBL_SETDNSCALLBACK -- set the DNS wait callback
00398 **
00399 **  Parameters:
00400 **     rbl -- RBL handle, created by rbl_init()
00401 **     func -- function to call; should take an opaque context pointer
00402 **
00403 **  Return value:
00404 **     None.
00405 */
00406 
00407 void
00408 rbl_setdnscallback(RBL *rbl, void (*func)(const void *context))
00409 {
00410        assert(rbl != NULL);
00411 
00412        rbl->rbl_dns_callback = func;
00413 }
00414 
00415 /*
00416 **  RBL_DNS_SET_QUERY_SERVICE -- stores a handle representing the DNS
00417 **                               query service to be used, returning any
00418 **                               previous handle
00419 **
00420 **  Parameters:
00421 **     rbl -- RBL library handle
00422 **     h -- handle to be used
00423 **
00424 **  Return value:
00425 **     Previously stored handle, or NULL if none.
00426 */
00427 
00428 void *
00429 rbl_dns_set_query_service(RBL *rbl, void *h)
00430 {
00431        void *old;
00432 
00433        assert(rbl != NULL);
00434 
00435        old = rbl->rbl_dns_service;
00436 
00437        rbl->rbl_dns_service = h;
00438 
00439        return old;
00440 }
00441 
00442 /*
00443 **  RBL_DNS_SET_QUERY_START -- stores a pointer to a query start function
00444 **
00445 **  Parameters:
00446 **     rbl -- RBL library handle
00447 **     func -- function to use to start queries
00448 **
00449 **  Return value:
00450 **     None.
00451 **
00452 **  Notes:
00453 **     "func" should match the following prototype:
00454 **            returns int (status)
00455 **            void *dns -- receives handle stored by
00456 **                         rbl_dns_set_query_service()
00457 **            int type -- DNS RR query type (C_IN assumed)
00458 **            char *query -- question to ask
00459 **            char *buf -- buffer into which to write reply
00460 **            size_t buflen -- size of buf
00461 **            void **qh -- returned query handle
00462 */
00463 
00464 void
00465 rbl_dns_set_query_start(RBL *rbl, int (*func)(void *, int,
00466                                               unsigned char *,
00467                                               unsigned char *,
00468                                               size_t, void **))
00469 {
00470        assert(rbl != NULL);
00471 
00472        rbl->rbl_dns_start = func;
00473 }
00474 
00475 /*
00476 **  RBL_DNS_SET_QUERY_CANCEL -- stores a pointer to a query cancel function
00477 **
00478 **  Parameters:
00479 **     rbl -- RBL library handle
00480 **     func -- function to use to cancel running queries
00481 **
00482 **  Return value:
00483 **     None.
00484 **
00485 **  Notes:
00486 **     "func" should match the following prototype:
00487 **            returns int (status)
00488 **            void *dns -- DNS service handle
00489 **            void *qh -- query handle to be canceled
00490 */
00491 
00492 void
00493 rbl_dns_set_query_cancel(RBL *rbl, int (*func)(void *, void *))
00494 {
00495        assert(rbl != NULL);
00496 
00497        rbl->rbl_dns_cancel = func;
00498 }
00499 
00500 /*
00501 **  RBL_DNS_SET_QUERY_WAITREPLY -- stores a pointer to wait for a DNS reply
00502 **
00503 **  Parameters:
00504 **     rbl -- RBL library handle
00505 **     func -- function to use to wait for a reply
00506 **
00507 **  Return value:
00508 **     None.
00509 **
00510 **  Notes:
00511 **     "func" should match the following prototype:
00512 **            returns int (status)
00513 **            void *dns -- DNS service handle
00514 **            void *qh -- handle of query that has completed
00515 **            struct timeval *timeout -- how long to wait
00516 **            size_t *bytes -- bytes returned
00517 **            int *error -- error code returned
00518 **            int *dnssec -- DNSSEC status returned
00519 */
00520 
00521 void
00522 rbl_dns_set_query_waitreply(RBL *rbl, int (*func)(void *, void *,
00523                                                   struct timeval *,
00524                                                   size_t *, int *,
00525                                                   int *))
00526 {
00527        assert(rbl != NULL);
00528 
00529        rbl->rbl_dns_waitreply = func;
00530 }
00531 
00532 /*
00533 **  RBL_QUERY_CANCEL -- cancel an open query to the RBL
00534 **
00535 **  Parameters:
00536 **     rbl -- RBL handle, created by rbl_init()
00537 **     qh -- query handle
00538 **
00539 **  Return value:
00540 **     RBL_STAT_* -- as defined
00541 */
00542 
00543 RBL_STAT
00544 rbl_query_cancel(RBL *rbl, void *qh)
00545 {
00546        struct rbl_query *rq;
00547 
00548        assert(rbl != NULL);
00549        assert(qh != NULL);
00550 
00551        rq = qh;
00552 
00553        rbl->rbl_dns_cancel(rbl->rbl_dns_service, rq->rq_qh);
00554 
00555        if (rbl->rbl_free != NULL)
00556               rbl->rbl_free(rbl->rbl_closure, rq);
00557        else
00558               free(rq);
00559 
00560        return RBL_STAT_OK;
00561 }
00562 
00563 /*
00564 **  RBL_QUERY_START -- initiate a query to the RBL for entries
00565 **
00566 **  Parameters:
00567 **     rbl -- RBL handle, created by rbl_init()
00568 **     query -- query string
00569 **     qh -- query handle (returned)
00570 **
00571 **  Return value:
00572 **     RBL_STAT_* -- as defined
00573 */
00574 
00575 RBL_STAT
00576 rbl_query_start(RBL *rbl, u_char *query, void **qh)
00577 {
00578        int status;
00579        struct rbl_query *rq;
00580        u_char rblquery[RBL_MAXHOSTNAMELEN + 1];
00581 
00582        assert(rbl != NULL);
00583        assert(query != NULL);
00584        assert(qh != NULL);
00585 
00586        if (rbl->rbl_qroot[0] == '\0')
00587        {
00588               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00589                        "query root not set");
00590               return RBL_STAT_INVALID;
00591        }
00592 
00593        snprintf(rblquery, sizeof rblquery, "%s.%s", query, rbl->rbl_qroot);
00594 
00595        if (rbl->rbl_malloc != NULL)
00596               rq = rbl->rbl_malloc(rbl->rbl_closure, sizeof(*rq));
00597        else
00598               rq = malloc(sizeof(*rq));
00599 
00600        if (rq == NULL)
00601               return RBL_STAT_NORESOURCE;
00602 
00603        memset(rq, '\0', sizeof *rq);
00604 
00605        status = rbl->rbl_dns_start(rbl->rbl_dns_service, T_A, rblquery,
00606                                    rq->rq_buf, sizeof rq->rq_buf, &rq->rq_qh);
00607 
00608        if (status == 0)
00609        {
00610               *qh = rq;
00611               return RBL_STAT_OK;
00612        }
00613        else
00614        {
00615               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00616                        "unable to start query for '%s'", rblquery);
00617               return RBL_STAT_DNSERROR;
00618        }
00619 }
00620 
00621 /*
00622 **  RBL_QUERY_CHECK -- check for a reply from an active query
00623 **
00624 **  Parameters:
00625 **     rbl -- RBL handle, created by rbl_init()
00626 **     qh -- query handle (returned)
00627 **     timeout -- timeout
00628 **     res -- 32-bit buffer into which to write the result (can be NULL)
00629 **
00630 **  Return value:
00631 **     RBL_STAT_* -- as defined
00632 */
00633 
00634 RBL_STAT
00635 rbl_query_check(RBL *rbl, void *qh, struct timeval *timeout, uint32_t *res)
00636 {
00637        int dnserr;
00638        int status;
00639        int n;
00640        int type;
00641        int class;
00642        int qdcount;
00643        int ancount;
00644        struct rbl_query *rq;
00645        u_char *cp;
00646        u_char *eom;
00647        u_char *found = NULL;
00648        HEADER hdr;
00649        u_char qname[RBL_MAXHOSTNAMELEN + 1];
00650 
00651        assert(rbl != NULL);
00652        assert(qh != NULL);
00653 
00654        rq = qh;
00655 
00656        status = rbl->rbl_dns_waitreply(rbl->rbl_dns_service,
00657                                        rq->rq_qh, timeout, &rq->rq_anslen,
00658                                        &dnserr, NULL);
00659 
00660        if (status == RBL_DNS_ERROR)
00661        {
00662               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00663                        "error during query");
00664               return RBL_STAT_ERROR;
00665        }
00666        else if (status == RBL_DNS_NOREPLY)
00667        {
00668               return RBL_STAT_NOREPLY;
00669        }
00670        else if (status == RBL_DNS_EXPIRED)
00671        {
00672               return RBL_STAT_EXPIRED;
00673        }
00674 
00675        /* set up pointers */
00676        memcpy(&hdr, rq->rq_buf, sizeof hdr);
00677        cp = (u_char *) rq->rq_buf + HFIXEDSZ;
00678        eom = (u_char *) rq->rq_buf + rq->rq_anslen;
00679 
00680        /* skip over the name at the front of the answer */
00681        for (qdcount = ntohs((unsigned short) hdr.qdcount);
00682             qdcount > 0;
00683             qdcount--)
00684        {
00685               /* copy it first */
00686               (void) dn_expand((unsigned char *) rq->rq_buf, eom, cp,
00687                                (char *) qname, sizeof qname);
00688  
00689               if ((n = dn_skipname(cp, eom)) < 0)
00690               {
00691                      snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00692                               "'%s' reply corrupt", qname);
00693                      return RBL_STAT_ERROR;
00694               }
00695               cp += n;
00696 
00697               /* extract the type and class */
00698               if (cp + INT16SZ + INT16SZ > eom)
00699               {
00700                      snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00701                               "'%s' reply corrupt", qname);
00702                      return RBL_STAT_ERROR;
00703               }
00704               GETSHORT(type, cp);
00705               GETSHORT(class, cp);
00706        }
00707 
00708        if (type != T_A || class != C_IN)
00709        {
00710               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00711                        "'%s' unexpected reply type/class", qname);
00712               return RBL_STAT_ERROR;
00713        }
00714 
00715        /* if NXDOMAIN, return DKIM_STAT_NOKEY */
00716        if (hdr.rcode == NXDOMAIN)
00717               return RBL_STAT_NOTFOUND;
00718 
00719        /* get the answer count */
00720        ancount = ntohs((unsigned short) hdr.ancount);
00721        if (ancount == 0)
00722               return RBL_STAT_NOTFOUND;
00723 
00724        /*
00725        **  Extract the data from the first TXT answer.
00726        */
00727 
00728        while (--ancount >= 0 && cp < eom)
00729        {
00730               /* grab the label, even though we know what we asked... */
00731               if ((n = dn_expand((unsigned char *) rq->rq_buf, eom, cp,
00732                                  (RES_UNC_T) qname, sizeof qname)) < 0)
00733               {
00734                      snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00735                               "'%s' reply corrupt", qname);
00736                      return RBL_STAT_ERROR;
00737               }
00738               /* ...and move past it */
00739               cp += n;
00740 
00741               /* extract the type and class */
00742               if (cp + INT16SZ + INT16SZ > eom)
00743               {
00744                      snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00745                               "'%s' reply corrupt", qname);
00746                      return RBL_STAT_ERROR;
00747               }
00748 
00749               GETSHORT(type, cp);
00750               GETSHORT(class, cp);
00751 
00752               /* skip the TTL */
00753               cp += INT32SZ;
00754 
00755               /* skip CNAME if found; assume it was resolved */
00756               if (type == T_CNAME)
00757               {
00758                      char chost[RBL_MAXHOSTNAMELEN + 1];
00759 
00760                      n = dn_expand((u_char *) rq->rq_buf, eom, cp,
00761                                    chost, RBL_MAXHOSTNAMELEN);
00762                      cp += n;
00763                      continue;
00764               }
00765               else if (type == T_RRSIG)
00766               {
00767                      /* get payload length */
00768                      if (cp + INT16SZ > eom)
00769                      {
00770                             snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00771                                      "'%s' reply corrupt", qname);
00772                             return RBL_STAT_ERROR;
00773                      }
00774                      GETSHORT(n, cp);
00775 
00776                      cp += n;
00777 
00778                      continue;
00779               }
00780               else if (type != T_A)
00781               {
00782                      snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00783                               "'%s' unexpected reply type/class", qname);
00784                      return RBL_STAT_ERROR;
00785               }
00786 
00787               if (found != NULL)
00788               {
00789                      snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00790                               "multiple replies for '%s'", qname);
00791                      return RBL_STAT_ERROR;
00792               }
00793 
00794               /* remember where this one started */
00795               found = cp;
00796 
00797               /* get payload length */
00798               if (cp + INT16SZ > eom)
00799               {
00800                      snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00801                               "'%s' reply corrupt", qname);
00802                      return RBL_STAT_ERROR;
00803               }
00804               GETSHORT(n, cp);
00805 
00806               /* move forward for now */
00807               cp += n;
00808        }
00809 
00810        /* if ancount went below 0, there were no good records */
00811        if (found == NULL)
00812        {
00813               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00814                        "'%s' reply was unresolved CNAME", qname);
00815               return RBL_STAT_ERROR;
00816        }
00817 
00818        /* come back to the one we found */
00819        cp = found;
00820 
00821        /* get payload length */
00822        if (cp + INT16SZ > eom)
00823        {
00824               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00825                        "'%s' reply corrupt", qname);
00826               return RBL_STAT_ERROR;
00827        }
00828 
00829        GETSHORT(n, cp);
00830        if (n != sizeof(uint32_t))
00831        {
00832               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00833                        "'%s' reply corrupt", qname);
00834               return RBL_STAT_ERROR;
00835        }
00836 
00837        if (cp + n > eom)
00838        {
00839               snprintf(rbl->rbl_error, sizeof rbl->rbl_error,
00840                        "'%s' reply corrupt", qname);
00841               return RBL_STAT_ERROR;
00842        }
00843 
00844        /* extract the payload */
00845        if (res != NULL)
00846        {
00847               uint32_t addr;
00848 
00849               GETLONG(addr, cp);
00850 
00851               *res = addr;
00852        }
00853 
00854        return RBL_STAT_FOUND;
00855 }