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