xref: /openbsd/libexec/spamd/spamd.c (revision 3bef86f7)
1 /*	$OpenBSD: spamd.c,v 1.161 2023/09/05 16:01:58 jca Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2002-2007 Bob Beck.  All rights reserved.
6  * Copyright (c) 2002 Theo de Raadt.  All rights reserved.
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/sysctl.h>
24 #include <sys/resource.h>
25 #include <sys/signal.h>
26 #include <sys/stat.h>
27 
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 
31 #include <err.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <limits.h>
35 #include <poll.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <syslog.h>
41 #include <unistd.h>
42 #include <tls.h>
43 
44 #include <netdb.h>
45 
46 #include "sdl.h"
47 #include "grey.h"
48 #include "sync.h"
49 
50 struct con {
51 	struct pollfd *pfd;
52 	int state;
53 	int laststate;
54 	int af;
55 	int il;
56 	struct sockaddr_storage ss;
57 	void *ia;
58 	char addr[32];
59 	char caddr[32];
60 	char helo[MAX_MAIL], mail[MAX_MAIL], rcpt[MAX_MAIL];
61 	struct sdlist **blacklists;
62 	struct tls *cctx;
63 
64 	/*
65 	 * we will do stuttering by changing these to time_t's of
66 	 * now + n, and only advancing when the time is in the past/now
67 	 */
68 	time_t r;
69 	time_t w;
70 	time_t s;
71 
72 	char ibuf[8192];
73 	char *ip;
74 	char rend[5];	/* any chars in here causes input termination */
75 
76 	char *obuf;
77 	char *lists;
78 	size_t osize;
79 	char *op;
80 	int ol;
81 	int data_lines;
82 	int data_body;
83 	int stutter;
84 	int badcmd;
85 	int sr;
86 	int tlsaction;
87 } *con;
88 
89 #define	SPAMD_TLS_ACT_NONE		0
90 #define	SPAMD_TLS_ACT_READ_POLLIN	1
91 #define	SPAMD_TLS_ACT_READ_POLLOUT	2
92 #define	SPAMD_TLS_ACT_WRITE_POLLIN	3
93 #define	SPAMD_TLS_ACT_WRITE_POLLOUT	4
94 
95 #define	SPAMD_USER			"_spamd"
96 
97 void     usage(void);
98 char    *grow_obuf(struct con *, int);
99 int      parse_configline(char *);
100 void     parse_configs(void);
101 void     do_config(void);
102 int      append_error_string (struct con *, size_t, char *, int, void *);
103 void     doreply(struct con *);
104 void     setlog(char *, size_t, char *);
105 void     initcon(struct con *, int, struct sockaddr *);
106 void     closecon(struct con *);
107 int      match(const char *, const char *);
108 void     nextstate(struct con *);
109 void     handler(struct con *);
110 void     handlew(struct con *, int one);
111 char    *loglists(struct con *);
112 void     getcaddr(struct con *);
113 void     gethelo(char *, size_t, char *);
114 int      read_configline(FILE *);
115 void	 spamd_tls_init(void);
116 void	 check_spamd_db(void);
117 void	 blackcheck(int);
118 
119 char hostname[HOST_NAME_MAX+1];
120 struct syslog_data sdata = SYSLOG_DATA_INIT;
121 char *nreply = "450";
122 char *spamd = "spamd IP-based SPAM blocker";
123 int greyback[2];
124 int greypipe[2];
125 int trappipe[2];
126 FILE *grey;
127 FILE *trapcfg;
128 time_t passtime = PASSTIME;
129 time_t greyexp = GREYEXP;
130 time_t whiteexp = WHITEEXP;
131 time_t trapexp = TRAPEXP;
132 struct passwd *pw;
133 pid_t jail_pid = -1;
134 u_short cfg_port;
135 u_short sync_port;
136 struct tls_config *tlscfg;
137 struct tls *tlsctx;
138 char 	*tlskeyfile = NULL;
139 char 	*tlscertfile = NULL;
140 
141 extern struct sdlist *blacklists;
142 extern int pfdev;
143 extern char *low_prio_mx_ip;
144 
145 time_t slowdowntill;
146 
147 int conffd = -1;
148 int trapfd = -1;
149 char *cb;
150 size_t cbs, cbu;
151 
152 time_t t;
153 
154 #define MAXCON 800
155 int maxfiles;
156 int maxcon = MAXCON;
157 int maxblack = MAXCON;
158 int blackcount;
159 int clients;
160 int debug;
161 int greylist = 1;
162 int grey_stutter = 10;
163 int verbose;
164 int stutter = 1;
165 int window;
166 int syncrecv;
167 int syncsend;
168 #define MAXTIME 400
169 
170 #define MAXIMUM(a,b) (((a)>(b))?(a):(b))
171 
172 void
173 usage(void)
174 {
175 	extern char *__progname;
176 
177 	fprintf(stderr,
178 	    "usage: %s [-45bdv] [-B maxblack] [-C file] [-c maxcon] "
179 	    "[-G passtime:greyexp:whiteexp]\n"
180 	    "\t[-h hostname] [-K file] [-l address] [-M address] [-n name]\n"
181 	    "\t[-p port] [-S secs] [-s secs] "
182 	    "[-w window] [-Y synctarget] [-y synclisten]\n",
183 	    __progname);
184 
185 	exit(1);
186 }
187 
188 char *
189 grow_obuf(struct con *cp, int off)
190 {
191 	char *tmp;
192 
193 	tmp = realloc(cp->obuf, cp->osize + 8192);
194 	if (tmp == NULL) {
195 		free(cp->obuf);
196 		cp->obuf = NULL;
197 		cp->osize = 0;
198 		return (NULL);
199 	} else {
200 		cp->osize += 8192;
201 		cp->obuf = tmp;
202 		return (cp->obuf + off);
203 	}
204 }
205 
206 int
207 parse_configline(char *line)
208 {
209 	char *cp, prev, *name, *msg, *tmp;
210 	char **v4 = NULL, **v6 = NULL;
211 	const char *errstr;
212 	u_int nv4 = 0, nv6 = 0;
213 	int mdone = 0;
214 	sa_family_t af;
215 
216 	name = line;
217 
218 	for (cp = name; *cp && *cp != ';'; cp++)
219 		;
220 	if (*cp != ';')
221 		goto parse_error;
222 	*cp++ = '\0';
223 	if (!*cp) {
224 		sdl_del(name);
225 		return (0);
226 	}
227 	msg = cp;
228 	if (*cp++ != '"')
229 		goto parse_error;
230 	prev = '\0';
231 	for (; !mdone; cp++) {
232 		switch (*cp) {
233 		case '\\':
234 			if (!prev)
235 				prev = *cp;
236 			else
237 				prev = '\0';
238 			break;
239 		case '"':
240 			if (prev != '\\') {
241 				cp++;
242 				if (*cp == ';') {
243 					mdone = 1;
244 					*cp = '\0';
245 				} else {
246 					if (debug > 0)
247 						printf("bad message: %s\n", msg);
248 					goto parse_error;
249 				}
250 			}
251 			break;
252 		case '\0':
253 			if (debug > 0)
254 				printf("bad message: %s\n", msg);
255 			goto parse_error;
256 		default:
257 			prev = '\0';
258 			break;
259 		}
260 	}
261 
262 	while ((tmp = strsep(&cp, ";")) != NULL) {
263 		char **av;
264 		u_int au, ac;
265 
266 		if (*tmp == '\0')
267 			continue;
268 
269 		if (strncmp(tmp, "inet", 4) != 0)
270 			goto parse_error;
271 		switch (tmp[4]) {
272 		case '\0':
273 			af = AF_INET;
274 			break;
275 		case '6':
276 			if (tmp[5] == '\0') {
277 				af = AF_INET6;
278 				break;
279 			}
280 			/* FALLTHROUGH */
281 		default:
282 			if (debug > 0)
283 				printf("unsupported address family: %s\n", tmp);
284 			goto parse_error;
285 		}
286 
287 		tmp = strsep(&cp, ";");
288 		if (tmp == NULL) {
289 			if (debug > 0)
290 				printf("missing address count\n");
291 			goto parse_error;
292 		}
293 		ac = strtonum(tmp, 0, UINT_MAX, &errstr);
294 		if (errstr != NULL) {
295 			if (debug > 0)
296 				printf("count \"%s\" is %s\n", tmp, errstr);
297 			goto parse_error;
298 		}
299 
300 		av = reallocarray(NULL, ac, sizeof(char *));
301 		for (au = 0; au < ac; au++) {
302 			tmp = strsep(&cp, ";");
303 			if (tmp == NULL) {
304 				if (debug > 0)
305 					printf("expected %u addrs, got %u\n",
306 					    ac, au + 1);
307 				free(av);
308 				goto parse_error;
309 			}
310 			if (*tmp == '\0')
311 				continue;
312 			av[au] = tmp;
313 		}
314 		if (af == AF_INET) {
315 			if (v4 != NULL) {
316 				if (debug > 0)
317 					printf("duplicate inet\n");
318 				goto parse_error;
319 			}
320 			v4 = av;
321 			nv4 = ac;
322 		} else {
323 			if (v6 != NULL) {
324 				if (debug > 0)
325 					printf("duplicate inet6\n");
326 				goto parse_error;
327 			}
328 			v6 = av;
329 			nv6 = ac;
330 		}
331 	}
332 	if (nv4 == 0 && nv6 == 0) {
333 		if (debug > 0)
334 			printf("no addresses\n");
335 		goto parse_error;
336 	}
337 	sdl_add(name, msg, v4, nv4, v6, nv6);
338 	free(v4);
339 	free(v6);
340 	return (0);
341 
342 parse_error:
343 	if (debug > 0)
344 		printf("bogus config line - need 'tag;message;af;count;a/m;a/m;a/m...'\n");
345 	free(v4);
346 	free(v6);
347 	return (-1);
348 }
349 
350 void
351 parse_configs(void)
352 {
353 	char *start, *end;
354 	size_t i;
355 
356 	/* We always leave an extra byte for the NUL. */
357 	cb[cbu++] = '\0';
358 
359 	start = cb;
360 	end = start;
361 	for (i = 0; i < cbu; i++) {
362 		if (*end == '\n') {
363 			*end = '\0';
364 			if (end > start + 1)
365 				parse_configline(start);
366 			start = ++end;
367 		} else
368 			++end;
369 	}
370 	if (end > start + 1)
371 		parse_configline(start);
372 }
373 
374 void
375 do_config(void)
376 {
377 	int n;
378 
379 	if (debug > 0)
380 		printf("got configuration connection\n");
381 
382 	/* Leave an extra byte for the terminating NUL. */
383 	if (cbu + 1 >= cbs) {
384 		char *tmp;
385 
386 		tmp = realloc(cb, cbs + (1024 * 1024));
387 		if (tmp == NULL) {
388 			if (debug > 0)
389 				warn("realloc");
390 			goto configdone;
391 		}
392 		cbs += 1024 * 1024;
393 		cb = tmp;
394 	}
395 
396 	n = read(conffd, cb + cbu, cbs - cbu);
397 	if (debug > 0)
398 		printf("read %d config bytes\n", n);
399 	if (n == 0) {
400 		if (cbu != 0)
401 			parse_configs();
402 		goto configdone;
403 	} else if (n == -1) {
404 		if (debug > 0)
405 			warn("read");
406 		goto configdone;
407 	} else
408 		cbu += n;
409 	return;
410 
411 configdone:
412 	free(cb);
413 	cb = NULL;
414 	cbs = 0;
415 	cbu = 0;
416 	close(conffd);
417 	conffd = -1;
418 	slowdowntill = 0;
419 }
420 
421 int
422 read_configline(FILE *config)
423 {
424 	char *buf;
425 	size_t len;
426 
427 	if ((buf = fgetln(config, &len))) {
428 		if (buf[len - 1] == '\n')
429 			buf[len - 1] = '\0';
430 		else
431 			return (-1);	/* all valid lines end in \n */
432 		parse_configline(buf);
433 	} else {
434 		syslog_r(LOG_DEBUG, &sdata, "read_configline: fgetln (%m)");
435 		return (-1);
436 	}
437 	return (0);
438 }
439 
440 void
441 spamd_tls_init(void)
442 {
443 	if (tlskeyfile == NULL && tlscertfile == NULL)
444 		return;
445 	if (tlskeyfile == NULL || tlscertfile == NULL)
446 		errx(1, "need key and certificate for TLS");
447 
448 	if ((tlscfg = tls_config_new()) == NULL)
449 		errx(1, "failed to get tls config");
450 	if ((tlsctx = tls_server()) == NULL)
451 		errx(1, "failed to get tls server");
452 
453 	if (tls_config_set_protocols(tlscfg, TLS_PROTOCOLS_ALL) != 0)
454 		errx(1, "failed to set tls protocols");
455 
456 	/* might need user-specified ciphers, tls_config_set_ciphers */
457 	if (tls_config_set_ciphers(tlscfg, "all") != 0)
458 		errx(1, "failed to set tls ciphers");
459 
460 	if (tls_config_set_cert_file(tlscfg, tlscertfile) == -1)
461 		errx(1, "unable to set TLS certificate file %s", tlscertfile);
462 	if (tls_config_set_key_file(tlscfg, tlskeyfile) == -1)
463 		errx(1, "unable to set TLS key file %s", tlskeyfile);
464 	if (tls_configure(tlsctx, tlscfg) != 0)
465 		errx(1, "failed to configure TLS - %s", tls_error(tlsctx));
466 
467 	/* set hostname to cert's CN unless explicitly given? */
468 }
469 
470 int
471 append_error_string(struct con *cp, size_t off, char *fmt, int af, void *ia)
472 {
473 	char sav = '\0';
474 	static int lastcont = 0;
475 	char *c = cp->obuf + off;
476 	char *s = fmt;
477 	size_t len = cp->osize - off;
478 	int i = 0;
479 
480 	if (off == 0)
481 		lastcont = 0;
482 
483 	if (lastcont != 0)
484 		cp->obuf[lastcont] = '-';
485 	snprintf(c, len, "%s ", nreply);
486 	i += strlen(c);
487 	lastcont = off + i - 1;
488 	if (*s == '"')
489 		s++;
490 	while (*s) {
491 		/*
492 		 * Make sure we at minimum, have room to add a
493 		 * format code (4 bytes), and a v6 address(39 bytes)
494 		 * and a byte saved in sav.
495 		 */
496 		if (i >= len - 46) {
497 			c = grow_obuf(cp, off);
498 			if (c == NULL)
499 				return (-1);
500 			len = cp->osize - (off + i);
501 		}
502 
503 		if (c[i-1] == '\n') {
504 			if (lastcont != 0)
505 				cp->obuf[lastcont] = '-';
506 			snprintf(c + i, len, "%s ", nreply);
507 			i += strlen(c);
508 			lastcont = off + i - 1;
509 		}
510 
511 		switch (*s) {
512 		case '\\':
513 		case '%':
514 			if (!sav)
515 				sav = *s;
516 			else {
517 				c[i++] = sav;
518 				sav = '\0';
519 				c[i] = '\0';
520 			}
521 			break;
522 		case '"':
523 		case 'A':
524 		case 'n':
525 			if (*(s+1) == '\0') {
526 				break;
527 			}
528 			if (sav == '\\' && *s == 'n') {
529 				c[i++] = '\n';
530 				sav = '\0';
531 				c[i] = '\0';
532 				break;
533 			} else if (sav == '\\' && *s == '"') {
534 				c[i++] = '"';
535 				sav = '\0';
536 				c[i] = '\0';
537 				break;
538 			} else if (sav == '%' && *s == 'A') {
539 				inet_ntop(af, ia, c + i, (len - i));
540 				i += strlen(c + i);
541 				sav = '\0';
542 				break;
543 			}
544 			/* FALLTHROUGH */
545 		default:
546 			if (sav)
547 				c[i++] = sav;
548 			c[i++] = *s;
549 			sav = '\0';
550 			c[i] = '\0';
551 			break;
552 		}
553 		s++;
554 	}
555 	return (i);
556 }
557 
558 char *
559 loglists(struct con *cp)
560 {
561 	static char matchlists[80];
562 	struct sdlist **matches;
563 	int s = sizeof(matchlists) - 4;
564 
565 	matchlists[0] = '\0';
566 	matches = cp->blacklists;
567 	if (matches == NULL)
568 		return (NULL);
569 	for (; *matches; matches++) {
570 
571 		/* don't report an insane amount of lists in the logs.
572 		 * just truncate and indicate with ...
573 		 */
574 		if (strlen(matchlists) + strlen(matches[0]->tag) + 1 >= s)
575 			strlcat(matchlists, " ...", sizeof(matchlists));
576 		else {
577 			strlcat(matchlists, " ", s);
578 			strlcat(matchlists, matches[0]->tag, s);
579 		}
580 	}
581 	return matchlists;
582 }
583 
584 void
585 doreply(struct con *cp)
586 {
587 	struct sdlist **matches;
588 	int off = 0;
589 
590 	matches = cp->blacklists;
591 	if (matches == NULL)
592 		goto nomatch;
593 	for (; *matches; matches++) {
594 		int used = 0;
595 		int left = cp->osize - off;
596 
597 		used = append_error_string(cp, off, matches[0]->string,
598 		    cp->af, cp->ia);
599 		if (used == -1)
600 			goto bad;
601 		off += used;
602 		left -= used;
603 		if (cp->obuf[off - 1] != '\n') {
604 			if (left < 1) {
605 				if (grow_obuf(cp, off) == NULL)
606 					goto bad;
607 			}
608 			cp->obuf[off++] = '\n';
609 			cp->obuf[off] = '\0';
610 		}
611 	}
612 	return;
613 nomatch:
614 	/* No match. give generic reply */
615 	free(cp->obuf);
616 	if (cp->blacklists != NULL)
617 		cp->osize = asprintf(&cp->obuf,
618 		    "%s-Sorry %s\n"
619 		    "%s-You are trying to send mail from an address "
620 		    "listed by one\n"
621 		    "%s or more IP-based registries as being a SPAM source.\n",
622 		    nreply, cp->addr, nreply, nreply);
623 	else
624 		cp->osize = asprintf(&cp->obuf,
625 		    "451 Temporary failure, please try again later.\r\n");
626 	if (cp->osize == -1)
627 		cp->obuf = NULL;
628 	cp->osize++; /* size includes the NUL (also changes -1 to 0) */
629 	return;
630 bad:
631 	if (cp->obuf != NULL) {
632 		free(cp->obuf);
633 		cp->obuf = NULL;
634 		cp->osize = 0;
635 	}
636 }
637 
638 void
639 setlog(char *p, size_t len, char *f)
640 {
641 	char *s;
642 
643 	s = strsep(&f, ":");
644 	if (!f)
645 		return;
646 	while (*f == ' ' || *f == '\t')
647 		f++;
648 	s = strsep(&f, " \t");
649 	if (s == NULL)
650 		return;
651 	strlcpy(p, s, len);
652 	s = strsep(&p, " \t\n\r");
653 	if (s == NULL)
654 		return;
655 	s = strsep(&p, " \t\n\r");
656 	if (s)
657 		*s = '\0';
658 }
659 
660 /*
661  * Get address client connected to, by doing a getsockname call.
662  * Must not be used with a NAT'ed connection (use divert-to instead of rdr-to).
663  */
664 void
665 getcaddr(struct con *cp)
666 {
667 	struct sockaddr_storage original_destination;
668 	struct sockaddr *odp = (struct sockaddr *) &original_destination;
669 	socklen_t len = sizeof(struct sockaddr_storage);
670 	int error;
671 
672 	cp->caddr[0] = '\0';
673 	if (getsockname(cp->pfd->fd, odp, &len) == -1)
674 		return;
675 	error = getnameinfo(odp, odp->sa_len, cp->caddr, sizeof(cp->caddr),
676 	    NULL, 0, NI_NUMERICHOST);
677 	if (error)
678 		cp->caddr[0] = '\0';
679 }
680 
681 void
682 gethelo(char *p, size_t len, char *f)
683 {
684 	char *s;
685 
686 	/* skip HELO/EHLO */
687 	f+=4;
688 	/* skip whitespace */
689 	while (*f == ' ' || *f == '\t')
690 		f++;
691 	s = strsep(&f, " \t");
692 	if (s == NULL)
693 		return;
694 	strlcpy(p, s, len);
695 	s = strsep(&p, " \t\n\r");
696 	if (s == NULL)
697 		return;
698 	s = strsep(&p, " \t\n\r");
699 	if (s)
700 		*s = '\0';
701 }
702 
703 void
704 initcon(struct con *cp, int fd, struct sockaddr *sa)
705 {
706 	struct pollfd *pfd = cp->pfd;
707 	char ctimebuf[26];
708 	time_t tt;
709 	int error;
710 
711 	if (sa->sa_family != AF_INET)
712 		errx(1, "not supported yet");
713 
714 	time(&tt);
715 	free(cp->obuf);
716 	free(cp->blacklists);
717 	free(cp->lists);
718 	memset(cp, 0, sizeof(*cp));
719 	if (grow_obuf(cp, 0) == NULL)
720 		err(1, "malloc");
721 	cp->pfd = pfd;
722 	cp->pfd->fd = fd;
723 	memcpy(&cp->ss, sa, sa->sa_len);
724 	cp->af = sa->sa_family;
725 	cp->ia = &((struct sockaddr_in *)&cp->ss)->sin_addr;
726 	cp->blacklists = sdl_lookup(blacklists, cp->af, cp->ia);
727 	cp->stutter = (greylist && !grey_stutter && cp->blacklists == NULL) ?
728 	    0 : stutter;
729 	error = getnameinfo(sa, sa->sa_len, cp->addr, sizeof(cp->addr), NULL, 0,
730 	    NI_NUMERICHOST);
731 	if (error)
732 		strlcpy(cp->addr, "<unknown>", sizeof(cp->addr));
733 	ctime_r(&t, ctimebuf);
734 	ctimebuf[sizeof(ctimebuf) - 2] = '\0'; /* nuke newline */
735 	snprintf(cp->obuf, cp->osize, "220 %s ESMTP %s; %s\r\n",
736 	    hostname, spamd, ctimebuf);
737 	cp->op = cp->obuf;
738 	cp->ol = strlen(cp->op);
739 	cp->w = tt + cp->stutter;
740 	cp->s = tt;
741 	strlcpy(cp->rend, "\n", sizeof cp->rend);
742 	clients++;
743 	if (cp->blacklists != NULL) {
744 		blackcount++;
745 		if (greylist && blackcount > maxblack)
746 			cp->stutter = 0;
747 		cp->lists = strdup(loglists(cp));
748 		if (cp->lists == NULL)
749 			err(1, "malloc");
750 	}
751 	else
752 		cp->lists = NULL;
753 }
754 
755 void
756 closecon(struct con *cp)
757 {
758 	time_t tt;
759 
760 	if (cp->cctx) {
761 		tls_close(cp->cctx);
762 		tls_free(cp->cctx);
763 	}
764 	close(cp->pfd->fd);
765 	cp->pfd->fd = -1;
766 
767 	slowdowntill = 0;
768 
769 	time(&tt);
770 	syslog_r(LOG_INFO, &sdata, "%s: disconnected after %lld seconds.%s%s",
771 	    cp->addr, (long long)(tt - cp->s),
772 	    ((cp->lists == NULL) ? "" : " lists:"),
773 	    ((cp->lists == NULL) ? "": cp->lists));
774 	if (debug > 0)
775 		printf("%s connected for %lld seconds.\n", cp->addr,
776 		    (long long)(tt - cp->s));
777 	free(cp->lists);
778 	cp->lists = NULL;
779 	if (cp->blacklists != NULL) {
780 		blackcount--;
781 		free(cp->blacklists);
782 		cp->blacklists = NULL;
783 	}
784 	if (cp->obuf != NULL) {
785 		free(cp->obuf);
786 		cp->obuf = NULL;
787 		cp->osize = 0;
788 	}
789 	clients--;
790 }
791 
792 int
793 match(const char *s1, const char *s2)
794 {
795 	return (strncasecmp(s1, s2, strlen(s2)) == 0);
796 }
797 
798 void
799 nextstate(struct con *cp)
800 {
801 	if (match(cp->ibuf, "QUIT") && cp->state < 99) {
802 		snprintf(cp->obuf, cp->osize, "221 %s\r\n", hostname);
803 		cp->op = cp->obuf;
804 		cp->ol = strlen(cp->op);
805 		cp->w = t + cp->stutter;
806 		cp->laststate = cp->state;
807 		cp->state = 99;
808 		return;
809 	}
810 
811 	if (match(cp->ibuf, "RSET") && cp->state > 2 && cp->state < 50) {
812 		snprintf(cp->obuf, cp->osize,
813 		    "250 Ok to start over.\r\n");
814 		cp->op = cp->obuf;
815 		cp->ol = strlen(cp->op);
816 		cp->w = t + cp->stutter;
817 		cp->laststate = cp->state;
818 		cp->state = 2;
819 		return;
820 	}
821 	switch (cp->state) {
822 	case 0:
823 	tlsinitdone:
824 		/* banner sent; wait for input */
825 		cp->ip = cp->ibuf;
826 		cp->il = sizeof(cp->ibuf) - 1;
827 		cp->laststate = cp->state;
828 		cp->state = 1;
829 		cp->r = t;
830 		break;
831 	case 1:
832 		/* received input: parse, and select next state */
833 		if (match(cp->ibuf, "HELO") ||
834 		    match(cp->ibuf, "EHLO")) {
835 			int nextstate = 2;
836 			cp->helo[0] = '\0';
837 			gethelo(cp->helo, sizeof cp->helo, cp->ibuf);
838 			if (cp->helo[0] == '\0') {
839 				nextstate = 0;
840 				snprintf(cp->obuf, cp->osize,
841 				    "501 helo requires domain name.\r\n");
842 			} else {
843 				if (cp->cctx == NULL && tlsctx != NULL &&
844 				    cp->blacklists == NULL &&
845 				    match(cp->ibuf, "EHLO")) {
846 					snprintf(cp->obuf, cp->osize,
847 					    "250-%s\r\n"
848 					    "250 STARTTLS\r\n",
849 					    hostname);
850 					nextstate = 7;
851 				} else {
852 					snprintf(cp->obuf, cp->osize,
853 					    "250 Hello, spam sender. Pleased "
854 					    "to be wasting your time.\r\n");
855 				}
856 			}
857 			cp->op = cp->obuf;
858 			cp->ol = strlen(cp->op);
859 			cp->laststate = cp->state;
860 			cp->state = nextstate;
861 			cp->w = t + cp->stutter;
862 			break;
863 		}
864 		goto mail;
865 	case 2:
866 		/* sent 250 Hello, wait for input */
867 		cp->ip = cp->ibuf;
868 		cp->il = sizeof(cp->ibuf) - 1;
869 		cp->laststate = cp->state;
870 		cp->state = 3;
871 		cp->r = t;
872 		break;
873 	case 3:
874 	mail:
875 		if (match(cp->ibuf, "MAIL")) {
876 			setlog(cp->mail, sizeof cp->mail, cp->ibuf);
877 			snprintf(cp->obuf, cp->osize,
878 			    "250 You are about to try to deliver spam. "
879 			    "Your time will be spent, for nothing.\r\n");
880 			cp->op = cp->obuf;
881 			cp->ol = strlen(cp->op);
882 			cp->laststate = cp->state;
883 			cp->state = 4;
884 			cp->w = t + cp->stutter;
885 			break;
886 		}
887 		goto rcpt;
888 	case 4:
889 		/* sent 250 Sender ok */
890 		cp->ip = cp->ibuf;
891 		cp->il = sizeof(cp->ibuf) - 1;
892 		cp->laststate = cp->state;
893 		cp->state = 5;
894 		cp->r = t;
895 		break;
896 	case 5:
897 	rcpt:
898 		if (match(cp->ibuf, "RCPT")) {
899 			setlog(cp->rcpt, sizeof(cp->rcpt), cp->ibuf);
900 			snprintf(cp->obuf, cp->osize,
901 			    "250 This is hurting you more than it is "
902 			    "hurting me.\r\n");
903 			cp->op = cp->obuf;
904 			cp->ol = strlen(cp->op);
905 			cp->laststate = cp->state;
906 			cp->state = 6;
907 			cp->w = t + cp->stutter;
908 
909 			if (cp->mail[0] && cp->rcpt[0]) {
910 				if (verbose)
911 					syslog_r(LOG_INFO, &sdata,
912 					    "(%s) %s: %s -> %s",
913 					    cp->blacklists ? "BLACK" : "GREY",
914 					    cp->addr, cp->mail,
915 					    cp->rcpt);
916 				if (debug)
917 					fprintf(stderr, "(%s) %s: %s -> %s\n",
918 					    cp->blacklists ? "BLACK" : "GREY",
919 					    cp->addr, cp->mail, cp->rcpt);
920 				if (greylist && cp->blacklists == NULL) {
921 					/* send this info to the greylister */
922 					getcaddr(cp);
923 					fprintf(grey,
924 					    "CO:%s\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
925 					    cp->caddr, cp->helo, cp->addr,
926 					    cp->mail, cp->rcpt);
927 					fflush(grey);
928 				}
929 			}
930 			break;
931 		}
932 		goto spam;
933 	case 6:
934 		/* sent 250 blah */
935 		cp->ip = cp->ibuf;
936 		cp->il = sizeof(cp->ibuf) - 1;
937 		cp->laststate = cp->state;
938 		cp->state = 5;
939 		cp->r = t;
940 		break;
941 	case 7:
942 		/* sent 250 STARTTLS, wait for input */
943 		cp->ip = cp->ibuf;
944 		cp->il = sizeof(cp->ibuf) - 1;
945 		cp->laststate = cp->state;
946 		cp->state = 8;
947 		cp->r = t;
948 		break;
949 	case 8:
950 		if (tlsctx != NULL && cp->blacklists == NULL &&
951 		    cp->cctx == NULL && match(cp->ibuf, "STARTTLS")) {
952 			snprintf(cp->obuf, cp->osize,
953 			    "220 glad you want to burn more CPU cycles on "
954 			    "your spam\r\n");
955 			cp->op = cp->obuf;
956 			cp->ol = strlen(cp->op);
957 			cp->laststate = cp->state;
958 			cp->state = 9;
959 			cp->w = t + cp->stutter;
960 			break;
961 		}
962 		goto mail;
963 	case 9:
964 		if (tls_accept_socket(tlsctx, &cp->cctx, cp->pfd->fd) == -1) {
965 			snprintf(cp->obuf, cp->osize,
966 			    "500 STARTTLS failed\r\n");
967 			cp->op = cp->obuf;
968 			cp->ol = strlen(cp->op);
969 			cp->laststate = cp->state;
970 			cp->state = 98;
971 			goto done;
972 		}
973 		goto tlsinitdone;
974 
975 	case 50:
976 	spam:
977 		if (match(cp->ibuf, "DATA")) {
978 			snprintf(cp->obuf, cp->osize,
979 			    "354 Enter spam, end with \".\" on a line by "
980 			    "itself\r\n");
981 			cp->state = 60;
982 			if (window && setsockopt(cp->pfd->fd, SOL_SOCKET,
983 			    SO_RCVBUF, &window, sizeof(window)) == -1) {
984 				syslog_r(LOG_DEBUG, &sdata,"setsockopt: %m");
985 				/* don't fail if this doesn't work. */
986 			}
987 			cp->ip = cp->ibuf;
988 			cp->il = sizeof(cp->ibuf) - 1;
989 			cp->op = cp->obuf;
990 			cp->ol = strlen(cp->op);
991 			cp->w = t + cp->stutter;
992 			if (greylist && cp->blacklists == NULL) {
993 				cp->laststate = cp->state;
994 				cp->state = 98;
995 				goto done;
996 			}
997 		} else {
998 			if (match(cp->ibuf, "NOOP"))
999 				snprintf(cp->obuf, cp->osize,
1000 				    "250 2.0.0 OK I did nothing\r\n");
1001 			else {
1002 				snprintf(cp->obuf, cp->osize,
1003 				    "500 5.5.1 Command unrecognized\r\n");
1004 				cp->badcmd++;
1005 				if (cp->badcmd > 20) {
1006 					cp->laststate = cp->state;
1007 					cp->state = 98;
1008 					goto done;
1009 				}
1010 			}
1011 			cp->state = cp->laststate;
1012 			cp->ip = cp->ibuf;
1013 			cp->il = sizeof(cp->ibuf) - 1;
1014 			cp->op = cp->obuf;
1015 			cp->ol = strlen(cp->op);
1016 			cp->w = t + cp->stutter;
1017 		}
1018 		break;
1019 	case 60:
1020 		/* sent 354 blah */
1021 		cp->ip = cp->ibuf;
1022 		cp->il = sizeof(cp->ibuf) - 1;
1023 		cp->laststate = cp->state;
1024 		cp->state = 70;
1025 		cp->r = t;
1026 		break;
1027 	case 70: {
1028 		char *p, *q;
1029 
1030 		for (p = q = cp->ibuf; q <= cp->ip; ++q)
1031 			if (*q == '\n' || q == cp->ip) {
1032 				*q = 0;
1033 				if (q > p && q[-1] == '\r')
1034 					q[-1] = 0;
1035 				if (!strcmp(p, ".") ||
1036 				    (cp->data_body && ++cp->data_lines >= 10)) {
1037 					cp->laststate = cp->state;
1038 					cp->state = 98;
1039 					goto done;
1040 				}
1041 				if (!cp->data_body && !*p)
1042 					cp->data_body = 1;
1043 				if (verbose && cp->data_body && *p)
1044 					syslog_r(LOG_DEBUG, &sdata, "%s: "
1045 					    "Body: %s", cp->addr, p);
1046 				else if (verbose && (match(p, "FROM:") ||
1047 				    match(p, "TO:") || match(p, "SUBJECT:")))
1048 					syslog_r(LOG_INFO, &sdata, "%s: %s",
1049 					    cp->addr, p);
1050 				p = ++q;
1051 			}
1052 		cp->ip = cp->ibuf;
1053 		cp->il = sizeof(cp->ibuf) - 1;
1054 		cp->r = t;
1055 		break;
1056 	}
1057 	case 98:
1058 	done:
1059 		doreply(cp);
1060 		cp->op = cp->obuf;
1061 		cp->ol = strlen(cp->op);
1062 		cp->w = t + cp->stutter;
1063 		cp->laststate = cp->state;
1064 		cp->state = 99;
1065 		break;
1066 	case 99:
1067 		closecon(cp);
1068 		break;
1069 	default:
1070 		errx(1, "illegal state %d", cp->state);
1071 		break;
1072 	}
1073 }
1074 
1075 void
1076 handler(struct con *cp)
1077 {
1078 	int end = 0;
1079 	ssize_t n;
1080 
1081 	if (cp->r || cp->tlsaction != SPAMD_TLS_ACT_NONE) {
1082 		if (cp->cctx) {
1083 			cp->tlsaction = SPAMD_TLS_ACT_NONE;
1084 			n = tls_read(cp->cctx, cp->ip, cp->il);
1085 			if (n == TLS_WANT_POLLIN)
1086 				cp->tlsaction = SPAMD_TLS_ACT_READ_POLLIN;
1087 			if (n == TLS_WANT_POLLOUT)
1088 				cp->tlsaction = SPAMD_TLS_ACT_READ_POLLOUT;
1089 			if (cp->tlsaction != SPAMD_TLS_ACT_NONE)
1090 				return;
1091 		} else
1092 			n = read(cp->pfd->fd, cp->ip, cp->il);
1093 
1094 		if (n == 0)
1095 			closecon(cp);
1096 		else if (n == -1) {
1097 			if (errno == EAGAIN)
1098 				return;
1099 			if (debug > 0)
1100 				warn("read");
1101 			closecon(cp);
1102 		} else {
1103 			cp->ip[n] = '\0';
1104 			if (cp->rend[0])
1105 				if (strpbrk(cp->ip, cp->rend))
1106 					end = 1;
1107 			cp->ip += n;
1108 			cp->il -= n;
1109 		}
1110 	}
1111 	if (end || cp->il == 0) {
1112 		while (cp->ip > cp->ibuf &&
1113 		    (cp->ip[-1] == '\r' || cp->ip[-1] == '\n'))
1114 			cp->ip--;
1115 		*cp->ip = '\0';
1116 		cp->r = 0;
1117 		nextstate(cp);
1118 	}
1119 }
1120 
1121 void
1122 handlew(struct con *cp, int one)
1123 {
1124 	ssize_t n;
1125 
1126 	/* kill stutter on greylisted connections after initial delay */
1127 	if (cp->stutter && greylist && cp->blacklists == NULL &&
1128 	    (t - cp->s) > grey_stutter)
1129 		cp->stutter=0;
1130 
1131 	if (cp->w || cp->tlsaction != SPAMD_TLS_ACT_NONE) {
1132 		if (*cp->op == '\n' && !cp->sr) {
1133 			/* insert \r before \n */
1134 			if (cp->cctx) {
1135 				cp->tlsaction = SPAMD_TLS_ACT_NONE;
1136 				n = tls_write(cp->cctx, "\r", 1);
1137 				if (n == TLS_WANT_POLLIN)
1138 					cp->tlsaction =
1139 					    SPAMD_TLS_ACT_WRITE_POLLIN;
1140 				if (n == TLS_WANT_POLLOUT)
1141 					cp->tlsaction =
1142 					    SPAMD_TLS_ACT_WRITE_POLLOUT;
1143 				if (cp->tlsaction != SPAMD_TLS_ACT_NONE)
1144 					return;
1145 			} else
1146 				n = write(cp->pfd->fd, "\r", 1);
1147 
1148 			if (n == 0) {
1149 				closecon(cp);
1150 				goto handled;
1151 			} else if (n == -1) {
1152 				if (errno == EAGAIN)
1153 					return;
1154 				if (debug > 0 && errno != EPIPE)
1155 					warn("write");
1156 				closecon(cp);
1157 				goto handled;
1158 			}
1159 		}
1160 		if (*cp->op == '\r')
1161 			cp->sr = 1;
1162 		else
1163 			cp->sr = 0;
1164 		if (cp->cctx) {
1165 			cp->tlsaction = SPAMD_TLS_ACT_NONE;
1166 			n = tls_write(cp->cctx, cp->op, cp->ol);
1167 			if (n == TLS_WANT_POLLIN)
1168 				cp->tlsaction = SPAMD_TLS_ACT_WRITE_POLLIN;
1169 			if (n == TLS_WANT_POLLOUT)
1170 				cp->tlsaction = SPAMD_TLS_ACT_WRITE_POLLOUT;
1171 			if (cp->tlsaction != SPAMD_TLS_ACT_NONE)
1172 				return;
1173 		} else
1174 			n = write(cp->pfd->fd, cp->op,
1175 			   (one && cp->stutter) ? 1 : cp->ol);
1176 
1177 		if (n == 0)
1178 			closecon(cp);
1179 		else if (n == -1) {
1180 			if (errno == EAGAIN)
1181 				return;
1182 			if (debug > 0 && errno != EPIPE)
1183 				warn("write");
1184 			closecon(cp);
1185 		} else {
1186 			cp->op += n;
1187 			cp->ol -= n;
1188 		}
1189 	}
1190 handled:
1191 	cp->w = t + cp->stutter;
1192 	if (cp->ol == 0) {
1193 		cp->w = 0;
1194 		nextstate(cp);
1195 	}
1196 }
1197 
1198 static int
1199 get_maxfiles(void)
1200 {
1201 	int mib[2], maxfiles;
1202 	size_t len;
1203 
1204 	mib[0] = CTL_KERN;
1205 	mib[1] = KERN_MAXFILES;
1206 	len = sizeof(maxfiles);
1207 	if (sysctl(mib, 2, &maxfiles, &len, NULL, 0) == -1)
1208 		return(MAXCON);
1209 	if ((maxfiles - 200) < 10)
1210 		errx(1, "kern.maxfiles is only %d, can not continue\n",
1211 		    maxfiles);
1212 	else
1213 		return(maxfiles - 200);
1214 }
1215 
1216 /* Symbolic indexes for pfd[] below */
1217 #define PFD_SMTPLISTEN	0
1218 #define PFD_CONFLISTEN	1
1219 #define PFD_SYNCFD	2
1220 #define PFD_CONFFD	3
1221 #define PFD_TRAPFD	4
1222 #define PFD_GREYBACK	5
1223 #define PFD_FIRSTCON	6
1224 
1225 int
1226 main(int argc, char *argv[])
1227 {
1228 	struct pollfd *pfd;
1229 	struct sockaddr_in sin;
1230 	struct sockaddr_in lin;
1231 	int ch, smtplisten, conflisten, syncfd = -1, i, one = 1;
1232 	u_short port;
1233 	long long passt, greyt, whitet;
1234 	struct servent *ent;
1235 	struct rlimit rlp;
1236 	char *bind_address = NULL;
1237 	const char *errstr;
1238 	char *sync_iface = NULL;
1239 	char *sync_baddr = NULL;
1240 	struct addrinfo hints, *res;
1241 	char *addr;
1242 	char portstr[6];
1243 	int error;
1244 
1245 	tzset();
1246 	openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);
1247 
1248 	if ((ent = getservbyname("spamd", "tcp")) == NULL)
1249 		errx(1, "Can't find service \"spamd\" in /etc/services");
1250 	port = ntohs(ent->s_port);
1251 	if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL)
1252 		errx(1, "Can't find service \"spamd-cfg\" in /etc/services");
1253 	cfg_port = ntohs(ent->s_port);
1254 	if ((ent = getservbyname("spamd-sync", "udp")) == NULL)
1255 		errx(1, "Can't find service \"spamd-sync\" in /etc/services");
1256 	sync_port = ntohs(ent->s_port);
1257 
1258 	if (gethostname(hostname, sizeof hostname) == -1)
1259 		err(1, "gethostname");
1260 	maxfiles = get_maxfiles();
1261 	if (maxcon > maxfiles)
1262 		maxcon = maxfiles;
1263 	if (maxblack > maxfiles)
1264 		maxblack = maxfiles;
1265 	while ((ch =
1266 	    getopt(argc, argv, "45l:c:B:p:bdG:h:s:S:M:n:vw:y:Y:C:K:")) != -1) {
1267 		switch (ch) {
1268 		case '4':
1269 			nreply = "450";
1270 			break;
1271 		case '5':
1272 			nreply = "550";
1273 			break;
1274 		case 'l':
1275 			bind_address = optarg;
1276 			break;
1277 		case 'B':
1278 			maxblack = strtonum(optarg, 0, INT_MAX, &errstr);
1279 			if (errstr)
1280 				errx(1, "-B %s: %s", optarg, errstr);
1281 			break;
1282 		case 'c':
1283 			maxcon = strtonum(optarg, 1, maxfiles, &errstr);
1284 			if (errstr) {
1285 				fprintf(stderr, "-c %s: %s\n", optarg, errstr);
1286 				usage();
1287 			}
1288 			break;
1289 		case 'p':
1290 			port = strtonum(optarg, 1, USHRT_MAX, &errstr);
1291 			if (errstr)
1292 				errx(1, "-p %s: %s", optarg, errstr);
1293 			break;
1294 		case 'd':
1295 			debug = 1;
1296 			break;
1297 		case 'b':
1298 			greylist = 0;
1299 			break;
1300 		case 'G':
1301 			if (sscanf(optarg, "%lld:%lld:%lld", &passt, &greyt,
1302 			    &whitet) != 3)
1303 				usage();
1304 			passtime = passt;
1305 			greyexp = greyt;
1306 			whiteexp = whitet;
1307 			/* convert to seconds from minutes */
1308 			passtime *= 60;
1309 			/* convert to seconds from hours */
1310 			whiteexp *= (60 * 60);
1311 			/* convert to seconds from hours */
1312 			greyexp *= (60 * 60);
1313 			break;
1314 		case 'h':
1315 			memset(hostname, 0, sizeof(hostname));
1316 			if (strlcpy(hostname, optarg, sizeof(hostname)) >=
1317 			    sizeof(hostname))
1318 				errx(1, "-h arg too long");
1319 			break;
1320 		case 's':
1321 			stutter = strtonum(optarg, 0, 10, &errstr);
1322 			if (errstr)
1323 				usage();
1324 			break;
1325 		case 'S':
1326 			grey_stutter = strtonum(optarg, 0, 90, &errstr);
1327 			if (errstr)
1328 				usage();
1329 			break;
1330 		case 'M':
1331 			low_prio_mx_ip = optarg;
1332 			break;
1333 		case 'n':
1334 			spamd = optarg;
1335 			break;
1336 		case 'v':
1337 			verbose = 1;
1338 			break;
1339 		case 'w':
1340 			window = strtonum(optarg, 1, INT_MAX, &errstr);
1341 			if (errstr)
1342 				errx(1, "-w %s: %s", optarg, errstr);
1343 			break;
1344 		case 'Y':
1345 			if (sync_addhost(optarg, sync_port) != 0)
1346 				sync_iface = optarg;
1347 			syncsend++;
1348 			break;
1349 		case 'y':
1350 			sync_baddr = optarg;
1351 			syncrecv++;
1352 			break;
1353 		case 'C':
1354 			tlscertfile = optarg;
1355 			break;
1356 		case 'K':
1357 			tlskeyfile = optarg;
1358 			break;
1359 		default:
1360 			usage();
1361 			break;
1362 		}
1363 	}
1364 
1365 	setproctitle("[priv]%s%s",
1366 	    greylist ? " (greylist)" : "",
1367 	    (syncrecv || syncsend) ? " (sync)" : "");
1368 
1369 	if (syncsend || syncrecv) {
1370 		syncfd = sync_init(sync_iface, sync_baddr, sync_port);
1371 		if (syncfd == -1)
1372 			err(1, "sync init");
1373 	}
1374 
1375 	if (geteuid())
1376 		errx(1, "need root privileges");
1377 
1378 	if ((pw = getpwnam(SPAMD_USER)) == NULL)
1379 		errx(1, "no such user %s", SPAMD_USER);
1380 
1381 	if (!greylist) {
1382 		maxblack = maxcon;
1383 	} else if (maxblack > maxcon)
1384 		usage();
1385 
1386 	spamd_tls_init();
1387 
1388 	rlp.rlim_cur = rlp.rlim_max = maxcon + 15;
1389 	if (setrlimit(RLIMIT_NOFILE, &rlp) == -1)
1390 		err(1, "setrlimit");
1391 
1392 	pfd = reallocarray(NULL, PFD_FIRSTCON + maxcon, sizeof(*pfd));
1393 	if (pfd == NULL)
1394 		err(1, "reallocarray");
1395 
1396 	con = calloc(maxcon, sizeof(*con));
1397 	if (con == NULL)
1398 		err(1, "calloc");
1399 
1400 	con->obuf = malloc(8192);
1401 
1402 	if (con->obuf == NULL)
1403 		err(1, "malloc");
1404 	con->osize = 8192;
1405 
1406 	for (i = 0; i < maxcon; i++) {
1407 		con[i].pfd = &pfd[PFD_FIRSTCON + i];
1408 		con[i].pfd->fd = -1;
1409 	}
1410 
1411 	signal(SIGPIPE, SIG_IGN);
1412 
1413 	smtplisten = socket(AF_INET, SOCK_STREAM, 0);
1414 	if (smtplisten == -1)
1415 		err(1, "socket");
1416 
1417 	if (setsockopt(smtplisten, SOL_SOCKET, SO_REUSEADDR, &one,
1418 	    sizeof(one)) == -1)
1419 		return (-1);
1420 
1421 	conflisten = socket(AF_INET, SOCK_STREAM, 0);
1422 	if (conflisten == -1)
1423 		err(1, "socket");
1424 
1425 	if (setsockopt(conflisten, SOL_SOCKET, SO_REUSEADDR, &one,
1426 	    sizeof(one)) == -1)
1427 		return (-1);
1428 
1429 	memset(&hints, 0, sizeof(hints));
1430 	hints.ai_family = AF_INET;
1431 	addr = bind_address;
1432 	snprintf(portstr, sizeof(portstr), "%hu", port);
1433 
1434 	if ((error = getaddrinfo(addr, portstr, &hints, &res)) != 0) {
1435 		errx(1, "getaddrinfo: %s", gai_strerror(error));
1436 	}
1437 
1438 	if (bind(smtplisten, res->ai_addr, res->ai_addrlen) == -1) {
1439 		freeaddrinfo(res);
1440 		err(1, "bind");
1441 	}
1442 	freeaddrinfo(res);
1443 
1444 	memset(&lin, 0, sizeof sin);
1445 	lin.sin_len = sizeof(sin);
1446 	lin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1447 	lin.sin_family = AF_INET;
1448 	lin.sin_port = htons(cfg_port);
1449 
1450 	if (bind(conflisten, (struct sockaddr *)&lin, sizeof lin) == -1)
1451 		err(1, "bind local");
1452 
1453 	if (debug == 0) {
1454 		if (daemon(1, 1) == -1)
1455 			err(1, "daemon");
1456 	}
1457 
1458 	if (greylist) {
1459 		pfdev = open("/dev/pf", O_RDWR);
1460 		if (pfdev == -1) {
1461 			syslog_r(LOG_ERR, &sdata, "open /dev/pf: %m");
1462 			exit(1);
1463 		}
1464 
1465 		check_spamd_db();
1466 
1467 		maxblack = (maxblack >= maxcon) ? maxcon - 100 : maxblack;
1468 		if (maxblack < 0)
1469 			maxblack = 0;
1470 
1471 		/* open pipe to talk to greylister */
1472 		if (socketpair(AF_UNIX, SOCK_DGRAM, 0, greyback) == -1) {
1473 			syslog(LOG_ERR, "socketpair (%m)");
1474 			exit(1);
1475 		}
1476 		if (pipe(greypipe) == -1) {
1477 			syslog(LOG_ERR, "pipe (%m)");
1478 			exit(1);
1479 		}
1480 		/* open pipe to receive spamtrap configs */
1481 		if (pipe(trappipe) == -1) {
1482 			syslog(LOG_ERR, "pipe (%m)");
1483 			exit(1);
1484 		}
1485 		jail_pid = fork();
1486 		switch (jail_pid) {
1487 		case -1:
1488 			syslog(LOG_ERR, "fork (%m)");
1489 			exit(1);
1490 		case 0:
1491 			/* child - continue */
1492 			signal(SIGPIPE, SIG_IGN);
1493 			grey = fdopen(greypipe[1], "w");
1494 			if (grey == NULL) {
1495 				syslog(LOG_ERR, "fdopen (%m)");
1496 				_exit(1);
1497 			}
1498 			close(greyback[0]);
1499 			close(greypipe[0]);
1500 			trapfd = trappipe[0];
1501 			trapcfg = fdopen(trappipe[0], "r");
1502 			if (trapcfg == NULL) {
1503 				syslog(LOG_ERR, "fdopen (%m)");
1504 				_exit(1);
1505 			}
1506 			close(trappipe[1]);
1507 
1508 			if (setgroups(1, &pw->pw_gid) ||
1509 			    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1510 			    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
1511 				err(1, "failed to drop privs");
1512 
1513 			goto jail;
1514 		}
1515 		/* parent - run greylister */
1516 		close(greyback[1]);
1517 		grey = fdopen(greypipe[0], "r");
1518 		if (grey == NULL) {
1519 			syslog(LOG_ERR, "fdopen (%m)");
1520 			exit(1);
1521 		}
1522 		close(greypipe[1]);
1523 		trapcfg = fdopen(trappipe[1], "w");
1524 		if (trapcfg == NULL) {
1525 			syslog(LOG_ERR, "fdopen (%m)");
1526 			exit(1);
1527 		}
1528 		close(trappipe[0]);
1529 		return (greywatcher());
1530 	}
1531 
1532 jail:
1533 	if (pledge("stdio inet", NULL) == -1)
1534 		err(1, "pledge");
1535 
1536 	if (listen(smtplisten, 10) == -1)
1537 		err(1, "listen");
1538 
1539 	if (listen(conflisten, 10) == -1)
1540 		err(1, "listen");
1541 
1542 	if (debug != 0)
1543 		printf("listening for incoming connections.\n");
1544 	syslog_r(LOG_WARNING, &sdata, "listening for incoming connections.");
1545 
1546 	/* We always check for trap and sync events if configured. */
1547 	if (trapfd != -1) {
1548 		pfd[PFD_TRAPFD].fd = trapfd;
1549 		pfd[PFD_TRAPFD].events = POLLIN;
1550 	} else {
1551 		pfd[PFD_TRAPFD].fd = -1;
1552 		pfd[PFD_TRAPFD].events = 0;
1553 	}
1554 	if (syncrecv) {
1555 		pfd[PFD_SYNCFD].fd = syncfd;
1556 		pfd[PFD_SYNCFD].events = POLLIN;
1557 	} else {
1558 		pfd[PFD_SYNCFD].fd = -1;
1559 		pfd[PFD_SYNCFD].events = 0;
1560 	}
1561 	if (greylist) {
1562 		pfd[PFD_GREYBACK].fd = greyback[1];
1563 		pfd[PFD_GREYBACK].events = POLLIN;
1564 	} else {
1565 		pfd[PFD_GREYBACK].fd = -1;
1566 		pfd[PFD_GREYBACK].events = 0;
1567 	}
1568 
1569 	/* events and pfd entries for con[] are filled in below. */
1570 	pfd[PFD_SMTPLISTEN].fd = smtplisten;
1571 	pfd[PFD_CONFLISTEN].fd = conflisten;
1572 
1573 	while (1) {
1574 		int numcon = 0, n, timeout, writers;
1575 
1576 		time(&t);
1577 
1578 		writers = 0;
1579 		for (i = 0; i < maxcon; i++) {
1580 			if (con[i].pfd->fd == -1)
1581 				continue;
1582 			con[i].pfd->events = 0;
1583 			if (con[i].r) {
1584 				if (con[i].r + MAXTIME <= t) {
1585 					closecon(&con[i]);
1586 					continue;
1587 				}
1588 				con[i].pfd->events |= POLLIN;
1589 			}
1590 			if (con[i].w) {
1591 				if (con[i].w + MAXTIME <= t) {
1592 					closecon(&con[i]);
1593 					continue;
1594 				}
1595 				if (con[i].w <= t)
1596 					con[i].pfd->events |= POLLOUT;
1597 				writers = 1;
1598 			}
1599 			if (con[i].tlsaction == SPAMD_TLS_ACT_READ_POLLIN ||
1600 			    con[i].tlsaction == SPAMD_TLS_ACT_WRITE_POLLIN)
1601 				con[i].pfd->events = POLLIN;
1602 			if (con[i].tlsaction == SPAMD_TLS_ACT_READ_POLLOUT ||
1603 			    con[i].tlsaction == SPAMD_TLS_ACT_WRITE_POLLOUT)
1604 				con[i].pfd->events = POLLOUT;
1605 			if (i + 1 > numcon)
1606 				numcon = i + 1;
1607 		}
1608 		pfd[PFD_SMTPLISTEN].events = 0;
1609 		pfd[PFD_CONFLISTEN].events = 0;
1610 		pfd[PFD_CONFFD].events = 0;
1611 		pfd[PFD_CONFFD].fd = conffd;
1612 		if (slowdowntill == 0) {
1613 			pfd[PFD_SMTPLISTEN].events = POLLIN;
1614 
1615 			/* only one active config conn at a time */
1616 			if (conffd == -1)
1617 				pfd[PFD_CONFLISTEN].events = POLLIN;
1618 			else
1619 				pfd[PFD_CONFFD].events = POLLIN;
1620 		}
1621 
1622 		/* If we are not listening, wake up at least once a second */
1623 		if (writers == 0 && slowdowntill == 0)
1624 			timeout = INFTIM;
1625 		else
1626 			timeout = 1000;
1627 
1628 		n = poll(pfd, PFD_FIRSTCON + numcon, timeout);
1629 		if (n == -1) {
1630 			if (errno != EINTR)
1631 				err(1, "poll");
1632 			continue;
1633 		}
1634 
1635 		/* Check if we can speed up accept() calls */
1636 		if (slowdowntill && slowdowntill > t)
1637 			slowdowntill = 0;
1638 
1639 		for (i = 0; i < maxcon; i++) {
1640 			if (con[i].pfd->fd == -1)
1641 				continue;
1642 			if (pfd[PFD_FIRSTCON + i].revents & POLLHUP) {
1643 				closecon(&con[i]);
1644 				continue;
1645 			}
1646 			if (pfd[PFD_FIRSTCON + i].revents & POLLIN) {
1647 				if (con[i].tlsaction ==
1648 				    SPAMD_TLS_ACT_WRITE_POLLIN)
1649 					handlew(&con[i], clients + 5 < maxcon);
1650 				else
1651 					handler(&con[i]);
1652 			}
1653 			if (pfd[PFD_FIRSTCON + i].revents & POLLOUT) {
1654 				if (con[i].tlsaction ==
1655 				    SPAMD_TLS_ACT_READ_POLLOUT)
1656 					handler(&con[i]);
1657 				else
1658 					handlew(&con[i], clients + 5 < maxcon);
1659 			}
1660 		}
1661 		if (pfd[PFD_SMTPLISTEN].revents & (POLLIN|POLLHUP)) {
1662 			socklen_t sinlen;
1663 			int s2;
1664 
1665 			sinlen = sizeof(sin);
1666 			s2 = accept4(smtplisten, (struct sockaddr *)&sin, &sinlen,
1667 			    SOCK_NONBLOCK);
1668 			if (s2 == -1) {
1669 				switch (errno) {
1670 				case EINTR:
1671 				case ECONNABORTED:
1672 					break;
1673 				case EMFILE:
1674 				case ENFILE:
1675 					slowdowntill = time(NULL) + 1;
1676 					break;
1677 				default:
1678 					errx(1, "accept");
1679 				}
1680 			} else {
1681 				/* Check if we hit the chosen fd limit */
1682 				for (i = 0; i < maxcon; i++)
1683 					if (con[i].pfd->fd == -1)
1684 						break;
1685 				if (i == maxcon) {
1686 					close(s2);
1687 					slowdowntill = 0;
1688 				} else {
1689 					initcon(&con[i], s2,
1690 					    (struct sockaddr *)&sin);
1691 					syslog_r(LOG_INFO, &sdata,
1692 					    "%s: connected (%d/%d)%s%s",
1693 					    con[i].addr, clients, blackcount,
1694 					    ((con[i].lists == NULL) ? "" :
1695 					    ", lists:"),
1696 					    ((con[i].lists == NULL) ? "":
1697 					    con[i].lists));
1698 				}
1699 			}
1700 		}
1701 		if (pfd[PFD_CONFLISTEN].revents & (POLLIN|POLLHUP)) {
1702 			socklen_t sinlen;
1703 
1704 			sinlen = sizeof(lin);
1705 			conffd = accept(conflisten, (struct sockaddr *)&lin,
1706 			    &sinlen);
1707 			if (conffd == -1) {
1708 				switch (errno) {
1709 				case EINTR:
1710 				case ECONNABORTED:
1711 					break;
1712 				case EMFILE:
1713 				case ENFILE:
1714 					slowdowntill = time(NULL) + 1;
1715 					break;
1716 				default:
1717 					errx(1, "accept");
1718 				}
1719 			} else if (ntohs(lin.sin_port) >= IPPORT_RESERVED) {
1720 				close(conffd);
1721 				conffd = -1;
1722 				slowdowntill = 0;
1723 			}
1724 		} else if (pfd[PFD_CONFFD].revents & (POLLIN|POLLHUP))
1725 			do_config();
1726 		if (pfd[PFD_TRAPFD].revents & (POLLIN|POLLHUP))
1727 			read_configline(trapcfg);
1728 		if (pfd[PFD_SYNCFD].revents & (POLLIN|POLLHUP))
1729 			sync_recv();
1730 		if (pfd[PFD_GREYBACK].revents & (POLLIN|POLLHUP))
1731 			blackcheck(greyback[1]);
1732 	}
1733 	exit(1);
1734 }
1735 
1736 void
1737 blackcheck(int fd)
1738 {
1739 	struct sockaddr_storage ss;
1740 	ssize_t nread;
1741 	void *ia;
1742 	char ch;
1743 
1744 	/* Read sockaddr from greylister and look it up in the blacklists. */
1745 	nread = recv(fd, &ss, sizeof(ss), 0);
1746 	if (nread == -1) {
1747 		syslog(LOG_ERR, "%s: recv: %m", __func__);
1748 		return;
1749 	}
1750 	if (nread != sizeof(struct sockaddr_in) &&
1751 	    nread != sizeof(struct sockaddr_in6)) {
1752 		syslog(LOG_ERR, "%s: invalid size %zd", __func__, nread);
1753 		return;
1754 	}
1755 	if (ss.ss_family == AF_INET) {
1756 		ia = &((struct sockaddr_in *)&ss)->sin_addr;
1757 	} else if (ss.ss_family == AF_INET6) {
1758 		ia = &((struct sockaddr_in6 *)&ss)->sin6_addr;
1759 	} else {
1760 		syslog(LOG_ERR, "%s: bad family %d", __func__, ss.ss_family);
1761 		return;
1762 	}
1763 	ch = sdl_check(blacklists, ss.ss_family, ia) ? '1' : '0';
1764 
1765 	/* Send '1' for match or '0' for no match. */
1766 	if (send(fd, &ch, sizeof(ch), 0) == -1) {
1767 		syslog(LOG_ERR, "%s: send: %m", __func__);
1768 		return;
1769 	}
1770 }
1771