1 /* libspf - Sender Policy Framework library
2 *
3 *  ANSI C implementation of spf-draft-200405.txt
4 *
5 *  Author: James Couzens <jcouzens@codeshare.ca>
6 *  Author: Sean Comeau   <scomeau@obscurity.org>
7 *
8 *  File:   util.c
9 *  Desc:   Utility functions
10 *
11 *  License:
12 *
13 *  The libspf Software License, Version 1.0
14 *
15 *  Copyright (c) 2004 James Couzens & Sean Comeau  All rights
16 *  reserved.
17 *
18 *  Redistribution and use in source and binary forms, with or without
19 *  modification, are permitted provided that the following conditions
20 *  are met:
21 *
22 *  1. Redistributions of source code must retain the above copyright
23 *     notice, this list of conditions and the following disclaimer.
24 *
25 *  2. Redistributions in binary form must reproduce the above copyright
26 *     notice, this list of conditions and the following disclaimer in
27 *     the documentation and/or other materials provided with the
28 *     distribution.
29 *
30 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
31 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33 *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS MAKING USE OF THIS LICESEN
34 *  OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
37 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
39 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
40 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 *  SUCH DAMAGE.
42 *
43 */
44 
45 
46 #include "../../config.h"
47 
48 #ifdef _WITH_PTHREADS
49 #include <pthread.h>          /* pthread_mutex_t */
50 #endif /* _WITH_PTHREADS */
51 
52 #include "util.h"             /* Utility functions */
53 #include "dns.h"              /* DNS Functions */
54 
55 #undef  VERSION               /* autoconf */
56 
57 
58 #ifdef _WITH_PTHREADS
59  /*
60  * pthread mutex used to facilitate reentrant competence within the
61  * utility functions (generally the debugging functionality, you
62  * could (after some poking around) probably safely remove this if
63  * debugging was disabled)
64 */
65 pthread_mutex_t util_mutex;
66 
67 #else
68 /* utility functions dummy pthread mutex wrapper */
69 void *util_mutex = NULL;
70 
71 #endif /* _WITH_PTHREADS */
72 
73 
74 
75  /*
76  * globals
77  *
78 */
79 
80 extern int errno;
81 
82 
83 
84 /* _pprintf_dbg
85 *
86 *  Author: James Couzens <jcouzens@codeshare.ca>
87 *
88 *  Date:   09/08/04
89 *
90 *  Desc:
91 *          Handles debugging output when no variadic macro's are desired
92 *  and the caller simply wishes to send a single string to output but
93 *  wishes to have the appropriate function and other identifiers
94 *  prepended.
95 *
96 *          Referenced by xpprintf (formerly used for profile output)
97 *  and xepprintf.
98 *
99 */
_pprintf_dbg(u_int8_t level,const char * func,const char * file,const size_t line,const char * s)100 void _pprintf_dbg(u_int8_t level, const char *func, const char *file,
101   const size_t line, const char *s)
102 {
103 #ifdef _SPF_DEBUG_LOGFILE
104   FILE     *fp      = NULL;    /* file pointer */
105   #endif /* _SPF_DEBUG_LOGFILE */
106 
107   char *buf = NULL;           /* working buffer */
108 
109 
110   if (!s)
111   {
112     fprintf(stderr, "_eprintf_dbg passed a NULL string\n");
113     fflush(stderr);
114 
115     return;
116   }
117 
118   buf = xmalloc(SPF_MAX_DEBUG + 1);
119   snprintf(buf, SPF_MAX_DEBUG,
120     "[%s :: %s->%zu]; %s", func, file, line, s);
121 
122   if (f_bit_set(confg.level, level))
123   {
124     if (level == FL_D)          /* xpprintf */
125     {
126 #ifndef _SPF_DEBUG_LOGFILE
127       fprintf(stdout, "%s", buf);
128       fflush(stdout);
129 #else
130       if ((fp = fopen(DEBUG_LOG_FILE, "a")) != NULL)
131       {
132         fprintf(fp, "%s", buf);
133         fclose(fp);
134       }
135       else
136       {
137         fprintf(stderr, "libSPF can't open file [%s] for writing!\n",
138           DEBUG_LOG_FILE);
139 
140         fflush(stderr);
141         perror(func);
142       }
143 #endif /* _SPF_DEBUG_LOGFILE */
144     }
145   }
146 
147   if (level == FL_F)           /* xepprintf */
148   {
149     fprintf(stderr, "%s", buf);
150     fflush(stderr);
151   }
152 
153   xfree(buf);
154 
155   return;
156 }
157 
158 
159 /* _printf_dbg
160 *
161 *  Author: James Couzens <jcouzens@codeshare.ca>
162 *
163 *  Date:   12/25/03
164 *  Date:   02/18/04 (updated)
165 *
166 *  Desc:
167 *          Tied to a compile time switch this can instantly and at little
168 *  to no real expense enable a discreet debugging with out hoards of
169 *  #ifdefs all over the place.
170 *
171 *  Date:   09/08/04 - James Couzens <jcouzens@codeshare.ca>
172 *
173 *  Desc:
174 *          Modified to handle output for xeprintf (behaviour adjusted to call
175 *  here instead) so that its actually clear where errors are being raised from.
176 *
177 */
_printf_dbg(u_int8_t level,const char * func,const char * file,const size_t line,const char * format,...)178 void _printf_dbg(u_int8_t level, const char *func, const char *file,
179   const size_t line, const char *format,...)
180 {
181 #ifdef _SPF_DEBUG_LOGFILE
182   FILE *fp = NULL;      /* file pointer */
183 #endif /* _SPF_DEBUG_LOGFILE */
184 
185   char *buf  = NULL;    /* working buffer */
186   char *tbuf = NULL;    /* working buffer for eprintf */
187 
188   va_list argptr;       /* pointer to current argument from array */
189 
190 
191   xpthread_mutex_lock(&util_mutex);
192 
193   if (!format || *format == '\0')
194   {
195     fprintf(stderr, "_printf_dbg passed null format array\n");
196     fflush(stderr);
197 
198     return;
199   }
200 
201   buf  = xmalloc(SPF_MAX_DEBUG + 1);
202   tbuf = xmalloc(SPF_MAX_DEBUG * 2);
203 
204   va_start(argptr, format);
205   vsnprintf(buf, SPF_MAX_DEBUG, format, argptr);
206   va_end(argptr);
207 
208   snprintf(tbuf, (SPF_MAX_DEBUG * 2),
209     "[%s :: %s->%zu]; %s", func, file, line, buf);
210 
211   /* xepprintf */
212   if (level == FL_E)
213   {
214     fprintf(stderr, "%s", tbuf);
215     fflush(stderr);
216   }
217   else
218   {
219     if (f_bit_set(confg.level, level))
220     {
221 #ifndef _SPF_DEBUG_LOGFILE
222       fprintf(stdout, tbuf);
223       fflush(stdout);
224 #else
225       if ((fp = fopen(DEBUG_LOG_FILE, "a")) != NULL)
226       {
227         fprintf(fp, "[%s :: %s->%i]; %s", func, file, line, buf);
228         fclose(fp);
229       }
230       else
231       {
232         fprintf(stderr, "libSPF can't open file [%s] for writing!\n",
233           DEBUG_LOG_FILE);
234 
235         fflush(stderr);
236         perror(func);
237       }
238 #endif /* _SPF_DEBUG_LOGFILE */
239     }
240   } /* else */
241 
242   free(buf);
243   free(tbuf);
244 
245   xpthread_mutex_unlock(&util_mutex);
246 
247   return;
248 }
249 
250 
251 #ifndef _SPF_DEBUG_LOGFILE
252 /* dummy_debug
253 *
254 *  Author: James Couzens <jcouzens@codeshare.ca>
255 *
256 *  Date:   12/25/03
257 *
258 *  Desc:
259 *          dummy function thats used instead of the _printf_dbg function
260 *  when compiling without debugging
261 *
262 */
_dummy_debug(const u_int8_t level,const char * func,const char * file,const size_t line,const char * format,...)263 void _dummy_debug(const u_int8_t level, const char *func, const char *file,
264   const size_t line, const char *format,...)
265 {
266   return;
267 }
268 
269 
270 /* dummy_debug
271 *
272 *  Author: James Couzens <jcouzens@codeshare.ca>
273 *
274 *  Date:   12/25/03
275 *
276 *  Desc:
277 *          dummy function thats used instead of the _printf_dbg function
278 *  when compiling without debugging
279 *
280 */
_dummy_pdebug(const u_int8_t level,const char * func,const char * file,const size_t line,const char * s)281 void _dummy_pdebug(const u_int8_t level, const char *func, const char *file,
282   const size_t line, const char *s)
283 {
284   return;
285 }
286 
287 #endif
288 
289 
290 /* UTIL_get_date
291 *
292 *  Author: James Couzens <jcouzens@codeshare.ca>
293 *  Date:   Sun Jan 18 06:02:13 PST 2004
294 *
295 *  Desc:
296 *          Returns in a buffer that must be freed by the caller, the date
297 *  in the format YY-MM-DD HH:MM:SS and is derived from UTC time.
298 *
299 *  Date:   09/08/04 - James Couzens <jcouzens@codeshare.ca>
300 *
301 *  Desc:
302 *          Added pthread mutex lock during body of this function because it
303 *  appears as those inspite of the use of localtime_r there is some un-safe
304 *  behaviour that goes on here so for now the mutex here appears to solve
305 *  the problem.
306 *
307 */
UTIL_get_date(void)308 char *UTIL_get_date(void)
309 {
310   struct tm *now  = NULL;    /* time in tm struct format  */
311   struct tm tmbuf = {0};     /* reentrant buffer for localtime_r */
312 
313   time_t curtime  = {0};     /* current time time_t struct format */
314 
315   char *my_time   = NULL;    /* time in human readable format */
316 
317 
318   xpthread_mutex_lock(&util_mutex);
319 
320   curtime = time(NULL);
321   now     = localtime_r(&curtime, &tmbuf);
322   my_time = xmalloc(SPF_MAX_DATETIME);
323 
324   strftime(my_time, SPF_MAX_DATETIME, "%Y-%m-%d %H:%M:%S ", now);
325   my_time[(SPF_MAX_DATETIME - 1)] = '\0';
326 
327   xpthread_mutex_unlock(&util_mutex);
328 
329   return(my_time);
330 }
331 
332 
333 /* UTIL_log_result
334 *
335 *  Author: James Couzens <jcouzens@codeshare.ca>
336 *
337 *  Date:   02/04/04
338 *
339 *  Desc:
340 *          Tied to a compile time switch this can instantly and at little
341 *  to no real expense enable a discreet debugging with out hoards of
342 *  #ifdefs all over the place.
343 *
344 *  Date:   09/08/04 - James Couzens <jcouzens@codeshare.ca>
345 *
346 *  Desc:
347 *          Added pthread mutex lock during body of this function because it
348 *  is to the detriment of stability to have multiple threads attempting to
349 *  open/write/close the same file at the same time!  :-)
350 *
351 */
UTIL_log_result(peer_info_t * p)352 void UTIL_log_result(peer_info_t *p)
353 {
354   FILE *fp   = NULL;    /* file pointer */
355 
356   char *buf  = NULL;    /* working buffer */
357   char *date = NULL;    /* date/time stamp */
358 
359 
360   date = UTIL_get_date();
361   buf  = xmalloc(SPF_MAX_DEBUG);
362   *(date + (strlen(date) - 1)) = '\0';
363 
364   if (p->spf_ver == 0)
365   {
366     p->spf_ver = SPF_VERSION;
367   }
368 
369   xpthread_mutex_lock(&util_mutex);
370 
371   snprintf(buf, SPF_MAX_DEBUG,
372     "[%s] result: %s :: %s [%s], ver: %i, depth: %i, error: [%s]\n",
373     date, p->spf_result[p->RES].s, p->from,
374     p->r_ip, p->spf_rlevel, p->spf_ver, p->error);
375 
376   if ((fp = fopen(OUTPUT_LOG_FILE, "a")) != NULL)
377   {
378     fprintf(fp, "%s", buf);
379     fclose(fp);
380   }
381 
382   xpthread_mutex_unlock(&util_mutex);
383 
384   xfree(date);
385   xfree(buf);
386 
387   return;
388 }
389 
390 
391 /* UTIL_strndup
392 *
393 *  Author: James Couzens <jcouzens@codeshare.ca>
394 *
395 *  Date:   12/25/03
396 *
397 *  Desc:
398 *          n bytes are allocated and then filled with \0 chars.  Char s
399 *  is copied over to the allocated memory writing n -1 bytes leaving the
400 *  new string NULL terminated.  This new string is returned upon success
401 *  and NULL upon failure.
402 *
403 */
UTIL_strndup(const char * s,const size_t n)404 char *UTIL_strndup(const char *s, const size_t n)
405 {
406   size_t i = 0;            /* length of s */
407 
408   char *ret_ptr = NULL;    /* return buffer */
409 
410 
411   if (!s || (n <= 0))
412   {
413     xvprintf("[%i] Passed string is NULL.  Abort!.\n", i);
414 
415     return(NULL);
416   }
417 
418   xvprintf("called with string: [%s] of len: %i\n", s, n);
419 
420   if ((i = (strlen(s) + 1)) > n)
421   {
422     ret_ptr = xmalloc(n);
423     xvprintf("Allocated %u bytes of memory.\n", n);
424     memcpy(ret_ptr, s, (n - 1));
425   }
426   else
427   {
428     ret_ptr = xmalloc(i);
429     xvprintf("Allocated %u bytes of memory.\n", i);
430     memcpy(ret_ptr, s, (i - 1));
431   }
432 
433   xvprintf("leaving func; returning string: [%s]\n", ret_ptr);
434 
435   return(ret_ptr);
436 }
437 
438 
439 /* xstrdup
440 *
441 *  Author: Patrick Earl (http://patearl.net/)
442 *          Adapted from xstrndup()
443 *
444 *  Date:   02/04/04
445 *
446 *  Desc:
447 *          strlen(s)+1 bytes are allocated and s is copied into the
448 * freshly allocated memory.  If the allocation or copy fails, NULL
449 * NULL will be returned.
450 *
451 */
UTIL_strdup(const char * s)452 char *UTIL_strdup(const char *s)
453 {
454   char *ret_ptr = NULL;    /* return buffer */
455 
456 
457   if (s == NULL)
458   {
459     xepprintf("Passed string is NULL.  Abort!.\n");
460 
461     return(NULL);
462   }
463 
464   if ((ret_ptr = strdup(s)) == NULL)
465   {
466     xepprintf("Unable to allocate memory\n");
467   }
468 
469   xvprintf("leaving func; returning string: [%s]\n", ret_ptr);
470 
471   return(ret_ptr);
472 }
473 
474 
475 /* UTIL_malloc
476 *
477 *  Author: Travis Anderson <travis@anthrax.ca>
478 *  Author: James Couzens <jcouzens@codeshare.ca>
479 *
480 *  Date:   02/17/04
481 *
482 *  Desc:
483 *         Wrapper for malloc.  Upon success, behaves as malloc does.
484 *  Wrapper functionality is to print an error message and exit upon failure.
485 *
486 */
UTIL_malloc(const int32_t n,const char * file,int32_t line,const char * func)487 void *UTIL_malloc(const int32_t n, const char *file, int32_t line,
488   const char *func)
489 {
490   void *x = malloc(n);
491 
492   if (x == NULL)
493   {
494     xvprintf("Unable to allocate %i bytes at %s:%i in %s\n",
495       n, file, line, func);
496 
497     /*
498      * Be advised this is the only place I do this, because quite
499      * honestly, if this library can't get a few bytes of memory,
500      * your mailserver segfaulting as a result of this exit, or
501      * quitting or whatever is the LEAST of your problems! - James
502     */
503     exit(0);
504   }
505 
506 #ifdef _WITH_PARANOID_MALLOC
507   memset(x, '\0', n);
508 #endif /* _WITH_PARANOID_MALLOC */
509 
510   return(x);
511 }
512 
513 
514 /* UTIL_realloc
515 *
516 *  Author: Travis Anderson <travis@anthrax.ca>
517 *  Author: James Couzens <jcouzens@codeshare.ca>
518 *
519 *  Date:   02/17/04
520 *
521 *  Desc:
522 *         Wrapper for realloc.  If 'p' is NULL, allocates memory via a call to
523 *  UTIL_malloc, otherwise if 'x' is assigned successfully, the behaviour is
524 *  identical to realloc.  Wrapper functionality is to print an error message
525 *  and exit upon failure.
526 *
527 */
UTIL_realloc(void * p,const int32_t n,const char * file,const int32_t line,const char * func)528 void *UTIL_realloc(void *p, const int32_t n, const char *file,
529   const int32_t line, const char *func)
530 {
531   void *x = NULL;  /* working pointer */
532 
533 
534   if (p == NULL)
535   {
536     return(UTIL_malloc(n, file, line, func));
537   }
538 
539   x = realloc(p, n);
540   if (x == NULL)
541   {
542     xvprintf("Unable to reallocate %i bytes at %s:%i in %s; " \
543       "original address 0x%x\n", n, file, line, func, (uintptr_t)p);
544 
545      exit(0);
546   }
547 
548   return(x);
549 }
550 
551 
552 /* UTIL_free
553 *
554 *  Author: Travis Anderson <travis@anthrax.ca>
555 *  Author: James Couzens <jcouzens@codeshare.ca>
556 *
557 *  Date:   02/17/04
558 *
559 *  Desc:
560 *         Wrapper for free.  Upon success, behaves as free does.
561 *  Wrapper functionality is to print an error message and exit upon
562 *  failure.
563 *
564 */
UTIL_free(void * p,const char * file,const int32_t line,const char * func)565 void UTIL_free(void *p, const char *file, const int32_t line, const char *func)
566 {
567   if (p == NULL)
568   {
569     xvprintf("Unable to free() on NULL pointer at %s:%i in %s; " \
570       "address 0x%x.\n", file, line, func, (uintptr_t)p);
571 
572     return;
573   }
574 
575   xvprintf("Free address 0x%x by %s on line %i [%s]\n",
576     (uintptr_t)p, func, line, file);
577 
578   free(p);
579 
580   return;
581 }
582 
583 
584 /* UTIL_index
585 *
586 *  Author:  James Couzens <jcouzens@codeshare.ca>
587 *
588 *  Date:   12/19/03
589 *
590 *  Desc:
591 *          s is walked until c is found, at which time i is returned
592 *  which is the number of bytes from the left it walked until c was
593 *  found (not including c its self);
594 *
595 *  Date:   09/01/04 - Roger Moser
596 *
597 *  Desc:
598 *          The return upon an error (s being NULL for exmaple) is now
599 *  -1 because returning 0 was really ambiguous.
600 *
601 *  Returns: -1 upon error
602 *  Returns: 0 upon no match
603 *  Returns: > 0 upon success which is the number of bytes from the left
604 *           the string was walked until 'c' was found.
605 *
606 */
UTIL_index(const char * s,const char c)607 int16_t UTIL_index(const char *s, const char c)
608 {
609   int16_t i = 0;     /* utility */
610 
611 
612   if (s == NULL)
613   {
614     xepprintf("passed a NULL string.  Abort!\n");
615 
616     return(-1);
617   }
618 
619   xvprintf("called with string: [%s]; char: %c\n", s, c);
620 
621   i = 0;
622   while (*s)
623   {
624     if (*s == c)
625     {
626       xvprintf("Found search char: (%c); Returning: (%i)\n", *s, i);
627 
628       return(i);
629     }
630     i++;
631     s++;
632   }
633 
634   xpprintf("leaving func\n");
635 
636   return(0);
637 }
638 
639 
640 /* UTIL_split_str
641 *
642 *  Author:  James Couzens <jcouzens@codeshare.ca>
643 *
644 *  Date:   01/21/04
645 *
646 *  Desc:
647 *          s is walked through to find 'delim' until it finds 'delim'
648 *  num times.  Upon final match it returns the remainder of the string
649 *  in a newly allocated buffer.  Upon failure returns NULL.
650 *
651 *  Date:   09/01/04 - Roger Moser
652 *
653 *  Desc:
654 *          'cp' was not being freed upon exiting the function when 'c'
655 *  was not found within the string.
656 *
657 */
UTIL_split_str(const char * s,const char c,const u_int8_t num)658 char *UTIL_split_str(const char *s, const char c, const u_int8_t num)
659 {
660   u_int8_t i = 0;       /* utility */
661 
662   char *cp  = NULL;     /* copy buffer of s */
663   char *p   = NULL;     /* pointer to copy */
664   char *ret = NULL;     /* return buffer */
665 
666 
667   if (s == NULL)
668   {
669     xepprintf("passed a NULL string.  Abort!\n");
670 
671     return(NULL);
672   }
673 
674   xvprintf("called with string: [%s]; char (%c); int: (%i)\n",
675     s, c, num);
676 
677   p = cp = xstrndup(s, SPF_MAX_STR);
678 
679   i = 0;
680   while(*p)
681   {
682     if (*p == c)
683     {
684       i++;
685       if (i == num)
686       {
687         p++;
688         ret = xstrndup(p, SPF_MAX_STR);
689 
690         xfree(cp);
691 
692         xvprintf("returning: %s\n", ret);
693 
694         return(ret);
695       }
696     }
697     p++;
698   }
699 
700   xfree(cp);
701   xvprintf("[%i] returning NULL\n", i);
702 
703   return(NULL);
704 }
705 
706 
707 /* UTIL_split_strr
708 *
709 *  Author:  James Couzens <jcouzens@codeshare.ca>
710 *
711 *  Date:   01/30/04
712 *
713 *  Desc:
714 *          s is walked to the end of its self, and then is walked back
715 *  towards the beginning of the string until it has found 'c' the
716 *  delimiter, 'num' times.  Upon success memory is allocated and the
717 *  remainder of s is returned.  Upon failure NULL is returned.
718 *
719 *  Date:    09/01/04 - Roger Moser
720 *
721 *  Desc:
722 *           Replaced 'p = (char *)&(s[strlen(s) - 1]);' with a more
723 *  optimized 'p = strchr(s, '\0') - 1;'.  Also applied a fix so that
724 *  the function properly returns NULL when 's' is an empty string.
725 *
726 */
UTIL_split_strr(const char * s,const char c,const u_int8_t num)727 char *UTIL_split_strr(const char *s, const char c, const u_int8_t num)
728 {
729   u_int8_t i = 0;      /* number of times delim (c) is found */
730 
731   char *p   = NULL;    /* pointer to the last character of s before the \0 */
732   char *ret = NULL;    /* return buffer */
733 
734 
735   if ((s == NULL) || (*s == '\0'))
736   {
737     xepprintf("passed a NULL string.  Abort!\n");
738 
739     return(NULL);
740   }
741 
742   xvprintf("called with [%s]\n", s);
743 
744 
745   /* assign 'p' to the last char before the null termination of 's'*/
746   /*p = (char *)&(s[strlen(s) - 1]);*/
747   p = (strchr(s, '\0') - 1);
748 
749   i = 0;
750   while(p != s)
751   {
752     if (*p == c)
753     {
754       i++;
755       if (i == num)
756       {
757         if (*p == '.')
758         {
759           p++; /* don't want that period */
760         }
761 
762         ret = xstrdup(p);
763 
764         xvprintf("delimiter found (%i) times; returning [%s].\n", i, ret);
765 
766         return(ret);
767       }
768     }
769     p--;
770   }
771 
772   xvprintf("delimiter (%c) found (%u) times; returing NULL\n", c, i);
773 
774   return(NULL);
775 }
776 
777 
778 /* UTIL_count_delim
779 *
780 *  Author:  James Couzens <jcouzens@codeshare.ca>
781 *
782 *  Date:   01/21/04
783 *
784 *  Desc:
785 *          s is walked through and each time 'delim' is found a counter is
786 *  incremented and once complete, that integer is returned to the calling
787 *  function.  Specifically for the purposes of this project i is limited by its
788 *  type to 255.
789 *
790 *  Returns: > 0 && <= 255 upon success
791 *  Returns: 0 upon failure
792 *
793 */
UTIL_count_delim(const char * s,const char c)794 u_int8_t UTIL_count_delim(const char *s, const char c)
795 {
796   u_int8_t i = 0;    /* utility */
797 
798 
799   if (s == NULL)
800   {
801     xepprintf("passed a NULL string.  Abort!\n");
802 
803     return(0);
804   }
805 
806   while (*s && i < SPF_MAX_DELIM)
807   {
808     if (*s == c)
809     {
810       i++;
811     }
812     s++;
813   }
814 
815   xvprintf("found (%i) number of delimiters; returning.\n", i);
816 
817   return(i);
818 }
819 
820 
821 /* UTIL_is_spf_delim
822 *
823 *  Author:  James Couzens <jcouzens@codeshare.ca>
824 *
825 *  Date:   01/21/04
826 *
827 *  Desc:
828 *          c is compared against all the valid spf delimiters and
829 *  in the case of a SPF_PASS, the function returns true, otherwise it will
830 *  return false.
831 *
832 */
UTIL_is_spf_delim(const char c)833 SPF_BOOL UTIL_is_spf_delim(const char c)
834 {
835   if (!c)
836   {
837     xepprintf("called with a NULL char!  Aborting check.\n");
838 
839     return(SPF_FALSE);
840   }
841 
842   xvprintf("called with char (%c)\n", c);
843 
844   if (c == '.' ||
845       c == '-' ||
846       c == '+' ||
847       c == ',' ||
848       c == '|' ||
849       c == '_')
850   {
851     xpprintf("leaving func; returning SPF_FALSE\n");
852 
853     return(SPF_TRUE);
854   }
855 
856   xpprintf("leaving func; returning SPF_FALSE\n");
857 
858   return(SPF_FALSE);
859 }
860 
861 
862 /* UTIL_is_spf_result
863 *
864 *  Author:  James Couzens <jcouzens@codeshare.ca>
865 *
866 *  Date:   01/27/04
867 *
868 *  Desc:
869 *          c is compared against all the valid spf prefixes and
870 *  will return SPF_TRUE in the event one is found.  Returns SPF_FALSE
871 *  when no valid prefix is found or c is empty.
872 *
873 */
UTIL_is_spf_result(const char c)874 SPF_BOOL UTIL_is_spf_result(const char c)
875 {
876   if (!c)
877   {
878     xpprintf("passed a NULL or empty char!\n");
879 
880     return(SPF_FALSE);
881   }
882 
883   xvprintf("called with char (%c)\n", c);
884 
885   if (c == '+' || c == '-' || c == '~' || c == '?')
886   {
887     xpprintf("leaving func; returning SPF_TRUE\n");
888 
889     return(SPF_TRUE);
890   }
891 
892   xpprintf("leaving func; returning SPF_FALSE\n");
893 
894   return(SPF_FALSE);
895 }
896 
897 
898 /* UTIL_is_macro
899 *
900 *  Author:  James Couzens <jcouzens@codeshare.ca>
901 *
902 *  Date:   01/30/04
903 *
904 *  Desc:
905 *          s is walked through in search of a macro which consist of
906 *  %{?}, ? being an SPF_UNKNOWN number of chars in between.  An instance of
907 *  a macro is searched for.  Upon success SPF_TRUE is returned.  Upon failure
908 *  to find a macro, SPF_FALSE is returned.
909 *
910 */
UTIL_is_macro(const char * s)911 SPF_BOOL UTIL_is_macro(const char *s)
912 {
913   if (s == NULL)
914   {
915     xepprintf("passed a NULL string.  Abort!\n");
916 
917     return(SPF_FALSE);
918   }
919 
920   xvprintf("called with string [%s]\n", s);
921 
922   while (*s++)
923   {
924     if ((*s == '%') && (*(s + 1) == '{'))
925     {
926       if (strstr(s, "}"))
927       {
928         xpprintf("leaving func; returning SPF_TRUE\n");
929 
930         return(SPF_TRUE);
931       }
932     }
933   }
934 
935   xpprintf("leaving func; returning SPF_FALSE\n");
936 
937   return(SPF_FALSE);
938 }
939 
940 
941 /* UTIL_mx_cmp
942 *
943 *  Author: James Couzens <jcouzens@codeshare.ca>
944 *
945 *  Date:   01/02/04
946 *
947 *  Desc:
948 *          Using the domain name found in the passed peer_info
949 *  structure, the associated T_MX records are looked up and then their
950 *  respective hostnames resolved (if they happen to be IP addresses the
951 *  resolver library takes care of this. <3).  Each IP is compared
952 *  against the remote peer's IP (from peer_info).  Upon success returns
953 *  SPF_TRUE, upon failure returns SPF_FALSE.
954 *
955 */
UTIL_mx_cmp(peer_info_t * p,const char * s,const int8_t cidr)956 SPF_BOOL UTIL_mx_cmp(peer_info_t *p, const char *s, const int8_t cidr)
957 {
958   SPF_BOOL MX_SPF_MATCH;       /* true / false */
959 
960   char *rr_data   = NULL;     /* record */
961   char *token     = NULL;     /* token for splitting records */
962   char *token_ptr = NULL;     /* working pointer when tokenizing */
963   char *peer_ip   = NULL;     /* remote host converted to string */
964 
965 
966   MX_SPF_MATCH = SPF_FALSE;
967 
968   token_ptr = rr_data;
969 
970   if ((rr_data = DNS_query(p, s, T_MX, NULL)) == NULL)
971   {
972     xpprintf("SPF_ERROR parsing DNS Query\n");
973 
974     return(SPF_FALSE);
975   }
976 
977   xvprintf("rr_data is: [%s]\n", rr_data);
978 
979   peer_ip = xstrndup(inet_ntoa(p->addr), 16);
980   token   = strtok_r(rr_data, " ", &token_ptr);
981 
982   while (token != NULL)
983   {
984     xvprintf("TOKEN: [%s]\n", token);
985 
986     if (UTIL_validate_hostname(p, token, cidr) == SPF_TRUE)
987     {
988       xvprintf("%s validated via [%s]\n", p->from, token);
989 
990       MX_SPF_MATCH = SPF_TRUE;
991       UTIL_assoc_prefix(p, SPF_PASS, NULL);
992       token = NULL;
993     }
994     else
995     {
996       token = strtok_r(NULL, " ", &token_ptr);
997     }
998   }
999 
1000   xfree(peer_ip);
1001   xfree(rr_data);
1002 
1003   return(MX_SPF_MATCH);
1004 }
1005 
1006 
1007 /* UTIL_a_cmp
1008 *
1009 *  Author: James Couzens <jcouzens@codeshare.ca>
1010 *
1011 *  Date:   01/02/04
1012 *
1013 *  Desc:
1014 *          Calls gethostbyname to grab all records associated with a the
1015 *  hostname contained within s.  On success returns SPF_TRUE, on SPF_ERROR
1016 *  returns SPF_FALSE.
1017 *
1018 *  Note:
1019 *          We need to define and set a limit on the number of recursive
1020 *  lookups.  I recall seeing this in the RFC, we should discuss this.
1021 *
1022 */
UTIL_a_cmp(peer_info_t * p,const char * s,const int8_t cidr)1023 SPF_BOOL UTIL_a_cmp(peer_info_t *p, const char *s, const int8_t cidr)
1024 {
1025   int16_t pos = 0;                    /* position in DNS packet */
1026 
1027   int tmp_errno = 0;                  /* temporary errno placeholder */
1028 
1029   size_t s_len = 0;                   /* length of s (hostname) */
1030 
1031   char *rr_data   = NULL;             /* data storage for DNS query */
1032   char *token_ptr = NULL;             /* token pointer */
1033   char *gbuf      = NULL;             /* buf for reentrant gethostbyname call */
1034   char *copy      = NULL;             /* copy of s */
1035   char *cp        = NULL;             /* pointer to copy of s */
1036 
1037   char **a;                           /**/
1038 
1039   struct hostent *hp    = NULL;       /* hostent structure */
1040   struct hostent tmp_hp = {0};        /* temporary hostent (xgethostbyname) */
1041 
1042   policy_addr_t policy_addr = {0};    /* used during CIDR comparisons */
1043 
1044 
1045   if (s == NULL)
1046   {
1047     xepprintf("Passed string is NULL.  Abort!.\n");
1048 
1049     return(SPF_FALSE);
1050   }
1051 
1052   xvprintf("called with [%s] and cidr: %i\n", s, cidr);
1053 
1054   gbuf      = xmalloc(SPF_MAX_GHBNR_DBUF);
1055   s_len     = strlen(s);
1056   token_ptr = rr_data;
1057 
1058   /* we're dealing with a:some.hostname/cidr */
1059   if ((s_len > 1) && (*(s + 1) == ':'))
1060   {
1061     cp = copy = xstrndup(s, (s_len + 1));
1062 
1063     if (cidr != 32)
1064     {
1065       /* We don't want a netmask, so lets remove it */
1066       cp[s_len - 3] = '\0';
1067     }
1068 
1069     if ((pos = UTIL_index(cp, ':')) <= 0)
1070     {
1071       xeprintf("ERROR parsing passed mechanism token [%s]\n", cp);
1072 
1073       xfree(copy);
1074       xfree(gbuf);
1075 
1076       return(SPF_FALSE);
1077     }
1078 
1079     /* move passed the mechanism text */
1080     cp += (pos + 1);
1081   }
1082   else
1083   {
1084     cp = copy = xstrndup(p->current_domain, SPF_MAX_HNAME);
1085   }
1086 
1087   if ((hp = xgethostbyname(cp, &tmp_hp, gbuf, SPF_MAX_GHBNR_DBUF,
1088     &tmp_errno)) != NULL)
1089   {
1090     for (a = hp->h_addr_list; *a; a++)
1091     {
1092       memcpy(&policy_addr.addr.s_addr, *a, SIZEOF(struct in_addr));
1093 
1094       xvprintf("IN ADDR; Checking: %lu\n", policy_addr.addr.s_addr);
1095 
1096       /* cidr is assumed checked by the calling function! */
1097       policy_addr.cidr = cidr;
1098 
1099       if (UTIL_cidr_cmp(&policy_addr, &p->addr) == SPF_TRUE)
1100       {
1101         *a = NULL;
1102 
1103         UTIL_assoc_prefix(p, SPF_PASS, NULL);
1104 
1105         xfree(copy);
1106         xfree(gbuf);
1107         xgethostbyname_free();    /* unlock mutex */
1108 
1109         return(SPF_TRUE);
1110       }
1111     } /* for */
1112 
1113     for (a = hp->h_aliases; *a; a++)
1114     {
1115       memcpy(&policy_addr.addr.s_addr, *a, SIZEOF(struct in_addr));
1116 
1117       xvprintf("IN CNAME; Checking: %lu\n", policy_addr.addr.s_addr);
1118 
1119       /* cidr is assumed checked by the calling function! */
1120       policy_addr.cidr = cidr;
1121 
1122       if (UTIL_cidr_cmp(&policy_addr, &p->addr) == SPF_TRUE)
1123       {
1124         *a = NULL;
1125 
1126         UTIL_assoc_prefix(p, SPF_PASS, NULL);
1127 
1128         xfree(copy);
1129         xfree(gbuf);
1130         xgethostbyname_free();    /* unlock mutex */
1131 
1132         return(SPF_TRUE);
1133       }
1134     } /* for */
1135   } /* if .. xgethostbyname */
1136   else
1137   {
1138     xvprintf("No address associated with hostname [%s]; Reason: %s\n",
1139       s, hstrerror(tmp_errno));
1140   }
1141 
1142   xfree(copy);
1143   xfree(gbuf);
1144   xgethostbyname_free();
1145 
1146   return(SPF_FALSE);
1147 }
1148 
1149 
1150 /* UTIL_ptr_cmp
1151 *
1152 *  Author: James Couzens <jcouzens@codeshare.ca>
1153 *
1154 *  Date:   01/08/04
1155 *
1156 *  Desc:
1157 *          gethostbyaddr is broken in linux.  ?->h_aliases is not NULL,
1158 *  however, it doesn't contain any valid pointers either.  It grabs the
1159 *  first (and of course this is random) hostname it gets and thats all.
1160 *  As tested in FreeBSD however this call works.  At any rate, I've
1161 *  written a function to deal with this called DNS_ptr_answer.  From here
1162 *  that function is called which handles the reverse lookups and then
1163 *  attempts to "validate" each returned hostname by in turn looking it up
1164 *  to confirm if the ip address SPF_MATCHes.  Returns SPF_TRUE on succes, and SPF_FALSE
1165 *  on failure.
1166 *
1167 *  When passed SPF_TRUE copies into p->ptr_mhost, when SPF_FALSE copies
1168 *  into p->r_vhname
1169 *
1170 *  Note:
1171 *          We need to define and set a limit on the number of recursive
1172 *  lookups.  I recall seeing this in the RFC, we should discuss this.
1173 *
1174 */
UTIL_ptr_cmp(peer_info_t * p,const char * s)1175 SPF_BOOL UTIL_ptr_cmp(peer_info_t *p, const char *s)
1176 {
1177   char *ptr_addr = NULL;    /* reversed address into PTR format */
1178   char *tmp_ptr  = NULL;    /* utility pointer */
1179 
1180 
1181   if (s == NULL)
1182   {
1183     xepprintf("Passed string is NULL.  Abort!\n");
1184 
1185     return(SPF_FALSE);
1186   }
1187 
1188   xvprintf("called with [%s]\n", s);
1189 
1190   /* reverse of rpeer */
1191   ptr_addr = UTIL_rev_addr(p->r_ip);
1192 
1193   xvprintf("address: %s\n", ptr_addr);
1194 
1195   if ((s = strstr(s, ":")) != NULL)
1196   {
1197     s++;
1198     tmp_ptr = xstrndup(s, (strlen(s) + 1));
1199   }
1200   else
1201   {
1202     tmp_ptr = xstrndup(p->current_domain, SPF_MAX_HNAME);
1203   }
1204 
1205   if (DNS_query(p, ptr_addr, T_PTR, tmp_ptr) != (char *)SPF_TRUE)
1206   {
1207     xvprintf("Failed to pass SPF PTR mechanism check:%s\n", "");
1208     xvprintf("the domain pointed to by %s is not a valid subdomain of %s\n",
1209       ptr_addr, tmp_ptr);
1210 
1211     xfree(ptr_addr);
1212     xfree(tmp_ptr);
1213 
1214     return(SPF_FALSE);
1215   }
1216 
1217   xvprintf("PTR lookup succeeded: [%s]:[%s]\n", p->rs,
1218     p->error);
1219 
1220   xfree(ptr_addr);
1221   xfree(tmp_ptr);
1222 
1223   return(SPF_TRUE);
1224 }
1225 
1226 
1227 /* UTIL_get_policy_mech
1228 *
1229 *  Author: James Couzens <jcouzens@codeshare.ca>
1230 *
1231 *  Date:   12/19/03
1232 *
1233 *  Desc:
1234 *          Examins 's' and attempts to determine which SPF_MECHANISM
1235 *  enumeration the matches a portion of the contents of the string.
1236 *
1237 *  Because this library only (currently) supports SPFv1 the check
1238 *  for an SPFv1 version is very stringent.
1239 *
1240 */
UTIL_get_policy_mech(const char * s)1241 SPF_MECHANISM UTIL_get_policy_mech(const char *s)
1242 {
1243   if (s == NULL || !s)
1244   {
1245     xepprintf("passed a NULL string. Abort!\n");
1246 
1247     return(NO_POLICY);
1248   }
1249 
1250   xvprintf("called with: [%s]\n", s);
1251 
1252   if (strncmp(s, "v=spf1", 6) == 0)
1253   {
1254     xvprintf("leaving func; returning %i (VERSION)\n", VERSION);
1255 
1256     return(VERSION);
1257   }
1258   else if (strncmp(s, "ip4:", 4) == 0 )
1259   {
1260     xvprintf("leaving func; returning %i (IP4)\n", IP4);
1261 
1262     return(IP4);
1263   }
1264   else if (strncmp(s, "ip6:", 4) == 0)
1265   {
1266     xvprintf("leaving func; returning %i (IP6)\n", IP6);
1267 
1268     return(IP6);
1269   }
1270   else if (strncmp(s, "all", 3) == 0)
1271   {
1272     xvprintf("leaving func; returning %i (ALL)\n", ALL);
1273 
1274     return(ALL);
1275   }
1276   else if (strncmp(s, "mx", 2) == 0)
1277   {
1278     xvprintf("leaving func; returning %i (MX)\n", MX);
1279 
1280     return(MX);
1281   }
1282   else if (strncmp(s, "a:", 2) == 0 || (*s == 'a' && *(s + 1) == '/')
1283     ||  ((*s == 'a') && !*(s + 1)))
1284   {
1285     xvprintf("leaving func; returning %i (A)\n", A);
1286 
1287     return(A);
1288   }
1289   else if (strncmp(s, "ptr", 3) == 0)
1290   {
1291     xvprintf("leaving func; returning %i (PTR)\n", PTR);
1292 
1293     return(PTR);
1294   }
1295   else if (strncmp(s, "include:", 7) == 0)
1296   {
1297     xvprintf("leaving func; returning %i (INCLUDE)\n", INCLUDE);
1298 
1299     return(INCLUDE);
1300   }
1301   else if (strncmp(s, "exists:", 6) == 0)
1302   {
1303     xvprintf("leaving func; returning %i (EXISTS)\n", EXISTS);
1304 
1305     return(EXISTS);
1306   }
1307   else if (strncmp(s, "redirect=", 9) == 0)
1308   {
1309     xvprintf("leaving func; returning %i (REDIRECT)\n", REDIRECT);
1310 
1311     return(REDIRECT);
1312   }
1313   else if (strncmp(s, "exp=", 3) == 0)
1314   {
1315     xvprintf("leaving func; returning %i (EXPLAIN)\n", EXPLAIN);
1316 
1317     return(EXPLAIN);
1318   }
1319   else if (strncmp(s, "default", 7) == 0)
1320   {
1321     xvprintf("leaving func; returning %i (DEFAULT)\n", DEFAULT);
1322 
1323     return(DEFAULT);
1324   }
1325   else if (strstr(s, ":"))
1326   {
1327     xvprintf("leaving func; returning %i (UNMECH)\n", UNMECH);
1328 
1329     return(UNMECH);
1330   }
1331 
1332   xpprintf("leaving func; returning NO_POLICY\n");
1333   return(NO_POLICY);
1334 }
1335 
1336 
1337 /* UTIL_assoc_prefix
1338 *
1339 *  Author: James Couzens <jcouzens@codeshare.ca>
1340 *
1341 *  Date:   01/28/04
1342 *
1343 *  Desc:
1344 *          Examins s and attempts to determine which SPF_MECHANISM_PREFIX
1345 *  enumeration the string SPF_MATCHes based on the its contents (which is a
1346 *  single char.  Upon a SPF_PASS it stores the appropriate value inside of
1347 *  the passed peer_info structure.  SPF_TRUE upon success, SPF_FALSE upon failure.
1348 *  Also upon failure SPF_ERROR (SPF_RESULT_TYPE) is stored in peer_info.
1349 *
1350 *
1351 */
UTIL_assoc_prefix(peer_info_t * p,SPF_RESULT res,const char * s)1352 SPF_BOOL UTIL_assoc_prefix(peer_info_t *p, SPF_RESULT res, const char *s)
1353 {
1354   int16_t pos = 0;      /* position in s string */
1355 
1356 
1357    /*
1358    * for whatever result happens to match the case, a debug string is
1359    * printed (if compiled with), the level of recursion is made known through
1360    * this. An appropriate SPF_RESULT is assigned to the passed peer_info_t
1361    * structure's 'p->RES' variable.  In addition a short SPF_RESULT string
1362    * (here is an example use of the spf_result_t structure spoke of in SPF_init)
1363    * is stored in 'p->rs', and finally an error string is stored in
1364    * 'p->error'.  This string encompasses all SPF query return types though,
1365    * it is not only populated upon a parse error, so don't hesitate to reference
1366    * it when looking for a verbose explanation as to the result of an SPF parse.
1367   */
1368 
1369   if (s != NULL)
1370   {
1371     xvprintf("Entering function (%i) [%s]\n", res, s);
1372 
1373     /* support for old school deprecated mechanism "default" */
1374     if (strncmp(s, "default", 7) == 0 && (pos = UTIL_index(s, '=')) > 0)
1375     {
1376       s += (pos + 1); /* move past default= */
1377       if (strncmp(s, "deny", 4) == 0)
1378       {
1379         xvprintf("Stored SPF_H_FAIL (%i) (%i)\n",
1380           res, SPF_H_FAIL);
1381 
1382         p->RES = SPF_H_FAIL;
1383         p->rs  = p->spf_result[SPF_H_FAIL].s;
1384 
1385         snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1386           p->rs, p->last_m);
1387 
1388         return(SPF_TRUE);
1389       }
1390       else if (strncmp(s, "pass", 4) == 0)
1391       {
1392         xvprintf("Stored SPF_PASS (%i) (%i)\n",
1393           res, SPF_PASS);
1394 
1395         p->RES = SPF_PASS;
1396         p->rs  = p->spf_result[SPF_PASS].s;
1397 
1398         snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1399           p->rs, p->last_m);
1400 
1401         return(SPF_TRUE);
1402       }
1403       else if (strncmp(s, "softdeny", 8) == 0)
1404       {
1405         xvprintf("Stored SPF_S_FAIL (%i) (%i)\n",
1406           res, SPF_S_FAIL);
1407 
1408         p->RES = SPF_S_FAIL;
1409         p->rs  = p->spf_result[SPF_S_FAIL].s;
1410 
1411         snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1412           p->rs, p->last_m);
1413 
1414         return(SPF_TRUE);
1415       }
1416       else if (strncmp(s, "unknown", 7) == 0)
1417       {
1418         xvprintf("Stored SPF_NEUTRAL (%i) (%i)\n",
1419           res, SPF_NEUTRAL);
1420 
1421         p->RES = SPF_NEUTRAL;
1422         p->rs  = p->spf_result[SPF_NEUTRAL].s;
1423 
1424         snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1425           p->rs, p->last_m);
1426 
1427         return(SPF_TRUE);
1428       }
1429       else if (strncmp(s, "include", 7) == 0)
1430       {
1431         xvprintf("Stored SPF_UNKNOWN (%i) (%i)\n", res, SPF_UNKNOWN);
1432 
1433         p->RES = SPF_UNKNOWN;
1434         p->rs  = p->spf_result[SPF_UNKNOWN].s;
1435 
1436         snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1437           p->rs, p->last_m);
1438 
1439         return(SPF_TRUE);
1440       }
1441       else
1442       {
1443         xvprintf("Stored SPF_ERROR (%i) (%i)\n", res, SPF_ERROR);
1444 
1445         p->RES = SPF_UNKNOWN;
1446         p->rs  = p->spf_result[SPF_UNKNOWN].s;
1447 
1448         snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1449           p->rs, p->last_m);
1450 
1451         return(SPF_FALSE);
1452       }
1453     }
1454   }
1455 
1456   switch (res)
1457   {
1458     /* */
1459     case SPF_PASS:
1460     {
1461       xvprintf("Stored SPF_PASS (%i) (%i)\n", res, SPF_PASS);
1462 
1463       p->RES = SPF_PASS;
1464       p->rs  = p->spf_result[SPF_PASS].s;
1465 
1466       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1467         p->rs, p->last_m);
1468 
1469       return(SPF_TRUE);
1470     } /* SPF_PASS */
1471 
1472     /* */
1473     case SPF_NONE:
1474     {
1475       xvprintf("Stored SPF_NONE (%i) (%i)\n", res, SPF_NONE);
1476 
1477       p->RES = SPF_NONE;
1478       p->rs  = p->spf_result[SPF_NONE].s;
1479 
1480       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1481         p->rs, p->last_m);
1482 
1483       return(SPF_TRUE);
1484     } /* SPF_NONE */
1485 
1486     /* */
1487     case SPF_S_FAIL:
1488     {
1489       xvprintf("Stored SPF_S_FAIL (%i) (%i)\n", res, SPF_S_FAIL);
1490 
1491       p->RES = SPF_S_FAIL;
1492       p->rs  = p->spf_result[SPF_S_FAIL].s;
1493 
1494       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1495         p->rs, p->last_m);
1496 
1497       return(SPF_TRUE);
1498     } /* SPF_S_FAIL */
1499 
1500     /* */
1501     case SPF_H_FAIL:
1502     {
1503       xvprintf("Stored SPF_H_FAIL (%i) (%i)\n",
1504         res, SPF_H_FAIL);
1505 
1506       p->RES = SPF_H_FAIL;
1507       p->rs  = p->spf_result[SPF_H_FAIL].s;
1508 
1509       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1510         p->rs, p->last_m);
1511 
1512       return(SPF_TRUE);
1513     } /* SPF_H_FAIL */
1514 
1515     /* */
1516     case SPF_NEUTRAL:
1517     {
1518       xvprintf("Stored SPF_NEUTRAL (%i) (%i)\n",
1519         res, SPF_NEUTRAL);
1520 
1521       p->RES = SPF_NEUTRAL;
1522       p->rs  = p->spf_result[SPF_NEUTRAL].s;
1523 
1524       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1525         p->rs, p->last_m);
1526 
1527       return(SPF_TRUE);
1528     } /* SPF_NEUTRAL */
1529 
1530     /* */
1531     case SPF_UNKNOWN:
1532     {
1533       xvprintf("Stored SPF_UNKNOWN (%i) (%i)\n",
1534         res, SPF_UNKNOWN);
1535 
1536       p->RES = SPF_UNKNOWN;
1537       p->rs  = p->spf_result[SPF_UNKNOWN].s;
1538 
1539       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1540         p->rs, p->last_m);
1541 
1542       return(SPF_TRUE);
1543     } /* SPF_UNKNOWN */
1544 
1545     /* */
1546     case SPF_ERROR:
1547     {
1548       xvprintf("Stored SPF_ERROR (%i) (%i)\n",
1549         p, SPF_ERROR);
1550 
1551       p->RES = SPF_ERROR;
1552       p->rs  = p->spf_result[SPF_ERROR].s;
1553 
1554       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1555         p->rs, p->last_m);
1556 
1557       return(SPF_TRUE);
1558     } /* SPF_ERROR */
1559 
1560     /* */
1561     case SPF_UNMECH:
1562     {
1563       xvprintf("Stored SPF_UNMECH (%i) (%i)\n",
1564         res, SPF_UNMECH);
1565 
1566       p->RES = SPF_UNMECH;
1567       p->rs  = p->spf_result[SPF_UNMECH].s;
1568 
1569       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1570         p->rs, p->last_m);
1571 
1572       return(SPF_TRUE);
1573     } /* SPF_UNMECH */
1574 
1575     /* */
1576     default:
1577     {
1578       xvprintf("Stored SPF_PASS (%i) (%i)\n",
1579         res, SPF_PASS);
1580 
1581       p->RES = SPF_PASS;
1582       p->rs  = p->spf_result[SPF_PASS].s;
1583 
1584       snprintf(p->error, SPF_MAX_ERROR, "policy result: [%s] from rule [%s]",
1585         p->rs, p->last_m);
1586 
1587       return(SPF_TRUE);
1588     } /* default */
1589 
1590   } /* switch */
1591 
1592   xepprintf("leaving func; returning SPF_FALSE.\n");
1593 
1594   return(SPF_FALSE);
1595 }
1596 
1597 
1598 /* UTIL_get_mech_prefix
1599 *
1600 *  Author: James Couzens <jcouzens@codeshare.ca>
1601 *
1602 *  Date:   12/31/03
1603 *
1604 *  Desc:
1605 *          Examins s and attempts to determine which SPF_RESULT_P
1606 *  enumeration the string SPF_MATCHes based on the its contents (which is a
1607 *  single char.  Returns SPF_PASS if a valid prefix is not specified.
1608 *  Valid prefixes are: + (permit), - (deny), ~ (softfail) and ? (SPF_NEUTRAL).
1609 *
1610 */
UTIL_get_mech_prefix(peer_info_t * p,const char * s)1611 SPF_RESULT UTIL_get_mech_prefix(peer_info_t *p, const char *s)
1612 {
1613   int16_t pos = 0;    /* position in s */
1614 
1615 
1616   if (s == NULL)
1617   {
1618     xepprintf("passed a NULL string.  Abort!\n");
1619 
1620     return(SPF_ERROR);
1621   }
1622 
1623   xprintf("called with char: [%s]\n", s);
1624   snprintf(p->last_m, SPF_MAX_HNAME, "%s", s);
1625 
1626   switch (*s)
1627   {
1628     /* */
1629     case '+':
1630     {
1631       p->RES_P = SPF_PASS;
1632       xvprintf("leaving func; returning SPF_PASS [%s] %i\n", s, SPF_PASS);
1633 
1634       return(SPF_PASS);
1635     } /* '+' */
1636 
1637     /* */
1638     case '-':
1639     {
1640       p->RES_P = SPF_H_FAIL;
1641       xvprintf("leaving func; returning SPF_H_FAIL [%s] %i\n", s, SPF_H_FAIL);
1642 
1643        return(SPF_H_FAIL);
1644     } /* '-' */
1645 
1646     /* */
1647     case '?':
1648     {
1649       p->RES_P = SPF_NEUTRAL;
1650       xvprintf("leaving func; returning SPF_NEUTRAL [%s] %i\n",
1651         s, SPF_NEUTRAL);
1652 
1653       return(SPF_NEUTRAL);
1654     } /* '?' */
1655 
1656     /* */
1657     case '~':
1658     {
1659       p->RES_P = SPF_S_FAIL;
1660       xvprintf("leaving func; returning SPF_S_FAIL [%s] %i\n", s, SPF_S_FAIL);
1661 
1662       return(SPF_S_FAIL);
1663     } /* '~' */
1664 
1665     /* */
1666     default:
1667     {
1668       if (p->ALL == SPF_TRUE)
1669       {
1670         p->RES_P = SPF_NEUTRAL;
1671         xvprintf("leaving func; returning (def) SPF_NEUTRAL [%s] %i\n",
1672           s, SPF_NEUTRAL);
1673       }
1674       else
1675       {
1676         p->RES_P = SPF_PASS;
1677         xvprintf("leaving func; returning (def) SPF_PASS [%s] %i\n",
1678           s, SPF_PASS);
1679       }
1680 
1681       xvprintf("leaving func; returning (%i)\n", p->RES_P);
1682 
1683       return(p->RES_P);
1684    } /* default */
1685 
1686   } /* switch */
1687 
1688    /*
1689    * The code in the following statement is for the
1690    * deprecated mechanism "default" and is only here to attempt
1691    * to support exceptionally tardy early adopters whom likely
1692    * at this time no longer exist.  As such this code is not
1693    * present in the current development versions of this library
1694   */
1695   if ((pos = UTIL_index(s, '=')) > 0)
1696   {
1697     s += (pos + 1); /* move past default= */
1698 
1699     if (strncmp(s, "deny", 4) == 0)
1700     {
1701       p->RES_P = SPF_H_FAIL;
1702       xvprintf("leaving func; returning SPF_H_FAIL on [%s]\n", s);
1703 
1704       return(SPF_H_FAIL);
1705     }
1706     else if (strncmp(s, "pass", 4) == 0)
1707     {
1708       p->RES_P = SPF_PASS;
1709       xvprintf("leaving func; returning SPF_PASS on [%s]\n", s);
1710 
1711       return(SPF_PASS);
1712     }
1713     else if (strncmp(s, "softdeny", 8) == 0)
1714     {
1715       p->RES_P = SPF_S_FAIL;
1716       xvprintf("leaving func; returning SPF_S_FAIL on [%s]\n", s);
1717 
1718       return(SPF_S_FAIL);
1719     }
1720     else if (strncmp(s, "unknown", 7) == 0)
1721     {
1722       p->RES_P = SPF_NEUTRAL;
1723       xvprintf("leaving func; returning SPF_NEUTRAL on [%s]\n", s);
1724 
1725       return(SPF_NEUTRAL);
1726     }
1727     else
1728     {
1729       xvprintf("leaving func; returning SPF_NEUTRAL on [%s]\n", s);
1730 
1731       return(SPF_NEUTRAL);
1732     }
1733   }
1734 
1735   xvprintf("leaving func; returning SPF_ERROR on [%s]\n", s);
1736 
1737   return(SPF_ERROR);
1738 }
1739 
1740 
1741 /* UTIL_expand_ip
1742 *
1743 *  Author: James Couzens <jcouzens@codeshare.ca>
1744 *
1745 *  Date:   12/26/03
1746 *
1747 *  Desc:
1748 *          Expands an ip4 policy mechanism string into a policy_addr_t
1749 *  structure which it allocates memory for and then returns.  The RFC
1750 *  specifies that if a cidr block is not specified then /32 isused.
1751 *
1752 *  Both 'ip' and 'policy_addr' are dynamically allocated, and must be
1753 *  explicitly freed before any negative activity resulting in a NULL
1754 *  return response.
1755 *
1756 *  On success returns a pointer to a policy_addr_t structure, on
1757 *  SPF_ERROR returns NULL.
1758 *
1759 */
UTIL_expand_ip(const char * s)1760 policy_addr_t *UTIL_expand_ip(const char *s)
1761 {
1762   int8_t cidr = 0;                       /* temporary storage for netmask */
1763 
1764   size_t len = 0;                        /* length of string passed */
1765   size_t pos = 0;                        /* position of break points in string */
1766 
1767   char *ip = NULL;                       /* temporary storage for ip address */
1768 
1769   const char *token_ptr = NULL;          /* our position in the token */
1770 
1771   policy_addr_t *policy_addr = NULL;     /* ip/mask return structure */
1772 
1773 
1774   if (s == NULL)
1775   {
1776     xepprintf("passed a NULL string.  Abort!\n");
1777 
1778     return(NULL);
1779   }
1780 
1781   len = strlen(s);
1782   token_ptr = s;
1783 
1784   xvprintf("called with string: [%s]\n", token_ptr);
1785 
1786   if ((pos = UTIL_index(token_ptr, ':')) == 0)
1787   {
1788     xvprintf("SPF_ERROR: Unable to get position on token [%s]\n",
1789       token_ptr);
1790 
1791     return(NULL);
1792   }
1793 
1794   /* jump past the ip4: portion */
1795   token_ptr += (pos + 1);
1796 
1797   policy_addr = xmalloc(SIZEOF(policy_addr_t));
1798 
1799   /* jump past the ip4: portion (to get length of ip) */
1800   if ((pos = UTIL_index(token_ptr, '/')) == 0)
1801   {
1802     xvprintf("Unable to get position on token [%s], assuming /32 cidr " \
1803       "block\n", token_ptr);
1804 
1805     pos = strlen(token_ptr);
1806     cidr = 32;
1807   }
1808 
1809   /* allocate space and dump the ip address there */
1810   ip = xstrndup(token_ptr, (pos + 1));
1811 
1812   /* copy it over to the policy_addr structure */
1813   if ((inet_pton(AF_INET, ip, &policy_addr->addr)) == 0)
1814   {
1815     xvprintf("SPF_ERROR: inet_pton unable to convert ip to binary " \
1816       "[%s]\n", ip);
1817 
1818     xfree(policy_addr);
1819     xfree(ip);
1820 
1821     return(NULL);
1822   }
1823 
1824   if (cidr != 32)
1825   {
1826     token_ptr += (pos + 1);    /* skip over the forward slash */
1827     cidr = atoi(token_ptr);    /* convert the string to an integer */
1828   }
1829 
1830   if ((cidr < 0) || (cidr > 32))
1831   {
1832     xvprintf("ERROR: cidr violation (%u)\n", cidr);
1833 
1834     xfree(ip);
1835     xfree(policy_addr);
1836 
1837     return(NULL);
1838   }
1839 
1840   policy_addr->cidr = cidr;    /* copy it over to the policy_addr structure */
1841 
1842   xvprintf("CIDR: (%i) IP: [%s]\n",
1843     policy_addr->cidr, inet_ntoa(policy_addr->addr));
1844 
1845   xfree(ip);
1846 
1847   return(policy_addr);
1848 }
1849 
1850 
1851 /*  UTIL_is_sid
1852 *
1853 *  Author: James Couzens <jcouzens@codeshare.ca>
1854 *
1855 *  Date:   12/07/04
1856 *
1857 *  Desc:
1858 *          Passed the entire T_TXT record from a DNS query and performs
1859 *  a check to see if the record is a valid SenderID record.
1860 *
1861 *  Return:
1862 *          SPF_TRUE - T_TXT record contained a valid SenderID record
1863 *          SPF_FALSE - T_TXT record did not contain a valid SenderID record
1864 *
1865 */
UTIL_is_sid(const char * s)1866 SPF_BOOL UTIL_is_sid(const char *s)
1867 {
1868   char *tmp = NULL;
1869 
1870 
1871   if (s && (s != NULL))
1872   {
1873     xvprintf("called with: [%s]\n", s);
1874 
1875     if ((tmp = strstr(s, "v=spf2.0/pra")) != NULL)
1876     {
1877       xvprintf("discovered a SenderID record [%s]\n", tmp);
1878 
1879       return(SPF_TRUE);
1880     }
1881   }
1882 
1883   return(SPF_FALSE);
1884 }
1885 
1886 
1887 /*  UTIL_is_ip
1888 *
1889 *  Author: James Couzens <jcouzens@codeshare.ca>
1890 *
1891 *  Date:   01/18/04
1892 *
1893 *  Desc:
1894 *         Is the passed string (limited to between 0 - 255 by type)
1895 *  contains a valid ip address or not.  I'm not sure if this is useful
1896 *  anymore but at one point I was using it to tell if the passed macro
1897 *  contained an ip address or not when passed as a string.
1898 *
1899 */
UTIL_is_ip(const char * id)1900 SPF_BOOL UTIL_is_ip(const char *id)
1901 {
1902   u_int8_t i = 0;    /* utility */
1903 
1904   if (!id)
1905   {
1906     xepprintf("called without an IP address!\n");
1907 
1908     return(SPF_FALSE);
1909   }
1910 
1911   xvprintf("called with address: [%s]\n", id);
1912 
1913   while (*id)
1914   {
1915     if (*id == '.')
1916     {
1917       i++;
1918     }
1919     else if (isdigit(*id) == 0)
1920     {
1921       return(SPF_FALSE);
1922     }
1923     id++;
1924   }
1925 
1926   if (i == 3)
1927   {
1928     return(SPF_TRUE);
1929   }
1930 
1931   xpprintf("leaving func; returning SPF_FALSE\n");
1932 
1933   return(SPF_FALSE);
1934 }
1935 
1936 
1937 /* UTIL_rev_addr
1938 *
1939 *  Author: James Couzens <jcouzens@codeshare.ca>
1940 *
1941 *  Date:   01/04/04
1942 *
1943 *  Desc:
1944 *          Using a passed ip address it will reverse it and slap
1945 *  .in-addr.arpa on the end of it making it ready for a proper PTR query
1946 *
1947 *  Notes:  Write additional code to handle IP6 addresses
1948 *
1949 */
UTIL_rev_addr(const char * s)1950 char *UTIL_rev_addr(const char *s)
1951 {
1952   u_int8_t i = 0;           /* utility */
1953   u_int8_t tmp[4][1];       /* temporary storage for ip integers */
1954 
1955   size_t len = 0;           /* length of to be allocated string */
1956 
1957   char *cp       = NULL;    /* copy of passed string */
1958   char *token    = NULL;    /* token for splitting apart ip address */
1959   char *new_addr = NULL;    /* allocate string for reversed address */
1960 
1961 
1962   if (s == NULL)
1963   {
1964     xepprintf("passed a null string.  Abort!\n");
1965 
1966     return(NULL);
1967   }
1968 
1969   len = (strlen(s) + 1);
1970 
1971   xprintf("called with: [%s] len: (%i)\n", s, (len - 1));
1972 
1973   cp    = xstrndup(s, len);
1974   token = strtok(cp, ".");
1975 
1976   while ((token != NULL) && (i <= 3))
1977   {
1978     xvprintf("token : [%s]\n", token);
1979 
1980     tmp[i][0] = atoi(token);
1981     token     = strtok(NULL, ".");
1982     i++;
1983   }
1984 
1985   xfree(cp);
1986 
1987   /* + .in-addr.arpa\0 */
1988   new_addr = xmalloc(len + 13);
1989 
1990   snprintf(new_addr, (len + 13), "%u.%u.%u.%u.in-addr.arpa",
1991     tmp[3][0], tmp[2][0], tmp[1][0], tmp[0][0]);
1992 
1993   xvprintf("leaving func; returning reversed ip: %s\n", new_addr);
1994 
1995   return(new_addr);
1996 }
1997 
1998 
1999 /* UTIL_get_dname
2000 *
2001 *  Author: James Couzens <jcouzens@codeshare.ca>
2002 *
2003 *  Date:   01/27/04
2004 *
2005 *  Desc:
2006 *          s contains a hostname which is then looked through and split
2007 *  apart, always leaving one '.' delimiter left so you are left with
2008 *  domain.tld.  domain.tld is then put into newly allocated memory and
2009 *  returned to the calling function.  Upon failure, returns NULL.
2010 *  Calling function is required to free the memory.
2011 *
2012 */
UTIL_get_dname(const char * s)2013 char *UTIL_get_dname(const char *s)
2014 {
2015   u_int8_t i = 0;      /* how many delimiters in s */
2016 
2017   char *buf = NULL;    /* return string to be allocated */
2018 
2019 
2020   if (s == NULL)
2021   {
2022     xepprintf("called with NULL.  Abort!\n");
2023 
2024     return(NULL);
2025   }
2026 
2027   xvprintf("called with [%s]\n", s);
2028 
2029   i = UTIL_count_delim(s, '.');
2030 
2031   switch (i)
2032   {
2033     case 0:
2034     {
2035       break;
2036     }
2037 
2038     case 1:
2039     {
2040       buf = xstrndup(s, (strlen(s) + 1));
2041       xprintf("leaving func; returning buffer: [%s]\n", buf);
2042 
2043       return(buf);
2044     }
2045 
2046     default:
2047     {
2048       buf = UTIL_split_str(s, '.', (i - 1));
2049       xprintf("leaving func; returning buffer: [%s]\n", buf);
2050 
2051       return(buf);
2052     }
2053 
2054   } /* switch */
2055 
2056   xepprintf("leaving func; returning NULL\n");
2057 
2058   return(NULL);
2059 }
2060 
2061 
2062 /* UTIL_cidr_cmp
2063 *
2064 *  Author: James Couzens <jcouzens@codeshare.ca>
2065 *
2066 *  Date:   12/26/03
2067 *
2068 *  Desc:
2069 *          Compares an IP address (connected peer) against another IP
2070 *  address (found in a policy lookup) using a netmask (also found from
2071 *  a policy lookup) to see if the peer address is legal within that
2072 *  netmask.  Returns SPF_TRUE if its valid, SPF_FALSE if not.
2073 *
2074 *  Notes:  Write additional comparisons to handle IP6 addresses
2075 *
2076 */
UTIL_cidr_cmp(const policy_addr_t * policy_addr,const struct in_addr * peer_addr)2077 SPF_BOOL UTIL_cidr_cmp(const policy_addr_t *policy_addr, const struct in_addr
2078   *peer_addr)
2079 {
2080   u_int32_t a = 0;           /* working buf */
2081   u_int32_t b = 0;           /* working buf */
2082 
2083   char *peer_ip   = NULL;    /* ip address of connected peer */
2084   char *policy_ip = NULL;    /* ip address of current SPF policy */
2085 
2086 
2087   if ((policy_addr->addr.s_addr <= 0) && (peer_addr->s_addr <= 0))
2088   {
2089     xepprintf("Passed with NULL chars.  Aborting.\n");
2090 
2091     return(SPF_FALSE);
2092   }
2093 
2094   xvprintf("POL: %lu PEER: %lu CIDR: %i\n", policy_addr->addr.s_addr,
2095     peer_addr->s_addr, policy_addr->cidr);
2096 
2097    /*
2098    * Packets come in off the wire in whats called "network byte order"
2099    * which does't work very well when trying to compute if one address
2100    * is masked by another using a CIDR calculation so the 'ntohl' is
2101    * issued on them to change their byte order
2102   */
2103   a = ntohl(peer_addr->s_addr);
2104   b = ntohl(policy_addr->addr.s_addr);
2105 
2106   if (policy_addr->cidr != 32)
2107   {
2108     if ((a&(~0U<<(32-policy_addr->cidr))) != (b&(~0U<<(32-policy_addr->cidr))))
2109     {
2110       return(SPF_FALSE);
2111     }
2112   }
2113   else
2114   {
2115     if (peer_addr->s_addr != policy_addr->addr.s_addr)
2116     {
2117       xvprintf("%lu and %lu using 32 cidr do not match\n",
2118         peer_addr->s_addr, policy_addr->addr.s_addr);
2119 
2120       return(SPF_FALSE);
2121     }
2122   }
2123 
2124   /* these are done here just for debugger remove later */
2125   peer_ip   = xstrndup(inet_ntoa(*peer_addr), SPF_MAX_IP_ADDR);
2126   policy_ip = xstrndup(inet_ntoa(policy_addr->addr), SPF_MAX_IP_ADDR);
2127 
2128   xvprintf("Peer: [%s] matches address %s with network %i\n",
2129     peer_ip, policy_ip, policy_addr->cidr);
2130 
2131   xfree(peer_ip);
2132   xfree(policy_ip);
2133 
2134   return(SPF_TRUE);
2135 }
2136 
2137 
2138 /* UTIL_validate_ptr
2139 *
2140 *  Author: James Couzens <jcouzens@codeshare.ca>
2141 *
2142 *  Date:   12/26/03
2143 *
2144 *  Desc:
2145 *          Using the ip address found in the passed peer_info structure,
2146 *  a PTR lookup is made and if a record exists then this is record is compared
2147 *  against the hostname the client wishes to send mail from.  If the tld matches
2148 *  the tld found during this query, this would be a 'valdiated' PTR.  If a match
2149 *  can not be found we return SPF_FALSE.  If a match is made we return SPF_TRUE.
2150 *
2151 *  Notes:  Write additional comparisons to handle IP6 addresses
2152 *
2153 */
UTIL_validate_ptr(peer_info_t * p)2154 SPF_BOOL UTIL_validate_ptr(peer_info_t *p)
2155 {
2156   char *ptr_addr = NULL;    /* reversed address into PTR format */
2157   char *tmp_ptr  = NULL;    /* utility pointer */
2158 
2159 
2160   if (!p)
2161   {
2162     xepprintf("called with an invalid peer info structure!\n");
2163 
2164     return(SPF_FALSE);
2165   }
2166 
2167   /* reverse of the address in peer_info */
2168   ptr_addr = UTIL_rev_addr(p->r_ip);
2169   xvprintf("[address: %s]\n", ptr_addr);
2170 
2171   tmp_ptr = xstrndup(p->current_domain, SPF_MAX_HNAME);
2172 
2173   if (DNS_query(p, ptr_addr, T_PTR, tmp_ptr) != (char *)SPF_TRUE)
2174   {
2175     xvprintf("PTR lookup failed: [%s] [%s]\n", p->rs, p->error);
2176 
2177     xfree(ptr_addr);
2178     xfree(tmp_ptr);
2179 
2180     return(SPF_FALSE);
2181   }
2182 
2183   xvprintf("Peer [%s] successfully validated hostname [%s]\n",
2184     p->r_ip, p->r_vhname);
2185 
2186   xfree(ptr_addr);
2187   xfree(tmp_ptr);
2188 
2189   return(SPF_TRUE);
2190 }
2191 
2192 
2193 /* UTIL_validate_hostname
2194 *
2195 *  Author: James Couzens <jcouzens@codeshare.ca>
2196 *
2197 *  Date:   01/08/04
2198 *
2199 *  Desc:
2200 *          s contains a hostname which is then looked up to find any
2201 *  of the returned ip addresses SPF_PASS the remote peers.  On a successful
2202 *  SPF_PASS returns SPF_TRUE.  When no SPF_PASS is made, returns SPF_FALSE.
2203 *
2204 *  Note:
2205 *          We need to define and set a limit on the number of recursive
2206 *  lookups.  I recall seeing this in the RFC, we should discuss this.
2207 *
2208 *  Date:  08/28/04 by James
2209 *
2210 *  Desc:
2211 *          Added buffer for reentrant gethostbyname wrapper and changed
2212 *  gethostbyname calls to use the MACRO wrapper xgethostbyname()
2213 *
2214 */
UTIL_validate_hostname(peer_info_t * p,const char * s,const int8_t cidr)2215 SPF_BOOL UTIL_validate_hostname(peer_info_t *p, const char *s, const int8_t cidr)
2216 {
2217   int tmp_errno = 0;                  /* temporary errno placeholder */
2218 
2219   char **a   = NULL;                  /* work ptr for list recursion */
2220 
2221   char *ip   = NULL;                  /* ip address */
2222   char *gbuf = NULL;                  /* reentrant buffer for gethostbyname_r */
2223 
2224   struct in_addr *addr = NULL;        /* connected peer's address */
2225 
2226   struct hostent *hp    = NULL;       /* hostent structure */
2227   struct hostent tmp_hp = {0};        /* temporary hostent structure */
2228 
2229   policy_addr_t policy_addr = {0};    /* used for cidr calculations */
2230 
2231 
2232   if (s == NULL)
2233   {
2234     xepprintf("passed a NULL string.\n");
2235 
2236     return(SPF_FALSE);
2237   }
2238 
2239   xvprintf("called with: (%lu) and [%s]\n", p->r_ip, s);
2240 
2241   gbuf = xmalloc(SPF_MAX_GHBNR_DBUF);
2242   memset(&tmp_hp, '\0', SIZEOF(struct hostent));
2243 
2244   if ((hp = xgethostbyname(s, &tmp_hp, gbuf, SPF_MAX_GHBNR_DBUF,
2245     &tmp_errno)) != NULL)
2246   {
2247     /* for each hostname in the list, perform CIDR checks */
2248     for (a = hp->h_addr_list; *a; a++)
2249     {
2250       addr = xmalloc(SIZEOF(struct in_addr));
2251       memcpy(&addr->s_addr, *a, SIZEOF(struct in_addr));
2252       ip = xstrndup(inet_ntoa(*addr), SPF_MAX_IP_ADDR);
2253 
2254       xvprintf("CLI: %s (%lu) SRV: %s (%lu)\n", ip, addr->s_addr,
2255         p->r_ip, p->addr.s_addr);
2256 
2257       if (cidr == 32)
2258       {
2259         if (((struct in_addr *)(*a))->s_addr == p->addr.s_addr)
2260         {
2261           xvprintf("%s (%lu) matches %s (%lu)\n", ip,
2262             ((struct in_addr *)(*a))->s_addr, p->r_ip, p->addr.s_addr);
2263 
2264           UTIL_assoc_prefix(p, SPF_PASS, NULL);
2265 
2266           xfree(ip);
2267           xfree(gbuf);
2268           xfree(addr);
2269           xgethostbyname_free();    /* unlock mutex */
2270 
2271           xpprintf("leaving func; returnining SPF_TRUE\n");
2272 
2273           return(SPF_TRUE);
2274         }
2275       }
2276       else if ((cidr < 32) && (cidr >= 8))
2277       {
2278         /* convert from character string to dotted quad notation */
2279         if ((inet_pton(AF_INET, ip, &policy_addr.addr)) == 0)
2280         {
2281           xepprintf("Unable to execute inet_pton()\n");
2282         }
2283 
2284         policy_addr.cidr = cidr;
2285 
2286         xvprintf("Address: %lu with CIDR %i\n",
2287           policy_addr.addr.s_addr, policy_addr.cidr);
2288 
2289         /* perform CIDR comparison of 'policy_addr' vs 'p->addr' */
2290         if ((UTIL_cidr_cmp(&policy_addr, &p->addr)) == SPF_TRUE)
2291         {
2292           xvprintf("(%lu) matches (%lu) with CIDR %u\n",
2293             policy_addr.addr.s_addr, p->addr.s_addr, cidr);
2294 
2295           /* associate an SPF prefix (-,+,~,?) with this check */
2296           UTIL_assoc_prefix(p, SPF_PASS, NULL);
2297 
2298           xfree(ip);
2299           xfree(gbuf);
2300           xfree(addr);
2301           xgethostbyname_free();
2302 
2303           xpprintf("leaving func; returnining SPF_TRUE\n");
2304 
2305           return(SPF_TRUE);
2306         }
2307       } /* else if */
2308 
2309       xfree(ip);
2310       xfree(addr);
2311 
2312     } /* for */
2313 
2314     /* for each alias (CNAME) perform a CIDR check */
2315     for (a = hp->h_aliases; *a; a++)
2316     {
2317       addr = xmalloc(SIZEOF(struct in_addr));
2318       memcpy(&addr->s_addr, *a, SIZEOF(struct in_addr));
2319       ip = xstrndup(inet_ntoa(*addr), SPF_MAX_IP_ADDR);
2320 
2321       xvprintf("client: %s (%lu); policy: %s (%lu)\n",
2322         ip, addr->s_addr, p->r_ip, p->addr.s_addr);
2323 
2324       if (cidr == 32)
2325       {
2326         if (((struct in_addr *)(*a))->s_addr == p->addr.s_addr)
2327         {
2328           xvprintf("IN A: client: %s (%lu) matches policy: %s (%lu)\n",
2329             ip, ((struct in_addr *)(*a))->s_addr, p->r_ip, p->addr.s_addr);
2330 
2331           xfree(ip);
2332           xfree(gbuf);
2333           xfree(addr);
2334           xgethostbyname_free();
2335 
2336           xpprintf("leaving func; returnining SPF_TRUE\n");
2337 
2338           return(SPF_TRUE);
2339         }
2340       }
2341       else if ((cidr < 32) && (cidr >= 8))
2342       {
2343         if (inet_pton(AF_INET, ip, &policy_addr.addr) == 0)
2344         {
2345           xepprintf("Unable to execute inet_pton()\n");
2346         }
2347 
2348         policy_addr.cidr = cidr;
2349 
2350         if (UTIL_cidr_cmp(&policy_addr, &p->addr) == SPF_TRUE)
2351         {
2352           xvprintf("client: (%lu) matches policy (%lu) with CIDR %u\n",
2353             policy_addr.addr.s_addr, p->addr.s_addr, cidr);
2354 
2355           /* associate an SPF prefix (-,+,~,?) with this check */
2356           UTIL_assoc_prefix(p, SPF_PASS, NULL);
2357 
2358           xfree(ip);
2359           xfree(gbuf);
2360           xfree(addr);
2361           xgethostbyname_free();
2362 
2363           xpprintf("leaving func; returnining SPF_TRUE\n");
2364 
2365           return(SPF_TRUE);
2366         }
2367       } /* else */
2368 
2369       xfree(ip);
2370       xfree(addr);
2371 
2372     } /* for */
2373   } /* if .. xgethostbyname() */
2374   else
2375   {
2376     xvprintf("No address associated with hostname [%s]; Reason: %s\n",
2377       s, hstrerror(tmp_errno));
2378   }
2379 
2380   xfree(gbuf);
2381   xgethostbyname_free();
2382 
2383   xpprintf("leaving func; returning SPF_FALSE\n");
2384 
2385   return(SPF_FALSE);
2386 }
2387 
2388 
2389 /* UTIL_url_encode
2390 *
2391 *  Author: Sean Comeau <scomeau@obscurity.org>
2392 *
2393 *  Date:   01/06/04
2394 *
2395 *  Desc:
2396 *          "URL-Encode" characters that are "unsafe" or "prohibited" from a URL.
2397 *  Upon success returns a pointer to a newly allocated memory containing the
2398 *  encoded string.  Upon failure returns a NULL pointer.  Failure
2399 *  indicates either a failure to allocate new memory, or the passed string
2400 *  did not contain any questionable characters and hence did not require
2401 *  encoding.
2402 *
2403 */
UTIL_url_encode(const char * s)2404 char *UTIL_url_encode(const char *s)
2405 {
2406   int len = 0;             /* length of passed string * 3*/
2407 
2408   char *new     = NULL;    /* newly allocated memory */
2409   char *encoded = NULL;    /* encoded return string */
2410 
2411 
2412   if (s != NULL)
2413   {
2414      /*
2415      * length of the string times 3 is the maximum possible size a
2416      * URL encoded string should ever be able to expand to.
2417     */
2418     len = (strlen(s) * 3);
2419   }
2420   else
2421   {
2422     xepprintf("passed a NULL string.  Abort!\n");
2423 
2424     return(NULL);
2425   }
2426 
2427   encoded = new = xmalloc(len);
2428 
2429   while (*s != '\0')
2430   {
2431     if (urlchr_test(*s) == 0)
2432     {
2433       *new++ = *s++;
2434     }
2435     else
2436     {
2437       /* this obviously NULL terminates, do we want this? */
2438       snprintf(new, 4, "%%%x", *s);
2439       new += 3;
2440       s++;
2441     }
2442   }
2443 
2444   *new++ = '\0';
2445   xvprintf("leaving func; returning [%s]\n", encoded);
2446 
2447   return(encoded);
2448 }
2449 
2450 
2451 /* UTIL_reverse
2452 *
2453 *  Author: James Couzens <jcouzens@codeshare.ca>
2454 *
2455 *  Date:   01/06/04
2456 *
2457 *  Desc:
2458 *          Takes a string and rebuilds it in reverse order using the
2459 *  supplied delimiter.  The string will always be rebuilt using a
2460 *  '.' char as per the RFC.
2461 *
2462 */
UTIL_reverse(const char * s,const char delim)2463 char *UTIL_reverse(const char *s, const char delim)
2464 {
2465   size_t len = 0;                        /* length of s */
2466 
2467   char *buf = NULL;                      /* return buffer */
2468   char *p   = NULL;                      /* working pointer */
2469   char *cp  = NULL;                      /* working pointer */
2470   char *c   = NULL;                      /* working pointer */
2471 
2472   split_str_t *master = NULL;            /* list pointers */
2473 
2474   split_str_node_t *c_node    = NULL;    /* c_node node */
2475   split_str_node_t *kill_node = NULL;    /* temp node used in destruction */
2476 
2477 
2478   if (s == NULL)
2479   {
2480     xepprintf("passed a NULL string.  Abort!\n");
2481 
2482     return(NULL);
2483   }
2484 
2485   xvprintf("called with [%s] and delim (%c)\n", s, delim);
2486 
2487   len = strlen(s);
2488   cp  = c = xstrndup(s, (len + 1));
2489 
2490   master           = xmalloc(SIZEOF(split_str_t));
2491   master->head     = NULL;
2492   master->tail     = NULL;
2493   master->elements = 0;
2494 
2495   /*
2496    *  Comment: James Couzens <jcouzens@codeshare.ca>
2497    *
2498    *  Date: 01/22/04
2499    *
2500    * we do not want the trailing delim so the first iteration of this
2501    * loop we call UTIL_addnode with 0, which tells it not to allocate
2502    * the extra byte for the trailing delimiter.  This is done only
2503    * when passed an IP address, and this is because when reversing we
2504    * want: 1.0.168.192. and not .1.0.168.192
2505    *
2506   */
2507 
2508   len++;
2509   buf = xmalloc(len);
2510   while ((p = strrchr(cp, delim)) != NULL)
2511   {
2512     p++;   /* remove period */
2513     UTIL_addnode(master, p, SPF_TRUE);
2514     p--;   /* bring it back */
2515     *p = '\0';
2516   }
2517 
2518   UTIL_addnode(master, cp, SPF_FALSE);
2519 
2520   c_node = master->head;
2521   while ((kill_node = c_node) != NULL)
2522   {
2523     strncat(buf, kill_node->s, kill_node->len);
2524     xfree(kill_node->s);
2525     c_node = c_node->next;
2526     xfree(kill_node);
2527   }
2528 
2529   xfree(cp);
2530   xfree(master);
2531 
2532   xvprintf("leaving func; returning [%s]\n", buf);
2533 
2534   return(buf);
2535 }
2536 
2537 
2538 /*  UTIL_addnode
2539 *
2540 *  Author: James Couzens <jcouzens@codeshare.ca>
2541 *
2542 *  Date:   01/18/04
2543 *
2544 *  Desc:
2545 *         Allocates memory for a new node and slaps it on the end of
2546 *  of the passed list.  In the process of doing so, when it comes to
2547 *  the last element in the list, which is indicated by the SPF_BOOL
2548 *  that is passed, a final '.' is either appended (SPF_TRUE) or not
2549 *  (SPF_FALSE).  Function returns SPF_TRUE upon success, and SPF_FALSE upon
2550 *  failure.
2551 *
2552 */
UTIL_addnode(split_str_t * master,const char * s,SPF_BOOL last)2553 SPF_BOOL UTIL_addnode(split_str_t *master, const char *s, SPF_BOOL last)
2554 {
2555   size_t len = 0;                        /* length of s */
2556 
2557   split_str_node_t *c_node    = NULL;    /* c_node working node */
2558   split_str_node_t *new_node  = NULL;    /* newly allocated node */
2559   split_str_node_t *prev_node = NULL;    /* previous working node */
2560 
2561 
2562   if (!s || (s == NULL))
2563   {
2564     xepprintf("passed a NULL string.  Abort!\n");
2565 
2566     return(SPF_FALSE);
2567   }
2568 
2569   xvprintf("called with string: [%s]; boolean: [%s]\n",
2570     s, last ? "TRUE" : "FALSE");
2571 
2572   len = strlen(s);
2573 
2574   if (last == SPF_TRUE)
2575   {
2576     len += 2;
2577   }
2578   else
2579   {
2580     len++;
2581   }
2582 
2583   /* create new node */
2584   new_node = xmalloc(SIZEOF(split_str_node_t));
2585 
2586   /* set the new nodes next value NULL, and store s */
2587   new_node->next = NULL;
2588 
2589   new_node->s = xmalloc(len);
2590 
2591    /*
2592    *  Comment: James Couzens <jcouzens@codeshare.ca>
2593    *
2594    *  Date: 01/22/04
2595    *
2596    * section 7.1 (Macro definitions) of the RFC v02.9.5 indicates that we must
2597    * always rebuild the macro using the delimiter '.' and not the passed
2598    * delimiting character.
2599    *
2600   */
2601   snprintf(new_node->s, len, "%s%c", s, last ? '.' : '\0');
2602   new_node->len = (len - 1); /* don't count \0 */
2603 
2604   prev_node = NULL;
2605   c_node    = master->head;
2606 
2607   /* reorder the list with the NEW element on the end */
2608   while (c_node != NULL)
2609   {
2610     prev_node = c_node;
2611     c_node    = c_node->next;
2612   }
2613 
2614   if (prev_node != NULL)
2615   {
2616     new_node->next  = prev_node->next;
2617     prev_node->next = new_node;
2618   }
2619   else
2620   {
2621     master->head = new_node;
2622   }
2623 
2624   master->tail = new_node;
2625   master->elements++;
2626 
2627   xpprintf("leaving func; returning SPF_TRUE\n");
2628 
2629   return(SPF_TRUE);
2630 }
2631 
2632 
2633 /*  UTIL_delnode
2634 *
2635 *  Author: James Couzens <jcouzens@codeshare.ca>
2636 *
2637 *  Date:   10/06/04
2638 *
2639 *  Desc:
2640 *         Iterate through the master list searching for a node whose string
2641 *  buffer matches the contents of 's'.  When found that node is free'd and the
2642 *  list continues until its end and then returns TRUE.  If it is not found,
2643 *  we return SPF_FALSE.
2644 *
2645 */
UTIL_delnode(split_str_t * master,const char * s)2646 SPF_BOOL UTIL_delnode(split_str_t *master, const char *s)
2647 {
2648   SPF_BOOL DELETE_SUCCESS = SPF_FALSE;
2649 
2650   split_str_node_t *c_node    = NULL;
2651   split_str_node_t *kill_node = NULL;
2652 
2653   if (s == NULL)
2654   {
2655     xepprintf("called with empty comparison string, returning FALSE\n");
2656 
2657     return(SPF_FALSE);
2658   }
2659 
2660   c_node = master->head;
2661 
2662   while ((kill_node = c_node) != NULL)
2663   {
2664     xvprintf("iterate include list: include:%s\n", kill_node->s);
2665 
2666     if (strcasecmp(kill_node->s, s) == 0)
2667     {
2668       xvprintf("match found (%s == %s), removing node.\n",
2669         kill_node->s, s);
2670 
2671       /* remove the matched node's contents */
2672       xfree(kill_node->s);
2673       kill_node->len = 0;
2674 
2675       /* iterate forward in the list to safely free the matched node */
2676       c_node = c_node->next;
2677 
2678       /* update the master list with a new head node and element count */
2679       master->head = c_node;
2680       master->elements--;
2681 
2682       /* free the structure of the matched node */
2683       xfree(kill_node);
2684 
2685       DELETE_SUCCESS = SPF_TRUE;
2686     }
2687     else
2688     {
2689       c_node = c_node->next;
2690     }
2691 
2692     if (DELETE_SUCCESS == SPF_TRUE)
2693     {
2694       c_node    = NULL;
2695       kill_node = NULL;
2696     }
2697   }
2698 
2699   xvprintf("returning %s\n", DELETE_SUCCESS ? "SPF_TRUE" : "SPF_FALSE");
2700 
2701   return(DELETE_SUCCESS);
2702 }
2703 
2704 
2705 /* _UTIL_pthread_mutex
2706 *
2707 *  Author: James Couzens <jcouzens@codeshare.ca>
2708 *
2709 *  Date:   09/08/04
2710 *
2711 *  Desc:
2712 *         Locks or unlocks the passed mutex based on the additional value
2713 *  which is and enumerattion (SPF_BOOL)
2714 *
2715 */
_UTIL_pthread_mutex(void * mutex,SPF_BOOL action)2716 void _UTIL_pthread_mutex(void *mutex, SPF_BOOL action)
2717 {
2718 
2719 #ifdef _WITH_PTHREADS
2720 
2721   switch (action)
2722   {
2723     case SPF_TRUE:
2724     {
2725       pthread_mutex_lock((pthread_mutex_t *)mutex);
2726 
2727       return;
2728     }
2729 
2730     case SPF_FALSE:
2731     {
2732       pthread_mutex_unlock((pthread_mutex_t *)mutex);
2733 
2734       return;
2735     }
2736 
2737   } /* switch */
2738 
2739 #endif /* _WITH_PTHREADS */
2740 
2741   return;
2742 }
2743 
2744 
2745 /* end of util.c */
2746