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 */

Generated on Thu Sep 16 18:10:46 2004 for libSPF v1.0 by doxygen 1.3.8