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  *
17  * This program is really a badly smashed together copy of spfquery.c and
18  * the public domain "helloserver" example daemon.
19  *
20  * The original helloserver code contained the following copyright notice:
21  *
22  * HELLOSERVER.C - a 'Hello World' TCP/IP based server daemon
23  *
24  * Implements a skeleton of a single process iterative server
25  * daemon.
26  *
27  * Wherever possible the code adheres to POSIX.
28  *
29  * David Gillies <daggillies@yahoo.com> Sep 2003
30  *
31  * Placed in the public domain. Unrestricted use or modification
32  * of this code is permitted without attribution to the author.
33  */
34 
35 
36 #ifdef __GNUC__
37 #define _GNU_SOURCE /* for strsignal() */
38 #endif
39 
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
43 
44 #ifdef STDC_HEADERS
45 # include <stdio.h>
46 # include <stdlib.h>       /* malloc / free */
47 # include <stddef.h>
48 # include <stdarg.h>
49 #endif
50 
51 #ifdef HAVE_SYS_TYPES_H
52 #include <sys/types.h>    /* types (u_char .. etc..) */
53 #endif
54 
55 #ifdef HAVE_INTTYPES_H
56 #include <inttypes.h>
57 #endif
58 
59 #ifdef HAVE_STRING_H
60 # include <string.h>       /* strstr / strdup */
61 #else
62 # ifdef HAVE_STRINGS_H
63 #  include <strings.h>       /* strstr / strdup */
64 # endif
65 #endif
66 
67 #ifdef HAVE_SYS_SOCKET_H
68 # include <sys/socket.h>   /* inet_ functions / structs */
69 #endif
70 #ifdef HAVE_NETINET_IN_H
71 # include <netinet/in.h>   /* inet_ functions / structs */
72 #endif
73 #ifdef HAVE_ARPA_INET_H
74 # include <arpa/inet.h>    /* in_addr struct */
75 #endif
76 
77 #ifdef HAVE_ARPA_NAMESER_H
78 # include <arpa/nameser.h> /* DNS HEADER struct */
79 #endif
80 
81 #include <sys/types.h>
82 
83 #ifdef HAVE_PWD_H
84 #include <pwd.h>
85 #endif
86 
87 #ifdef HAVE_GRP_H
88 #include <grp.h>
89 #endif
90 
91 #ifdef HAVE_GETOPT_LONG_ONLY
92 #define _GNU_SOURCE
93 #include <getopt.h>
94 #else
95 #include "libreplace/getopt.h"
96 #endif
97 
98 #include <unistd.h>
99 #include <netdb.h>
100 #include <fcntl.h>
101 #include <time.h>
102 #include <signal.h>
103 #include <syslog.h>
104 #include <errno.h>
105 #include <sys/types.h>
106 #include <sys/stat.h>
107 #include <sys/socket.h>
108 #include <sys/un.h>
109 #include <netinet/in.h>
110 #include <ctype.h>
111 #include <sys/wait.h>
112 
113 #include <pthread.h>
114 
115 #include "spf.h"
116 #include "spf_dns.h"
117 #include "spf_dns_null.h"
118 #include "spf_dns_resolv.h"
119 #include "spf_dns_test.h"
120 #include "spf_dns_cache.h"
121 
122 
123 #define TRUE 1
124 #define FALSE 0
125 
126 #define bool int
127 
128 #define FREE(x, f) do { if ((x)) (f)((x)); (x) = NULL; } while(0)
129 #define FREE_REQUEST(x) FREE((x), SPF_request_free)
130 #define FREE_RESPONSE(x) FREE((x), SPF_response_free)
131 #define FREE_STRING(x) FREE((x), free)
132 
133 typedef
134 struct _config_t {
135 	int		 tcpport;
136 	int		 udpport;
137 	char	*path;
138 #ifdef HAVE_PWD_H
139 	uid_t	 pathuser;
140 #endif
141 #ifdef HAVE_GRP_H
142 	gid_t	 pathgroup;
143 #endif
144 	int		 pathmode;
145 #ifdef HAVE_PWD_H
146 	uid_t	 setuser;
147 #endif
148 #ifdef HAVE_GRP_H
149 	gid_t	 setgroup;
150 #endif
151 
152 	int		 debug;
153 	bool	 sec_mx;
154 	char	*fallback;
155 
156 	char	*rec_dom;
157 	bool	 sanitize;
158 	int		 max_lookup;
159 	char	*localpolicy;
160 	bool	 use_trusted;
161 	char	*explanation;
162 } config_t;
163 
164 typedef
165 struct _request_t {
166 	int		 sock;
167 	union {
168 		struct sockaddr_in	in;
169 		struct sockaddr_un	un;
170 	} addr;
171 	socklen_t	 addrlen;
172 	char		*data;
173 	int			 datalen;
174 
175 	char		*ip;
176 	char		*helo;
177 	char		*sender;
178 	char		*rcpt_to;
179 
180 	SPF_errcode_t	 spf_err;
181 	SPF_request_t	*spf_request;
182 	SPF_response_t	*spf_response;
183 
184 	char		 fmt[4096];
185 	int			 fmtlen;
186 } request_t;
187 
188 typedef
189 struct _state_t {
190 	int	sock_udp;
191 	int	sock_tcp;
192 	int	sock_unix;
193 } state_t;
194 
195 static SPF_server_t	*spf_server;
196 static config_t		 spfd_config;
197 static state_t		 spfd_state;
198 
199 static void
response_print_errors(const char * context,SPF_response_t * spf_response,SPF_errcode_t err)200 response_print_errors(const char *context,
201 				SPF_response_t *spf_response, SPF_errcode_t err)
202 {
203 	SPF_error_t	*spf_error;
204 	int			 i;
205 
206 	if (context != NULL)
207 		printf("Context: %s\n", context);
208 	if (err != SPF_E_SUCCESS)
209 		printf("ErrorCode: (%d) %s\n", err, SPF_strerror(err));
210 
211 	if (spf_response != NULL) {
212 		for (i = 0; i < SPF_response_messages(spf_response); i++) {
213 			spf_error = SPF_response_message(spf_response, i);
214 			printf( "%s: %s%s\n",
215 					SPF_error_errorp(spf_error) ? "Error" : "Warning",
216 					((SPF_error_errorp(spf_error) && (!err))
217 							? "[UNRETURNED] "
218 							: ""),
219 					SPF_error_message(spf_error) );
220 		}
221 	}
222 	else {
223 		printf("Error: libspf2 gave a NULL spf_response");
224 	}
225 }
226 
227 static void
response_print(const char * context,SPF_response_t * spf_response)228 response_print(const char *context, SPF_response_t *spf_response)
229 {
230 	printf("--vv--\n");
231 	printf("Context: %s\n", context);
232 	if (spf_response == NULL) {
233 		printf("NULL RESPONSE!\n");
234 	}
235 	else {
236 		printf("Response result: %s\n",
237 					SPF_strresult(SPF_response_result(spf_response)));
238 		printf("Response reason: %s\n",
239 					SPF_strreason(SPF_response_reason(spf_response)));
240 		printf("Response err: %s\n",
241 					SPF_strerror(SPF_response_errcode(spf_response)));
242 		response_print_errors(NULL, spf_response,
243 						SPF_response_errcode(spf_response));
244 	}
245 	printf("--^^--\n");
246 }
247 
248 static const char *
request_check(request_t * req)249 request_check(request_t *req)
250 {
251 	const char	*msg = NULL;
252 	if (!req->ip)
253 		msg = "No IP address given";
254 	else if (!req->sender)
255 		msg = "No sender address given";
256 	else
257 		return NULL;
258 	snprintf(req->fmt, 4095,
259 		"result=unknown\n"
260 		"reason=%s\n",
261 		msg);
262 	return msg;
263 }
264 
265 static void
request_query(request_t * req)266 request_query(request_t *req)
267 {
268 	SPF_request_t	*spf_request = NULL;
269 	SPF_response_t	*spf_response = NULL;
270 	SPF_response_t	*spf_response_2mx = NULL;
271 	SPF_errcode_t	 err;
272 	char			*p, *p_end;
273 
274 #define UNLESS(x) err = (x); if (err)
275 // #define FAIL(x) do { response_print_errors((x), spf_response, err); goto fail; } while(0)
276 #define FAIL(x) do { goto fail; } while(0)
277 #define WARN(x, r) response_print_errors((x), (r), err)
278 
279 	spf_request = SPF_request_new(spf_server);
280 
281 	if (strchr(req->ip, ':')) {
282 		UNLESS(SPF_request_set_ipv6_str(spf_request, req->ip)) {
283 			FAIL("Setting IPv6 address");
284 		}
285 	}
286 	else {
287 		UNLESS(SPF_request_set_ipv4_str(spf_request, req->ip)) {
288 			FAIL("Setting IPv4 address");
289 		}
290 	}
291 
292 	if (req->helo) {
293 		UNLESS(SPF_request_set_helo_dom(spf_request, req->helo)) {
294 			FAIL("Failed to set HELO domain");
295 		}
296 		/* XXX Set some flag saying to query on helo */
297 	}
298 
299 	if (req->sender) {
300 		UNLESS(SPF_request_set_env_from(spf_request, req->sender)) {
301 			FAIL("Failed to set envelope-from address");
302 		}
303 		/* XXX Set some flag saying to query on sender */
304 	}
305 
306 	/* XXX If flag not set, FAIL() */
307 
308 	UNLESS(SPF_request_query_mailfrom(spf_request, &spf_response)) {
309 		FAIL("Failed to query based on mail-from address");
310 	}
311 
312 	if (spfd_config.sec_mx) {
313 		if (req->rcpt_to && *req->rcpt_to) {
314 			p = req->rcpt_to;
315 			p_end = p + strcspn(p, " ,;");
316 			while (SPF_response_result(spf_response)!=SPF_RESULT_PASS) {
317 				if (*p_end)
318 					*p_end = '\0';
319 				else
320 					p_end = NULL;   /* Note this is last rcpt */
321 				UNLESS(SPF_request_query_rcptto(spf_request,
322 								&spf_response_2mx, p)) {
323 					WARN("Failed to query based on 2mx recipient",
324 									spf_response_2mx);
325 					FREE_RESPONSE(spf_response_2mx);
326 				}
327 				else {
328 					spf_response = SPF_response_combine(spf_response,
329 									spf_response_2mx);
330 					spf_response_2mx = NULL;	/* freed */
331 				}
332 
333 				if (!p_end)
334 					break;
335 				p = p_end + 1;
336 			}
337 		}
338 	}
339 
340 	if (spfd_config.fallback) {
341 		UNLESS(SPF_request_query_fallback(spf_request,
342 						&spf_response, spfd_config.fallback)) {
343 			FAIL("Querying fallback record");
344 		}
345 	}
346 
347 	goto ok;
348 
349 fail:
350 	req->spf_err = err;
351 	FREE_RESPONSE(spf_response);
352 	FREE_REQUEST(spf_request);
353 
354 ok:
355 	// response_print("Result: ", spf_response);
356 	(void)response_print;
357 
358 	req->spf_response = spf_response;
359 	req->spf_request = spf_request;
360 }
361 
362 /* This is needed on HP/UX, IIRC */
363 static inline const char *
W(const char * c)364 W(const char *c)
365 {
366 	if (c)
367 		return c;
368 	return "(null)";
369 }
370 
371 static void
request_format(request_t * req)372 request_format(request_t *req)
373 {
374 	SPF_response_t	*spf_response;
375 
376 	spf_response = req->spf_response;
377 
378 	if (spf_response) {
379 		req->fmtlen = snprintf(req->fmt, 4095,
380 			"ip=%s\n"
381 			"sender=%s\n"
382 			"result=%s\n"
383 			"reason=%s\n"
384 			"smtp_comment=%s\n"
385 			"header_comment=%s\n"
386 			"error=%s\n"
387 			, req->ip, req->sender
388 			, W(SPF_strresult(SPF_response_result(spf_response)))
389 			, W(SPF_strreason(SPF_response_reason(spf_response)))
390 			, W(SPF_response_get_smtp_comment(spf_response))
391 			, W(SPF_response_get_header_comment(spf_response))
392 			, W(SPF_strerror(SPF_response_errcode(spf_response)))
393 			);
394 	}
395 	else {
396 		req->fmtlen = snprintf(req->fmt, 4095,
397 			"ip=%s\n"
398 			"sender=%s\n"
399 			"result=unknown\n"
400 			"error=%s\n"
401 			, req->ip, req->sender
402 			, SPF_strerror(req->spf_err)
403 			);
404 	}
405 
406 	req->fmt[4095] = '\0';
407 }
408 
409 static void
request_handle(request_t * req)410 request_handle(request_t *req)
411 {
412 	printf("| %s\n", req->sender); fflush(stdout);
413 	if (!request_check(req)) {
414 		request_query(req);
415 		request_format(req);
416 	}
417 	// printf("==\n%s\n", req->fmt);
418 }
419 
420 static const struct option longopts[] = {
421 	{ "debug",		required_argument,	NULL,	'd', },
422 	{ "tcpport",	required_argument,	NULL,	't', },
423 	{ "udpport",	required_argument,	NULL,	'p', },
424 	{ "path",		required_argument,	NULL,	'f', },
425 #ifdef HAVE_PWD_H
426 	{ "pathuser",	required_argument,	NULL,	'x', },
427 #endif
428 #ifdef HAVE_GRP_H
429 	{ "pathgroup",	required_argument,	NULL,	'y', },
430 #endif
431 	{ "pathmode",	required_argument,	NULL,	'm', },
432 #ifdef HAVE_PWD_H
433 	{ "setuser",	required_argument,	NULL,	'u', },
434 #endif
435 #ifdef HAVE_GRP_H
436 	{ "setgroup",	required_argument,	NULL,	'g', },
437 #endif
438 	{ "help",       no_argument,		NULL,	'h', },
439 };
440 
441 static const char *shortopts = "d:t:p:f:x:y:m:u:g:h:";
442 
usage(void)443 void usage (void) {
444 	fprintf(stdout,"Flags\n");
445 	fprintf(stdout,"\t-tcpport\n");
446 	fprintf(stdout,"\t-udpport\n");
447 	fprintf(stdout,"\t-path\n");
448 #ifdef HAVE_PWD_H
449 	fprintf(stdout,"\t-pathuser\n");
450 #endif
451 #ifdef HAVE_GRP_H
452 	fprintf(stdout,"\t-pathgroup\n");
453 #endif
454 	fprintf(stdout,"\t-pathmode\n");
455 #ifdef HAVE_PWD_H
456 	fprintf(stdout,"\t-setuser\n");
457 #endif
458 #ifdef HAVE_GRP_H
459 	fprintf(stdout,"\t-setgroup\n");
460 #endif
461 	fprintf(stdout,"\t-help\n");
462 
463 }
464 
465 #define DIE(x) do { fprintf(stderr, "%s\n", x); exit(1); } while(0)
466 
467 #ifdef HAVE_PWD_H
468 static gid_t
daemon_get_user(const char * arg)469 daemon_get_user(const char *arg)
470 {
471 	struct passwd	*pwd;
472 	if (isdigit(arg[0]))
473 		pwd = getpwuid(atol(arg));
474 	else
475 		pwd = getpwnam(arg);
476 	if (pwd == NULL) {
477 		fprintf(stderr, "Failed to find user %s\n", arg);
478 		DIE("Unknown user");
479 	}
480 	return pwd->pw_uid;
481 }
482 #endif
483 
484 #ifdef HAVE_GRP_H
485 static gid_t
daemon_get_group(const char * arg)486 daemon_get_group(const char *arg)
487 {
488 	struct group	*grp;
489 	if (isdigit(arg[0]))
490 		grp = getgrgid(atol(arg));
491 	else
492 		grp = getgrnam(arg);
493 	if (grp == NULL) {
494 		fprintf(stderr, "Failed to find user %s\n", arg);
495 		DIE("Unknown group");
496 	}
497 	return grp->gr_gid;
498 }
499 #endif
500 
501 static void
daemon_config(int argc,char * argv[])502 daemon_config(int argc, char *argv[])
503 {
504 	int		 idx;
505 	char	 c;
506 
507 	memset(&spfd_config, 0, sizeof(spfd_config));
508 
509 	while ((c =
510 		getopt_long(argc, argv, shortopts, longopts, &idx)
511 			) != -1) {
512 		switch (c) {
513 			case 't':
514 				spfd_config.tcpport = atol(optarg);
515 				break;
516 			case 'p':
517 				spfd_config.udpport = atol(optarg);
518 				break;
519 			case 'f':
520 				spfd_config.path = optarg;
521 				break;
522 
523 			case 'd':
524 				spfd_config.debug = atol(optarg);
525 				break;
526 
527 #ifdef HAVE_PWD_H
528 			case 'x':
529 				spfd_config.pathuser = daemon_get_user(optarg);
530 				break;
531 #endif
532 #ifdef HAVE_GRP_H
533 			case 'y':
534 				spfd_config.pathgroup = daemon_get_group(optarg);
535 				break;
536 #endif
537 
538 			case 'm':
539 				spfd_config.pathmode = atol(optarg);
540 				break;
541 
542 #ifdef HAVE_PWD_H
543 			case 'u':
544 				spfd_config.setuser = daemon_get_user(optarg);
545 				break;
546 #endif
547 #ifdef HAVE_GRP_H
548 			case 'g':
549 				spfd_config.setgroup = daemon_get_group(optarg);
550 				break;
551 #endif
552 
553 			case 0:
554 			case '?':
555 				usage();
556 				DIE("Invalid argument");
557 				break;
558 			case 'h' :
559 				usage();
560 				DIE("");
561 				break;
562 
563 			default:
564 				fprintf(stderr, "Error: getopt returned character code 0%o ??\n", c);
565 				DIE("WHAT?");
566 		}
567 	}
568 }
569 
570 static int
daemon_bind_inet_udp()571 daemon_bind_inet_udp()
572 {
573 	struct sockaddr_in	 addr;
574 	int					 sock;
575 
576 	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
577 		perror("socket");
578 		DIE("Failed to create socket");
579 	}
580 	memset(&addr, 0, sizeof(addr));
581 	addr.sin_family = AF_INET;
582 	addr.sin_port = htons(spfd_config.udpport);
583 	addr.sin_addr.s_addr = INADDR_ANY;
584 	if (bind(sock, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
585 		perror("bind");
586 		DIE("Failed to bind socket");
587 	}
588 
589 	fprintf(stderr, "Accepting datagrams on %d\n", spfd_config.udpport);
590 
591 	return sock;
592 }
593 
594 static int
daemon_bind_inet_tcp()595 daemon_bind_inet_tcp()
596 {
597 	struct sockaddr_in	 addr;
598 	int					 sock;
599 
600 	int					 optval;
601 	size_t				 optlen;
602 
603 	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
604 		perror("socket");
605 		DIE("Failed to create socket");
606 	}
607 
608 	optval = 1;
609 	optlen = sizeof(int);
610 	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, optlen);
611 
612 	memset(&addr, 0, sizeof(addr));
613 	addr.sin_family = AF_INET;
614 	addr.sin_port = htons(spfd_config.tcpport);
615 	addr.sin_addr.s_addr = INADDR_ANY;
616 	if (bind(sock, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
617 		perror("bind");
618 		DIE("Failed to bind socket");
619 	}
620 
621 	if (listen(sock, 5) < 0) {
622 		perror("listen");
623 		DIE("Failed to listen on socket");
624 	}
625 
626 	fprintf(stderr, "Accepting connections on %d\n", spfd_config.tcpport);
627 
628 	return sock;
629 }
630 
631 static int
daemon_bind_unix()632 daemon_bind_unix()
633 {
634 	struct sockaddr_un	 addr;
635 	int					 sock;
636 
637 	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
638 		perror("socket");
639 		DIE("Failed to create socket");
640 	}
641 	memset(&addr, 0, sizeof(addr));
642 	addr.sun_family = AF_UNIX;
643 	strncpy(addr.sun_path, spfd_config.path, sizeof(addr.sun_path) - 1);
644 	if (unlink(spfd_config.path) < 0) {
645 		if (errno != ENOENT) {
646 			perror("unlink");
647 			DIE("Failed to unlink socket");
648 		}
649 	}
650 	if (bind(sock, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
651 		perror("bind");
652 		DIE("Failed to bind socket");
653 	}
654 	if (listen(sock, 5) < 0) {
655 		perror("listen");
656 		DIE("Failed to listen on socket");
657 	}
658 
659 	fprintf(stderr, "Accepting connections on %s\n", spfd_config.path);
660 
661 	return sock;
662 }
663 
664 static void
daemon_init()665 daemon_init()
666 {
667 	SPF_response_t	*spf_response = NULL;
668 	SPF_errcode_t	 err;
669 
670 	memset(&spfd_state, 0, sizeof(spfd_state));
671 
672 	spf_server = SPF_server_new(SPF_DNS_CACHE, spfd_config.debug);
673 
674 	if (spfd_config.rec_dom) {
675 		UNLESS(SPF_server_set_rec_dom(spf_server,
676 						spfd_config.rec_dom)) {
677 			DIE("Failed to set receiving domain name");
678 		}
679 	}
680 
681 	if (spfd_config.sanitize) {
682 		UNLESS(SPF_server_set_sanitize(spf_server,
683 						spfd_config.sanitize)) {
684 			DIE("Failed to set server sanitize flag");
685 		}
686 	}
687 
688 	if (spfd_config.max_lookup) {
689 		UNLESS(SPF_server_set_max_dns_mech(spf_server,
690 						spfd_config.max_lookup)){
691 			DIE("Failed to set maximum DNS requests");
692 		}
693 	}
694 
695 	if (spfd_config.localpolicy) {
696 		UNLESS(SPF_server_set_localpolicy(spf_server,
697 						spfd_config.localpolicy,
698 						spfd_config.use_trusted,
699 						&spf_response)){
700 			response_print_errors("Compiling local policy",
701 							spf_response, err);
702 			DIE("Failed to set local policy");
703 		}
704 		FREE_RESPONSE(spf_response);
705 	}
706 
707 	if (spfd_config.explanation) {
708 		UNLESS(SPF_server_set_explanation(spf_server,
709 						spfd_config.explanation,
710 						&spf_response)){
711 			response_print_errors("Setting default explanation",
712 							spf_response, err);
713 			DIE("Failed to set default explanation");
714 		}
715 		FREE_RESPONSE(spf_response);
716 	}
717 
718 	if (spfd_config.udpport)
719 		spfd_state.sock_udp = daemon_bind_inet_udp();
720 	if (spfd_config.tcpport)
721 		spfd_state.sock_tcp = daemon_bind_inet_tcp();
722 	if (spfd_config.path)
723 		spfd_state.sock_unix = daemon_bind_unix();
724 	/* XXX Die if none of the above. */
725 }
726 
727 /* This has a return value so we can decide whether to malloc and/or
728  * free in the caller. */
729 static char **
find_field(request_t * req,const char * key)730 find_field(request_t *req, const char *key)
731 {
732 #define STREQ(a, b) (strcmp((a), (b)) == 0)
733 
734 	if (STREQ(key, "ip"))
735 		return &req->ip;
736 	if (STREQ(key, "helo"))
737 		return &req->helo;
738 	if (STREQ(key, "sender"))
739 		return &req->sender;
740 	if (STREQ(key, "rcpt"))
741 		return &req->rcpt_to;
742 	fprintf(stderr, "Invalid key %s\n", key);
743 	return NULL;
744 }
745 
746 /* This is called with req->data malloc'd */
747 static void *
handle_datagram(void * arg)748 handle_datagram(void *arg)
749 {
750 	request_t	*req;
751 	char		**fp;
752 	char		*key;
753 	char		*value;
754 	char		*end;
755 	int			 err;
756 
757 	req = (request_t *)arg;
758 	key = req->data;
759 
760 	// printf("req: %s\n", key);
761 
762 	while (key < (req->data + req->datalen)) {
763 		end = key + strcspn(key, "\r\n");
764 		*end = '\0';
765 		value = strchr(key, '=');
766 
767 		/* Did that line contain an '='? */
768 		if (!value)	/* XXX WARN */
769 			continue;
770 
771 		*value++ = '\0';
772 		fp = find_field(req, key);
773 		if (fp != NULL)
774 			*fp = value;
775 		else
776 			/* warned already */ ;
777 
778 		key = end + 1;
779 		while (key < (req->data + req->datalen)) {
780 			if (strchr("\r\n", *key))
781 				key++;
782 			else
783 				break;
784 		}
785 	}
786 
787 	request_handle(req);
788 
789 #ifdef DEBUG
790 	printf("Target address length is %d: %s:%d\n", req->addrlen,
791 					inet_ntoa(req->addr.in.sin_addr),
792 					req->addr.in.sin_port);
793 #endif
794 
795 	printf("- %s\n", req->sender); fflush(stdout);
796 	err = sendto(req->sock, req->fmt, req->fmtlen, 0,
797 			(struct sockaddr *)(&req->addr.in), req->addrlen);
798 	if (err == -1)
799 		perror("sendto");
800 
801 	FREE_RESPONSE(req->spf_response);
802 	FREE_REQUEST(req->spf_request);
803 
804 	FREE_STRING(req->data);
805 	free(arg);
806 	return NULL;
807 }
808 
809 /* Only req is malloc'd in this. */
810 static void *
handle_stream(void * arg)811 handle_stream(void *arg)
812 {
813 	request_t	*req;
814 	char		**fp;
815 	FILE		*stream;
816 	char		 key[BUFSIZ];
817 	char		*value;
818 	char		*end;
819 
820 	req = (request_t *)arg;
821 	stream = fdopen(req->sock, "r");
822 
823 	do {
824 		while (fgets(key, BUFSIZ, stream) != NULL) {
825 			key[strcspn(key, "\r\n")] = '\0';
826 
827 			/* Break on a blank line and permit another query */
828 			if (*key == '\0')
829 				break;
830 
831 			end = key + strcspn(key, "\r\n");
832 			*end = '\0';
833 			value = strchr(key, '=');
834 
835 			if (!value)	/* XXX WARN */
836 				continue;
837 
838 			*value++ = '\0';
839 			fp = find_field(req, key);
840 			if (fp != NULL)
841 				*fp = strdup(value);
842 			else
843 				/* warned already */ ;
844 		}
845 
846 		request_handle(req);
847 
848 		printf("- %s\n", req->sender); fflush(stdout);
849 		send(req->sock, req->fmt, req->fmtlen, 0);
850 
851 		FREE_STRING(req->ip);
852 		FREE_STRING(req->helo);
853 		FREE_STRING(req->sender);
854 		FREE_STRING(req->rcpt_to);
855 	} while (!feof(stream));
856 
857 	free(arg);
858 	return NULL;
859 }
860 
861 static void
daemon_main()862 daemon_main()
863 {
864 	pthread_attr_t	 attr;
865 	pthread_t		 th;
866 
867 	request_t		*req;
868 	char			 buf[4096];
869 	fd_set			 rfd;
870 	fd_set			 sfd;
871 	int				 maxfd;
872 
873 
874 	pthread_attr_init(&attr);
875 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
876 
877 	FD_ZERO(&rfd);
878 	maxfd = 0;
879 
880 	if (spfd_state.sock_udp) {
881 		// printf("UDP socket is %d\n", spfd_state.sock_udp);
882 		FD_SET(spfd_state.sock_udp, &rfd);
883 		if (spfd_state.sock_udp > maxfd)
884 			maxfd = spfd_state.sock_udp;
885 	}
886 	if (spfd_state.sock_tcp) {
887 		// printf("TCP socket is %d\n", spfd_state.sock_tcp);
888 		FD_SET(spfd_state.sock_tcp, &rfd);
889 		if (spfd_state.sock_tcp > maxfd)
890 			maxfd = spfd_state.sock_tcp;
891 	}
892 	if (spfd_state.sock_unix) {
893 		// printf("UNIX socket is %d\n", spfd_state.sock_unix);
894 		FD_SET(spfd_state.sock_unix, &rfd);
895 		if (spfd_state.sock_unix > maxfd)
896 			maxfd = spfd_state.sock_unix;
897 	}
898 	// printf("MaxFD is %d\n", maxfd);
899 
900 #define NEW_REQUEST	((request_t *)calloc(1, sizeof(request_t)));
901 
902 	for (;;) {
903 		memcpy(&sfd, &rfd, sizeof(rfd));
904 		if (select(maxfd + 1, &sfd, NULL, NULL, NULL) == -1)
905 			break;
906 
907 		if (spfd_state.sock_udp) {
908 			if (FD_ISSET(spfd_state.sock_udp, &sfd)) {
909 				req = NEW_REQUEST;
910 				req->addrlen = sizeof(req->addr);
911 				// printf("UDP\n");
912 				req->sock = spfd_state.sock_udp;
913 				req->datalen = recvfrom(spfd_state.sock_udp, buf,4095,0,
914 					(struct sockaddr *)(&req->addr.in), &req->addrlen);
915 				if (req->datalen >= 0) {
916 					buf[req->datalen] = '\0';
917 					req->data = strdup(buf);
918 					pthread_create(&th, &attr, handle_datagram, req);
919 				}
920 				else {
921 					free(req);
922 				}
923 			}
924 		}
925 		if (spfd_state.sock_tcp) {
926 			if (FD_ISSET(spfd_state.sock_tcp, &sfd)) {
927 				req = NEW_REQUEST;
928 				req->addrlen = sizeof(req->addr);
929 				// printf("TCP\n");
930 				req->sock = accept(spfd_state.sock_tcp,
931 					(struct sockaddr *)(&req->addr.in), &req->addrlen);
932 				if (req->sock >= 0)
933 					pthread_create(&th, &attr, handle_stream, req);
934 				else
935 					free(req);
936 			}
937 		}
938 		if (spfd_state.sock_unix) {
939 			if (FD_ISSET(spfd_state.sock_unix, &sfd)) {
940 				req = NEW_REQUEST;
941 				req->addrlen = sizeof(req->addr);
942 				// printf("UNIX\n");
943 				req->sock = accept(spfd_state.sock_unix,
944 					(struct sockaddr *)(&req->addr.un), &req->addrlen);
945 				if (req->sock >= 0)
946 					pthread_create(&th, &attr, handle_stream, req);
947 				else
948 					free(req);
949 			}
950 		}
951 	}
952 
953 	pthread_attr_destroy(&attr);
954 }
955 
956 int
main(int argc,char * argv[])957 main(int argc, char *argv[])
958 {
959 	daemon_config(argc, argv);
960 	daemon_init();
961 	daemon_main();
962 	return 0;
963 }
964