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