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