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
47 typedef
48 enum SPF_cidr_enum {
49 CIDR_NONE, CIDR_OPTIONAL, CIDR_ONLY
50 } SPF_cidr_t;
51
52 typedef
53 enum SPF_domspec_enum {
54 DOMSPEC_NONE, DOMSPEC_OPTIONAL, DOMSPEC_REQUIRED
55 } SPF_domspec_t;
56
57 /**
58 * This is greater than any possible total mechanism or modifier.
59 * SPF_MAX_MOD_LEN + SPF_MAX_STR_LEN
60 * SPF_MAX_MECH_LEN + SPF_MAX_STR_LEN
61 */
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
69 typedef
70 struct SPF_mechtype_struct
71 {
72 unsigned char mech_type;
73 unsigned char is_dns_mech;
74 SPF_domspec_t has_domainspec;
75 SPF_cidr_t has_cidr;
76 } SPF_mechtype_t;
77
78 static const SPF_mechtype_t spf_mechtypes[] = {
79 { MECH_UNKNOWN, FALSE, DOMSPEC_NONE, CIDR_NONE },
80 { MECH_A, TRUE, DOMSPEC_OPTIONAL, CIDR_OPTIONAL },
81 { MECH_MX, TRUE, DOMSPEC_OPTIONAL, CIDR_OPTIONAL },
82 { MECH_PTR, TRUE, DOMSPEC_OPTIONAL, CIDR_NONE },
83 { MECH_INCLUDE, TRUE, DOMSPEC_REQUIRED, CIDR_NONE },
84 { MECH_IP4, FALSE, DOMSPEC_REQUIRED, CIDR_OPTIONAL },
85 { MECH_IP6, FALSE, DOMSPEC_REQUIRED, CIDR_OPTIONAL },
86 { MECH_EXISTS, TRUE, DOMSPEC_REQUIRED, CIDR_NONE },
87 { MECH_ALL, FALSE, DOMSPEC_NONE, CIDR_NONE },
88 { MECH_REDIRECT, TRUE, DOMSPEC_REQUIRED, CIDR_NONE },
89 };
90
91 #define spf_num_mechanisms \
92 sizeof(spf_mechtypes) / sizeof(spf_mechtypes[0])
93
94 static const SPF_mechtype_t *
SPF_mechtype_find(int mech_type)95 SPF_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))
106 static int
SPF_c_ensure_capacity(void ** datap,size_t * sizep,size_t length)107 SPF_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
123 /**
124 * Parses an ip6 CIDR.
125 *
126 * Called with src pointing to the '/'.
127 *
128 * If a struct for IP addresses is added which itself contains a
129 * CIDR field, then this must be modified to take a (cidr *) rather
130 * than a (SPF_data_cidr_t *)
131 */
132 static SPF_errcode_t
SPF_c_parse_cidr_ip6(SPF_response_t * spf_response,unsigned char * maskp,const char * src)133 SPF_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) {
147 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
148 NULL, src,
149 "Invalid IPv6 CIDR netmask (>128)");
150 }
151 else if (mask == 0) {
152 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
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
165 /**
166 * Parses an ip4 CIDR.
167 *
168 * Called with src pointing to the '/', the second '/' if we are in
169 * a '//' notation, so that the digits start at src + 1.
170 *
171 * SPF_c_parse_cidr relies on the behaviour of strtoul terminating
172 * on a '/' as well as a nul byte here.
173 */
174 static SPF_errcode_t
SPF_c_parse_cidr_ip4(SPF_response_t * spf_response,unsigned char * maskp,const char * src)175 SPF_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 ) {
189 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
190 NULL, src,
191 "Invalid IPv4 CIDR netmask (>32)");
192 }
193 else if ( mask == 0 ) {
194 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
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
207 /**
208 * Parses an SPF CIDR.
209 *
210 * Modifies *src_len if a CIDR is found.
211 */
212 static SPF_errcode_t
SPF_c_parse_cidr(SPF_response_t * spf_response,SPF_data_cidr_t * data,const char * src,size_t * src_len)213 SPF_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
271 static SPF_errcode_t
SPF_c_parse_var(SPF_response_t * spf_response,SPF_data_var_t * data,const char * src,int is_mod)272 SPF_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 */
319 data->parm_type = PARM_CLIENT_IP;
320 break;
321
322 case 'c': /* SMTP client IP (pretty) */
323 SPF_CHECK_IN_MODIFIER();
324 data->parm_type = PARM_CLIENT_IP_P;
325 break;
326
327 case 't': /* time in UTC epoch secs */
328 SPF_CHECK_IN_MODIFIER();
329 data->parm_type = PARM_TIME;
330 break;
331
332 case 'p': /* SMTP client domain name */
333 data->parm_type = PARM_CLIENT_DOM;
334 break;
335
336 case 'v': /* IP ver str - in-addr/ip6 */
337 data->parm_type = PARM_CLIENT_VER;
338 break;
339
340 case 'h': /* HELO/EHLO domain */
341 data->parm_type = PARM_HELO_DOM;
342 break;
343
344 case 'r': /* receiving domain */
345 SPF_CHECK_IN_MODIFIER();
346 data->parm_type = PARM_REC_DOM;
347 break;
348
349 default:
350 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_VAR,
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,
427 SPF_E_INVALID_DELIM, NULL, p,
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 ds_avail = _avail; \
459 ds_len = 0; \
460 } while(0)
461
462 #define SPF_ENSURE_STRING_AVAIL(_len) do { \
463 if (ds_len + _len > ds_avail) \
464 return SPF_response_add_error_ptr(spf_response, \
465 SPF_E_BIG_STRING, NULL, src, \
466 "String literal fragment too long " \
467 "(%d chars, %d max)", \
468 ds_len, ds_avail); \
469 } while(0)
470
471 #define SPF_FINI_STRING_LITERAL() do { \
472 if ( ds_len > 0 ) { \
473 if ( ds_len > SPF_MAX_STR_LEN ) { \
474 return SPF_response_add_error_ptr(spf_response, \
475 SPF_E_BIG_STRING, NULL, src, \
476 "String literal too long " \
477 "(%d chars, %d max)", \
478 ds_len, SPF_MAX_STR_LEN); \
479 } \
480 data->ds.len = ds_len; \
481 len = sizeof( *data ) + ds_len; \
482 SPF_ADD_LEN_TO(*data_used, len, data_avail); \
483 data = SPF_data_next( data ); \
484 ds_len = 0; \
485 } \
486 } while(0)
487
488 /**
489 * Parses an SPF macro string.
490 *
491 * Note that we cannot write data_avail bytes from data, since we
492 * might be called with a modified data pointer. We MUST compare
493 * data_used with data_avail.
494 *
495 * @param spf_server The SPF server on whose behalf the record is being compiled.
496 * @param spf_response The SPF response in which to store errors.
497 * @param data Output buffer pointer.
498 * @param data_used Output parameter for amount of data written to output buffer.
499 * @param data_avail Input parameter for size of output buffer.
500 * @param src Input buffer pointer.
501 * @param src_len Input buffer length.
502 * @param big_err The error code to return on an over-length condition.
503 * @param is_mod True if this is a modifier.
504 */
505 static SPF_errcode_t
SPF_c_parse_macro(SPF_server_t * spf_server,SPF_response_t * spf_response,SPF_data_t * data,size_t * data_used,size_t data_avail,const char * src,size_t src_len,SPF_errcode_t big_err,int is_mod)506 SPF_c_parse_macro(SPF_server_t *spf_server,
507 SPF_response_t *spf_response,
508 SPF_data_t *data, size_t *data_used, size_t data_avail,
509 const char *src, size_t src_len,
510 SPF_errcode_t big_err,
511 int is_mod)
512 {
513 SPF_errcode_t err;
514 /* Generic parsing iterators and boundaries */
515 size_t idx;
516 size_t len;
517 /* For parsing strings. */
518 char *dst;
519 size_t ds_avail;
520 size_t ds_len;
521
522 if (spf_server->debug)
523 SPF_debugf("Parsing macro starting at %s", src);
524
525 #if 0
526 if ((void *)data != _align_ptr((void *)data))
527 SPF_errorf("Data pointer %p is not aligned: Cannot compile.",
528 data);
529 #endif
530
531 /*
532 * Create the data blocks
533 */
534 idx = 0;
535
536 /* Initialise the block as a string. If ds_len == 0 later, we
537 * will just clobber it. */
538 SPF_INIT_STRING_LITERAL(data_avail - *data_used);
539
540 // while ( p != end ) {
541 while (idx < src_len) {
542 if (spf_server->debug > 3)
543 SPF_debugf("Current data is at %p", data);
544 /* Either the unit is terminated by a space, or we hit a %.
545 * We should only hit a space if we run past src_len. */
546 len = strcspn(&src[idx], " %"); // XXX Also tab?
547 if (len > 0) { /* An optimisation */
548 /* Don't over-run into the CIDR. */
549 if (idx + len > src_len)
550 len = src_len - idx;
551 if (spf_server->debug > 3)
552 SPF_debugf("Adding string literal (%lu): '%*.*s'",
553 (unsigned long)len,
554 (int)len, (int)len, &src[idx]);
555 /* XXX Bounds-check here. */
556 SPF_ENSURE_STRING_AVAIL(len);
557 memcpy(dst, &src[idx], len);
558 ds_len += len;
559 dst += len;
560 idx += len;
561
562 /* If len == 0 then we never entered the while(). Thus
563 * if idx == src_len, then len != 0 and we reach this test.
564 */
565 }
566 /* However, this logic is overcomplex and I am a simpleton,
567 * so I have moved it out of the condition above. */
568 if (idx == src_len)
569 break;
570
571 /* Now, we must have a %-escape code, since if we hit a
572 * space, then we are at the end.
573 * Incrementing idx consumes the % we hit first, and then
574 * we switch on the following character, which also
575 * increments idx. */
576 idx++;
577 switch (src[idx]) {
578 case '%':
579 if (spf_server->debug > 3)
580 SPF_debugf("Adding literal %%");
581 SPF_ENSURE_STRING_AVAIL(1);
582 *dst++ = '%';
583 ds_len++;
584 idx++;
585 break;
586
587 case '_':
588 if (spf_server->debug > 3)
589 SPF_debugf("Adding literal space");
590 SPF_ENSURE_STRING_AVAIL(1);
591 *dst++ = ' ';
592 ds_len++;
593 idx++;
594 break;
595
596 case '-':
597 if (spf_server->debug > 3)
598 SPF_debugf("Adding escaped space");
599 SPF_ENSURE_STRING_AVAIL(3);
600 *dst++ = '%'; *dst++ = '2'; *dst++ = '0';
601 ds_len += 3;
602 idx++;
603 break;
604
605 default:
606 if (spf_server->debug > 3)
607 SPF_debugf("Adding illegal %%-follower '%c' at %d",
608 src[idx], idx);
609 /* SPF spec says to treat it as a literal, not
610 * SPF_E_INVALID_ESC */
611 /* FIXME issue a warning? */
612 SPF_ENSURE_STRING_AVAIL(1);
613 *dst++ = '%';
614 ds_len++;
615 break;
616
617 case '{': /*vi:}*/
618 SPF_FINI_STRING_LITERAL();
619 if (spf_server->debug > 3)
620 SPF_debugf("Adding macro, data is at %p", data);
621
622 /* this must be a variable */
623 idx++;
624 err = SPF_c_parse_var(spf_response, &data->dv, &src[idx], is_mod);
625 if (err != SPF_E_SUCCESS)
626 return err;
627 idx += strcspn(&src[idx], "} ");
628 if (src[idx] == '}')
629 idx++;
630 else if (src[idx] == ' ')
631 return SPF_response_add_error_ptr(spf_response,
632 SPF_E_INVALID_VAR,
633 src, &src[idx],
634 "Unterminated variable?");
635
636
637 len = SPF_data_len(data);
638 SPF_ADD_LEN_TO(*data_used, len, data_avail);
639 data = SPF_data_next( data );
640 if (spf_server->debug > 3)
641 SPF_debugf("Next data is at %p", data);
642
643 SPF_INIT_STRING_LITERAL(data_avail - *data_used);
644
645 break;
646 }
647 }
648
649 SPF_FINI_STRING_LITERAL();
650
651 return SPF_E_SUCCESS;
652
653 }
654
655 /* What a fuck-ugly prototype. */
656 /**
657 * Parses an SPF domainspec.
658 *
659 * @param spf_server The SPF server on whose behalf the record is being compiled.
660 * @param spf_response The SPF response in which to store errors.
661 * @param data Output buffer pointer.
662 * @param data_used Output parameter for amount of data written to output buffer.
663 * @param data_avail Input parameter for size of output buffer.
664 * @param src Input buffer pointer.
665 * @param src_len Input buffer length.
666 * @param big_err The error code to return on an over-length condition.
667 * @param cidr_ok True if a CIDR mask is permitted on this domainspec.
668 * @param is_mod True if this is a modifier.
669 */
670 static SPF_errcode_t
SPF_c_parse_domainspec(SPF_server_t * spf_server,SPF_response_t * spf_response,SPF_data_t * data,size_t * data_used,size_t data_avail,const char * src,size_t src_len,SPF_errcode_t big_err,SPF_cidr_t cidr_ok,int is_mod)671 SPF_c_parse_domainspec(SPF_server_t *spf_server,
672 SPF_response_t *spf_response,
673 SPF_data_t *data, size_t *data_used, size_t data_avail,
674 const char *src, size_t src_len,
675 SPF_errcode_t big_err,
676 SPF_cidr_t cidr_ok, int is_mod)
677 {
678 SPF_errcode_t err;
679 /* Generic parsing iterators and boundaries */
680 size_t len;
681
682 if (spf_server->debug)
683 SPF_debugf("Parsing domainspec starting at %s, cidr is %s",
684 src,
685 cidr_ok == CIDR_OPTIONAL ? "optional" :
686 cidr_ok == CIDR_ONLY ? "only" :
687 cidr_ok == CIDR_NONE ? "forbidden" :
688 "ERROR!"
689 );
690
691 /*
692 * create the CIDR length info
693 */
694 if (cidr_ok == CIDR_OPTIONAL || cidr_ok == CIDR_ONLY) {
695 err = SPF_c_parse_cidr(spf_response, &data->dc, src, &src_len);
696 if (err != SPF_E_SUCCESS)
697 return err;
698 if (data->dc.ipv4 != 0 || data->dc.ipv6 != 0) {
699 len = SPF_data_len(data);
700 SPF_ADD_LEN_TO(*data_used, len, data_avail);
701 data = SPF_data_next(data);
702 }
703
704 if (cidr_ok == CIDR_ONLY && src_len > 0) {
705 /* We had a mechanism followed by a '/', thus it HAS to be
706 * a CIDR, and the peculiar-looking error message is
707 * justified. However, we don't know _which_ CIDR. */
708 return SPF_response_add_error_ptr(spf_response,
709 SPF_E_INVALID_CIDR,
710 NULL, src,
711 "Invalid CIDR after mechanism");
712 }
713 }
714
715 return SPF_c_parse_macro(spf_server, spf_response,
716 data, data_used, data_avail,
717 src, src_len, big_err, is_mod);
718 }
719
720
721 /**
722 * Parses the data for an ip4 mechanism.
723 *
724 * When this method is called, start points to the ':'.
725 */
726 static SPF_errcode_t
SPF_c_parse_ip4(SPF_response_t * spf_response,SPF_mech_t * mech,char const * start)727 SPF_c_parse_ip4(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
728 {
729 const char *end;
730 const char *p;
731
732 char buf[ INET6_ADDRSTRLEN ];
733 size_t len;
734 SPF_errcode_t err;
735
736 unsigned char mask;
737 struct in_addr *addr;
738
739 start++;
740 len = strcspn(start, " ");
741 end = start + len;
742 p = end - 1;
743
744 mask = 0;
745 while (isdigit( (unsigned char)(*p) ))
746 p--;
747 if (p != (end - 1) && *p == '/') {
748 err = SPF_c_parse_cidr_ip4(spf_response, &mask, p);
749 if (err)
750 return err;
751 end = p;
752 }
753 mech->mech_len = mask;
754
755 len = end - start;
756 if ( len > sizeof( buf ) - 1 )
757 return SPF_E_INVALID_IP4;
758
759 memcpy( buf, start, len );
760 buf[ len ] = '\0';
761 addr = SPF_mech_ip4_data(mech);
762 err = inet_pton( AF_INET, buf, addr );
763 if ( err <= 0 )
764 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_IP4,
765 NULL, buf, NULL);
766
767 return SPF_E_SUCCESS;
768 }
769
770 /**
771 * Parses the data for an ip6 mechanism.
772 *
773 * When this method is called, start points to the ':'.
774 */
775 static SPF_errcode_t
SPF_c_parse_ip6(SPF_response_t * spf_response,SPF_mech_t * mech,char const * start)776 SPF_c_parse_ip6(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
777 {
778 const char *end;
779 const char *p;
780
781 char buf[ INET6_ADDRSTRLEN ];
782 size_t len;
783 int err;
784
785 unsigned char mask;
786 struct in6_addr *addr;
787
788 start++;
789 len = strcspn(start, " ");
790 end = start + len;
791 p = end - 1;
792
793 mask = 0;
794 while (isdigit( (unsigned char)(*p) ))
795 p--;
796 if (p != (end - 1) && *p == '/') {
797 err = SPF_c_parse_cidr_ip6(spf_response, &mask, p);
798 if (err)
799 return err;
800 end = p;
801 }
802 mech->mech_len = mask;
803
804 len = end - start;
805 if ( len > sizeof( buf ) - 1 )
806 return SPF_E_INVALID_IP6;
807
808 memcpy( buf, start, len );
809 buf[ len ] = '\0';
810 addr = SPF_mech_ip6_data(mech);
811 err = inet_pton( AF_INET6, buf, addr );
812 if ( err <= 0 )
813 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_IP6,
814 NULL, buf, NULL);
815
816 return SPF_E_SUCCESS;
817 }
818
819
820 /* XXX TODO: Make this take (const char *) instead of (const char **)
821 * because the caller ignores the modified value. */
822 __attribute__((warn_unused_result))
823 static SPF_errcode_t
SPF_c_mech_add(SPF_server_t * spf_server,SPF_record_t * spf_record,SPF_response_t * spf_response,const SPF_mechtype_t * mechtype,int prefix,const char ** mech_value)824 SPF_c_mech_add(SPF_server_t *spf_server,
825 SPF_record_t *spf_record, SPF_response_t *spf_response,
826 const SPF_mechtype_t *mechtype, int prefix,
827 const char **mech_value)
828 {
829 /* If this buffer is an irregular size, intel gcc does not align
830 * it properly, and all hell breaks loose. */
831 ALIGN_DECL(
832 char buf[SPF_RECORD_BUFSIZ];
833 );
834 SPF_mech_t *spf_mechanism = (SPF_mech_t *)ALIGNED_DECL(buf);
835 SPF_data_t *data;
836 size_t data_len;
837 size_t len;
838 size_t src_len;
839
840 SPF_errcode_t err;
841
842 memset(u.buf, 'B', sizeof(u.buf)); /* Poison the buffer. */
843 memset(spf_mechanism, 0, sizeof(SPF_mech_t));
844
845 if (spf_server->debug)
846 SPF_debugf("SPF_c_mech_add: type=%d, value=%s",
847 mechtype->mech_type, *mech_value);
848
849 spf_mechanism->prefix_type = prefix;
850 spf_mechanism->mech_type = mechtype->mech_type;
851 spf_mechanism->mech_len = 0;
852
853 len = sizeof( SPF_mech_t );
854
855 if ( spf_record->mech_len + len > SPF_MAX_MECH_LEN )
856 return SPF_E_BIG_MECH;
857
858 data = SPF_mech_data(spf_mechanism);
859 data_len = 0;
860
861 src_len = strcspn(*mech_value, " ");
862
863 switch (mechtype->mech_type) {
864 /* We know the properties of IP4 and IP6. */
865 case MECH_IP4:
866 if (**mech_value == ':') {
867 err = SPF_c_parse_ip4(spf_response, spf_mechanism, *mech_value);
868 data_len = sizeof(struct in_addr);
869 }
870 else {
871 err = SPF_E_MISSING_OPT;
872 SPF_response_add_error_ptr(spf_response, err,
873 NULL, *mech_value,
874 "Mechanism requires a value.");
875 }
876 break;
877
878 case MECH_IP6:
879 if (**mech_value == ':') {
880 err = SPF_c_parse_ip6(spf_response, spf_mechanism, *mech_value);
881 data_len = sizeof(struct in6_addr);
882 }
883 else {
884 err = SPF_E_MISSING_OPT;
885 SPF_response_add_error_ptr(spf_response, err,
886 NULL, *mech_value,
887 "Mechanism requires a value.");
888 }
889 break;
890
891 default:
892 if (**mech_value == ':' || **mech_value == '=') {
893 if (mechtype->has_domainspec == DOMSPEC_NONE) {
894 err = SPF_E_INVALID_OPT;
895 SPF_response_add_error_ptr(spf_response, err,
896 NULL, *mech_value,
897 "Mechanism does not permit a value.");
898 }
899 else {
900 (*mech_value)++; src_len--;
901 err = SPF_c_parse_domainspec(spf_server,
902 spf_response,
903 data, &data_len, SPF_MAX_MECH_LEN,
904 *mech_value, src_len,
905 SPF_E_BIG_MECH,
906 mechtype->has_cidr, FALSE);
907 }
908 }
909 else if (**mech_value == '/') {
910 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
911 err = SPF_E_MISSING_OPT;
912 SPF_response_add_error_ptr(spf_response, err,
913 NULL, *mech_value,
914 "Mechanism requires a value.");
915 }
916 else if (mechtype->has_cidr == CIDR_NONE) {
917 err = SPF_E_INVALID_CIDR;
918 SPF_response_add_error_ptr(spf_response, err,
919 NULL, *mech_value,
920 "Mechanism does not permit a CIDR.");
921 }
922 else {
923 err = SPF_c_parse_domainspec(spf_server,
924 spf_response,
925 data, &data_len, SPF_MAX_MECH_LEN,
926 *mech_value, src_len,
927 SPF_E_BIG_MECH,
928 CIDR_ONLY, FALSE);
929 }
930 }
931 else if (**mech_value == ' ' || **mech_value == '\0') {
932 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
933 err = SPF_E_MISSING_OPT;
934 SPF_response_add_error_ptr(spf_response, err,
935 NULL, *mech_value,
936 "Mechanism requires a value.");
937 }
938 else {
939 err = SPF_E_SUCCESS;
940 }
941 }
942 else {
943 err = SPF_E_SYNTAX;
944 SPF_response_add_error_ptr(spf_response, err,
945 NULL, *mech_value,
946 "Unknown character '%c' after mechanism.",
947 **mech_value);
948 }
949
950 /* Does not apply to ip4/ip6 */
951 spf_mechanism->mech_len = data_len;
952 break;
953 }
954
955 len += data_len;
956
957 /* Copy the thing in. */
958 if (err == SPF_E_SUCCESS) {
959 if (mechtype->is_dns_mech)
960 spf_record->num_dns_mech++;
961 if (SPF_c_ensure_capacity((void **)&spf_record->mech_first,
962 &spf_record->mech_size,
963 spf_record->mech_len + len) < 0)
964 return SPF_response_add_error_ptr(spf_response,
965 SPF_E_NO_MEMORY,
966 NULL, NULL,
967 "Failed to allocate memory for mechanism");
968 memcpy( (char *)spf_record->mech_first + spf_record->mech_len,
969 spf_mechanism,
970 len);
971 spf_record->mech_len += len;
972 spf_record->num_mech++;
973 }
974
975 *mech_value += src_len;
976
977 return err;
978 }
979
980 __attribute__((warn_unused_result))
981 static SPF_errcode_t
SPF_c_mod_add(SPF_server_t * spf_server,SPF_record_t * spf_record,SPF_response_t * spf_response,const char * mod_name,size_t name_len,const char ** mod_value)982 SPF_c_mod_add(SPF_server_t *spf_server,
983 SPF_record_t *spf_record, SPF_response_t *spf_response,
984 const char *mod_name, size_t name_len,
985 const char **mod_value)
986 {
987 /* If this buffer is an irregular size, intel gcc does not align
988 * it properly, and all hell breaks loose. */
989 ALIGN_DECL(
990 char buf[SPF_RECORD_BUFSIZ];
991 );
992 SPF_mod_t *spf_modifier = (SPF_mod_t *)u.buf;
993 SPF_data_t *data;
994 size_t data_len;
995 size_t len;
996 size_t src_len;
997
998 SPF_errcode_t err;
999
1000 if (spf_server->debug)
1001 SPF_debugf("Adding modifier name=%lu@%s, value=%s",
1002 (unsigned long)name_len, mod_name, *mod_value);
1003
1004 memset(u.buf, 'A', sizeof(u.buf));
1005 memset(spf_modifier, 0, sizeof(SPF_mod_t));
1006
1007 if ( name_len > SPF_MAX_MOD_LEN )
1008 return SPF_E_BIG_MOD;
1009
1010 spf_modifier->name_len = name_len;
1011 spf_modifier->data_len = 0;
1012
1013 /* So that spf_modifier + len == SPF_mod_data(spf_modifier) */
1014 len = _align_sz(sizeof( SPF_mod_t ) + name_len);
1015
1016 if ( spf_record->mod_len + len > SPF_MAX_MOD_LEN )
1017 return SPF_E_BIG_MOD;
1018
1019 memcpy(SPF_mod_name(spf_modifier), mod_name, name_len);
1020
1021 data = SPF_mod_data(spf_modifier);
1022 data_len = 0;
1023
1024 src_len = strcspn(*mod_value, " ");
1025
1026 err = SPF_c_parse_macro(spf_server,
1027 spf_response,
1028 data, &data_len, SPF_MAX_MOD_LEN,
1029 *mod_value, src_len,
1030 SPF_E_BIG_MOD,
1031 TRUE );
1032 spf_modifier->data_len = data_len;
1033 len += data_len;
1034
1035 /* Copy the thing in. */
1036 if (err == SPF_E_SUCCESS) {
1037 if (SPF_c_ensure_capacity((void **)&spf_record->mod_first,
1038 &spf_record->mod_size,
1039 spf_record->mod_len + len) < 0)
1040 return SPF_response_add_error_ptr(spf_response,
1041 SPF_E_NO_MEMORY,
1042 NULL, NULL,
1043 "Failed to allocate memory for modifier");
1044 memcpy( (char *)spf_record->mod_first + spf_record->mod_len,
1045 spf_modifier,
1046 len);
1047 spf_record->mod_len += len;
1048 spf_record->num_mod++;
1049 }
1050
1051 return err;
1052 }
1053
1054 static void
SPF_record_lint(SPF_server_t * spf_server,SPF_response_t * spf_response,SPF_record_t * spf_record)1055 SPF_record_lint(SPF_server_t *spf_server,
1056 SPF_response_t *spf_response,
1057 SPF_record_t *spf_record)
1058 {
1059 SPF_data_t *d, *data_end;
1060
1061 char *s;
1062 char *s_end;
1063
1064 int found_non_ip;
1065 int found_valid_tld;
1066
1067 SPF_mech_t *mech;
1068 SPF_data_t *data;
1069
1070 int i;
1071
1072 /* FIXME these warnings suck. Should call SPF_id2str to give more
1073 * context. */
1074
1075 mech = spf_record->mech_first;
1076 for (i = 0;
1077 i < spf_record->num_mech;
1078 i++,
1079 mech = SPF_mech_next( mech ) )
1080 {
1081 if ( ( mech->mech_type == MECH_ALL
1082 || mech->mech_type == MECH_REDIRECT )
1083 && i != spf_record->num_mech - 1 )
1084 {
1085 SPF_response_add_warn(spf_response, SPF_E_MECH_AFTER_ALL,
1086 "Mechanisms found after the \"all:\" "
1087 "mechanism will be ignored.");
1088 }
1089
1090 /*
1091 * if we are dealing with a mechanism, make sure that the data
1092 * at least looks like a valid host name.
1093 *
1094 * note: this routine isn't called to handle ip4: and ip6: and all
1095 * the other mechanisms require a host name.
1096 */
1097
1098 if ( mech->mech_type == MECH_IP4
1099 || mech->mech_type == MECH_IP6 )
1100 continue;
1101
1102 data = SPF_mech_data( mech );
1103 data_end = SPF_mech_end_data( mech );
1104 if ( data == data_end )
1105 continue;
1106
1107 if ( data->dc.parm_type == PARM_CIDR )
1108 {
1109 data = SPF_data_next( data );
1110 if ( data == data_end )
1111 continue;
1112 }
1113
1114
1115 found_valid_tld = FALSE;
1116 found_non_ip = FALSE;
1117
1118 for( d = data; d < data_end; d = SPF_data_next( d ) )
1119 {
1120 switch( d->dv.parm_type )
1121 {
1122 case PARM_CIDR:
1123 SPF_error( "Multiple CIDR parameters found" );
1124 break;
1125
1126 case PARM_CLIENT_IP:
1127 case PARM_CLIENT_IP_P:
1128 case PARM_LP_FROM:
1129 found_valid_tld = FALSE;
1130 break;
1131
1132 case PARM_STRING:
1133 found_valid_tld = FALSE;
1134
1135 s = SPF_data_str( d );
1136 s_end = s + d->ds.len;
1137 for( ; s < s_end; s++ ) {
1138 if ( !isdigit( (unsigned char)( *s ) ) && *s != '.' && *s != ':' )
1139 found_non_ip = TRUE;
1140
1141 if ( *s == '.' )
1142 found_valid_tld = TRUE;
1143 else if ( !isalpha( (unsigned char)( *s ) ) )
1144 found_valid_tld = FALSE;
1145 }
1146 break;
1147
1148 default:
1149 found_non_ip = TRUE;
1150 found_valid_tld = TRUE;
1151
1152 break;
1153 }
1154 }
1155
1156 if ( !found_valid_tld || !found_non_ip ) {
1157 if ( !found_non_ip )
1158 SPF_response_add_warn(spf_response, SPF_E_BAD_HOST_IP,
1159 "Invalid hostname (an IP address?)");
1160 else if ( !found_valid_tld )
1161 SPF_response_add_warn(spf_response, SPF_E_BAD_HOST_TLD,
1162 "Hostname has a missing or invalid TLD");
1163 }
1164
1165 }
1166
1167 /* FIXME check for modifiers that should probably be mechanisms */
1168 }
1169
1170
1171
1172 /**
1173 * The SPF compiler.
1174 *
1175 * It converts the SPF record in string format that is easy for people
1176 * to deal with into a compact binary format that is easy for
1177 * computers to deal with.
1178 */
1179 SPF_errcode_t
SPF_record_compile(SPF_server_t * spf_server,SPF_response_t * spf_response,SPF_record_t ** spf_recordp,const char * record)1180 SPF_record_compile(SPF_server_t *spf_server,
1181 SPF_response_t *spf_response,
1182 SPF_record_t **spf_recordp,
1183 const char *record)
1184 {
1185 const SPF_mechtype_t*mechtype;
1186 SPF_record_t *spf_record;
1187 SPF_error_t *spf_error;
1188 SPF_errcode_t err;
1189
1190 const char *name_start;
1191 size_t name_len;
1192
1193 const char *val_start;
1194 const char *val_end;
1195
1196 int prefix;
1197
1198 const char *p;
1199 int i;
1200
1201
1202 /*
1203 * make sure we were passed valid data to work with
1204 */
1205 SPF_ASSERT_NOTNULL(spf_server);
1206 SPF_ASSERT_NOTNULL(spf_recordp);
1207 SPF_ASSERT_NOTNULL(record);
1208
1209 if (spf_server->debug)
1210 SPF_debugf("Compiling record %s", record);
1211
1212 /*
1213 * and make sure that we will always set *spf_recordp
1214 * just incase we can't find a valid SPF record
1215 */
1216 *spf_recordp = NULL;
1217
1218 /*
1219 * See if this is record is even an SPF record
1220 */
1221 p = record;
1222
1223 if (strncasecmp(p, SPF_VER_STR, sizeof(SPF_VER_STR) - 1) != 0)
1224 return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
1225 NULL, p,
1226 "Could not find a valid SPF record");
1227 p += sizeof( SPF_VER_STR ) - 1;
1228
1229 if ( *p != '\0' && *p != ' ' )
1230 return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
1231 NULL, p,
1232 "Could not find a valid SPF record");
1233
1234 spf_record = SPF_record_new(spf_server, record);
1235 if (spf_record == NULL) {
1236 *spf_recordp = NULL;
1237 return SPF_response_add_error_ptr(spf_response, SPF_E_NO_MEMORY,
1238 NULL, p,
1239 "Failed to allocate an SPF record");
1240 }
1241 spf_record->version = 1;
1242 *spf_recordp = spf_record;
1243
1244 /*
1245 * parse the SPF record
1246 */
1247 while (*p != '\0') {
1248 /* TODO WARN: If it's a \n or a \t */
1249 /* skip to the next token */
1250 while (*p == ' ')
1251 p++;
1252
1253 if (*p == '\0')
1254 break;
1255
1256 /* see if we have a valid prefix */
1257 prefix = PREFIX_UNKNOWN;
1258 switch (*p) {
1259 case '+':
1260 prefix = PREFIX_PASS;
1261 p++;
1262 break;
1263
1264 case '-':
1265 prefix = PREFIX_FAIL;
1266 p++;
1267 break;
1268
1269 case '~':
1270 prefix = PREFIX_SOFTFAIL;
1271 p++;
1272 break;
1273
1274 case '?':
1275 prefix = PREFIX_NEUTRAL;
1276 p++;
1277 break;
1278
1279 default:
1280 while (ispunct((unsigned char)(*p))) {
1281 SPF_response_add_error_ptr(spf_response,
1282 SPF_E_INVALID_PREFIX, NULL, p,
1283 "Invalid prefix '%c'", *p);
1284 p++;
1285 }
1286 break;
1287 }
1288
1289 name_start = p;
1290 val_end = name_start + strcspn(p, " ");
1291
1292 /* get the mechanism/modifier */
1293 if ( ! isalpha( (unsigned char)*p ) ) {
1294 /* We could just bail on this one. */
1295 SPF_response_add_error_ptr(spf_response,
1296 SPF_E_INVALID_CHAR, NULL, p,
1297 "Invalid character at start of mechanism");
1298 p += strcspn(p, " ");
1299 continue;
1300 }
1301 while ( isalnum( (unsigned char)*p ) || *p == '_' || *p == '-' )
1302 p++;
1303
1304 /* TODO: These or macros like them are used in several places. Merge. */
1305 #define STREQ_SIZEOF(a, b) \
1306 (strncasecmp((a), (b), sizeof( (b) ) - 1) == 0)
1307 #define STREQ_SIZEOF_N(a, b, n) \
1308 (((n) == sizeof(b) - 1) && (strncasecmp((a),(b),(n)) == 0))
1309
1310 /* See if we have a modifier or a prefix */
1311 name_len = p - name_start;
1312
1313 if (spf_server->debug)
1314 SPF_debugf("Name starts at %s", name_start);
1315
1316 switch ( *p )
1317 {
1318 case ':':
1319 case '/':
1320 case ' ':
1321 case '\0':
1322 compile_mech: /* A bona fide label */
1323
1324 /*
1325 * parse the mechanism
1326 */
1327
1328 /* mechanisms default to PREFIX_PASS */
1329 if ( prefix == PREFIX_UNKNOWN )
1330 prefix = PREFIX_PASS;
1331
1332 if ( STREQ_SIZEOF_N(name_start, "a", name_len) )
1333 mechtype = SPF_mechtype_find(MECH_A);
1334 else if ( STREQ_SIZEOF_N(name_start, "mx", name_len) )
1335 mechtype = SPF_mechtype_find(MECH_MX);
1336 else if ( STREQ_SIZEOF_N(name_start, "ptr", name_len) )
1337 mechtype = SPF_mechtype_find(MECH_PTR);
1338 else if ( STREQ_SIZEOF_N(name_start, "include", name_len) )
1339 mechtype = SPF_mechtype_find(MECH_INCLUDE);
1340 else if ( STREQ_SIZEOF_N(name_start, "ip4", name_len) )
1341 mechtype = SPF_mechtype_find(MECH_IP4);
1342 else if ( STREQ_SIZEOF_N(name_start, "ip6", name_len) )
1343 mechtype = SPF_mechtype_find(MECH_IP6);
1344 else if ( STREQ_SIZEOF_N(name_start, "exists", name_len) )
1345 mechtype = SPF_mechtype_find(MECH_EXISTS);
1346 else if ( STREQ_SIZEOF_N(name_start, "all", name_len) )
1347 mechtype = SPF_mechtype_find(MECH_ALL);
1348 #ifdef SPF_ALLOW_DEPRECATED_DEFAULT
1349 else if ( STREQ_SIZEOF_N(name_start,
1350 "default=allow", name_len) )
1351 {
1352 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
1353 NULL, name_start,
1354 "Deprecated option 'default=allow'");
1355 mechtype = SPF_mechtype_find(MECH_ALL);
1356 prefix = PREFIX_PASS;
1357 }
1358 else if (STREQ_SIZEOF_N(name_start,
1359 "default=softfail",name_len))
1360 {
1361 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
1362 NULL, name_start,
1363 "Deprecated option 'default=softfail'");
1364 mechtype = SPF_mechtype_find(MECH_ALL);
1365 prefix = PREFIX_SOFTFAIL;
1366 }
1367 else if ( STREQ_SIZEOF_N(name_start,
1368 "default=deny", name_len) )
1369 {
1370 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
1371 NULL, name_start,
1372 "Deprecated option 'default=deny'");
1373 mechtype = SPF_mechtype_find(MECH_ALL);
1374 prefix = PREFIX_FAIL;
1375 }
1376 else if ( STREQ_SIZEOF(name_start, "default=") )
1377 {
1378 SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_OPT,
1379 NULL, name_start,
1380 "Invalid modifier 'default=...'");
1381 p = val_end;
1382 continue;
1383 }
1384 #endif
1385 /* FIXME the redirect mechanism needs to be moved to
1386 * the very end */
1387 else if ( STREQ_SIZEOF_N(name_start, "redirect", name_len) )
1388 mechtype = SPF_mechtype_find(MECH_REDIRECT);
1389 else
1390 {
1391 SPF_response_add_error_ptr(spf_response, SPF_E_UNKNOWN_MECH,
1392 NULL, name_start,
1393 "Unknown mechanism found");
1394 p = val_end;
1395 continue;
1396 }
1397
1398 if (mechtype == NULL) {
1399 return SPF_response_add_error_ptr(spf_response,
1400 SPF_E_INTERNAL_ERROR,
1401 NULL, name_start,
1402 "Failed to find specification for "
1403 "a recognised mechanism");
1404 }
1405
1406 if (spf_server->debug)
1407 SPF_debugf("Adding mechanism type %d",
1408 (int)mechtype->mech_type);
1409
1410 val_start = p;
1411 err = SPF_c_mech_add(spf_server,
1412 spf_record, spf_response,
1413 mechtype, prefix, &val_start);
1414 if (err == SPF_E_NO_MEMORY)
1415 return err;
1416 /* XXX Else do nothing. Continue for the next error. */
1417 /* We shouldn't have to worry about the child function
1418 * updating the pointer. So we just use our 'well known'
1419 * copy. */
1420 p = val_end;
1421 break;
1422
1423 case '=':
1424
1425 /*
1426 * parse the modifier
1427 */
1428
1429 /* modifiers can't have prefixes */
1430 if (prefix != PREFIX_UNKNOWN)
1431 SPF_response_add_error_ptr(spf_response, SPF_E_MOD_W_PREF,
1432 NULL, name_start,
1433 "Modifiers may not have prefixes");
1434 prefix = PREFIX_UNKNOWN; /* For redirect/include */
1435
1436 #ifdef SPF_ALLOW_DEPRECATED_DEFAULT
1437 /* Deal with legacy special case */
1438 if ( STREQ_SIZEOF(name_start, "default=") ) {
1439 /* Consider the whole 'default=foo' as a token. */
1440 p = val_end;
1441 name_len = p - name_start;
1442 goto compile_mech;
1443 }
1444 #endif
1445
1446 /* We treat 'redirect' as a mechanism. */
1447 if ( STREQ_SIZEOF(name_start, "redirect=") )
1448 goto compile_mech;
1449
1450 p++;
1451 val_start = p;
1452 err = SPF_c_mod_add(spf_server,
1453 spf_record, spf_response,
1454 name_start, name_len, &val_start);
1455 if (err == SPF_E_NO_MEMORY)
1456 return err;
1457 /* XXX Else do nothing. Continue for the next error. */
1458 p = val_end;
1459 break;
1460
1461
1462 default:
1463 SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CHAR,
1464 NULL, p,
1465 "Invalid character in middle of mechanism");
1466 p = val_end;
1467 break;
1468 }
1469 }
1470
1471
1472 /*
1473 * check for common mistakes
1474 */
1475 SPF_record_lint(spf_server, spf_response, spf_record);
1476
1477
1478 /*
1479 * do final cleanup on the record
1480 */
1481
1482 /* FIXME realloc (shrink) spfi buffers? */
1483
1484 if (SPF_response_errors(spf_response) > 0) {
1485 for (i = 0; i < SPF_response_messages(spf_response); i++) {
1486 spf_error = SPF_response_message(spf_response, i);
1487 if (SPF_error_errorp(spf_error))
1488 return SPF_error_code(spf_error);
1489 }
1490 return SPF_response_add_error(spf_response,
1491 SPF_E_INTERNAL_ERROR,
1492 "Response has errors but can't find one!");
1493 }
1494
1495 return SPF_E_SUCCESS;
1496 }
1497
1498 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)1499 SPF_record_compile_macro(SPF_server_t *spf_server,
1500 SPF_response_t *spf_response,
1501 SPF_macro_t **spf_macrop,
1502 const char *record)
1503 {
1504 ALIGN_DECL(
1505 char buf[sizeof(SPF_macro_t) + SPF_MAX_MOD_LEN];
1506 );
1507 SPF_macro_t *spf_macro = (SPF_macro_t *)ALIGNED_DECL(buf);
1508 SPF_data_t *data;
1509 SPF_errcode_t err;
1510 size_t size;
1511
1512 data = SPF_macro_data(spf_macro);
1513 spf_macro->macro_len = 0;
1514
1515 err = SPF_c_parse_macro(spf_server, spf_response,
1516 data, &spf_macro->macro_len, SPF_MAX_MOD_LEN,
1517 record, strlen(record),
1518 SPF_E_BIG_MOD, TRUE);
1519 if (err != SPF_E_SUCCESS)
1520 return err;
1521
1522 /* XXX TODO: Tidy this up? */
1523 size = sizeof(SPF_macro_t) + spf_macro->macro_len;
1524 *spf_macrop = (SPF_macro_t *)malloc(size);
1525 if (!*spf_macrop)
1526 return SPF_E_NO_MEMORY;
1527 memcpy(*spf_macrop, ALIGNED_DECL(buf), size);
1528
1529 return SPF_E_SUCCESS;
1530 }
1531