libspf2 1.2.11
spf_compile.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of either:
4 *
5 * a) The GNU Lesser General Public License as published by the Free
6 * Software Foundation; either version 2.1, or (at your option) any
7 * later version,
8 *
9 * OR
10 *
11 * b) The two-clause BSD license.
12 *
13 * These licenses can be found with the distribution in the file LICENSES
14 */
15
16#include "spf_sys_config.h"
17#include "spf_internal.h"
18
19
20#ifdef STDC_HEADERS
21# include <stdio.h> /* stdin / stdout */
22# include <stdlib.h> /* malloc / free */
23# include <ctype.h> /* isupper / tolower */
24#endif
25
26#ifdef HAVE_INTTYPES_H
27#include <inttypes.h>
28#endif
29
30#ifdef HAVE_STRING_H
31# include <string.h> /* strstr / strdup */
32#else
33# ifdef HAVE_STRINGS_H
34# include <strings.h> /* strstr / strdup */
35# endif
36#endif
37
38
39
40#undef SPF_ALLOW_DEPRECATED_DEFAULT
41
42#include "spf.h"
43#include "spf_internal.h"
44#include "spf_response.h"
45#include "spf_record.h"
46
47typedef
48enum SPF_cidr_enum {
51
52typedef
53enum SPF_domspec_enum {
56
62#define SPF_RECORD_BUFSIZ 4096
63
64#define ALIGN_DECL(decl) union { double d; long l; decl } __attribute__((aligned(_ALIGN_SZ))) u
65#define ALIGNED_DECL(var) u.var
66
67
68
69typedef
70struct SPF_mechtype_struct
71{
72 unsigned char mech_type;
73 unsigned char is_dns_mech;
77
78static const SPF_mechtype_t spf_mechtypes[] = {
89};
90
91#define spf_num_mechanisms \
92 sizeof(spf_mechtypes) / sizeof(spf_mechtypes[0])
93
94static const SPF_mechtype_t *
95SPF_mechtype_find(int mech_type)
96{
97 size_t i;
98 for (i = 0; i < spf_num_mechanisms; i++) {
99 if (spf_mechtypes[i].mech_type == mech_type)
100 return &spf_mechtypes[i];
101 }
102 return NULL;
103}
104
105__attribute__((warn_unused_result))
106static int
107SPF_c_ensure_capacity(void **datap, size_t *sizep, size_t length)
108{
109 size_t size = *sizep;
110 if (length > size)
111 size = length + (length / 4);
112 if (size > *sizep) {
113 void *tmp = realloc(*datap, size);
114 if (!tmp)
115 return -1;
116 // memset(tmp + *sizep, 'C', (size - *sizep));
117 *datap = tmp;
118 *sizep = size;
119 }
120 return 0;
121}
122
132static SPF_errcode_t
133SPF_c_parse_cidr_ip6(SPF_response_t *spf_response,
134 unsigned char *maskp,
135 const char *src)
136{
137 int mask;
138
139 /*
140 if (spf_server->debug > 2)
141 SPF_debugf("Parsing ip6 CIDR starting at %s", src);
142 */
143
144 mask = strtoul(src + 1, NULL, 10);
145
146 if (mask > 128) {
148 NULL, src,
149 "Invalid IPv6 CIDR netmask (>128)");
150 }
151 else if (mask == 0) {
153 NULL, src,
154 "Invalid IPv6 CIDR netmask (=0)");
155 }
156 else if (mask == 128) {
157 mask = 0;
158 }
159
160 *maskp = mask;
161
162 return SPF_E_SUCCESS;
163}
164
174static SPF_errcode_t
175SPF_c_parse_cidr_ip4(SPF_response_t *spf_response,
176 unsigned char *maskp,
177 const char *src)
178{
179 int mask;
180
181 /*
182 if (spf_server->debug > 2)
183 SPF_debugf("Parsing ip4 CIDR starting at %s", src);
184 */
185
186 mask = strtoul(src + 1, NULL, 10);
187
188 if ( mask > 32 ) {
190 NULL, src,
191 "Invalid IPv4 CIDR netmask (>32)");
192 }
193 else if ( mask == 0 ) {
195 NULL, src,
196 "Invalid IPv4 CIDR netmask (=0)");
197 }
198 else if ( mask == 32 ) {
199 mask = 0;
200 }
201
202 *maskp = mask;
203
204 return SPF_E_SUCCESS;
205}
206
212static SPF_errcode_t
213SPF_c_parse_cidr(SPF_response_t *spf_response,
214 SPF_data_cidr_t *data,
215 const char *src, size_t *src_len)
216{
217 SPF_errcode_t err;
218 size_t idx;
219
220 memset(data, 0, sizeof(SPF_data_cidr_t));
221 data->parm_type = PARM_CIDR;
222
223 /* Find the beginning of the CIDR length notation.
224 * XXX This assumes that there is a non-digit in the string.
225 * This is always true for SPF records with domainspecs, since
226 * there has to be an = or a : before it. */
227 idx = *src_len - 1;
228 while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
229 idx--;
230
231 /* Something is frying my brain and I can't pull an invariant
232 * out of this suitable for resetting *endp. So I nested the
233 * 'if's instead. Perhaps I'll manage to refactor later. */
234
235 /* If we have a slash which isn't the last character. */
236 if (idx < (*src_len - 1) && src[idx] == '/') {
237 if (idx > 0 && src[idx - 1] == '/') {
238 /* get IPv6 CIDR length */
239 err = SPF_c_parse_cidr_ip6(spf_response, &data->ipv6, &src[idx]);
240 if (err)
241 return err;
242 /* now back up and see if there is a ipv4 cidr length */
243 *src_len = idx - 1; /* The index of the first '/' */
244 idx = *src_len - 1; /* Last character of what is before. */
245 while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
246 idx--;
247
248 /* get IPv4 CIDR length */
249 if (idx < (*src_len - 1) && src[idx] == '/') {
250 /* - we know that strtoul terminates on the
251 * '/' so we don't need to null-terminate the
252 * input string. */
253 err = SPF_c_parse_cidr_ip4(spf_response, &data->ipv4, &src[idx]);
254 if (err)
255 return err;
256 *src_len = idx;
257 }
258 }
259 else {
260 /* get IPv4 CIDR length */
261 err = SPF_c_parse_cidr_ip4(spf_response, &data->ipv4, &src[idx]);
262 if (err)
263 return err;
264 *src_len = idx;
265 }
266 }
267
268 return SPF_E_SUCCESS;
269}
270
271static SPF_errcode_t
272SPF_c_parse_var(SPF_response_t *spf_response, SPF_data_var_t *data,
273 const char *src, int is_mod)
274{
275 const char *token;
276 const char *p;
277 char c;
278 int val;
279
280 memset(data, 0, sizeof(SPF_data_var_t));
281
282 p = src;
283
284 /* URL encoding */
285 c = *p;
286 if ( isupper( (unsigned char)( c ) ) )
287 {
288 data->url_encode = TRUE;
289 c = tolower(c);
290 }
291 else
292 data->url_encode = FALSE;
293
294#define SPF_CHECK_IN_MODIFIER() \
295 if ( !is_mod ) \
296 return SPF_response_add_error_ptr(spf_response, \
297 SPF_E_INVALID_VAR, NULL, p, \
298 "'%c' macro is only valid in modifiers", c);
299
300 switch ( c )
301 {
302 case 'l': /* local-part of envelope-sender */
303 data->parm_type = PARM_LP_FROM;
304 break;
305
306 case 's': /* envelope-sender */
307 data->parm_type = PARM_ENV_FROM;
308 break;
309
310 case 'o': /* envelope-domain */
311 data->parm_type = PARM_DP_FROM;
312 break;
313
314 case 'd': /* current-domain */
315 data->parm_type = PARM_CUR_DOM;
316 break;
317
318 case 'i': /* SMTP client IP */
320 break;
321
322 case 'c': /* SMTP client IP (pretty) */
325 break;
326
327 case 't': /* time in UTC epoch secs */
329 data->parm_type = PARM_TIME;
330 break;
331
332 case 'p': /* SMTP client domain name */
334 break;
335
336 case 'v': /* IP ver str - in-addr/ip6 */
338 break;
339
340 case 'h': /* HELO/EHLO domain */
341 data->parm_type = PARM_HELO_DOM;
342 break;
343
344 case 'r': /* receiving domain */
346 data->parm_type = PARM_REC_DOM;
347 break;
348
349 default:
351 NULL, p,
352 "Unknown variable '%c'", c);
353 }
354 p++;
355 token = p;
356
357 /* get the number of subdomains to truncate to */
358 val = 0;
359 while ( isdigit( (unsigned char)( *p ) ) )
360 {
361 val *= 10;
362 val += *p - '0';
363 p++;
364 }
365 if ( val > 128 || (val <= 0 && p != token) )
366 return SPF_response_add_error_ptr(spf_response, SPF_E_BIG_SUBDOM,
367 NULL, token,
368 "Subdomain truncation depth too large");
369 data->num_rhs = val;
370 token = p;
371
372 /* should the string be reversed? */
373 if ( *p == 'r' )
374 {
375 data->rev = 1;
376 p++;
377 }
378 else
379 data->rev = FALSE;
380 token = p;
381
382
383 /* check for delimiters */
384 data->delim_dot = FALSE;
385 data->delim_dash = FALSE;
386 data->delim_plus = FALSE;
387 data->delim_equal = FALSE;
388 data->delim_bar = FALSE;
389 data->delim_under = FALSE;
390
391 /*vi:{*/
392 if ( *p == '}' )
393 data->delim_dot = TRUE;
394
395 /*vi:{*/
396 while( *p != '}' )
397 {
398 token = p;
399 switch( *p )
400 {
401 case '.':
402 data->delim_dot = TRUE;
403 break;
404
405 case '-':
406 data->delim_dash = TRUE;
407 break;
408
409 case '+':
410 data->delim_plus = TRUE;
411 break;
412
413 case '=':
414 data->delim_equal = TRUE;
415 break;
416
417 case '|':
418 data->delim_bar = TRUE;
419 break;
420
421 case '_':
422 data->delim_under = TRUE;
423 break;
424
425 default:
426 return SPF_response_add_error_ptr(spf_response,
428 "Invalid delimiter '%c'", *p);
429 }
430 p++;
431 }
432 p++;
433 token = p;
434
435
436 return SPF_E_SUCCESS;
437}
438
439
440 /* Sorry, Wayne. */
441#define SPF_ADD_LEN_TO(_val, _len, _max) do { \
442 if ( (_val) + _align_sz(_len) > (_max) ) { \
443 return SPF_response_add_error_ptr(spf_response, \
444 big_err, NULL, src, \
445 "SPF domainspec too long " \
446 "(%d chars, %d max)", \
447 (_val) + (_len), _max); \
448 } \
449 (_val) += _align_sz(_len); \
450 } while(0)
451
452#define SPF_INIT_STRING_LITERAL(_avail) do { \
453 data->ds.parm_type = PARM_STRING; \
454 data->ds.len = 0; \
455 /* Magic numbers for x/Nc in gdb. */ \
456 data->ds.__unused0 = 0xba; data->ds.__unused1 = 0xbe; \
457 dst = SPF_data_str( data ); \
458 if ((_avail) < sizeof(SPF_data_t)) \
459 return SPF_response_add_error_ptr(spf_response, \
460 SPF_E_BIG_STRING, NULL, src, \
461 "Out of memory for string literal");\
462 ds_avail = (_avail) - sizeof(SPF_data_t); \
463 ds_len = 0; \
464 } while(0)
465
466#define SPF_ENSURE_STRING_AVAIL(_len) do { \
467 if (ds_len + _len > ds_avail) \
468 return SPF_response_add_error_ptr(spf_response, \
469 SPF_E_BIG_STRING, NULL, src, \
470 "String literal fragment too long " \
471 "(%d chars, %d max)", \
472 ds_len, ds_avail); \
473 } while(0)
474
475#define SPF_FINI_STRING_LITERAL() do { \
476 if ( ds_len > 0 ) { \
477 if ( ds_len > SPF_MAX_STR_LEN ) { \
478 return SPF_response_add_error_ptr(spf_response, \
479 SPF_E_BIG_STRING, NULL, src, \
480 "String literal too long " \
481 "(%d chars, %d max)", \
482 ds_len, SPF_MAX_STR_LEN); \
483 } \
484 data->ds.len = ds_len; \
485 len = sizeof( *data ) + ds_len; \
486 SPF_ADD_LEN_TO(*data_used, len, data_avail); \
487 data = SPF_data_next( data ); \
488 ds_len = 0; \
489 } \
490 } while(0)
491
509static SPF_errcode_t
510SPF_c_parse_macro(SPF_server_t *spf_server,
511 SPF_response_t *spf_response,
512 SPF_data_t *data, size_t *data_used, size_t data_avail,
513 const char *src, size_t src_len,
514 SPF_errcode_t big_err,
515 int is_mod)
516{
517 SPF_errcode_t err;
518 /* Generic parsing iterators and boundaries */
519 size_t idx;
520 size_t len;
521 /* For parsing strings. */
522 char *dst;
523 size_t ds_avail;
524 size_t ds_len;
525
526 if (spf_server->debug)
527 SPF_debugf("Parsing macro starting at %s", src);
528
529#if 0
530 if ((void *)data != _align_ptr((void *)data))
531 SPF_errorf("Data pointer %p is not aligned: Cannot compile.",
532 data);
533#endif
534
535 /*
536 * Create the data blocks
537 */
538 idx = 0;
539
540 /* Initialise the block as a string. If ds_len == 0 later, we
541 * will just clobber it. */
542 SPF_INIT_STRING_LITERAL(data_avail - *data_used);
543
544 // while ( p != end ) {
545 while (idx < src_len) {
546 if (spf_server->debug > 3)
547 SPF_debugf("Current data is at %p", data);
548 /* Either the unit is terminated by a space, or we hit a %.
549 * We should only hit a space if we run past src_len. */
550 len = strcspn(&src[idx], " %"); // XXX Also tab?
551 if (len > 0) { /* An optimisation */
552 /* Don't over-run into the CIDR. */
553 if (idx + len > src_len)
554 len = src_len - idx;
555 if (spf_server->debug > 3)
556 SPF_debugf("Adding string literal (%lu): '%*.*s'",
557 (unsigned long)len,
558 (int)len, (int)len, &src[idx]);
559 /* XXX Bounds-check here. */
561 memcpy(dst, &src[idx], len);
562 ds_len += len;
563 dst += len;
564 idx += len;
565
566 /* If len == 0 then we never entered the while(). Thus
567 * if idx == src_len, then len != 0 and we reach this test.
568 */
569 }
570 /* However, this logic is overcomplex and I am a simpleton,
571 * so I have moved it out of the condition above. */
572 if (idx == src_len)
573 break;
574
575 /* Now, we must have a %-escape code, since if we hit a
576 * space, then we are at the end.
577 * Incrementing idx consumes the % we hit first, and then
578 * we switch on the following character, which also
579 * increments idx. */
580 idx++;
581 switch (src[idx]) {
582 case '%':
583 if (spf_server->debug > 3)
584 SPF_debugf("Adding literal %%");
586 *dst++ = '%';
587 ds_len++;
588 idx++;
589 break;
590
591 case '_':
592 if (spf_server->debug > 3)
593 SPF_debugf("Adding literal space");
595 *dst++ = ' ';
596 ds_len++;
597 idx++;
598 break;
599
600 case '-':
601 if (spf_server->debug > 3)
602 SPF_debugf("Adding escaped space");
604 *dst++ = '%'; *dst++ = '2'; *dst++ = '0';
605 ds_len += 3;
606 idx++;
607 break;
608
609 default:
610 if (spf_server->debug > 3)
611 SPF_debugf("Adding illegal %%-follower '%c' at %zu",
612 src[idx], idx);
613 /* SPF spec says to treat it as a literal, not
614 * SPF_E_INVALID_ESC */
615 /* FIXME issue a warning? */
617 *dst++ = '%';
618 ds_len++;
619 break;
620
621 case '{': /*vi:}*/
623 if (spf_server->debug > 3)
624 SPF_debugf("Adding macro, data is at %p", data);
625
626 /* this must be a variable */
627 idx++;
628 err = SPF_c_parse_var(spf_response, &data->dv, &src[idx], is_mod);
629 if (err != SPF_E_SUCCESS)
630 return err;
631 idx += strcspn(&src[idx], "} ");
632 if (src[idx] == '}')
633 idx++;
634 else if (src[idx] == ' ')
635 return SPF_response_add_error_ptr(spf_response,
637 src, &src[idx],
638 "Unterminated variable?");
639
640
641 len = SPF_data_len(data);
642 SPF_ADD_LEN_TO(*data_used, len, data_avail);
643 data = SPF_data_next( data );
644 if (spf_server->debug > 3)
645 SPF_debugf("Next data is at %p", data);
646
647 SPF_INIT_STRING_LITERAL(data_avail - *data_used);
648
649 break;
650 }
651 }
652
654
655 return SPF_E_SUCCESS;
656
657}
658
659/* What a fuck-ugly prototype. */
674static SPF_errcode_t
675SPF_c_parse_domainspec(SPF_server_t *spf_server,
676 SPF_response_t *spf_response,
677 SPF_data_t *data, size_t *data_used, size_t data_avail,
678 const char *src, size_t src_len,
679 SPF_errcode_t big_err,
680 SPF_cidr_t cidr_ok, int is_mod)
681{
682 SPF_errcode_t err;
683 /* Generic parsing iterators and boundaries */
684 size_t len;
685
686 if (spf_server->debug)
687 SPF_debugf("Parsing domainspec starting at %s, cidr is %s",
688 src,
689 cidr_ok == CIDR_OPTIONAL ? "optional" :
690 cidr_ok == CIDR_ONLY ? "only" :
691 cidr_ok == CIDR_NONE ? "forbidden" :
692 "ERROR!"
693 );
694
695 /*
696 * create the CIDR length info
697 */
698 if (cidr_ok == CIDR_OPTIONAL || cidr_ok == CIDR_ONLY) {
699 err = SPF_c_parse_cidr(spf_response, &data->dc, src, &src_len);
700 if (err != SPF_E_SUCCESS)
701 return err;
702 if (data->dc.ipv4 != 0 || data->dc.ipv6 != 0) {
703 len = SPF_data_len(data);
704 SPF_ADD_LEN_TO(*data_used, len, data_avail);
705 data = SPF_data_next(data);
706 }
707
708 if (cidr_ok == CIDR_ONLY && src_len > 0) {
709 /* We had a mechanism followed by a '/', thus it HAS to be
710 * a CIDR, and the peculiar-looking error message is
711 * justified. However, we don't know _which_ CIDR. */
712 return SPF_response_add_error_ptr(spf_response,
714 NULL, src,
715 "Invalid CIDR after mechanism");
716 }
717 }
718
719 return SPF_c_parse_macro(spf_server, spf_response,
720 data, data_used, data_avail,
721 src, src_len, big_err, is_mod);
722}
723
724
730static SPF_errcode_t
731SPF_c_parse_ip4(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
732{
733 const char *end;
734 const char *p;
735
736 char buf[ INET6_ADDRSTRLEN ];
737 size_t len;
738 SPF_errcode_t err;
739
740 unsigned char mask;
741 struct in_addr *addr;
742
743 start++;
744 len = strcspn(start, " ");
745 end = start + len;
746 p = end - 1;
747
748 mask = 0;
749 while (isdigit( (unsigned char)(*p) ))
750 p--;
751 if (p != (end - 1) && *p == '/') {
752 err = SPF_c_parse_cidr_ip4(spf_response, &mask, p);
753 if (err)
754 return err;
755 end = p;
756 }
757 mech->mech_len = mask;
758
759 len = end - start;
760 if ( len > sizeof( buf ) - 1 )
761 return SPF_E_INVALID_IP4;
762
763 memcpy( buf, start, len );
764 buf[ len ] = '\0';
765 addr = SPF_mech_ip4_data(mech);
766 err = inet_pton( AF_INET, buf, addr );
767 if ( err <= 0 )
769 NULL, buf, NULL);
770
771 return SPF_E_SUCCESS;
772}
773
779static SPF_errcode_t
780SPF_c_parse_ip6(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
781{
782 const char *end;
783 const char *p;
784
785 char buf[ INET6_ADDRSTRLEN ];
786 size_t len;
787 int err;
788
789 unsigned char mask;
790 struct in6_addr *addr;
791
792 start++;
793 len = strcspn(start, " ");
794 end = start + len;
795 p = end - 1;
796
797 mask = 0;
798 while (isdigit( (unsigned char)(*p) ))
799 p--;
800 if (p != (end - 1) && *p == '/') {
801 err = SPF_c_parse_cidr_ip6(spf_response, &mask, p);
802 if (err)
803 return err;
804 end = p;
805 }
806 mech->mech_len = mask;
807
808 len = end - start;
809 if ( len > sizeof( buf ) - 1 )
810 return SPF_E_INVALID_IP6;
811
812 memcpy( buf, start, len );
813 buf[ len ] = '\0';
814 addr = SPF_mech_ip6_data(mech);
815 err = inet_pton( AF_INET6, buf, addr );
816 if ( err <= 0 )
818 NULL, buf, NULL);
819
820 return SPF_E_SUCCESS;
821}
822
823
824/* XXX TODO: Make this take (const char *) instead of (const char **)
825 * because the caller ignores the modified value. */
826__attribute__((warn_unused_result))
827static SPF_errcode_t
828SPF_c_mech_add(SPF_server_t *spf_server,
829 SPF_record_t *spf_record, SPF_response_t *spf_response,
830 const SPF_mechtype_t *mechtype, int prefix,
831 const char **mech_value)
832{
833 /* If this buffer is an irregular size, intel gcc does not align
834 * it properly, and all hell breaks loose. */
836 char buf[SPF_RECORD_BUFSIZ];
837);
838 SPF_mech_t *spf_mechanism = (SPF_mech_t *)ALIGNED_DECL(buf);
839 SPF_data_t *data;
840 size_t data_len;
841 size_t len;
842 size_t src_len;
843
844 SPF_errcode_t err;
845
846 memset(u.buf, 'B', sizeof(u.buf)); /* Poison the buffer. */
847 memset(spf_mechanism, 0, sizeof(SPF_mech_t));
848
849 if (spf_server->debug)
850 SPF_debugf("SPF_c_mech_add: type=%d, value=%s",
851 mechtype->mech_type, *mech_value);
852
853 spf_mechanism->prefix_type = prefix;
854 spf_mechanism->mech_type = mechtype->mech_type;
855 spf_mechanism->mech_len = 0;
856
857 len = sizeof( SPF_mech_t );
858
859 if ( spf_record->mech_len + len > SPF_MAX_MECH_LEN )
860 return SPF_E_BIG_MECH;
861
862 data = SPF_mech_data(spf_mechanism);
863 data_len = 0;
864
865 src_len = strcspn(*mech_value, " ");
866
867 switch (mechtype->mech_type) {
868 /* We know the properties of IP4 and IP6. */
869 case MECH_IP4:
870 if (**mech_value == ':') {
871 err = SPF_c_parse_ip4(spf_response, spf_mechanism, *mech_value);
872 data_len = sizeof(struct in_addr);
873 }
874 else {
875 err = SPF_E_MISSING_OPT;
876 SPF_response_add_error_ptr(spf_response, err,
877 NULL, *mech_value,
878 "Mechanism requires a value.");
879 }
880 break;
881
882 case MECH_IP6:
883 if (**mech_value == ':') {
884 err = SPF_c_parse_ip6(spf_response, spf_mechanism, *mech_value);
885 data_len = sizeof(struct in6_addr);
886 }
887 else {
888 err = SPF_E_MISSING_OPT;
889 SPF_response_add_error_ptr(spf_response, err,
890 NULL, *mech_value,
891 "Mechanism requires a value.");
892 }
893 break;
894
895 default:
896 if (**mech_value == ':' || **mech_value == '=') {
897 if (mechtype->has_domainspec == DOMSPEC_NONE) {
898 err = SPF_E_INVALID_OPT;
899 SPF_response_add_error_ptr(spf_response, err,
900 NULL, *mech_value,
901 "Mechanism does not permit a value.");
902 }
903 else {
904 (*mech_value)++; src_len--;
905 err = SPF_c_parse_domainspec(spf_server,
906 spf_response,
907 data, &data_len, SPF_MAX_MECH_LEN,
908 *mech_value, src_len,
910 mechtype->has_cidr, FALSE);
911 }
912 }
913 else if (**mech_value == '/') {
914 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
915 err = SPF_E_MISSING_OPT;
916 SPF_response_add_error_ptr(spf_response, err,
917 NULL, *mech_value,
918 "Mechanism requires a value.");
919 }
920 else if (mechtype->has_cidr == CIDR_NONE) {
921 err = SPF_E_INVALID_CIDR;
922 SPF_response_add_error_ptr(spf_response, err,
923 NULL, *mech_value,
924 "Mechanism does not permit a CIDR.");
925 }
926 else {
927 err = SPF_c_parse_domainspec(spf_server,
928 spf_response,
929 data, &data_len, SPF_MAX_MECH_LEN,
930 *mech_value, src_len,
933 }
934 }
935 else if (**mech_value == ' ' || **mech_value == '\0') {
936 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
937 err = SPF_E_MISSING_OPT;
938 SPF_response_add_error_ptr(spf_response, err,
939 NULL, *mech_value,
940 "Mechanism requires a value.");
941 }
942 else {
943 err = SPF_E_SUCCESS;
944 }
945 }
946 else {
947 err = SPF_E_SYNTAX;
948 SPF_response_add_error_ptr(spf_response, err,
949 NULL, *mech_value,
950 "Unknown character '%c' after mechanism.",
951 **mech_value);
952 }
953
954 /* Does not apply to ip4/ip6 */
955 spf_mechanism->mech_len = data_len;
956 break;
957 }
958
959 len += data_len;
960
961 /* Copy the thing in. */
962 if (err == SPF_E_SUCCESS) {
963 if (mechtype->is_dns_mech)
964 spf_record->num_dns_mech++;
965 if (SPF_c_ensure_capacity((void **)&spf_record->mech_first,
966 &spf_record->mech_size,
967 spf_record->mech_len + len) < 0)
968 return SPF_response_add_error_ptr(spf_response,
970 NULL, NULL,
971 "Failed to allocate memory for mechanism");
972 memcpy( (char *)spf_record->mech_first + spf_record->mech_len,
973 spf_mechanism,
974 len);
975 spf_record->mech_len += len;
976 spf_record->num_mech++;
977 }
978
979 *mech_value += src_len;
980
981 return err;
982}
983
984__attribute__((warn_unused_result))
985static SPF_errcode_t
986SPF_c_mod_add(SPF_server_t *spf_server,
987 SPF_record_t *spf_record, SPF_response_t *spf_response,
988 const char *mod_name, size_t name_len,
989 const char **mod_value)
990{
991 /* If this buffer is an irregular size, intel gcc does not align
992 * it properly, and all hell breaks loose. */
994 char buf[SPF_RECORD_BUFSIZ];
995);
996 SPF_mod_t *spf_modifier = (SPF_mod_t *)u.buf;
997 SPF_data_t *data;
998 size_t data_len;
999 size_t len;
1000 size_t src_len;
1001
1002 SPF_errcode_t err;
1003
1004 if (spf_server->debug)
1005 SPF_debugf("Adding modifier name=%lu@%s, value=%s",
1006 (unsigned long)name_len, mod_name, *mod_value);
1007
1008 memset(u.buf, 'A', sizeof(u.buf));
1009 memset(spf_modifier, 0, sizeof(SPF_mod_t));
1010
1011 if ( name_len > SPF_MAX_MOD_LEN )
1012 return SPF_E_BIG_MOD;
1013
1014 spf_modifier->name_len = name_len;
1015 spf_modifier->data_len = 0;
1016
1017 /* So that spf_modifier + len == SPF_mod_data(spf_modifier) */
1018 len = _align_sz(sizeof( SPF_mod_t ) + name_len);
1019
1020 if ( spf_record->mod_len + len > SPF_MAX_MOD_LEN )
1021 return SPF_E_BIG_MOD;
1022
1023 memcpy(SPF_mod_name(spf_modifier), mod_name, name_len);
1024
1025 data = SPF_mod_data(spf_modifier);
1026 data_len = 0;
1027
1028 src_len = strcspn(*mod_value, " ");
1029
1030 err = SPF_c_parse_macro(spf_server,
1031 spf_response,
1032 data, &data_len, SPF_MAX_MOD_LEN,
1033 *mod_value, src_len,
1035 TRUE );
1036 spf_modifier->data_len = data_len;
1037 len += data_len;
1038
1039 /* Copy the thing in. */
1040 if (err == SPF_E_SUCCESS) {
1041 if (SPF_c_ensure_capacity((void **)&spf_record->mod_first,
1042 &spf_record->mod_size,
1043 spf_record->mod_len + len) < 0)
1044 return SPF_response_add_error_ptr(spf_response,
1046 NULL, NULL,
1047 "Failed to allocate memory for modifier");
1048 memcpy( (char *)spf_record->mod_first + spf_record->mod_len,
1049 spf_modifier,
1050 len);
1051 spf_record->mod_len += len;
1052 spf_record->num_mod++;
1053 }
1054
1055 return err;
1056}
1057
1058static void
1059SPF_record_lint(SPF_server_t *spf_server,
1060 SPF_response_t *spf_response,
1061 SPF_record_t *spf_record)
1062{
1063 SPF_data_t *d, *data_end;
1064
1065 char *s;
1066 char *s_end;
1067
1068 int found_non_ip;
1069 int found_valid_tld;
1070
1071 SPF_mech_t *mech;
1072 SPF_data_t *data;
1073
1074 int i;
1075
1076 /* FIXME these warnings suck. Should call SPF_id2str to give more
1077 * context. */
1078
1079 mech = spf_record->mech_first;
1080 for (i = 0;
1081 i < spf_record->num_mech;
1082 i++,
1083 mech = SPF_mech_next( mech ) )
1084 {
1085 if ( ( mech->mech_type == MECH_ALL
1086 || mech->mech_type == MECH_REDIRECT )
1087 && i != spf_record->num_mech - 1 )
1088 {
1090 "Mechanisms found after the \"all:\" "
1091 "mechanism will be ignored.");
1092 }
1093
1094 /*
1095 * if we are dealing with a mechanism, make sure that the data
1096 * at least looks like a valid host name.
1097 *
1098 * note: this routine isn't called to handle ip4: and ip6: and all
1099 * the other mechanisms require a host name.
1100 */
1101
1102 if ( mech->mech_type == MECH_IP4
1103 || mech->mech_type == MECH_IP6 )
1104 continue;
1105
1106 data = SPF_mech_data( mech );
1107 data_end = SPF_mech_end_data( mech );
1108 if ( data == data_end )
1109 continue;
1110
1111 if ( data->dc.parm_type == PARM_CIDR )
1112 {
1113 data = SPF_data_next( data );
1114 if ( data == data_end )
1115 continue;
1116 }
1117
1118
1119 found_valid_tld = FALSE;
1120 found_non_ip = FALSE;
1121
1122 for( d = data; d < data_end; d = SPF_data_next( d ) )
1123 {
1124 switch( d->dv.parm_type )
1125 {
1126 case PARM_CIDR:
1127 SPF_error( "Multiple CIDR parameters found" );
1128 break;
1129
1130 case PARM_CLIENT_IP:
1131 case PARM_CLIENT_IP_P:
1132 case PARM_LP_FROM:
1133 found_valid_tld = FALSE;
1134 break;
1135
1136 case PARM_STRING:
1137 found_valid_tld = FALSE;
1138
1139 s = SPF_data_str( d );
1140 s_end = s + d->ds.len;
1141 for( ; s < s_end; s++ ) {
1142 if ( !isdigit( (unsigned char)( *s ) ) && *s != '.' && *s != ':' )
1143 found_non_ip = TRUE;
1144
1145 if ( *s == '.' )
1146 found_valid_tld = TRUE;
1147 else if ( !isalpha( (unsigned char)( *s ) ) )
1148 found_valid_tld = FALSE;
1149 }
1150 break;
1151
1152 default:
1153 found_non_ip = TRUE;
1154 found_valid_tld = TRUE;
1155
1156 break;
1157 }
1158 }
1159
1160 if ( !found_valid_tld || !found_non_ip ) {
1161 if ( !found_non_ip )
1163 "Invalid hostname (an IP address?)");
1164 else if ( !found_valid_tld )
1166 "Hostname has a missing or invalid TLD");
1167 }
1168
1169 }
1170
1171 /* FIXME check for modifiers that should probably be mechanisms */
1172}
1173
1174
1175
1184SPF_record_compile(SPF_server_t *spf_server,
1185 SPF_response_t *spf_response,
1186 SPF_record_t **spf_recordp,
1187 const char *record)
1188{
1189 const SPF_mechtype_t*mechtype;
1190 SPF_record_t *spf_record;
1191 SPF_error_t *spf_error;
1192 SPF_errcode_t err;
1193
1194 const char *name_start;
1195 size_t name_len;
1196
1197 const char *val_start;
1198 const char *val_end;
1199
1200 int prefix;
1201
1202 const char *p;
1203 int i;
1204
1205
1206 /*
1207 * make sure we were passed valid data to work with
1208 */
1209 SPF_ASSERT_NOTNULL(spf_server);
1210 SPF_ASSERT_NOTNULL(spf_recordp);
1211 SPF_ASSERT_NOTNULL(record);
1212
1213 if (spf_server->debug)
1214 SPF_debugf("Compiling record %s", record);
1215
1216 /*
1217 * and make sure that we will always set *spf_recordp
1218 * just incase we can't find a valid SPF record
1219 */
1220 *spf_recordp = NULL;
1221
1222 /*
1223 * See if this is record is even an SPF record
1224 */
1225 p = record;
1226
1227 if (strncasecmp(p, SPF_VER_STR, sizeof(SPF_VER_STR) - 1) != 0)
1228 return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
1229 NULL, p,
1230 "Could not find a valid SPF record");
1231 p += sizeof( SPF_VER_STR ) - 1;
1232
1233 if ( *p != '\0' && *p != ' ' )
1234 return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
1235 NULL, p,
1236 "Could not find a valid SPF record");
1237
1238 spf_record = SPF_record_new(spf_server, record);
1239 if (spf_record == NULL) {
1240 *spf_recordp = NULL;
1241 return SPF_response_add_error_ptr(spf_response, SPF_E_NO_MEMORY,
1242 NULL, p,
1243 "Failed to allocate an SPF record");
1244 }
1245 spf_record->version = 1;
1246 *spf_recordp = spf_record;
1247
1248 /*
1249 * parse the SPF record
1250 */
1251 while (*p != '\0') {
1252 /* TODO WARN: If it's a \n or a \t */
1253 /* skip to the next token */
1254 while (*p == ' ')
1255 p++;
1256
1257 if (*p == '\0')
1258 break;
1259
1260 /* see if we have a valid prefix */
1261 prefix = PREFIX_UNKNOWN;
1262 switch (*p) {
1263 case '+':
1264 prefix = PREFIX_PASS;
1265 p++;
1266 break;
1267
1268 case '-':
1269 prefix = PREFIX_FAIL;
1270 p++;
1271 break;
1272
1273 case '~':
1274 prefix = PREFIX_SOFTFAIL;
1275 p++;
1276 break;
1277
1278 case '?':
1279 prefix = PREFIX_NEUTRAL;
1280 p++;
1281 break;
1282
1283 default:
1284 while (ispunct((unsigned char)(*p))) {
1285 SPF_response_add_error_ptr(spf_response,
1287 "Invalid prefix '%c'", *p);
1288 p++;
1289 }
1290 break;
1291 }
1292
1293 name_start = p;
1294 val_end = name_start + strcspn(p, " ");
1295
1296 /* get the mechanism/modifier */
1297 if ( ! isalpha( (unsigned char)*p ) ) {
1298 /* We could just bail on this one. */
1299 SPF_response_add_error_ptr(spf_response,
1301 "Invalid character at start of mechanism");
1302 p += strcspn(p, " ");
1303 continue;
1304 }
1305 while ( isalnum( (unsigned char)*p ) || *p == '_' || *p == '-' )
1306 p++;
1307
1308/* TODO: These or macros like them are used in several places. Merge. */
1309#define STREQ_SIZEOF(a, b) \
1310 (strncasecmp((a), (b), sizeof( (b) ) - 1) == 0)
1311#define STREQ_SIZEOF_N(a, b, n) \
1312 (((n) == sizeof(b) - 1) && (strncasecmp((a),(b),(n)) == 0))
1313
1314 /* See if we have a modifier or a prefix */
1315 name_len = p - name_start;
1316
1317 if (spf_server->debug)
1318 SPF_debugf("Name starts at %s", name_start);
1319
1320 switch ( *p )
1321 {
1322 case ':':
1323 case '/':
1324 case ' ':
1325 case '\0':
1326 compile_mech: /* A bona fide label */
1327
1328 /*
1329 * parse the mechanism
1330 */
1331
1332 /* mechanisms default to PREFIX_PASS */
1333 if ( prefix == PREFIX_UNKNOWN )
1334 prefix = PREFIX_PASS;
1335
1336 if ( STREQ_SIZEOF_N(name_start, "a", name_len) )
1337 mechtype = SPF_mechtype_find(MECH_A);
1338 else if ( STREQ_SIZEOF_N(name_start, "mx", name_len) )
1339 mechtype = SPF_mechtype_find(MECH_MX);
1340 else if ( STREQ_SIZEOF_N(name_start, "ptr", name_len) )
1341 mechtype = SPF_mechtype_find(MECH_PTR);
1342 else if ( STREQ_SIZEOF_N(name_start, "include", name_len) )
1343 mechtype = SPF_mechtype_find(MECH_INCLUDE);
1344 else if ( STREQ_SIZEOF_N(name_start, "ip4", name_len) )
1345 mechtype = SPF_mechtype_find(MECH_IP4);
1346 else if ( STREQ_SIZEOF_N(name_start, "ip6", name_len) )
1347 mechtype = SPF_mechtype_find(MECH_IP6);
1348 else if ( STREQ_SIZEOF_N(name_start, "exists", name_len) )
1349 mechtype = SPF_mechtype_find(MECH_EXISTS);
1350 else if ( STREQ_SIZEOF_N(name_start, "all", name_len) )
1351 mechtype = SPF_mechtype_find(MECH_ALL);
1352#ifdef SPF_ALLOW_DEPRECATED_DEFAULT
1353 else if ( STREQ_SIZEOF_N(name_start,
1354 "default=allow", name_len) )
1355 {
1357 NULL, name_start,
1358 "Deprecated option 'default=allow'");
1359 mechtype = SPF_mechtype_find(MECH_ALL);
1360 prefix = PREFIX_PASS;
1361 }
1362 else if (STREQ_SIZEOF_N(name_start,
1363 "default=softfail",name_len))
1364 {
1366 NULL, name_start,
1367 "Deprecated option 'default=softfail'");
1368 mechtype = SPF_mechtype_find(MECH_ALL);
1369 prefix = PREFIX_SOFTFAIL;
1370 }
1371 else if ( STREQ_SIZEOF_N(name_start,
1372 "default=deny", name_len) )
1373 {
1375 NULL, name_start,
1376 "Deprecated option 'default=deny'");
1377 mechtype = SPF_mechtype_find(MECH_ALL);
1378 prefix = PREFIX_FAIL;
1379 }
1380 else if ( STREQ_SIZEOF(name_start, "default=") )
1381 {
1383 NULL, name_start,
1384 "Invalid modifier 'default=...'");
1385 p = val_end;
1386 continue;
1387 }
1388#endif
1389 /* FIXME the redirect mechanism needs to be moved to
1390 * the very end */
1391 else if ( STREQ_SIZEOF_N(name_start, "redirect", name_len) )
1392 mechtype = SPF_mechtype_find(MECH_REDIRECT);
1393 else
1394 {
1396 NULL, name_start,
1397 "Unknown mechanism found");
1398 p = val_end;
1399 continue;
1400 }
1401
1402 if (mechtype == NULL) {
1403 return SPF_response_add_error_ptr(spf_response,
1405 NULL, name_start,
1406 "Failed to find specification for "
1407 "a recognised mechanism");
1408 }
1409
1410 if (spf_server->debug)
1411 SPF_debugf("Adding mechanism type %d",
1412 (int)mechtype->mech_type);
1413
1414 val_start = p;
1415 err = SPF_c_mech_add(spf_server,
1416 spf_record, spf_response,
1417 mechtype, prefix, &val_start);
1418 if (err == SPF_E_NO_MEMORY)
1419 return err;
1420 /* XXX Else do nothing. Continue for the next error. */
1421 /* We shouldn't have to worry about the child function
1422 * updating the pointer. So we just use our 'well known'
1423 * copy. */
1424 p = val_end;
1425 break;
1426
1427 case '=':
1428
1429 /*
1430 * parse the modifier
1431 */
1432
1433 /* modifiers can't have prefixes */
1434 if (prefix != PREFIX_UNKNOWN)
1436 NULL, name_start,
1437 "Modifiers may not have prefixes");
1438 prefix = PREFIX_UNKNOWN; /* For redirect/include */
1439
1440#ifdef SPF_ALLOW_DEPRECATED_DEFAULT
1441 /* Deal with legacy special case */
1442 if ( STREQ_SIZEOF(name_start, "default=") ) {
1443 /* Consider the whole 'default=foo' as a token. */
1444 p = val_end;
1445 name_len = p - name_start;
1446 goto compile_mech;
1447 }
1448#endif
1449
1450 /* We treat 'redirect' as a mechanism. */
1451 if ( STREQ_SIZEOF(name_start, "redirect=") )
1452 goto compile_mech;
1453
1454 p++;
1455 val_start = p;
1456 err = SPF_c_mod_add(spf_server,
1457 spf_record, spf_response,
1458 name_start, name_len, &val_start);
1459 if (err == SPF_E_NO_MEMORY)
1460 return err;
1461 /* XXX Else do nothing. Continue for the next error. */
1462 p = val_end;
1463 break;
1464
1465
1466 default:
1468 NULL, p,
1469 "Invalid character in middle of mechanism");
1470 p = val_end;
1471 break;
1472 }
1473 }
1474
1475
1476 /*
1477 * check for common mistakes
1478 */
1479 SPF_record_lint(spf_server, spf_response, spf_record);
1480
1481
1482 /*
1483 * do final cleanup on the record
1484 */
1485
1486 /* FIXME realloc (shrink) spfi buffers? */
1487
1488 if (SPF_response_errors(spf_response) > 0) {
1489 for (i = 0; i < SPF_response_messages(spf_response); i++) {
1490 spf_error = SPF_response_message(spf_response, i);
1491 if (SPF_error_errorp(spf_error))
1492 return SPF_error_code(spf_error);
1493 }
1494 return SPF_response_add_error(spf_response,
1496 "Response has errors but can't find one!");
1497 }
1498
1499 return SPF_E_SUCCESS;
1500}
1501
1503SPF_record_compile_macro(SPF_server_t *spf_server,
1504 SPF_response_t *spf_response,
1505 SPF_macro_t **spf_macrop,
1506 const char *record)
1507{
1509 char buf[sizeof(SPF_macro_t) + SPF_MAX_MOD_LEN];
1510);
1511 SPF_macro_t *spf_macro = (SPF_macro_t *)ALIGNED_DECL(buf);
1512 SPF_data_t *data;
1513 SPF_errcode_t err;
1514 size_t size;
1515
1516 data = SPF_macro_data(spf_macro);
1517 spf_macro->macro_len = 0;
1518
1519 err = SPF_c_parse_macro(spf_server, spf_response,
1520 data, &spf_macro->macro_len, SPF_MAX_MOD_LEN,
1521 record, strlen(record),
1523 if (err != SPF_E_SUCCESS)
1524 return err;
1525
1526 /* XXX TODO: Tidy this up? */
1527 size = sizeof(SPF_macro_t) + spf_macro->macro_len;
1528 *spf_macrop = (SPF_macro_t *)malloc(size);
1529 if (!*spf_macrop)
1530 return SPF_E_NO_MEMORY;
1531 memcpy(*spf_macrop, ALIGNED_DECL(buf), size);
1532
1533 return SPF_E_SUCCESS;
1534}
int strncasecmp(const char *s1, const char *s2, size_t n)
Definition strncasecmp.c:11
SPF_errcode_t SPF_response_add_warn_ptr(SPF_response_t *rp, SPF_errcode_t code, const char *text, const char *tptr, const char *format,...)
SPF_errcode_t SPF_response_add_error_ptr(SPF_response_t *rp, SPF_errcode_t code, const char *text, const char *tptr, const char *format,...)
SPF_errcode_t SPF_response_add_warn(SPF_response_t *rp, SPF_errcode_t code, const char *format,...)
SPF_errcode_t SPF_response_add_error(SPF_response_t *rp, SPF_errcode_t code, const char *format,...)
SPF_error_t * SPF_response_message(SPF_response_t *rp, int idx)
int SPF_response_messages(SPF_response_t *rp)
char SPF_error_errorp(SPF_error_t *err)
SPF_errcode_t SPF_error_code(SPF_error_t *err)
SPF_errcode_t
@ SPF_E_MECH_AFTER_ALL
@ SPF_E_MOD_W_PREF
@ SPF_E_UNKNOWN_MECH
@ SPF_E_INVALID_CHAR
@ SPF_E_SYNTAX
@ SPF_E_INVALID_IP6
@ SPF_E_INTERNAL_ERROR
@ SPF_E_INVALID_OPT
@ SPF_E_BAD_HOST_TLD
@ SPF_E_NO_MEMORY
@ SPF_E_BAD_HOST_IP
@ SPF_E_INVALID_VAR
@ SPF_E_BIG_SUBDOM
@ SPF_E_NOT_SPF
@ SPF_E_INVALID_DELIM
@ SPF_E_MISSING_OPT
@ SPF_E_INVALID_IP4
@ SPF_E_BIG_MECH
@ SPF_E_INVALID_CIDR
@ SPF_E_SUCCESS
@ SPF_E_INVALID_PREFIX
@ SPF_E_BIG_MOD
int SPF_response_errors(SPF_response_t *rp)
#define PARM_LP_FROM
Definition spf_record.h:88
#define PARM_CUR_DOM
Definition spf_record.h:91
#define MECH_EXISTS
Definition spf_record.h:169
#define PREFIX_PASS
Definition spf_record.h:153
#define PARM_CLIENT_VER
Definition spf_record.h:96
#define PARM_REC_DOM
Definition spf_record.h:98
#define MECH_REDIRECT
Definition spf_record.h:171
#define PARM_STRING
Definition spf_record.h:100
#define PREFIX_NEUTRAL
Definition spf_record.h:156
#define MECH_INCLUDE
Definition spf_record.h:166
#define PARM_HELO_DOM
Definition spf_record.h:97
#define PARM_CLIENT_IP_P
Definition spf_record.h:93
#define MECH_IP6
Definition spf_record.h:168
#define PARM_CIDR
Definition spf_record.h:99
#define MECH_IP4
Definition spf_record.h:167
#define PARM_CLIENT_DOM
Definition spf_record.h:95
#define MECH_MX
Definition spf_record.h:164
#define MECH_PTR
Definition spf_record.h:165
#define PARM_ENV_FROM
Definition spf_record.h:89
#define PREFIX_UNKNOWN
Definition spf_record.h:157
#define SPF_MAX_MOD_LEN
Definition spf_record.h:78
#define MECH_ALL
Definition spf_record.h:170
#define MECH_UNKNOWN
Definition spf_record.h:162
#define SPF_MAX_MECH_LEN
Definition spf_record.h:77
#define MECH_A
Definition spf_record.h:163
#define PARM_TIME
Definition spf_record.h:94
#define PARM_CLIENT_IP
Definition spf_record.h:92
#define PARM_DP_FROM
Definition spf_record.h:90
#define PREFIX_SOFTFAIL
Definition spf_record.h:155
SPF_record_t * SPF_record_new(SPF_server_t *spf_server, const char *text)
Definition spf_record.c:48
#define PREFIX_FAIL
Definition spf_record.h:154
#define SPF_VER_STR
Definition spf.h:35
#define __attribute__(x)
Definition spf.h:17
#define SPF_errorf
Definition spf_log.h:77
#define SPF_ASSERT_NOTNULL(x)
Definition spf_log.h:118
#define SPF_error(errmsg)
Definition spf_log.h:40
#define SPF_debugf
Definition spf_log.h:80
#define NULL
#define TRUE
#define FALSE
#define SPF_ENSURE_STRING_AVAIL(_len)
#define SPF_RECORD_BUFSIZ
Definition spf_compile.c:62
#define ALIGN_DECL(decl)
Definition spf_compile.c:64
#define STREQ_SIZEOF(a, b)
#define SPF_ADD_LEN_TO(_val, _len, _max)
SPF_domspec_t
Definition spf_compile.c:53
@ DOMSPEC_REQUIRED
Definition spf_compile.c:54
@ DOMSPEC_OPTIONAL
Definition spf_compile.c:54
@ DOMSPEC_NONE
Definition spf_compile.c:54
#define SPF_FINI_STRING_LITERAL()
SPF_cidr_t
Definition spf_compile.c:48
@ CIDR_ONLY
Definition spf_compile.c:49
@ CIDR_NONE
Definition spf_compile.c:49
@ CIDR_OPTIONAL
Definition spf_compile.c:49
SPF_errcode_t SPF_record_compile(SPF_server_t *spf_server, SPF_response_t *spf_response, SPF_record_t **spf_recordp, const char *record)
#define ALIGNED_DECL(var)
Definition spf_compile.c:65
#define STREQ_SIZEOF_N(a, b, n)
#define SPF_INIT_STRING_LITERAL(_avail)
#define SPF_CHECK_IN_MODIFIER()
#define spf_num_mechanisms
Definition spf_compile.c:91
SPF_errcode_t SPF_record_compile_macro(SPF_server_t *spf_server, SPF_response_t *spf_response, SPF_macro_t **spf_macrop, const char *record)
unsigned char len
Definition spf_record.h:107
unsigned short rev
Definition spf_record.h:119
unsigned short delim_under
Definition spf_record.h:126
unsigned short delim_dot
Definition spf_record.h:121
unsigned char num_rhs
Definition spf_record.h:118
unsigned short delim_bar
Definition spf_record.h:125
unsigned char parm_type
Definition spf_record.h:117
unsigned short delim_dash
Definition spf_record.h:122
unsigned short delim_plus
Definition spf_record.h:123
unsigned short delim_equal
Definition spf_record.h:124
unsigned short url_encode
Definition spf_record.h:120
unsigned char ipv6
Definition spf_record.h:134
unsigned char ipv4
Definition spf_record.h:133
unsigned char parm_type
Definition spf_record.h:132
SPF_data_str_t ds
Definition spf_record.h:144
SPF_data_cidr_t dc
Definition spf_record.h:145
SPF_data_var_t dv
Definition spf_record.h:143
unsigned short mech_len
Definition spf_record.h:178
SPF_cidr_t has_cidr
Definition spf_compile.c:75
unsigned char mech_type
Definition spf_compile.c:72
unsigned char is_dns_mech
Definition spf_compile.c:73
SPF_domspec_t has_domainspec
Definition spf_compile.c:74