Bug Summary

File:src/libexec/spamd/spamd.c
Warning:line 643, column 2
Value stored to 's' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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