git clone milter milter Log gph
Found at: gopher.r-36.net:70/scm/bmf-milter/file/bmf-milter.c.gph
tbmf-milter.c - bmf-milter - bmf filter milter daemon
git clone git://r-36.net/bmf-milter
---
tbmf-milter.c (10322B)
---
1 /*
2 * Copy me if you can.
3 * by 20h
4 */
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/wait.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <grp.h>
13 #include <pwd.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <stdarg.h>
17 #include <sysexits.h>
18 #include <unistd.h>
19 #include <signal.h>
20
21 #include "libmilter/mfapi.h"
22 #include "libmilter/mfdef.h"
23
24 #include "arg.h"
25
26 /*
27 * In case any requires feature is not negotiable, simply do nothing,
28 * thus always return SMFI_CONTINUE. If we would return any FAIL, the
29 * message would be rejected.
30 */
31 int donothing = 0, dodebug = 0;
32 char *bmfdb = NULL, *bmfpath = "/usr/bin/bmf";
33
34 struct Priv {
35 /* read from execpipe[0], write to execpipe[1] */
36 int execpipe[2];
37 int execpid;
38 };
39
40 #define MLFIPRIV ((struct Priv *) smfi_getpriv(ctx))
41
42 int
43 start_bmf(SMFICTX *ctx)
44 {
45 struct Priv *priv;
46 char *ident, *bmfargs[6];
47 int pid, nullfd, argp;
48
49 if (dodebug)
50 fprintf(stderr, "start_bmf()\n");
51
52 if (donothing) {
53 smfi_setpriv(ctx, NULL);
54 return 1;
55 }
56
57 priv = malloc(sizeof(*priv));
58 if (priv == NULL)
59 return 1;
60 memset(priv, '\0', sizeof(*priv));
61
62 smfi_setpriv(ctx, priv);
63
64 if (pipe(priv->execpipe) < 0) {
65 free(priv);
66 smfi_setpriv(ctx, NULL);
67 return 1;
68 }
69
70 switch ((pid = fork())) {
71 case 0:
72 while(dup2(priv->execpipe[0], 0) < 0 && errno == EINTR);
73 close(priv->execpipe[1]);
74
75 argp = 0;
76 bmfargs[argp++] = "bmf";
77 /* Test for spam mode. */
78 bmfargs[argp++] = "-t";
79
80 if (!dodebug) {
81 nullfd = open("/dev/null", O_WRONLY);
82 if (nullfd < 0) {
83 perror("open");
84 _exit(1);
85 }
86 while(dup2(priv->execpipe[0], 1) < 0
87 && errno == EINTR);
88 while(dup2(priv->execpipe[0], 2) < 0
89 && errno == EINTR);
90
91 /* Verbose for bmf. */
92 bmfargs[argp++] = "-v";
93 }
94
95 if (bmfdb != NULL) {
96 /* Set database directory, if set. */
97 bmfargs[argp++] = "-d";
98 bmfargs[argp++] = bmfdb;
99 }
100 bmfargs[argp++] = NULL;
101
102 if (execv(bmfpath, bmfargs) < 0) {
103 perror("execv");
104 _exit(1);
105 }
106 break;
107 case -1:
108 free(priv);
109 smfi_setpriv(ctx, NULL);
110 break;
111 default:
112 if (dodebug)
113 fprintf(stderr, "start_bmf(pid = %d)\n", pid);
114 priv->execpid = pid;
115 close(priv->execpipe[0]);
116 break;
117 }
118
119 return 0;
120 }
121
122 sfsistat
123 mlfi_cleanup(SMFICTX *ctx, int iseom)
124 {
125 struct Priv *priv = MLFIPRIV;
126 int retcode = -1;
127
128 if (dodebug)
129 fprintf(stderr, "mlfi_cleanup(iseom = %d)\n", iseom);
130
131 if (priv == NULL)
132 return SMFIS_CONTINUE;
133 if (dodebug)
134 fprintf(stderr, "mlfi_cleanup(closing execpipe[1])\n");
135 close(priv->execpipe[1]);
136 priv->execpipe[1] = -1;
137 if (dodebug)
138 fprintf(stderr, "mlfi_cleanup(waitpid)\n");
139 waitpid(priv->execpid, &retcode, 0);
140 if (dodebug)
141 fprintf(stderr, "mlfi_cleanup(retcode = %d)\n", retcode);
142
143 /*
144 * smfi_addheader is only allowed in eom.
145 */
146 if (iseom) {
147 if (retcode == 0) {
148 if (smfi_addheader(ctx, "X-Spam-Flag",
149 " YES") == MI_FAILURE) {
150 if (dodebug) {
151 fprintf(stderr,
152 "mlfi_cleanup(x-spam-flag failed)\n");
153 }
154 } else {
155 if (dodebug) {
156 fprintf(stderr,
157 "mlfi_cleanup(x-spam-flag = yes added)\n");
158 }
159 }
160 }
161 if (smfi_addheader(ctx, "X-BMF-Processed",
162 " YES") == MI_FAILURE) {
163 if (dodebug) {
164 fprintf(stderr,
165 "mlfi_cleanup(x-bmf-processed failed)\n");
166 }
167 } else {
168 if (dodebug) {
169 fprintf(stderr,
170 "mlfi_cleanup(x-bmf-processed = yes added)\n");
171 }
172 }
173 }
174
175 return SMFIS_CONTINUE;
176 }
177
178 sfsistat
179 mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
180 {
181 if (dodebug)
182 fprintf(stderr, "mlfi_connect(%s)\n", hostname);
183
184 return SMFIS_CONTINUE;
185 }
186
187 sfsistat
188 mlfi_helo(SMFICTX *ctx, char *helohost)
189 {
190 if (dodebug)
191 fprintf(stderr, "mlfi_helo(%s)\n", helohost);
192
193 smfi_setpriv(ctx, NULL);
194
195 return SMFIS_CONTINUE;
196 }
197
198 sfsistat
199 mlfi_envfrom(SMFICTX *ctx, char *argv[])
200 {
201 struct Priv *priv = MLFIPRIV;
202
203 if (dodebug)
204 fprintf(stderr, "mlfi_envfrom(%s)\n", argv[0]);
205
206 if (priv == NULL) {
207 if (start_bmf(ctx))
208 return SMFIS_CONTINUE;
209 priv = MLFIPRIV;
210 }
211 dprintf(priv->execpipe[1], "From: %s\n", argv[0]);
212
213 return SMFIS_CONTINUE;
214 }
215
216 sfsistat
217 mlfi_envrcpt(SMFICTX *ctx, char *argv[])
218 {
219 struct Priv *priv = MLFIPRIV;
220
221 if (dodebug)
222 fprintf(stderr, "mlfi_envrcpt(%s)\n", argv[0]);
223
224 if (priv == NULL) {
225 if (start_bmf(ctx))
226 return SMFIS_CONTINUE;
227 priv = MLFIPRIV;
228 }
229 dprintf(priv->execpipe[1], "To: %s\n", argv[0]);
230
231 return SMFIS_CONTINUE;
232 }
233
234 sfsistat
235 mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
236 {
237 struct Priv *priv = MLFIPRIV;
238
239 if (dodebug)
240 fprintf(stderr, "mlfi_header(%s = '%s')\n", headerf, headerv);
241
242 if (priv == NULL) {
243 if (start_bmf(ctx))
244 return SMFIS_CONTINUE;
245 priv = MLFIPRIV;
246 }
247 dprintf(priv->execpipe[1], "%s:%s\n", headerf, headerv);
248
249 return SMFIS_CONTINUE;
250 }
251
252 sfsistat
253 mlfi_eoh(SMFICTX *ctx)
254 {
255 struct Priv *priv = MLFIPRIV;
256
257 if (dodebug)
258 fprintf(stderr, "mlfi_eoh()\n");
259
260 if (priv == NULL) {
261 if (start_bmf(ctx))
262 return SMFIS_CONTINUE;
263 priv = MLFIPRIV;
264 }
265 dprintf(priv->execpipe[1], "\r\n");
266
267 return SMFIS_CONTINUE;
268 }
269
270 sfsistat
271 mlfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t bodylen)
272 {
273 struct Priv *priv = MLFIPRIV;
274 int written;
275
276 if (dodebug)
277 fprintf(stderr, "mlfi_body(%ld bytes)\n", bodylen);
278
279 if (priv == NULL) {
280 if (start_bmf(ctx))
281 return SMFIS_CONTINUE;
282 priv = MLFIPRIV;
283 }
284 for (int written = 0, rw = 0; written < bodylen; written += rw)
285 rw = write(priv->execpipe[1], bodyp+written, bodylen-written);
286
287 return SMFIS_CONTINUE;
288 }
289
290 sfsistat
291 mlfi_eom(SMFICTX *ctx)
292 {
293 if (dodebug)
294 fprintf(stderr, "mlfi_eom()\n");
295
296 return mlfi_cleanup(ctx, 1);
297 }
298
299 sfsistat
300 mlfi_abort(SMFICTX *ctx)
301 {
302 if (dodebug)
303 fprintf(stderr, "mlfi_abort()\n");
304
305 return mlfi_cleanup(ctx, 0);
306 }
307
308 sfsistat
309 mlfi_close(SMFICTX *ctx)
310 {
311 struct Priv *priv = MLFIPRIV;
312
313 if (dodebug)
314 fprintf(stderr, "mlfi_close()\n");
315
316 if (priv != NULL) {
317 if (priv->execpipe[1] > 0) {
318 if (dodebug) {
319 fprintf(stderr,
320 "mlfi_close(close execpipe[1])\n");
321 }
322 close(priv->execpipe[1]);
323 }
324 if (priv->execpid > 0) {
325 if (dodebug) {
326 fprintf(stderr,
327 "mlfi_close(kill pid %d)\n", priv->execpid);
328 }
329 kill(priv->execpid, SIGKILL);
330 waitpid(priv->execpid, NULL, 0);
331 }
332 if (dodebug)
333 fprintf(stderr, "mlfi_close(free priv)\n");
334 free(priv);
335 smfi_setpriv(ctx, NULL);
336 }
337
338 return SMFIS_CONTINUE;
339 }
340
341 sfsistat
342 mlfi_negotiate(SMFICTX *ctx,
343 unsigned long f0,
344 unsigned long f1,
345 unsigned long f2,
346 unsigned long f3,
347 unsigned long *pf0,
348 unsigned long *pf1,
349 unsigned long *pf2,
350 unsigned long *pf3)
351 {
352 if (dodebug)
353 fprintf(stderr, "mlfi_negotiate()\n");
354
355 /* milter actions */
356 *pf0 = 0;
357 if (f0 & SMFIF_ADDHDRS) {
358 *pf0 |= SMFIF_ADDHDRS;
359 } else {
360 donothing = 1;
361 }
362
363 /* milter protocol steps */
364 *pf1 = f1 & (SMFIP_NOUNKNOWN|SMFIP_NODATA);
365 if (f1 & SMFIP_HDR_LEADSPC) {
366 *pf1 |= SMFIP_HDR_LEADSPC;
367 } else {
368 donothing = 1;
369 }
370
371 /* future */
372 *pf2 = 0;
373 *pf3 = 0;
374
375 return SMFIS_CONTINUE;
376 }
377
378 struct smfiDesc smfilter =
379 {
380 "BMF", /* filter name */
381 SMFI_VERSION, /* version code -- do not change */
382 SMFIF_ADDHDRS, /* flags */
383 mlfi_connect, /* connection info filter */
384 mlfi_helo, /* SMTP HELO command filter */
385 mlfi_envfrom, /* envelope sender filter */
386 mlfi_envrcpt, /* envelope recipient filter */
387 mlfi_header, /* header filter */
388 mlfi_eoh, /* end of header */
389 mlfi_body, /* body block filter */
390 mlfi_eom, /* end of message */
391 mlfi_abort, /* message aborted */
392 mlfi_close, /* connection cleanup */
393 NULL, /* unknown SMTP commands */
394 NULL, /* DATA command */
395 mlfi_negotiate /* Once, at the start of each SMTP connection */
396 };
397
398 void
399 sighandler(int sig)
400 {
401 int i;
402
403 switch (sig) {
404 case SIGCHLD:
405 while (waitpid(-1, NULL, WNOHANG) > 0);
406 break;
407 case SIGINT:
408 case SIGQUIT:
409 case SIGABRT:
410 case SIGTERM:
411 case SIGKILL:
412 smfi_stop();
413 break;
414 default:
415 break;
416 }
417 }
418
419 void
420 initsignals(void)
421 {
422 signal(SIGCHLD, sighandler);
423 signal(SIGHUP, sighandler);
424 signal(SIGINT, sighandler);
425 signal(SIGQUIT, sighandler);
426 signal(SIGABRT, sighandler);
427 signal(SIGTERM, sighandler);
428 signal(SIGKILL, sighandler);
429
430 /*
431 * done by smfi_main():
432 * signal(SIGPIPE, SIG_IGN);
433 */
434 }
435
436 void
437 usage(char *argv0)
438 {
439 fprintf(stderr,
440 "Usage: %s [-hd] [-b bmfdb] [-c conndef] [-f bmfpath] "
441 "[-g group] [-t timeout] [-u user] [-v dbglevel]\n",
442 argv0);
443 }
444
445 int
446 main(int argc, char *argv[])
447 {
448 char *argv0, *user = NULL, *group = NULL, *conndef = "inet:9957";
449 int timeout = -1, dofork = 1;
450 struct passwd *us = NULL;
451 struct group *gr = NULL;
452
453 ARGBEGIN(argv0) {
454 case 'b':
455 bmfdb = EARGF(usage(argv0));
456 break;
457 case 'c':
458 conndef = EARGF(usage(argv0));
459 break;
460 case 'd':
461 dofork = 0;
462 break;
463 case 'f':
464 bmfpath = EARGF(usage(argv0));
465 break;
466 case 'g':
467 group = EARGF(usage(argv0));
468 break;
469 case 't':
470 timeout = atoi(EARGF(usage(argv0)));
471 break;
472 case 'u':
473 user = EARGF(usage(argv0));
474 break;
475 case 'v':
476 smfi_setdbg(atoi(EARGF(argv0)));
477 dodebug = 1;
478 break;
479 default:
480 usage(argv0);
481 return 1;
482 } ARGEND;
483
484 if (group != NULL) {
485 errno = 0;
486 if ((gr = getgrnam(group)) == NULL) {
487 if (errno == 0) {
488 fprintf(stderr, "no such group '%s'\n",
489 group);
490 } else {
491 perror("getgrnam");
492 }
493 return 1;
494 }
495 }
496
497 if (user != NULL) {
498 errno = 0;
499 if ((us = getpwnam(user)) == NULL) {
500 if (errno == 0) {
501 fprintf(stderr, "no such user '%s'\n",
502 user);
503 } else {
504 perror("getpwnam");
505 }
506 return 1;
507 }
508 }
509
510 if (dofork) {
511 switch (fork()) {
512 case -1:
513 perror("fork");
514 return 1;
515 case 0:
516 break;
517 default:
518 return 0;
519 }
520 }
521
522 if (gr != NULL) {
523 if (setgroups(1, &gr->gr_gid) != 0 || setgid(gr->gr_gid) != 0) {
524 perror("setgroups");
525 return -1;
526 }
527 }
528 if (us != NULL) {
529 if (gr == NULL) {
530 if (setgroups(1, &us->pw_gid) != 0 ||
531 setgid(us->pw_gid) != 0) {
532 perror("setgroups");
533 return -1;
534 }
535 }
536 if (setuid(us->pw_uid) != 0) {
537 perror("setuid");
538 return -1;
539 }
540 }
541
542 if (smfi_setconn(conndef) == MI_FAILURE) {
543 perror("smfi_setconn");
544 return 1;
545 }
546 if (dodebug)
547 fprintf(stderr, "conn set to '%s'\n", conndef);
548
549 if (timeout > 0) {
550 if (smfi_settimeout(timeout) == MI_FAILURE) {
551 perror("smfi_settimout");
552 return 1;
553 }
554 if (dodebug)
555 fprintf(stderr, "timeout set to %d\n", timeout);
556 }
557
558 if (smfi_register(smfilter) == MI_FAILURE) {
559 perror("smfi_register");
560 return 1;
561 }
562
563 return smfi_main();
564 }
565
.