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