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