libSPF v1.0: util.c Source File

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

# util.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:   util.c
    00009 *  Desc:   Utility 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 #ifdef _WITH_PTHREADS
    00046 #include <pthread.h>          /* pthread_mutex_t */
    00047 #endif /* _WITH_PTHREADS */
    00048
    00049 #include "util.h"             /* Utility functions */
    00050 #include "dns.h"              /* DNS Functions */
    00051
    00052 #undef  VERSION               /* autoconf */
    00053
    00054
    00055 #ifdef _WITH_PTHREADS
    00056  /*
    00057  * pthread mutex used to facilitate reentrant competence within the
    00058  * utility functions (generally the debugging functionality, you
    00059  * could (after some poking around) probably safely remove this if
    00060  * debugging was disabled)
    00061 */
    00062 pthread_mutex_t util_mutex;
    00063
    00064 #else
    00065 /* utility functions dummy pthread mutex wrapper */
    00066 void *util_mutex;
    00067
    00068 #endif /* _WITH_PTHREADS */
    00069
    00070
    00071
    00072  /*
    00073  * globals
    00074  *
    00075 */
    00076
    00077 extern int errno;
    00078
    00079
    00080
    00081 /* _pprintf_dbg
    00082 *
    00083 *  Author: James Couzens <jcouzens@codeshare.ca>
    00084 *
    00085 *  Date:   09/08/04
    00086 *
    00087 *  Desc:
    00088 *          Handles debugging output when no variadic macro's are desired
    00089 *  and the caller simply wishes to send a single string to output but
    00090 *  wishes to have the appropriate function and other identifiers
    00091 *  prepended.
    00092 *
    00093 *          Referenced by xpprintf (formerly used for profile output)
    00094 *  and xepprintf.
    00095 *
    00096 */
    00097 void _pprintf_dbg(u_int8_t level, const char *func, const char * file,
    00098   const size_t line, const char *s)
    00099 {
    00100   char *buf;    /* working buffer */
    00101
    00102
    00103   if (!s)
    00104   {
    00105     fprintf(stderr, "_eprintf_dbg passed a NULL string\n");
    00106     fflush(stderr);
    00107
    00108     return;
    00109   }
    00110
    00111   buf = xmalloc(SPF_MAX_DEBUG + 1);
    00112   snprintf(buf, SPF_MAX_DEBUG, "[%s :: %s->%i]; %s", func, file, line, s);
    00113
    00114   if (f_bit_set(confg.level, level))
    00115   {
    00116     if (level == FL_D)          /* xpprintf */
    00117     {
    00118       fprintf(stdout, buf);
    00119       fflush(stdout);
    00120     }
    00121   }
    00122
    00123   if (level == FL_F)           /* xepprintf */
    00124   {
    00125     fprintf(stderr, buf);
    00126     fflush(stderr);
    00127   }
    00128
    00129   xfree(buf);
    00130
    00131   return;
    00132 }
    00133
    00134
    00135 /* _printf_dbg
    00136 *
    00137 *  Author: James Couzens <jcouzens@codeshare.ca>
    00138 *
    00139 *  Date:   12/25/03
    00140 *  Date:   02/18/04 (updated)
    00141 *
    00142 *  Desc:
    00143 *          Tied to a compile time switch this can instantly and at little
    00144 *  to no real expense enable a discreet debugging with out hoards of
    00145 *  #ifdefs all over the place.
    00146 *
    00147 *  Date:   09/08/04 - James Couzens <jcouzens@codeshare.ca>
    00148 *
    00149 *  Desc:
    00150 *          Modified to handle output for xeprintf (behaviour adjusted to call
    00151 *  here instead) so that its actually clear where errors are being raised from.
    00152 *
    00153 */
    00154 void _printf_dbg(u_int8_t level, const char *func, const char *file,
    00155   const size_t line, const char *format,...)
    00156 {
    00157 #ifdef _SPF_DEBUG_LOGFILE
    00158   FILE     *fp      = NULL;    /* file pointer */
    00159 #endif /* _SPF_DEBUG_LOGFILE */
    00160
    00161   char *buf;                   /* working buffer */
    00162   char *tbuf;                  /* working buffer for eprintf */
    00163
    00164   va_list argptr;              /* pointer to current argument from array */
    00165
    00166
    00167   xpthread_mutex_lock(&util_mutex);
    00168
    00169   if (!format || *format == '\0')
    00170   {
    00171     fprintf(stderr, "_printf_dbg passed null format array\n");
    00172     fflush(stderr);
    00173
    00174     return;
    00175   }
    00176
    00177   buf  = xmalloc(SPF_MAX_DEBUG + 1);
    00178   tbuf = xmalloc(SPF_MAX_DEBUG + 1);
    00179
    00180   va_start(argptr, format);
    00181   vsnprintf(buf, SPF_MAX_DEBUG, format, argptr);
    00182   va_end(argptr);
    00183
    00184   snprintf(tbuf, SPF_MAX_DEBUG, "[%s :: %s->%i]; %s", func, file, line, buf);
    00185
    00186   /* xepprintf */
    00187   if (level == FL_E)
    00188   {
    00189     fprintf(stderr, tbuf);
    00190     fflush(stderr);
    00191   }
    00192   else
    00193   {
    00194     if (f_bit_set(confg.level, level))
    00195     {
    00196 #ifndef _SPF_DEBUG_LOGFILE
    00197       fprintf(stdout, tbuf);
    00198       fflush(stdout);
    00199 #else
    00200       if ((fp = fopen(DEBUG_LOG_FILE, "a")) != NULL)
    00201       {
    00202         fprintf(fp, "[%s :: %s->%i]; %s", func, file, line, buf);
    00203         fclose(fp);
    00204       }
    00205       else
    00206       {
    00207         fprintf(stderr, "libSPF can't open file (%s) for writing!\n",
    00208           DEBUG_LOG_FILE);
    00209         fflush(stderr);
    00210         perror(func);
    00211       }
    00212 #endif /* _SPF_DEBUG_LOGFILE */
    00213     }
    00214   } /* else */
    00215
    00216   free(buf);
    00217   free(tbuf);
    00218
    00219   xpthread_mutex_unlock(&util_mutex);
    00220
    00221   return;
    00222 }
    00223
    00224
    00225 #ifndef _SPF_DEBUG_LOGFILE
    00226 /* dummy_debug
    00227 *
    00228 *  Author: James Couzens <jcouzens@codeshare.ca>
    00229 *
    00230 *  Date:   12/25/03
    00231 *
    00232 *  Desc:
    00233 *          dummy function thats used instead of the _printf_dbg function
    00234 *  when compiling without debugging
    00235 *
    00236 */
    00237 void _dummy_debug(const u_int8_t level, const char *func, const char *file,
    00238   const size_t line, const char *format,...)
    00239 {
    00240   return;
    00241 }
    00242
    00243
    00244 /* dummy_debug
    00245 *
    00246 *  Author: James Couzens <jcouzens@codeshare.ca>
    00247 *
    00248 *  Date:   12/25/03
    00249 *
    00250 *  Desc:
    00251 *          dummy function thats used instead of the _printf_dbg function
    00252 *  when compiling without debugging
    00253 *
    00254 */
    00255 void _dummy_pdebug(const u_int8_t level, const char *func, const char *file,
    00256   const size_t line, const char *s)
    00257 {
    00258   return;
    00259 }
    00260
    00261 #endif
    00262
    00263
    00264 /* UTIL_get_date
    00265 *
    00266 *  Author: James Couzens <jcouzens@codeshare.ca>
    00267 *  Date:   Sun Jan 18 06:02:13 PST 2004
    00268 *
    00269 *  Desc:
    00270 *          Returns in a buffer that must be freed by the caller, the date
    00271 *  in the format YY-MM-DD HH:MM:SS and is derived from UTC time.
    00272 *
    00273 *  Date:   09/08/04 - James Couzens <jcouzens@codeshare.ca>
    00274 *
    00275 *  Desc:
    00276 *          Added pthread mutex lock during body of this function because it
    00277 *  appears as those inspite of the use of localtime_r there is some un-safe
    00278 *  behaviour that goes on here so for now the mutex here appears to solve
    00279 *  the problem.
    00280 *
    00281 */
    00282 char *UTIL_get_date(void)
    00283 {
    00284   struct tm *now;     /* time in tm struct format  */
    00285   struct tm tmbuf;    /* reentrant buffer for localtime_r */
    00286
    00287   time_t curtime;     /* current time time_t struct format */
    00288
    00289   char *my_time;      /* time in human readable format */
    00290
    00291
    00292   xpthread_mutex_lock(&util_mutex);
    00293
    00294   curtime = time(NULL);
    00295   now     = localtime_r(&curtime, &tmbuf);
    00296   my_time = xmalloc(SPF_MAX_DATETIME);
    00297
    00298   strftime(my_time, SPF_MAX_DATETIME, "%Y-%m-%d %H:%M:%S ", now);
    00299   my_time[(SPF_MAX_DATETIME - 1)] = '\0';
    00300
    00301   xpthread_mutex_unlock(&util_mutex);
    00302
    00303   return(my_time);
    00304 }
    00305
    00306
    00307 /* UTIL_log_result
    00308 *
    00309 *  Author: James Couzens <jcouzens@codeshare.ca>
    00310 *
    00311 *  Date:   02/04/04
    00312 *
    00313 *  Desc:
    00314 *          Tied to a compile time switch this can instantly and at little
    00315 *  to no real expense enable a discreet debugging with out hoards of
    00316 *  #ifdefs all over the place.
    00317 *
    00318 *  Date:   09/08/04 - James Couzens <jcouzens@codeshare.ca>
    00319 *
    00320 *  Desc:
    00321 *          Added pthread mutex lock during body of this function because it
    00322 *  is to the detriment of stability to have multiple threads attempting to
    00323 *  open/write/close the same file at the same time!  :-)
    00324 *
    00325 */
    00326 void UTIL_log_result(peer_info_t *p)
    00327 {
    00328   FILE *fp = NULL;    /* file pointer */
    00329
    00330   char *buf;          /* working buffer */
    00331   char *date;         /* date/time stamp */
    00332
    00333
    00334   date = UTIL_get_date();
    00335   buf  = xmalloc(SPF_MAX_DEBUG);
    00336   *(date + (strlen(date) - 1)) = '\0';
    00337
    00338   if (p->spf_ver == 0)
    00339   {
    00340     p->spf_ver = SPF_VERSION;
    00341   }
    00342
    00343   xpthread_mutex_lock(&util_mutex);
    00344
    00345   snprintf(buf, SPF_MAX_DEBUG,
    00346     "[%s] result: %s :: %s [%s], ver: %i, depth: %i, error: (%s)\n",
    00347     date, p->spf_result[p->RES].s, p->from,
    00348     p->r_ip, p->spf_rlevel, p->spf_ver, p->error);
    00349
    00350   if ((fp = fopen(OUTPUT_LOG_FILE, "a")) != NULL)
    00351   {
    00352     fprintf(fp, "%s", buf);
    00353     fclose(fp);
    00354   }
    00355
    00356   xpthread_mutex_unlock(&util_mutex);
    00357
    00358   xfree(date);
    00359   xfree(buf);
    00360
    00361   return;
    00362 }
    00363
    00364
    00365 /* xstrndup
    00366 *
    00367 *  Author: James Couzens <jcouzens@codeshare.ca>
    00368 *
    00369 *  Date:   12/25/03
    00370 *
    00371 *  Desc:
    00372 *          n bytes are allocated and then filled with \0 chars.  Char s
    00373 *  is copied over to the allocated memory writing n -1 bytes leaving the
    00374 *  new string NULL terminated.  This new string is returned upon success
    00375 *  and NULL upon failure.
    00376 *
    00377 */
    00378 char *UTIL_strndup(const char *s, const size_t n)
    00379 {
    00380   char *ret_ptr = NULL;    /* return buffer */
    00381
    00382
    00383   if ((s == NULL) || (n <= 0))
    00384   {
    00385     xepprintf("Passed string is NULL.  Abort!.\n");
    00386
    00387     return(SPF_FALSE);
    00388   }
    00389
    00390   ret_ptr = xmalloc(n + 1);
    00391   xvprintf("Allocated %u bytes of memory.\n", n);
    00392   snprintf(ret_ptr, n, s);
    00393   xvprintf("leaving function; returning string: (%s)\n", ret_ptr);
    00394
    00395   return(ret_ptr);
    00396 }
    00397
    00398
    00399 /* xstrdup
    00400 *
    00401 *  Author: Patrick Earl (http://patearl.net/)
    00402 *          Adapted from xstrndup()
    00403 *
    00404 *  Date:   02/04/04
    00405 *
    00406 *  Desc:
    00407 *          strlen(s)+1 bytes are allocated and s is copied into the
    00408 * freshly allocated memory.  If the allocation or copy fails, NULL
    00409 * NULL will be returned.
    00410 *
    00411 */
    00412 char *UTIL_strdup(const char *s)
    00413 {
    00414   char *ret_ptr = NULL;    /* return buffer */
    00415
    00416
    00417   if (s == NULL)
    00418   {
    00419     xepprintf("Passed string is NULL.  Abort!.\n");
    00420
    00421     return(SPF_FALSE);
    00422   }
    00423
    00424   if ((ret_ptr = strdup(s)) == NULL)
    00425   {
    00426     xepprintf("Unable to allocate memory\n");
    00427   }
    00428
    00429   xvprintf("leaving function; returning string: (%s)\n", ret_ptr);
    00430
    00431   return(ret_ptr);
    00432 }
    00433
    00434
    00435 /* UTIL_malloc
    00436 *
    00437 *  Author: Travis Anderson <travis@anthrax.ca>
    00438 *  Author: James Couzens <jcouzens@codeshare.ca>
    00439 *
    00440 *  Date:   02/17/04
    00441 *
    00442 *  Desc:
    00443 *         Wrapper for malloc.  Upon success, behaves as malloc does.
    00444 *  Wrapper functionality is to print an error message and exit upon failure.
    00445 *
    00446 */
    00447 void *UTIL_malloc(const int32_t n, const char *file, int32_t line,
    00448   const char *func)
    00449 {
    00450     void *x = malloc(n);
    00451
    00452     if (x == NULL)
    00453     {
    00454         xvprintf("Unable to allocate %i bytes at %s:%i in %s\n",
    00455           n, file, line, func);
    00456
    00457          /*
    00458          * Be advised this is the only place I do this, because quite
    00459          * honestly, if this library can't get a few bytes of memory,
    00460          * your mailserver segfaulting as a result of this exit, or
    00461          * quitting or whatever is the LEAST of your problems!
    00462         */
    00463         exit(0);
    00464     }
    00465
    00466     memset(x, '\0', n);
    00467
    00468     return(x);
    00469 }
    00470
    00471
    00472 /* UTIL_realloc
    00473 *
    00474 *  Author: Travis Anderson <travis@anthrax.ca>
    00475 *  Author: James Couzens <jcouzens@codeshare.ca>
    00476 *
    00477 *  Date:   02/17/04
    00478 *
    00479 *  Desc:
    00480 *         Wrapper for realloc.  If 'p' is NULL, allocates memory via a call to
    00481 *  UTIL_malloc, otherwise if 'x' is assigned successfully, the behaviour is
    00482 *  identical to realloc.  Wrapper functionality is to print an error message
    00483 *  and exit upon failure.
    00484 *
    00485 */
    00486 void *UTIL_realloc(void *p, const int32_t n, const char *file,
    00487   const int32_t line, const char *func)
    00488 {
    00489     void *x = NULL;
    00490
    00491     if (p == NULL)
    00492     {
    00493       return(UTIL_malloc(n, file, line, func));
    00494     }
    00495
    00496     x = realloc(p, n);
    00497     if (x == NULL)
    00498     {
    00499         xvprintf("Unable to reallocate %i bytes at %s:%i in %s; " \
    00500           "original address 0x%x\n", n, file, line, func, (uintptr_t)p);
    00501
    00502         exit(0);
    00503     }
    00504
    00505     return(x);
    00506 }
    00507
    00508
    00509 /* UTIL_free
    00510 *
    00511 *  Author: Travis Anderson <travis@anthrax.ca>
    00512 *  Author: James Couzens <jcouzens@codeshare.ca>
    00513 *
    00514 *  Date:   02/17/04
    00515 *
    00516 *  Desc:
    00517 *         Wrapper for free.  Upon success, behaves as free does.
    00518 *  Wrapper functionality is to print an error message and exit upon
    00519 *  failure.
    00520 *
    00521 */
    00522 void UTIL_free(void *p, const char *file, const int32_t line, const char *func)
    00523 {
    00524     if (p == NULL)
    00525     {
    00526         xvprintf("Unable to free() on NULL pointer at %s:%i in %s; " \
    00527           "address 0x%x.\n", file, line, func, (uintptr_t)p);
    00528
    00529         return;
    00530     }
    00531
    00532     xvprintf("Free address 0x%x by %s on line %i (%s)\n",
    00533       (uintptr_t)p, func, line, file);
    00534
    00535     free(p);
    00536
    00537     return;
    00538 }
    00539
    00540
    00541 /* UTIL_index
    00542 *
    00543 *  Author:  James Couzens <jcouzens@codeshare.ca>
    00544 *
    00545 *  Date:   12/19/03
    00546 *
    00547 *  Desc:
    00548 *          s is walked until c is found, at which time i is returned
    00549 *  which is the number of bytes from the left it walked until c was
    00550 *  found (not including c its self);
    00551 *
    00552 *  Date:   09/01/04 - Roger Moser
    00553 *
    00554 *  Desc:
    00555 *          The return upon an error (s being NULL for exmaple) is now
    00556 *  -1 because returning 0 was really ambiguous.
    00557 *
    00558 *  Returns: -1 upon error
    00559 *  Returns: 0 upon no match
    00560 *  Returns: > 0 upon success which is the number of bytes from the left
    00561 *           the string was walked until 'c' was found.
    00562 *
    00563 */
    00564 int16_t UTIL_index(const char *s, const char c)
    00565 {
    00566   int16_t i;     /* utility */
    00567
    00568
    00569   if (s == NULL)
    00570   {
    00571     xepprintf("passed a NULL string.  Abort!\n");
    00572
    00573     return(-1);
    00574   }
    00575
    00576   xvprintf("called with string: (%s); char: %c\n", s, c);
    00577
    00578   i = 0;
    00579   while (*s)
    00580   {
    00581     if (*s == c)
    00582     {
    00583       xvprintf("Found search char: (%c); Returning: (%i)\n", *s, i);
    00584
    00585       return(i);
    00586     }
    00587     i++;
    00588     s++;
    00589   }
    00590
    00591   xpprintf("leaving function\n");
    00592
    00593   return(0);
    00594 }
    00595
    00596
    00597 /* UTIL_split_str
    00598 *
    00599 *  Author:  James Couzens <jcouzens@codeshare.ca>
    00600 *
    00601 *  Date:   01/21/04
    00602 *
    00603 *  Desc:
    00604 *          s is walked through to find 'delim' until it finds 'delim'
    00605 *  num times.  Upon final match it returns the remainder of the string
    00606 *  in a newly allocated buffer.  Upon failure returns NULL.
    00607 *
    00608 *  Date:   09/01/04 - Roger Moser
    00609 *
    00610 *  Desc:
    00611 *          'cp' was not being freed upon exiting the function when 'c'
    00612 *  was not found within the string.
    00613 *
    00614 */
    00615 char *UTIL_split_str(const char *s, const char c, const u_int8_t num)
    00616 {
    00617   u_int8_t i;    /* utility */
    00618
    00619   char *cp;      /* copy buffer of s */
    00620   char *p;       /* pointer to copy */
    00621   char *ret;     /* return buffer */
    00622
    00623
    00624   if (s == NULL)
    00625   {
    00626     xepprintf("passed a NULL string.  Abort!\n");
    00627
    00628     return(NULL);
    00629   }
    00630
    00631   xvprintf("called with string: (%s); char (%c); int: (%i)\n",
    00632     s, c, num);
    00633
    00634   p = cp = xstrndup(s, SPF_MAX_STR);
    00635
    00636   i = 0;
    00637   while(*p)
    00638   {
    00639     if (*p == c)
    00640     {
    00641       i++;
    00642       if (i == num)
    00643       {
    00644         p++;
    00645         ret = xstrndup(p, SPF_MAX_STR);
    00646
    00647         xfree(cp);
    00648
    00649         xvprintf("returning: %s\n", ret);
    00650
    00651         return(ret);
    00652       }
    00653     }
    00654     p++;
    00655   }
    00656
    00657   xfree(cp);
    00658   xvprintf("[%i] returning NULL\n", i);
    00659
    00660   return(NULL);
    00661 }
    00662
    00663
    00664 /* UTIL_split_strr
    00665 *
    00666 *  Author:  James Couzens <jcouzens@codeshare.ca>
    00667 *
    00668 *  Date:   01/30/04
    00669 *
    00670 *  Desc:
    00671 *          s is walked to the end of its self, and then is walked back
    00672 *  towards the beginning of the string until it has found 'c' the
    00673 *  delimiter, 'num' times.  Upon success memory is allocated and the
    00674 *  remainder of s is returned.  Upon failure NULL is returned.
    00675 *
    00676 *  Date:    09/01/04 - Roger Moser
    00677 *
    00678 *  Desc:
    00679 *           Replaced 'p = (char *)&(s[strlen(s) - 1]);' with a more
    00680 *  optimized 'p = strchr(s, '\0') - 1;'.  Also applied a fix so that
    00681 *  the function properly returns NULL when 's' is an empty string.
    00682 *
    00683 */
    00684 char *UTIL_split_strr(const char *s, const char c, const u_int8_t num)
    00685 {
    00686   u_int8_t i;    /* number of times delim (c) is found */
    00687
    00688   char *p;       /* pointer to the last character of s before the \0 */
    00689   char *ret;     /* return buffer */
    00690
    00691
    00692   if ((s == NULL) || (*s == '\0'))
    00693   {
    00694     xepprintf("passed a NULL string.  Abort!\n");
    00695
    00696     return(NULL);
    00697   }
    00698
    00699   xvprintf("called with (%s)\n", s);
    00700
    00701
    00702   /* assign 'p' to the last char before the null termination of 's'*/
    00703   /*p = (char *)&(s[strlen(s) - 1]);*/
    00704   p = (strchr(s, '\0') - 1);
    00705
    00706   i = 0;
    00707   while(p != s)
    00708   {
    00709     if (*p == c)
    00710     {
    00711       i++;
    00712       if (i == num)
    00713       {
    00714         if (*p == '.')
    00715         {
    00716           p++; /* don't want that period */
    00717         }
    00718
    00719         ret = xstrdup(p);
    00720
    00721         xvprintf("delimiter found (%i) times; returning (%s).\n", i, ret);
    00722
    00723         return(ret);
    00724       }
    00725     }
    00726     p--;
    00727   }
    00728
    00729   xvprintf("delimiter (%c) found (%u) times; returing NULL\n", c, i);
    00730
    00731   return(NULL);
    00732 }
    00733
    00734
    00735 /* UTIL_count_delim
    00736 *
    00737 *  Author:  James Couzens <jcouzens@codeshare.ca>
    00738 *
    00739 *  Date:   01/21/04
    00740 *
    00741 *  Desc:
    00742 *          s is walked through and each time 'delim' is found a counter is
    00743 *  incremented and once complete, that integer is returned to the calling
    00744 *  function.  Specifically for the purposes of this project i is limited by its
    00745 *  type to 255.
    00746 *
    00747 *  Returns: > 0 && <= 255 upon success
    00748 *  Returns: 0 upon failure
    00749 *
    00750 */
    00751 u_int8_t UTIL_count_delim(const char *s, const char c)
    00752 {
    00753   u_int8_t i = 0;    /* utility */
    00754
    00755
    00756   if (s == NULL)
    00757   {
    00758     xepprintf("passed a NULL string.  Abort!\n");
    00759
    00760     return(0);
    00761   }
    00762
    00763   while (*s && i < SPF_MAX_DELIM)
    00764   {
    00765     if (*s == c)
    00766     {
    00767       i++;
    00768     }
    00769     s++;
    00770   }
    00771
    00772   xvprintf("found (%i) number of delimiters; returning.\n", i);
    00773
    00774   return(i);
    00775 }
    00776
    00777
    00778 /* UTIL_is_spf_delim
    00779 *
    00780 *  Author:  James Couzens <jcouzens@codeshare.ca>
    00781 *
    00782 *  Date:   01/21/04
    00783 *
    00784 *  Desc:
    00785 *          c is compared against all the valid spf delimiters and
    00786 *  in the case of a SPF_PASS, the function returns true, otherwise it will
    00787 *  return false.
    00788 *
    00789 */
    00790 SPF_BOOL UTIL_is_spf_delim(const char c)
    00791 {
    00792
    00793   if (!c)
    00794   {
    00795     xepprintf("called with a NULL char!  Aborting check.\n");
    00796
    00797     return(SPF_FALSE);
    00798   }
    00799
    00800   xvprintf("called with char (%c)\n", c);
    00801
    00802   if (c == '.' ||
    00803       c == '-' ||
    00804       c == '+' ||
    00805       c == ',' ||
    00806       c == '|' ||
    00807       c == '_')
    00808   {
    00809     xpprintf("leaving function; returning SPF_FALSE\n");
    00810
    00811     return(SPF_TRUE);
    00812   }
    00813
    00814   xpprintf("leaving function; returning SPF_FALSE\n");
    00815
    00816   return(SPF_FALSE);
    00817 }
    00818
    00819
    00820 /* UTIL_is_spf_result
    00821 *
    00822 *  Author:  James Couzens <jcouzens@codeshare.ca>
    00823 *
    00824 *  Date:   01/27/04
    00825 *
    00826 *  Desc:
    00827 *          c is compared against all the valid spf prefixes and
    00828 *  in the case of a SPF_PASS, the function returns true, otherwise it will
    00829 *  return false.
    00830 *
    00831 */
    00832 SPF_BOOL UTIL_is_spf_result(const char c)
    00833 {
    00834   if (!c)
    00835   {
    00836     xpprintf("passed a NULL or empty char!\n");
    00837
    00838     return(SPF_FALSE);
    00839   }
    00840
    00841   xvprintf("called with char (%c)\n", c);
    00842
    00843   if (c == '+' || c == '-' || c == '~' || c == '?')
    00844   {
    00845     xpprintf("leaving function; returning SPF_TRUE\n");
    00846
    00847     return(SPF_TRUE);
    00848   }
    00849
    00850   xpprintf("leaving function; returning SPF_FALSE\n");
    00851
    00852   return(SPF_FALSE);
    00853 }
    00854
    00855
    00856 /* UTIL_is_macro
    00857 *
    00858 *  Author:  James Couzens <jcouzens@codeshare.ca>
    00859 *
    00860 *  Date:   01/30/04
    00861 *
    00862 *  Desc:
    00863 *          s is walked through in search of a macro which consist of
    00864 *  %{?}, ? being an SPF_UNKNOWN number of chars in between.  An instance of
    00865 *  a macro is searched for.  Upon success SPF_TRUE is returned.  Upon failure
    00866 *  to find a macro, SPF_FALSE is returned.
    00867 *
    00868 */
    00869 SPF_BOOL UTIL_is_macro(const char *s)
    00870 {
    00871   if (s == NULL)
    00872   {
    00873     xepprintf("passed a NULL string.  Abort!\n");
    00874
    00875     return(SPF_FALSE);
    00876   }
    00877
    00878   xvprintf("called with string (%s)\n", s);
    00879
    00880   while (*s++)
    00881   {
    00882     if ((*s == '%') && (*(s + 1) == '{'))
    00883     {
    00884       if (strstr(s, "}"))
    00885       {
    00886         xpprintf("leaving function; returning SPF_TRUE\n");
    00887
    00888         return(SPF_TRUE);
    00889       }
    00890     }
    00891   }
    00892
    00893   xpprintf("leaving function; returning SPF_FALSE\n");
    00894
    00895   return(SPF_FALSE);
    00896 }
    00897
    00898
    00899 /* UTIL_mx_cmp
    00900 *
    00901 *  Author: James Couzens <jcouzens@codeshare.ca>
    00902 *
    00903 *  Date:   01/02/04
    00904 *
    00905 *  Desc:
    00906 *          Using the domain name found in the passed peer_info
    00907 *  structure, the associated T_MX records are looked up and then their
    00908 *  respective hostnames resolved (if they happen to be IP addresses the
    00909 *  resolver library takes care of this. <3).  Each IP is compared
    00910 *  against the remote peer's IP (from peer_info).  Upon success returns
    00911 *  SPF_TRUE, upon failure returns SPF_FALSE.
    00912 *
    00913 */
    00914 SPF_BOOL UTIL_mx_cmp(peer_info_t *p, const char *s, const int8_t cidr)
    00915 {
    00916   SPF_BOOL MX_SPF_MATCH;    /* true / false */
    00917
    00918   char *rr_data = NULL;     /* record */
    00919   char *token;              /* token for splitting records */
    00920   char *token_ptr;          /* working pointer when tokenizing */
    00921   char *peer_ip;            /* remote host converted to string */
    00922
    00923
    00924   MX_SPF_MATCH = SPF_FALSE;
    00925   token_ptr    = rr_data;
    00926
    00927   if ((rr_data = DNS_query(p, s, T_MX, NULL)) == NULL)
    00928   {
    00929     xpprintf("SPF_ERROR parsing DNS Query\n");
    00930
    00931     return(SPF_FALSE);
    00932   }
    00933
    00934   xvprintf("rr_data is: (%s)\n", rr_data);
    00935
    00936   peer_ip = xstrndup(inet_ntoa(p->addr), 16);
    00937   token   = strtok_r(rr_data, " ", &token_ptr);
    00938
    00939   while (token != NULL)
    00940   {
    00941     xvprintf("TOKEN: (%s)\n", token);
    00942
    00943     if (UTIL_validate_hostname(p, token, cidr) == SPF_TRUE)
    00944     {
    00945       xvprintf("%s validated via (%s)\n", p->from, token);
    00946
    00947       MX_SPF_MATCH = SPF_TRUE;
    00948       UTIL_assoc_prefix(p, SPF_PASS, NULL);
    00949       token = NULL;
    00950     }
    00951     else
    00952     {
    00953       token = strtok_r(NULL, " ", &token_ptr);
    00954     }
    00955   }
    00956
    00957   xfree(peer_ip);
    00958   xfree(rr_data);
    00959
    00960   return(MX_SPF_MATCH);
    00961 }
    00962
    00963
    00964 /* UTIL_a_cmp
    00965 *
    00966 *  Author: James Couzens <jcouzens@codeshare.ca>
    00967 *
    00968 *  Date:   01/02/04
    00969 *
    00970 *  Desc:
    00971 *          Calls gethostbyname to grab all records associated with a the
    00972 *  hostname contained within s.  On success returns SPF_TRUE, on SPF_ERROR
    00973 *  returns SPF_FALSE.
    00974 *
    00975 *  Note:
    00976 *          We need to define and set a limit on the number of recursive
    00977 *  lookups.  I recall seeing this in the RFC, we should discuss this.
    00978 *
    00979 */
    00980 SPF_BOOL UTIL_a_cmp(peer_info_t *p, const char *s, const int8_t cidr)
    00981 {
    00982   int16_t pos;                      /* position in DNS packet */
    00983
    00984   int tmp_errno = 0;                /* temporary errno placeholder */
    00985
    00986   size_t s_len;                     /* length of s (hostname) */
    00987
    00988   char *rr_data   = NULL;           /* data storage for DNS query */
    00989   char *token_ptr = NULL;           /* token pointer */
    00990   char *gbuf      = NULL;           /* buf for reentrant gethostbyname call */
    00991   char *copy      = NULL;           /* copy of s */
    00992   char *cp        = NULL;           /* pointer to copy of s */
    00993
    00994   char **a;
    00995
    00996   struct hostent *hp = NULL;        /* hostent structure */
    00997   struct hostent tmp_hp;            /* temporary hostent (xgethostbyname) */
    00998
    00999   policy_addr_t policy_addr;        /* used during CIDR comparisons */
    01000
    01001
    01002   if (s == NULL)
    01003   {
    01004     xepprintf("Passed string is NULL.  Abort!.\n");
    01005
    01006     return(SPF_FALSE);
    01007   }
    01008
    01009   xvprintf("called with (%s) and cidr: %i\n", s, cidr);
    01010
    01011   gbuf      = xmalloc(SPF_MAX_GHBNR_DBUF);
    01012   s_len     = strlen(s);
    01013   token_ptr = rr_data;
    01014
    01015   /* we're dealing with a:some.hostname/cidr */
    01016   if ((s_len > 1) && (*(s + 1) == ':'))
    01017   {
    01018     cp = copy = xstrndup(s, (s_len + 1));
    01019
    01020     if (cidr != 32)
    01021     {
    01022       /* We don't want a netmask, so lets remove it */
    01023       cp[s_len - 3] = '\0';
    01024     }
    01025
    01026     if ((pos = UTIL_index(cp, ':')) <= 0)
    01027     {
    01028       xeprintf("ERROR parsing passed mechanism token (%s)\n", cp);
    01029
    01030       xfree(copy);
    01031       xfree(gbuf);
    01032
    01033       return(SPF_FALSE);
    01034     }
    01035
    01036     /* move passed the mechanism text */
    01037     cp += (pos + 1);
    01038   }
    01039   else
    01040   {
    01041     cp = copy = xstrndup(p->current_domain, SPF_MAX_HNAME);
    01042   }
    01043
    01044   if ((hp = xgethostbyname(cp, &tmp_hp, gbuf, SPF_MAX_GHBNR_DBUF,
    01045     &tmp_errno)) != NULL)
    01046   {
    01047     for (a = hp->h_addr_list; *a; a++)
    01048     {
    01049       memcpy(&policy_addr.addr.s_addr, *a, SIZEOF(struct in_addr));
    01050
    01051       xvprintf("IN ADDR; Checking: %lu\n", policy_addr.addr.s_addr);
    01052
    01053       /* cidr is assumed checked by the calling function! */
    01054       policy_addr.cidr = cidr;
    01055
    01056       if (UTIL_cidr_cmp(&policy_addr, &p->addr) == SPF_TRUE)
    01057       {
    01058         *a = NULL;
    01059
    01060         UTIL_assoc_prefix(p, SPF_PASS, NULL);
    01061
    01062         xfree(copy);
    01063         xfree(gbuf);
    01064
    01065         xgethostbyname_free();    /* unlock mutex */
    01066
    01067         return(SPF_TRUE);
    01068       }
    01069     } /* for */
    01070
    01071     for (a = hp->h_aliases; *a; a++)
    01072     {
    01073       memcpy(&policy_addr.addr.s_addr, *a, SIZEOF(struct in_addr));
    01074
    01075       xvprintf("IN CNAME; Checking: %lu\n", policy_addr.addr.s_addr);
    01076
    01077       /* cidr is assumed checked by the calling function! */
    01078       policy_addr.cidr = cidr;
    01079
    01080       if (UTIL_cidr_cmp(&policy_addr, &p->addr) == SPF_TRUE)
    01081       {
    01082         *a = NULL;
    01083
    01084         UTIL_assoc_prefix(p, SPF_PASS, NULL);
    01085
    01086         xfree(copy);
    01087         xfree(gbuf);
    01088
    01089         xgethostbyname_free();    /* unlock mutex */
    01090
    01091         return(SPF_TRUE);
    01092       }
    01093     } /* for */
    01094   } /* if .. xgethostbyname */
    01095   else
    01096   {
    01097     xvprintf("No address associated with hostname (%s); Reason: %s\n",
    01098       s, hstrerror(tmp_errno));
    01099   }
    01100
    01101   xfree(copy);
    01102   xfree(gbuf);
    01103
    01104   xgethostbyname_free();
    01105
    01106   return(SPF_FALSE);
    01107 }
    01108
    01109
    01110 /* UTIL_ptr_cmp
    01111 *
    01112 *  Author: James Couzens <jcouzens@codeshare.ca>
    01113 *
    01114 *  Date:   01/08/04
    01115 *
    01116 *  Desc:
    01117 *          gethostbyaddr is broken in linux.  ?->h_aliases is not NULL,
    01118 *  however, it doesn't contain any valid pointers either.  It grabs the
    01119 *  first (and of course this is random) hostname it gets and thats all.
    01120 *  As tested in FreeBSD however this call works.  At any rate, I've
    01121 *  written a function to deal with this called DNS_ptr_answer.  From here
    01122 *  that function is called which handles the reverse lookups and then
    01123 *  attempts to "validate" each returned hostname by in turn looking it up
    01124 *  to confirm if the ip address SPF_MATCHes.  Returns SPF_TRUE on succes, and SPF_FALSE
    01125 *  on failure.
    01126 *
    01127 *  When passed SPF_TRUE copies into p->ptr_mhost, when SPF_FALSE copies
    01128 *  into p->r_vhname
    01129 *
    01130 *  Note:
    01131 *          We need to define and set a limit on the number of recursive
    01132 *  lookups.  I recall seeing this in the RFC, we should discuss this.
    01133 *
    01134 */
    01135 SPF_BOOL UTIL_ptr_cmp(peer_info_t *p, const char *s)
    01136 {
    01137   char *ptr_addr;    /* reversed address into PTR format */
    01138   char *tmp_ptr;     /* utility pointer */
    01139
    01140
    01141   if (s == NULL)
    01142   {
    01143     xepprintf("Passed string is NULL.  Abort!\n");
    01144
    01145     return(SPF_FALSE);
    01146   }
    01147
    01148   xvprintf("called with (%s)\n", s);
    01149
    01150   /* reverse of rpeer */
    01151   ptr_addr = UTIL_rev_addr(p->r_ip);
    01152
    01153   xvprintf("address: %s\n", ptr_addr);
    01154
    01155   if ((s = strstr(s, ":")) != NULL)
    01156   {
    01157     s++;
    01158     tmp_ptr = xstrndup(s, (strlen(s) + 1));
    01159   }
    01160   else
    01161   {
    01162     tmp_ptr = xstrndup(p->current_domain, SPF_MAX_HNAME);
    01163   }
    01164
    01165   if (DNS_query(p, ptr_addr, T_PTR, tmp_ptr) != (char *)SPF_TRUE)
    01166   {
    01167     xvprintf("Failed to pass SPF PTR mechanism check:%s\n", "");
    01168     xvprintf("the domain pointed to by %s is not a valid subdomain of %s\n",
    01169       ptr_addr, tmp_ptr);
    01170
    01171     xfree(ptr_addr);
    01172     xfree(tmp_ptr);
    01173
    01174     return(SPF_FALSE);
    01175   }
    01176
    01177   xvprintf("PTR lookup succeeded: (%s):(%s)\n", p->rs,
    01178     p->error);
    01179
    01180   xfree(ptr_addr);
    01181   xfree(tmp_ptr);
    01182
    01183   return(SPF_TRUE);
    01184 }
    01185
    01186
    01187 /* UTIL_get_policy_mech
    01188 *
    01189 *  Author: James Couzens <jcouzens@codeshare.ca>
    01190 *
    01191 *  Date:   12/19/03
    01192 *
    01193 *  Desc:
    01194 *          Examins s and attempts to determine which SPF_MECHANISM
    01195 *  enumeration the string SPF_MATCHes based on its conent.
    01196 *
    01197 */
    01198 SPF_MECHANISM UTIL_get_policy_mech(const char *s)
    01199 {
    01200   if (s == NULL || !s)
    01201   {
    01202     xepprintf("passed a NULL string. Abort!\n");
    01203
    01204     return(NO_POLICY);
    01205   }
    01206
    01207   xvprintf("called with: (%s)\n", s);
    01208
    01209   if (strncmp(s, "v=", 2) == 0)
    01210   {
    01211     xvprintf("leaving function; returning %i (VERSION)\n", VERSION);
    01212
    01213     return(VERSION);
    01214   }
    01215   else if (strncmp(s, "ip4:", 4) == 0 )
    01216   {
    01217     xvprintf("leaving function; returning %i (IP4)\n", IP4);
    01218
    01219     return(IP4);
    01220   }
    01221   else if (strncmp(s, "ip6:", 4) == 0)
    01222   {
    01223     xvprintf("leaving function; returning %i (IP6)\n", IP6);
    01224
    01225     return(IP6);
    01226   }
    01227   else if (strncmp(s, "all", 3) == 0)
    01228   {
    01229     xvprintf("leaving function; returning %i (ALL)\n", ALL);
    01230
    01231     return(ALL);
    01232   }
    01233   else if (strncmp(s, "mx", 2) == 0)
    01234   {
    01235     xvprintf("leaving function; returning %i (MX)\n", MX);
    01236
    01237     return(MX);
    01238   }
    01239   else if (strncmp(s, "a:", 2) == 0 || (*s == 'a' && *(s + 1) == '/')
    01240     ||  ((*s == 'a') && !*(s + 1)))
    01241   {
    01242     xvprintf("leaving function; returning %i (A)\n", A);
    01243
    01244     return(A);
    01245   }
    01246   else if (strncmp(s, "ptr", 3) == 0)
    01247   {
    01248     xvprintf("leaving function; returning %i (PTR)\n", PTR);
    01249
    01250     return(PTR);
    01251   }
    01252   else if (strncmp(s, "include:", 7) == 0)
    01253   {
    01254     xvprintf("leaving function; returning %i (INCLUDE)\n", INCLUDE);
    01255
    01256     return(INCLUDE);
    01257   }
    01258   else if (strncmp(s, "exists:", 6) == 0)
    01259   {
    01260     xvprintf("leaving function; returning %i (EXISTS)\n", EXISTS);
    01261
    01262     return(EXISTS);
    01263   }
    01264   else if (strncmp(s, "redirect=", 9) == 0)
    01265   {
    01266     xvprintf("leaving function; returning %i (REDIRECT)\n", REDIRECT);
    01267
    01268     return(REDIRECT);
    01269   }
    01270   else if (strncmp(s, "exp=", 3) == 0)
    01271   {
    01272     xvprintf("leaving function; returning %i (EXPLAIN)\n", EXPLAIN);
    01273
    01274     return(EXPLAIN);
    01275   }
    01276   else if (strncmp(s, "default", 7) == 0)
    01277   {
    01278     xvprintf("leaving function; returning %i (DEFAULT)\n", DEFAULT);
    01279
    01280     return(DEFAULT);
    01281   }
    01282   else if (strstr(s, ":"))
    01283   {
    01284     xvprintf("leaving function; returning %i (UNMECH)\n", UNMECH);
    01285
    01286     return(UNMECH);
    01287   }
    01288
    01289   xpprintf("leaving function; returning NO_POLICY\n");
    01290   return(NO_POLICY);
    01291 }
    01292
    01293
    01294 /* UTIL_assoc_prefix
    01295 *
    01296 *  Author: James Couzens <jcouzens@codeshare.ca>
    01297 *
    01298 *  Date:   01/28/04
    01299 *
    01300 *  Desc:
    01301 *          Examins s and attempts to determine which SPF_MECHANISM_PREFIX
    01302 *  enumeration the string SPF_MATCHes based on the its contents (which is a
    01303 *  single char.  Upon a SPF_PASS it stores the appropriate value inside of
    01304 *  the passed peer_info structure.  SPF_TRUE upon success, SPF_FALSE upon failure.
    01305 *  Also upon failure SPF_ERROR (SPF_RESULT_TYPE) is stored in peer_info.
    01306 *
    01307 *
    01308 */
    01309 SPF_BOOL UTIL_assoc_prefix(peer_info_t *p, SPF_RESULT res, const char *s)
    01310 {
    01311   int16_t pos;      /* position in s string */
    01312
    01313
    01314    /*
    01315    * for whatever result happens to match the case, a debug string is
    01316    * printed (if compiled with), the level of recursion is made known through
    01317    * this. An appropriate SPF_RESULT is assigned to the passed peer_info_t
    01318    * structure's 'p->RES' variable.  In addition a short SPF_RESULT string
    01319    * (here is an example use of the spf_result_t structure spoke of in SPF_init)
    01320    * is stored in 'p->rs', and finally an error string is stored in
    01321    * 'p->error'.  This string encompasses all SPF query return types though,
    01322    * it is not only populated upon a parse error, so don't hesitate to reference
    01323    * it when looking for a verbose explanation as to the result of an SPF parse.
    01324   */
    01325
    01326   if (s != NULL)
    01327   {
    01328     xvprintf("Entering function (%i) (%s)\n", res, s);
    01329
    01330     /* support for old school deprecated mechanism "default" */
    01331     if (strncmp(s, "default", 7) == 0 && (pos = UTIL_index(s, '=')) > 0)
    01332     {
    01333       s += (pos + 1); /* move past default= */
    01334       if (strncmp(s, "deny", 4) == 0)
    01335       {
    01336         xvprintf("Stored SPF_H_FAIL (%i) (%i)\n",
    01337           res, SPF_H_FAIL);
    01338
    01339         p->RES = SPF_H_FAIL;
    01340         p->rs  = p->spf_result[SPF_H_FAIL].s;
    01341
    01342         snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01343           p->rs, p->last_m);
    01344
    01345         return(SPF_TRUE);
    01346       }
    01347       else if (strncmp(s, "pass", 4) == 0)
    01348       {
    01349         xvprintf("Stored SPF_PASS (%i) (%i)\n",
    01350           res, SPF_PASS);
    01351
    01352         p->RES = SPF_PASS;
    01353         p->rs  = p->spf_result[SPF_PASS].s;
    01354
    01355         snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01356           p->rs, p->last_m);
    01357
    01358         return(SPF_TRUE);
    01359       }
    01360       else if (strncmp(s, "softdeny", 8) == 0)
    01361       {
    01362         xvprintf("Stored SPF_S_FAIL (%i) (%i)\n",
    01363           res, SPF_S_FAIL);
    01364
    01365         p->RES = SPF_S_FAIL;
    01366         p->rs  = p->spf_result[SPF_S_FAIL].s;
    01367
    01368         snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01369           p->rs, p->last_m);
    01370
    01371         return(SPF_TRUE);
    01372       }
    01373       else if (strncmp(s, "unknown", 7) == 0)
    01374       {
    01375         xvprintf("Stored SPF_NEUTRAL (%i) (%i)\n",
    01376           res, SPF_NEUTRAL);
    01377
    01378         p->RES = SPF_NEUTRAL;
    01379         p->rs  = p->spf_result[SPF_NEUTRAL].s;
    01380
    01381         snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01382           p->rs, p->last_m);
    01383
    01384         return(SPF_TRUE);
    01385       }
    01386       else if (strncmp(s, "include", 7) == 0)
    01387       {
    01388         xvprintf("Stored SPF_UNKNOWN (%i) (%i)\n",
    01389           res, SPF_UNKNOWN);
    01390
    01391         p->RES = SPF_UNKNOWN;
    01392         p->rs  = p->spf_result[SPF_UNKNOWN].s;
    01393
    01394         snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01395           p->rs, p->last_m);
    01396
    01397         return(SPF_TRUE);
    01398       }
    01399       else
    01400       {
    01401         xvprintf("Stored SPF_ERROR (%i) (%i)\n",
    01402           res, SPF_ERROR);
    01403
    01404         p->RES = SPF_UNKNOWN;
    01405         p->rs  = p->spf_result[SPF_UNKNOWN].s;
    01406
    01407         snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01408           p->rs, p->last_m);
    01409
    01410         return(SPF_FALSE);
    01411       }
    01412     }
    01413   }
    01414
    01415   switch (res)
    01416   {
    01417
    01418     /* */
    01419     case SPF_PASS:
    01420       xvprintf("Stored SPF_PASS (%i) (%i)\n",
    01421         res, SPF_PASS);
    01422
    01423       p->RES = SPF_PASS;
    01424       p->rs  = p->spf_result[SPF_PASS].s;
    01425
    01426       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01427         p->rs, p->last_m);
    01428
    01429       return(SPF_TRUE);
    01430
    01431    /* */
    01432     case SPF_NONE:
    01433       xvprintf("Stored SPF_NONE (%i) (%i)\n",
    01434         res, SPF_NONE);
    01435
    01436       p->RES = SPF_NONE;
    01437       p->rs  = p->spf_result[SPF_NONE].s;
    01438
    01439       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01440         p->rs, p->last_m);
    01441
    01442       return(SPF_TRUE);
    01443
    01444    /* */
    01445     case SPF_S_FAIL:
    01446       xvprintf("Stored SPF_S_FAIL (%i) (%i)\n",
    01447         res, SPF_S_FAIL);
    01448
    01449       p->RES = SPF_S_FAIL;
    01450       p->rs  = p->spf_result[SPF_S_FAIL].s;
    01451
    01452       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01453         p->rs, p->last_m);
    01454
    01455       return(SPF_TRUE);
    01456
    01457     /* */
    01458     case SPF_H_FAIL:
    01459       xvprintf("Stored SPF_H_FAIL (%i) (%i)\n",
    01460         res, SPF_H_FAIL);
    01461
    01462       p->RES = SPF_H_FAIL;
    01463       p->rs  = p->spf_result[SPF_H_FAIL].s;
    01464
    01465       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01466         p->rs, p->last_m);
    01467
    01468       return(SPF_TRUE);
    01469
    01470     /* */
    01471     case SPF_NEUTRAL:
    01472       xvprintf("Stored SPF_NEUTRAL (%i) (%i)\n",
    01473         res, SPF_NEUTRAL);
    01474
    01475       p->RES = SPF_NEUTRAL;
    01476       p->rs  = p->spf_result[SPF_NEUTRAL].s;
    01477
    01478       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01479         p->rs, p->last_m);
    01480
    01481       return(SPF_TRUE);
    01482
    01483     /* */
    01484     case SPF_UNKNOWN:
    01485       xvprintf("Stored SPF_UNKNOWN (%i) (%i)\n",
    01486         res, SPF_UNKNOWN);
    01487
    01488       p->RES = SPF_UNKNOWN;
    01489       p->rs  = p->spf_result[SPF_UNKNOWN].s;
    01490
    01491       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01492         p->rs, p->last_m);
    01493
    01494       return(SPF_TRUE);
    01495
    01496    /* */
    01497     case SPF_ERROR:
    01498       xvprintf("Stored SPF_ERROR (%i) (%i)\n",
    01499         p, SPF_ERROR);
    01500
    01501       p->RES = SPF_ERROR;
    01502       p->rs  = p->spf_result[SPF_ERROR].s;
    01503
    01504       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01505         p->rs, p->last_m);
    01506
    01507       return(SPF_TRUE);
    01508
    01509     /* */
    01510     case SPF_UNMECH:
    01511       xvprintf("Stored SPF_UNMECH (%i) (%i)\n",
    01512         res, SPF_UNMECH);
    01513
    01514       p->RES = SPF_UNMECH;
    01515       p->rs  = p->spf_result[SPF_UNMECH].s;
    01516
    01517       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01518         p->rs, p->last_m);
    01519
    01520       return(SPF_TRUE);
    01521
    01522     /* */
    01523     default:
    01524       xvprintf("Stored SPF_PASS (%i) (%i)\n",
    01525         res, SPF_PASS);
    01526
    01527       p->RES = SPF_PASS;
    01528       p->rs  = p->spf_result[SPF_PASS].s;
    01529
    01530       snprintf(p->error, SPF_MAX_ERROR, "policy result: (%s) from rule (%s)",
    01531         p->rs, p->last_m);
    01532
    01533       return(SPF_TRUE);
    01534   }
    01535
    01536   xepprintf("leaving function; returning SPF_FALSE.\n");
    01537
    01538   return(SPF_FALSE);
    01539 }
    01540
    01541
    01542 /* UTIL_get_mech_prefix
    01543 *
    01544 *  Author: James Couzens <jcouzens@codeshare.ca>
    01545 *
    01546 *  Date:   12/31/03
    01547 *
    01548 *  Desc:
    01549 *          Examins s and attempts to determine which SPF_RESULT_P
    01550 *  enumeration the string SPF_MATCHes based on the its contents (which is a
    01551 *  single char.  Returns SPF_PASS if a valid prefix is not specified.
    01552 *  Valid prefixes are: + (permit), - (deny), ~ (softfail) and ? (SPF_NEUTRAL).
    01553 *
    01554 */
    01555 SPF_RESULT UTIL_get_mech_prefix(peer_info_t *p, const char *s)
    01556 {
    01557   int16_t pos;    /* position in s */
    01558
    01559
    01560   if (s == NULL)
    01561   {
    01562     xepprintf("passed a NULL string.  Abort!\n");
    01563
    01564     return(SPF_ERROR);
    01565   }
    01566
    01567   xprintf("called with char: (%s)\n", s);
    01568   snprintf(p->last_m, SPF_MAX_HNAME, "%s", s);
    01569
    01570   switch (*s)
    01571   {
    01572
    01573     /* */
    01574     case '+':
    01575       p->RES_P = SPF_PASS;
    01576       xvprintf("leaving function; returning SPF_PASS (%s) %i\n",
    01577         s, SPF_PASS);
    01578
    01579       return(SPF_PASS);
    01580
    01581
    01582     /* */
    01583     case '-':
    01584        p->RES_P = SPF_H_FAIL;
    01585       xvprintf("leaving function; returning SPF_H_FAIL (%s) %i\n",
    01586         s, SPF_H_FAIL);
    01587
    01588        return(SPF_H_FAIL);
    01589
    01590     /* */
    01591     case '?':
    01592       p->RES_P = SPF_NEUTRAL;
    01593       xvprintf("leaving function; returning SPF_NEUTRAL (%s) %i\n",
    01594         s, SPF_NEUTRAL);
    01595
    01596       return(SPF_NEUTRAL);
    01597
    01598     /* */
    01599     case '~':
    01600       p->RES_P = SPF_S_FAIL;
    01601       xvprintf("leaving function; returning SPF_S_FAIL (%s) %i\n",
    01602         s, SPF_S_FAIL);
    01603
    01604       return(SPF_S_FAIL);
    01605
    01606     /* */
    01607     default:
    01608       if (p->ALL == SPF_TRUE)
    01609       {
    01610         p->RES_P = SPF_NEUTRAL;
    01611         xvprintf("leaving function; returning (def) SPF_NEUTRAL (%s) %i\n",
    01612           s, SPF_NEUTRAL);
    01613       }
    01614       else
    01615       {
    01616         p->RES_P = SPF_PASS;
    01617         xvprintf("leaving function; returning (def) SPF_PASS (%s) %i\n",
    01618           s, SPF_PASS);
    01619       }
    01620
    01621     xvprintf("leaving function; returning (%i)\n", p->RES_P);
    01622
    01623     return(p->RES_P);
    01624   } /* switch */
    01625
    01626    /*
    01627    * The code in the following statement is for the
    01628    * deprecated mechanism "default" and is only here to attempt
    01629    * to support exceptionally tardy early adopters whom likely
    01630    * at this time no longer exist.  As such this code is not
    01631    * present in the current development versions of this library
    01632   */
    01633   if ((pos = UTIL_index(s, '=')) > 0)
    01634   {
    01635     s += (pos + 1); /* move past default= */
    01636
    01637     if (strncmp(s, "deny", 4) == 0)
    01638     {
    01639       p->RES_P = SPF_H_FAIL;
    01640       xvprintf("leaving function; returning SPF_H_FAIL on (%s)\n", s);
    01641
    01642       return(SPF_H_FAIL);
    01643     }
    01644     else if (strncmp(s, "pass", 4) == 0)
    01645     {
    01646       p->RES_P = SPF_PASS;
    01647       xvprintf("leaving function; returning SPF_PASS on (%s)\n", s);
    01648
    01649       return(SPF_PASS);
    01650     }
    01651     else if (strncmp(s, "softdeny", 8) == 0)
    01652     {
    01653       p->RES_P = SPF_S_FAIL;
    01654       xvprintf("leaving function; returning SPF_S_FAIL on (%s)\n", s);
    01655
    01656       return(SPF_S_FAIL);
    01657     }
    01658     else if (strncmp(s, "unknown", 7) == 0)
    01659     {
    01660       p->RES_P = SPF_NEUTRAL;
    01661       xvprintf("leaving function; returning SPF_NEUTRAL on (%s)\n", s);
    01662
    01663       return(SPF_NEUTRAL);
    01664     }
    01665     else
    01666     {
    01667       xvprintf("leaving function; returning SPF_NEUTRAL on (%s)\n", s);
    01668
    01669       return(SPF_NEUTRAL);
    01670     }
    01671   }
    01672
    01673   xvprintf("leaving function; returning SPF_ERROR on (%s)\n", s);
    01674
    01675   return(SPF_ERROR);
    01676 }
    01677
    01678
    01679 /* UTIL_expand_ip
    01680 *
    01681 *  Author: James Couzens <jcouzens@codeshare.ca>
    01682 *
    01683 *  Date:   12/26/03
    01684 *
    01685 *  Desc:
    01686 *          Expands an ip4 policy mechanism string into a policy_addr_t
    01687 *  structure which it allocates memory for and then returns.  The RFC
    01688 *  specifies that if a cidr block is not specified then /32 isused.
    01689 *
    01690 *  On success returns a pointer to a policy_addr_t structure, on
    01691 *  SPF_ERROR returns NULL.
    01692 *
    01693 */
    01694 policy_addr_t *UTIL_expand_ip(const char *s)
    01695 {
    01696   int8_t cidr = 0;               /* temporary storage for netmask */
    01697
    01698   size_t len;                    /* length of string passed */
    01699   size_t pos;                    /* position of break points in string */
    01700
    01701   char *ip;                      /* temporary storage for ip address */
    01702
    01703   const char *token_ptr;         /* our position in the token */
    01704
    01705   policy_addr_t *policy_addr;    /* ip/mask return structure */
    01706
    01707
    01708   if (s == NULL)
    01709   {
    01710     xepprintf("passed a NULL string.  Abort!\n");
    01711
    01712     return(NULL);
    01713   }
    01714
    01715   len = strlen(s);
    01716   token_ptr = s;
    01717
    01718   xvprintf("called with string: (%s)\n", token_ptr);
    01719
    01720   if ((pos = UTIL_index(token_ptr, ':')) == 0)
    01721   {
    01722     xvprintf("SPF_ERROR: Unable to get position on token (%s)\n",
    01723       token_ptr);
    01724
    01725     return(NULL);
    01726   }
    01727
    01728   /* jump past the ip4: portion */
    01729   token_ptr += pos + 1;
    01730
    01731   policy_addr = xmalloc(SIZEOF(policy_addr_t));
    01732
    01733   /* jump past the ip4: portion (to get length of ip) */
    01734   if ((pos = UTIL_index(token_ptr, '/')) == 0)
    01735   {
    01736     xvprintf("Unable to get position on token (%s), assuming /32 cidr " \
    01737       "block\n", token_ptr);
    01738
    01739     pos = strlen(token_ptr);
    01740     cidr = 32;
    01741   }
    01742
    01743   /* allocate space and dump the ip address there */
    01744   ip = xstrndup(token_ptr, (pos + 1));
    01745
    01746   /* copy it over to the policy_addr structure */
    01747   if ((inet_pton(AF_INET, ip, &policy_addr->addr)) == 0)
    01748   {
    01749     xvprintf("SPF_ERROR: inet_pton unable to convert ip to binary " \
    01750       "(%s)\n", ip);
    01751
    01752     xfree(ip);
    01753     return(NULL);
    01754   }
    01755
    01756   if (cidr != 32)
    01757   {
    01758     token_ptr += (pos + 1);    /* skip over the forward slash */
    01759     cidr = atoi(token_ptr);    /* convert the string to an integer */
    01760   }
    01761
    01762   xfree(ip);
    01763
    01764   if ((cidr < 0) || (cidr > 32))
    01765   {
    01766     xvprintf("ERROR: cidr violation (%u)\n", cidr);
    01767
    01768     return(NULL);
    01769   }
    01770
    01771   policy_addr->cidr = cidr;    /* copy it over to the policy_addr structure */
    01772
    01773   xvprintf("CIDR: (%i) IP: (%s)\n",
    01774     policy_addr->cidr, inet_ntoa(policy_addr->addr));
    01775
    01776   return(policy_addr);
    01777 }
    01778
    01779
    01780 /*  UTIL_is_ip
    01781 *
    01782 *  Author: James Couzens <jcouzens@codeshare.ca>
    01783 *
    01784 *  Date:   01/18/04
    01785 *
    01786 *  Desc:
    01787 *         Is the passed string (limited to between 0 - 255 by type)
    01788 *  contains a valid ip address or not.  I'm not sure if this is useful
    01789 *  anymore but at one point I was using it to tell if the passed macro
    01790 *  contained an ip address or not when passed as a string.
    01791 *
    01792 */
    01793 SPF_BOOL UTIL_is_ip(const char *id)
    01794 {
    01795   u_int8_t i = 0;    /* utility */
    01796
    01797   if (!id)
    01798   {
    01799     xepprintf("called without an IP address!\n");
    01800
    01801     return(SPF_FALSE);
    01802   }
    01803
    01804   xvprintf("called with address: (%s)\n", id);
    01805
    01806   while (*id)
    01807   {
    01808     if (*id == '.')
    01809     {
    01810       i++;
    01811     }
    01812     else if (isdigit(*id) == 0)
    01813     {
    01814       return(SPF_FALSE);
    01815     }
    01816     id++;
    01817   }
    01818
    01819   if (i == 3)
    01820   {
    01821     return(SPF_TRUE);
    01822   }
    01823
    01824   xpprintf("leaving function; returning SPF_FALSE\n");
    01825
    01826   return(SPF_FALSE);
    01827 }
    01828
    01829
    01830 /* UTIL_rev_addr
    01831 *
    01832 *  Author: James Couzens <jcouzens@codeshare.ca>
    01833 *
    01834 *  Date:   01/04/04
    01835 *
    01836 *  Desc:
    01837 *          Using a passed ip address it will reverse it and slap
    01838 *  .in-addr.arpa on the end of it making it ready for a proper PTR query
    01839 *
    01840 *  Notes:  Write additional code to handle IP6 addresses
    01841 *
    01842 */
    01843 char *UTIL_rev_addr(const char *s)
    01844 {
    01845   u_int8_t i = 0;        /* utility */
    01846   u_int8_t tmp[4][1];    /* temporary storage for ip integers */
    01847
    01848   u_int16_t len = 0;     /* length of to be allocated string */
    01849
    01850   char *cp;              /* copy of passed string */
    01851   char *token;           /* token for splitting apart ip address */
    01852   char *new_addr;        /* allocate string for reversed address */
    01853
    01854
    01855   if (s == NULL)
    01856   {
    01857     xepprintf("passed a null string.  Abort!\n");
    01858
    01859     return(NULL);
    01860   }
    01861
    01862   len = (strlen(s) + 1);
    01863
    01864   xprintf("called with: (%s) len: (%i)\n", s, (len - 1));
    01865
    01866   cp    = xstrndup(s, len);
    01867   token = strtok(cp, ".");
    01868
    01869   while ((token != NULL) && (i <= 3))
    01870   {
    01871     xvprintf("token : (%s)\n", token);
    01872
    01873     tmp[i][0] = atoi(token);
    01874     token     = strtok(NULL, ".");
    01875     i++;
    01876   }
    01877
    01878   xfree(cp);
    01879
    01880   /* + .in-addr.arpa\0 */
    01881   new_addr = xmalloc(len + 13);
    01882
    01883   snprintf(new_addr, (len + 13), "%u.%u.%u.%u.in-addr.arpa",
    01884     tmp[3][0], tmp[2][0], tmp[1][0], tmp[0][0]);
    01885
    01886   xvprintf("leaving function; returning reversed ip: %s\n", new_addr);
    01887
    01888   return(new_addr);
    01889 }
    01890
    01891
    01892 /* UTIL_get_dname
    01893 *
    01894 *  Author: James Couzens <jcouzens@codeshare.ca>
    01895 *
    01896 *  Date:   01/27/04
    01897 *
    01898 *  Desc:
    01899 *          s contains a hostname which is then looked through and split
    01900 *  apart, always leaving one '.' delimiter left so you are left with
    01901 *  domain.tld.  domain.tld is then put into newly allocated memory and
    01902 *  returned to the calling function.  Upon failure, returns NULL.
    01903 *  Calling function is required to free the memory.
    01904 *
    01905 */
    01906 char *UTIL_get_dname(const char *s)
    01907 {
    01908   u_int8_t i = 0;      /* how many delimiters in s */
    01909
    01910   char *buf = NULL;    /* return string to be allocated */
    01911
    01912
    01913   if (s == NULL)
    01914   {
    01915     xepprintf("called with NULL.  Abort!\n");
    01916
    01917     return(NULL);
    01918   }
    01919
    01920   xvprintf("called with (%s)\n", s);
    01921
    01922   i = UTIL_count_delim(s, '.');
    01923
    01924   switch (i)
    01925   {
    01926     case 0:
    01927       break;
    01928     case 1:
    01929       buf = xstrndup(s, (strlen(s) + 1));
    01930       xprintf("leaving function; returning buffer: (%s)\n", buf);
    01931
    01932       return(buf);
    01933     default:
    01934       buf = UTIL_split_str(s, '.', (i - 1));
    01935       xprintf("leaving function; returning buffer: (%s)\n", buf);
    01936
    01937       return(buf);
    01938   }
    01939
    01940   xepprintf("leaving function; returning NULL\n");
    01941
    01942   return(NULL);
    01943 }
    01944
    01945
    01946 /* UTIL_cidr_cmp
    01947 *
    01948 *  Author: James Couzens <jcouzens@codeshare.ca>
    01949 *
    01950 *  Date:   12/26/03
    01951 *
    01952 *  Desc:
    01953 *          Compares an IP address (connected peer) against another IP
    01954 *  address (found in a policy lookup) using a netmask (also found from
    01955 *  a policy lookup) to see if the peer address is legal within that
    01956 *  netmask.  Returns SPF_TRUE if its valid, SPF_FALSE if not.
    01957 *
    01958 *  Notes:  Write additional comparisons to handle IP6 addresses
    01959 *
    01960 */
    01961 SPF_BOOL UTIL_cidr_cmp(const policy_addr_t *policy_addr, const struct in_addr
    01962   *peer_addr)
    01963 {
    01964   u_int32_t a;               /* working buf */
    01965   u_int32_t b;               /* working buf */
    01966
    01967   char *peer_ip  = NULL;     /* ip address of connected peer */
    01968   char *policy_ip = NULL;    /* ip address of current SPF policy */
    01969
    01970
    01971   if ((policy_addr->addr.s_addr <= 0) && (peer_addr->s_addr <= 0))
    01972   {
    01973     xepprintf("Passed with NULL chars.  Aborting.\n");
    01974
    01975     return(SPF_FALSE);
    01976   }
    01977
    01978   xvprintf("POL: %lu PEER: %lu CIDR: %i\n", policy_addr->addr.s_addr,
    01979     peer_addr->s_addr, policy_addr->cidr);
    01980
    01981    /*
    01982    * Packets come in off the wire in whats called "network byte order"
    01983    * which does't work very well when trying to compute if one address
    01984    * is masked by another using a CIDR calculation so the 'ntohl' is
    01985    * issued on them to change their byte order
    01986   */
    01987   a = ntohl(peer_addr->s_addr);
    01988   b = ntohl(policy_addr->addr.s_addr);
    01989
    01990   if (policy_addr->cidr != 32)
    01991   {
    01992     if ((a&(~0U<<(32-policy_addr->cidr))) != (b&(~0U<<(32-policy_addr->cidr))))
    01993     {
    01994       return(SPF_FALSE);
    01995     }
    01996   }
    01997   else
    01998   {
    01999     if (peer_addr->s_addr != policy_addr->addr.s_addr)
    02000     {
    02001       xvprintf("%lu and %lu using 32 cidr do not match\n",
    02002         peer_addr->s_addr, policy_addr->addr.s_addr);
    02003
    02004       return(SPF_FALSE);
    02005     }
    02006   }
    02007
    02008   /* these are done here just for debugger remove later */
    02009   peer_ip   = xstrndup(inet_ntoa(*peer_addr), SPF_MAX_IP_ADDR);
    02010   policy_ip = xstrndup(inet_ntoa(policy_addr->addr), SPF_MAX_IP_ADDR);
    02011
    02012   xvprintf("Peer: (%s) matches address %s with network %i\n",
    02013     peer_ip, policy_ip, policy_addr->cidr);
    02014
    02015   xfree(peer_ip);
    02016   xfree(policy_ip);
    02017
    02018   return(SPF_TRUE);
    02019 }
    02020
    02021
    02022 /* UTIL_validate_ptr
    02023 *
    02024 *  Author: James Couzens <jcouzens@codeshare.ca>
    02025 *
    02026 *  Date:   12/26/03
    02027 *
    02028 *  Desc:
    02029 *          Using the ip address found in the passed peer_info structure,
    02030 *  a PTR lookup is made and if a record exists then this is record is compared
    02031 *  against the hostname the client wishes to send mail from.  If the tld matches
    02032 *  the tld found during this query, this would be a 'valdiated' PTR.  If a match
    02033 *  can not be found we return SPF_FALSE.  If a match is made we return SPF_TRUE.
    02034 *
    02035 *  Notes:  Write additional comparisons to handle IP6 addresses
    02036 *
    02037 */
    02038 SPF_BOOL UTIL_validate_ptr(peer_info_t *p)
    02039 {
    02040   char *ptr_addr;    /* reversed address into PTR format */
    02041   char *tmp_ptr;     /* utility pointer */
    02042
    02043
    02044   if (!p)
    02045   {
    02046     xepprintf("called with an invalid peer info structure!\n");
    02047
    02048     return(SPF_FALSE);
    02049   }
    02050
    02051   /* reverse of the address in peer_info */
    02052   ptr_addr = UTIL_rev_addr(p->r_ip);
    02053   xvprintf("[address: %s]\n", ptr_addr);
    02054
    02055   tmp_ptr = xstrndup(p->current_domain, SPF_MAX_HNAME);
    02056
    02057   if (DNS_query(p, ptr_addr, T_PTR, tmp_ptr) != (char *)SPF_TRUE)
    02058   {
    02059     xvprintf("PTR lookup failed: (%s) (%s)\n", p->rs, p->error);
    02060
    02061     xfree(ptr_addr);
    02062     xfree(tmp_ptr);
    02063
    02064     return(SPF_FALSE);
    02065   }
    02066
    02067   xvprintf("Peer (%s) successfully validated hostname (%s)\n",
    02068     p->r_ip, p->r_vhname);
    02069
    02070   xfree(ptr_addr);
    02071   xfree(tmp_ptr);
    02072
    02073   return(SPF_TRUE);
    02074 }
    02075
    02076
    02077 /* UTIL_validate_hostname
    02078 *
    02079 *  Author: James Couzens <jcouzens@codeshare.ca>
    02080 *
    02081 *  Date:   01/08/04
    02082 *
    02083 *  Desc:
    02084 *          s contains a hostname which is then looked up to find any
    02085 *  of the returned ip addresses SPF_PASS the remote peers.  On a successful
    02086 *  SPF_PASS returns SPF_TRUE.  When no SPF_PASS is made, returns SPF_FALSE.
    02087 *
    02088 *  Note:
    02089 *          We need to define and set a limit on the number of recursive
    02090 *  lookups.  I recall seeing this in the RFC, we should discuss this.
    02091 *
    02092 *  Date:  08/28/04 by James
    02093 *
    02094 *  Desc:
    02095 *          Added buffer for reentrant gethostbyname wrapper
    02096 *  and change gethostbyname calls to use the MACRO wrapper
    02097 *  xgethostbyname()
    02098 *
    02099 */
    02100 SPF_BOOL UTIL_validate_hostname(peer_info_t *p, const char *s, const int8_t cidr)
    02101 {
    02102   int tmp_errno = 0;                    /* temporary errno placeholder */
    02103
    02104   char **a     = NULL;                  /* work ptr for list recursion */
    02105
    02106   char *ip     = NULL;                  /* ip address */
    02107   char *gbuf   = NULL;                  /* reentrant buffer for gethostbyname_r */
    02108
    02109   struct in_addr addr;                  /* connected peer's address */
    02110   struct hostent *hp = NULL;            /* hostent structure */
    02111   struct hostent tmp_hp;                /* temporary hostent structure */
    02112
    02113   policy_addr_t policy_addr;            /* used for cidr calculations */
    02114
    02115
    02116   if (s == NULL)
    02117   {
    02118     xepprintf("passed a NULL string.\n");
    02119
    02120     return(SPF_FALSE);
    02121   }
    02122
    02123   xvprintf("called with: (%lu) and (%s)\n", p->r_ip, s);
    02124
    02125   gbuf = xmalloc(SPF_MAX_GHBNR_DBUF);
    02126
    02127   if ((hp = xgethostbyname(s, &tmp_hp, gbuf, SPF_MAX_GHBNR_DBUF,
    02128     &tmp_errno)) != NULL)
    02129   {
    02130     /* for each hostname in the list, perform CIDR checks */
    02131     for (a = hp->h_addr_list; *a; a++)
    02132     {
    02133       memcpy(&addr.s_addr, *a, SIZEOF(struct in_addr));
    02134       ip = xstrndup(inet_ntoa(addr), SPF_MAX_IP_ADDR);
    02135
    02136       xvprintf("CLI: %s (%lu) SRV: %s (%lu)\n", ip, addr.s_addr,
    02137         p->r_ip, p->addr.s_addr);
    02138
    02139       if (cidr == 32)
    02140       {
    02141         if (((struct in_addr *)(*a))->s_addr == p->addr.s_addr)
    02142         {
    02143           xvprintf("%s (%lu) matches %s (%lu)\n", ip,
    02144             ((struct in_addr *)(*a))->s_addr, p->r_ip, p->addr.s_addr);
    02145
    02146           UTIL_assoc_prefix(p, SPF_PASS, NULL);
    02147
    02148           xfree(ip);
    02149           xfree(gbuf);
    02150
    02151           xgethostbyname_free();    /* unlock mutex */
    02152
    02153           return(SPF_TRUE);
    02154         }
    02155       }
    02156       else if ((cidr < 32) && (cidr >= 8))
    02157       {
    02158         /* convert from character string to dotted quad notation */
    02159         if ((inet_pton(AF_INET, ip, &policy_addr.addr)) == 0)
    02160         {
    02161           xepprintf("Unable to execute inet_pton()\n");
    02162         }
    02163
    02164         policy_addr.cidr = cidr;
    02165
    02166         xvprintf("Address: %lu with CIDR %i\n",
    02167           policy_addr.addr.s_addr, policy_addr.cidr);
    02168
    02169         /* perform CIDR comparison of 'policy_addr' vs 'p->addr' */
    02170         if ((UTIL_cidr_cmp(&policy_addr, &p->addr)) == SPF_TRUE)
    02171         {
    02172           xvprintf("(%lu) matches (%lu) with CIDR %u\n",
    02173             policy_addr.addr.s_addr, p->addr.s_addr, cidr);
    02174
    02175           /* associate an SPF prefix (-,+,~,?) with this check */
    02176           UTIL_assoc_prefix(p, SPF_PASS, NULL);
    02177
    02178           xfree(ip);
    02179           xfree(gbuf);
    02180
    02181           xgethostbyname_free();
    02182
    02183           return(SPF_TRUE);
    02184         }
    02185       } /* else if */
    02186
    02187       xfree(ip);
    02188
    02189     } /* for */
    02190
    02191     /* for each alias (CNAME) perform a CIDR check */
    02192     for (a = hp->h_aliases; *a; a++)
    02193     {
    02194       memcpy(&addr.s_addr, *a, SIZEOF(struct in_addr));
    02195       ip = xstrndup(inet_ntoa(addr), SPF_MAX_IP_ADDR);
    02196
    02197       xvprintf("client: %s (%lu); policy: %s (%lu)\n",
    02198         ip, addr.s_addr, p->r_ip, p->addr.s_addr);
    02199
    02200       if (cidr == 32)
    02201       {
    02202         if (((struct in_addr *)(*a))->s_addr == p->addr.s_addr)
    02203         {
    02204           xvprintf("IN A: client: %s (%lu) matches policy: %s (%lu)\n",
    02205             ip, ((struct in_addr *)(*a))->s_addr, p->r_ip, p->addr.s_addr);
    02206
    02207           xfree(ip);
    02208           xfree(gbuf);
    02209
    02210           return(SPF_TRUE);
    02211         }
    02212       }
    02213       else if ((cidr < 32) && (cidr >= 8))
    02214       {
    02215         if (inet_pton(AF_INET, ip, &policy_addr.addr) == 0)
    02216         {
    02217           xepprintf("Unable to execute inet_pton()\n");
    02218         }
    02219
    02220         policy_addr.cidr = cidr;
    02221
    02222         if (UTIL_cidr_cmp(&policy_addr, &p->addr) == SPF_TRUE)
    02223         {
    02224           xvprintf("client: (%lu) matches policy (%lu) with CIDR %u\n",
    02225             policy_addr.addr.s_addr, p->addr.s_addr, cidr);
    02226
    02227           xfree(ip);
    02228           xfree(gbuf);
    02229
    02230           /* associate an SPF prefix (-,+,~,?) with this check */
    02231           UTIL_assoc_prefix(p, SPF_PASS, NULL);
    02232
    02233           return(SPF_TRUE);
    02234         }
    02235       } /* else */
    02236
    02237       xfree(ip);
    02238
    02239     } /* for */
    02240   } /* if .. xgethostbyname() */
    02241   else
    02242   {
    02243     xvprintf("No address associated with hostname (%s); Reason: %s\n",
    02244       s, hstrerror(tmp_errno));
    02245   }
    02246
    02247   xfree(gbuf);
    02248
    02249   xgethostbyname_free();
    02250
    02251   xpprintf("leaving function; returning SPF_FALSE\n");
    02252
    02253   return(SPF_FALSE);
    02254 }
    02255
    02256
    02257 /* UTIL_url_encode
    02258 *
    02259 *  Author: Sean Comeau <scomeau@obscurity.org>
    02260 *
    02261 *  Date:   01/06/04
    02262 *
    02263 *  Desc:
    02264 *          "URL-Encode" characters that are "unsafe" or "prohibited" from a URL.
    02265 *  Upon success returns a pointer to a newly allocated memory containing the
    02266 *  encoded string.  Upon failure returns a NULL pointer.  Failure
    02267 *  indicates either a failure to allocate new memory, or the passed string
    02268 *  did not contain any questionable characters and hence did not require
    02269 *  encoding.
    02270 *
    02271 */
    02272 char *UTIL_url_encode(const char *s)
    02273 {
    02274   int len;          /* length of passed string * 3*/
    02275
    02276   char *new;        /* newly allocated memory */
    02277   char *encoded;    /* encoded return string */
    02278
    02279
    02280   if (s != NULL)
    02281   {
    02282      /*
    02283      * length of the string times 3 is the maximum possible size a
    02284      * URL encoded string should ever be able to expand to.
    02285     */
    02286     len = (strlen(s) * 3);
    02287   }
    02288   else
    02289   {
    02290     xepprintf("passed a NULL string.  Abort!\n");
    02291
    02292     return(NULL);
    02293   }
    02294
    02295   encoded = new = xmalloc(len);
    02296
    02297   while (*s != '\0')
    02298   {
    02299     if (urlchr_test(*s) == 0)
    02300     {
    02301       *new++ = *s++;
    02302     }
    02303     else
    02304     {
    02305       /* this obviously NULL terminates, do we want this? */
    02306       snprintf(new, 4, "%%%x", *s);
    02307       new += 3;
    02308       s++;
    02309     }
    02310   }
    02311
    02312   *new++ = '\0';
    02313   xvprintf("leaving function; returning (%s)\n", encoded);
    02314
    02315   return(encoded);
    02316 }
    02317
    02318
    02319 /* UTIL_reverse
    02320 *
    02321 *  Author: James Couzens <jcouzens@codeshare.ca>
    02322 *
    02323 *  Date:   01/06/04
    02324 *
    02325 *  Desc:
    02326 *          Takes a string and rebuilds it in reverse order using the
    02327 *  supplied delimiter.  The string will always be rebuilt using a
    02328 *  '.' char as per the RFC.
    02329 *
    02330 */
    02331 char *UTIL_reverse(const char *s, const char delim)
    02332 {
    02333   size_t len;                     /* length of s */
    02334
    02335   char *buf;                      /* return buffer */
    02336   char *p;                        /* working pointer */
    02337   char *cp;                       /* working pointer */
    02338   char *c;                        /* working pointer */
    02339
    02340   split_str_t *master;            /* list pointers */
    02341
    02342   split_str_node_t *c_node;       /* c_node node */
    02343   split_str_node_t *kill_node;    /* temp node used in destruction */
    02344
    02345
    02346   if (s == NULL)
    02347   {
    02348     xepprintf("passed a NULL string.  Abort!\n");
    02349
    02350     return(NULL);
    02351   }
    02352
    02353   xvprintf("called with (%s) and delim (%c)\n", s, delim);
    02354
    02355   len = strlen(s);
    02356   cp  = c = xstrndup(s, (len + 1));
    02357
    02358   master           = xmalloc(SIZEOF(split_str_t));
    02359   master->head     = NULL;
    02360   master->tail     = NULL;
    02361   master->elements = 0;
    02362
    02363   /*
    02364    *  Comment: James Couzens <jcouzens@codeshare.ca>
    02365    *
    02366    *  Date: 01/22/04
    02367    *
    02368    * we do not want the trailing delim so the first iteration of this
    02369    * loop we call UTIL_addnode with 0, which tells it not to allocate
    02370    * the extra byte for the trailing delimiter.  This is done only
    02371    * when passed an IP address, and this is because when reversing we
    02372    * want: 1.0.168.192. and not .1.0.168.192
    02373    *
    02374   */
    02375
    02376   len++;
    02377   buf = xmalloc(len);
    02378   while ((p = strrchr(cp, delim)) != NULL)
    02379   {
    02380     p++;   /* remove period */
    02381     UTIL_addnode(master, p, SPF_TRUE);
    02382     p--;   /* bring it back */
    02383     *p = '\0';
    02384   }
    02385
    02386   UTIL_addnode(master, cp, SPF_FALSE);
    02387
    02388   c_node = master->head;
    02389   while ((kill_node = c_node) != NULL)
    02390   {
    02391     strncat(buf, kill_node->s, kill_node->len);
    02392     xfree(kill_node->s);
    02393     c_node = c_node->next;
    02394     xfree(kill_node);
    02395   }
    02396
    02397   xfree(cp);
    02398   xfree(master);
    02399
    02400   xvprintf("leaving function; returning (%s)\n", buf);
    02401
    02402   return(buf);
    02403 }
    02404
    02405
    02406 /*  UTIL_addnode
    02407 *
    02408 *  Author: James Couzens <jcouzens@codeshare.ca>
    02409 *
    02410 *  Date:   01/18/04
    02411 *
    02412 *  Desc:
    02413 *         Allocates memory for a new node and slaps it on the end of
    02414 *  of the passed list.  In the process of doing so, when it comes to
    02415 *  the last element in the list, which is indicated by the SPF_BOOL
    02416 *  that is passed, a final '.' is either appended (SPF_TRUE) or not
    02417 *  (SPF_FALSE).  Function returns SPF_TRUE upon success, and SPF_FALSE upon
    02418 *  failure.
    02419 *
    02420 */
    02421 SPF_BOOL UTIL_addnode(split_str_t *master, const char *s, SPF_BOOL last)
    02422 {
    02423   size_t len;                     /* length of s */
    02424
    02425   split_str_node_t *c_node;       /* c_node working node */
    02426   split_str_node_t *new_node;     /* newly allocated node */
    02427   split_str_node_t *prev_node;    /* previous working node */
    02428
    02429
    02430   if (!s || (s == NULL))
    02431   {
    02432     xepprintf("passed a NULL string.  Abort!\n");
    02433
    02434     return(SPF_FALSE);
    02435   }
    02436
    02437   xvprintf("called with string: (%s); boolean: (%s)\n", s,
    02438     last ? "FALSE" : "TRUE");
    02439
    02440   len = strlen(s);
    02441
    02442   if (last == SPF_TRUE)
    02443   {
    02444     len += 2;
    02445   }
    02446   else
    02447   {
    02448     len++;
    02449   }
    02450
    02451   /* create new node */
    02452   new_node = xmalloc(SIZEOF(split_str_node_t));
    02453
    02454   /* set the new nodes next value NULL, and store s */
    02455   new_node->next = NULL;
    02456
    02457   new_node->s = xmalloc(len);
    02458
    02459    /*
    02460    *  Comment: James Couzens <jcouzens@codeshare.ca>
    02461    *
    02462    *  Date: 01/22/04
    02463    *
    02464    * section 7.1 (Macro definitions) of the RFC v02.9.5 indicates that we must
    02465    * always rebuild the macro using the delimiter '.' and not the passed
    02466    * delimiting character.
    02467    *
    02468   */
    02469   snprintf(new_node->s, len, "%s%c", s, last ? '.' : '\0');
    02470   new_node->len = (len - 1); /* don't count \0 */
    02471
    02472   prev_node = NULL;
    02473   c_node    = master->head;
    02474
    02475   /* reorder the list with the NEW element on the end */
    02476   while (c_node != NULL)
    02477   {
    02478     prev_node = c_node;
    02479     c_node    = c_node->next;
    02480   }
    02481
    02482   if (prev_node != NULL)
    02483   {
    02484     new_node->next  = prev_node->next;
    02485     prev_node->next = new_node;
    02486   }
    02487   else
    02488   {
    02489     master->head = new_node;
    02490   }
    02491
    02492   master->tail = new_node;
    02493   master->elements++;
    02494
    02495   xpprintf("leaving function; returning SPF_TRUE\n");
    02496
    02497   return(SPF_TRUE);
    02498 }
    02499
    02500
    02501 /* _UTIL_pthread_mutex
    02502 *
    02503 *  Author: James Couzens <jcouzens@codeshare.ca>
    02504 *
    02505 *  Date:   09/08/04
    02506 *
    02507 *  Desc:
    02508 *         Locks or unlocks the passed mutex based on the additional value
    02509 *  which is and enumerattion (SPF_BOOL)
    02510 *
    02511 */
    02512 void _UTIL_pthread_mutex(void *mutex, SPF_BOOL action)
    02513 {
    02514
    02515 #ifdef _WITH_PTHREADS
    02516
    02517   switch (action)
    02518   {
    02519     case SPF_TRUE:
    02520       pthread_mutex_lock((pthread_mutex_t *)mutex);
    02521       return;
    02522
    02523     case SPF_FALSE:
    02524       pthread_mutex_unlock((pthread_mutex_t *)mutex);
    02525       return;
    02526   }
    02527
    02528 #endif /* _WITH_PTHREADS */
    02529
    02530   return;
    02531 }
    02532
    02533
    02534 /* end of util.c */