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