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