Remove some more dead initialization.
[dragonfly.git] / libexec / dma / dma.c
blob70cb8ae6c83df8be26e5ceeb937eb5086f77229e
1 /*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * $DragonFly: src/libexec/dma/dma.c,v 1.5 2008/09/30 17:47:21 swildner Exp $
37 #include <sys/ipc.h>
38 #include <sys/param.h>
39 #include <sys/queue.h>
40 #include <sys/sem.h>
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
46 #ifdef HAVE_CRYPTO
47 #include <openssl/ssl.h>
48 #endif /* HAVE_CRYPTO */
50 #include <dirent.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <grp.h>
55 #include <inttypes.h>
56 #include <netdb.h>
57 #include <paths.h>
58 #include <pwd.h>
59 #include <signal.h>
60 #include <stdarg.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <syslog.h>
65 #include <unistd.h>
67 #include "dma.h"
71 static void deliver(struct qitem *, int);
72 static void deliver_smarthost(struct queue *, int);
73 static int add_recp(struct queue *, const char *, const char *, int);
75 struct aliases aliases = LIST_HEAD_INITIALIZER(aliases);
76 static struct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs);
77 struct virtusers virtusers = LIST_HEAD_INITIALIZER(virtusers);
78 struct authusers authusers = LIST_HEAD_INITIALIZER(authusers);
79 static int daemonize = 1;
80 struct config *config;
81 int controlsocket_df, clientsocket_df, controlsocket_wl, clientsocket_wl, semkey;
83 static void
84 release_children(void)
86 struct sembuf sema;
87 int null = 0;
89 /*
90 * Try to decrement semaphore as we start communicating with
91 * write_to_local_user()
93 sema.sem_num = SEM_WL;
94 sema.sem_op = -1;
95 sema.sem_flg = 0;
96 if (semop(semkey, &sema, 1) == -1) {
97 err(1, "semaphore decrement failed");
101 * write_to_local_user() will exit and kill dotforwardhandler(), too
102 * if the corresponding semaphore is zero
103 * otherwise nothing happens
105 write(controlsocket_wl, &null, sizeof(null));
108 * Increment semaphore as we stop communicating with
109 * write_to_local_user()
111 sema.sem_op = 1;
112 if (semop(semkey, &sema, 1) == -1) {
113 err(1, "semaphore decrement failed");
117 char *
118 hostname(void)
120 static char name[MAXHOSTNAMELEN+1];
122 if (gethostname(name, sizeof(name)) != 0)
123 strcpy(name, "(unknown hostname)");
125 return name;
128 static char *
129 set_from(const char *osender)
131 struct virtuser *v;
132 char *sender;
134 if ((config->features & VIRTUAL) != 0) {
135 SLIST_FOREACH(v, &virtusers, next) {
136 if (strcmp(v->login, getlogin()) == 0) {
137 sender = strdup(v->address);
138 if (sender == NULL)
139 return(NULL);
140 goto out;
145 if (osender) {
146 sender = strdup(osender);
147 if (sender == NULL)
148 return(NULL);
149 } else {
150 if (asprintf(&sender, "%s@%s", getlogin(), hostname()) <= 0)
151 return(NULL);
154 if (strchr(sender, '\n') != NULL) {
155 errno = EINVAL;
156 return(NULL);
159 out:
160 return(sender);
163 static int
164 read_aliases(void)
166 yyin = fopen(config->aliases, "r");
167 if (yyin == NULL)
168 return(0); /* not fatal */
169 if (yyparse())
170 return(-1); /* fatal error, probably malloc() */
171 fclose(yyin);
172 return(0);
175 static int
176 add_recp(struct queue *queue, const char *str, const char *sender, int expand)
178 struct qitem *it, *tit;
179 struct stritem *sit;
180 struct alias *al;
181 struct passwd *pw;
182 char *host;
183 int aliased = 0;
185 it = calloc(1, sizeof(*it));
186 if (it == NULL)
187 return(-1);
188 it->addr = strdup(str);
189 if (it->addr == NULL)
190 return(-1);
192 it->sender = sender;
193 host = strrchr(it->addr, '@');
194 if (host != NULL &&
195 (strcmp(host + 1, hostname()) == 0 ||
196 strcmp(host + 1, "localhost") == 0)) {
197 *host = 0;
199 LIST_FOREACH(tit, &queue->queue, next) {
200 /* weed out duplicate dests */
201 if (strcmp(tit->addr, it->addr) == 0) {
202 free(it->addr);
203 free(it);
204 return(0);
207 LIST_INSERT_HEAD(&queue->queue, it, next);
208 if (strrchr(it->addr, '@') == NULL) {
209 /* local = 1 means its a username or mailbox */
210 it->local = 1;
211 /* only search for aliases and .forward if asked for */
212 /* needed to have the possibility to add an mailbox directly */
213 if (expand) {
214 /* first check /etc/aliases */
215 LIST_FOREACH(al, &aliases, next) {
216 if (strcmp(al->alias, it->addr) != 0)
217 continue;
218 SLIST_FOREACH(sit, &al->dests, next) {
219 if (add_recp(queue, sit->str,
220 sender, 1) != 0)
221 return(-1);
223 aliased = 1;
225 if (aliased) {
226 LIST_REMOVE(it, next);
227 } else {
228 /* then check .forward of user */
229 fd_set rfds;
230 int ret;
231 uint8_t len, type;
232 struct sembuf sema;
233 /* is the username valid */
234 pw = getpwnam(it->addr);
235 endpwent();
236 if (pw == NULL)
237 goto out;
240 * Try to decrement semaphore as we start
241 * communicating with dotforwardhandler()
243 sema.sem_num = SEM_DF;
244 sema.sem_op = -1;
245 sema.sem_flg = 0;
246 if (semop(semkey, &sema, 1) == -1) {
247 err(1, "semaphore decrement failed");
250 /* write username to dotforwardhandler */
251 len = strlen(it->addr);
252 write(controlsocket_df, &len, sizeof(len));
253 write(controlsocket_df, it->addr, len);
254 FD_ZERO(&rfds);
255 FD_SET(controlsocket_df, &rfds);
257 /* wait for incoming redirects and pipes */
258 while ((ret = select(controlsocket_df + 1,
259 &rfds, NULL, NULL, NULL))) {
261 * Receive back list of mailboxnames
262 * and/or emailadresses
264 if (ret == -1) {
266 * increment semaphore because
267 * we stopped communicating
268 * with dotforwardhandler()
270 sema.sem_op = 1;
271 semop(semkey, &sema, 1);
272 return(-1);
274 /* read type of .forward entry */
275 read(controlsocket_df, &type, 1);
276 if (type & ENDOFDOTFORWARD) {
277 /* end of .forward */
279 * If there are redirects, then
280 * we do not need the original
281 * qitem any longer
283 if (aliased) {
284 LIST_REMOVE(it, next);
286 break;
287 } else if (type & ISMAILBOX) {
288 /* redirect -> user/emailaddress */
290 * FIXME shall there be the possibility to use
291 * usernames instead of mailboxes?
293 char *username;
294 read(controlsocket_df, &len, sizeof(len));
295 username = calloc(1, len + 1);
296 read(controlsocket_df, username, len);
298 * Do not further expand since
299 * its remote or local mailbox
301 if (add_recp(queue, username, sender, 0) != 0) {
302 aliased = 1;
304 } else if (type & ISPIPE) {
305 /* redirect to a pipe */
307 * Create new qitem and save
308 * information in it
310 struct qitem *pit;
311 pit = calloc(1, sizeof(*pit));
312 if (pit == NULL) {
314 * Increment semaphore
315 * because we stopped
316 * communicating with
317 * dotforwardhandler()
319 sema.sem_op = 1;
320 semop(semkey, &sema, 1);
321 return(-1);
323 LIST_INSERT_HEAD(&queue->queue, pit, next);
325 * Save username to qitem,
326 * because its overwritten by
327 * pipe command
329 pit->pipeuser = strdup(it->addr);
330 pit->sender = sender;
331 /* local = 2 means redirect to pipe */
332 pit->local = 2;
333 read(controlsocket_df, &len, sizeof(len));
334 pit->addr = realloc(pit->addr, len + 1);
335 memset(pit->addr, 0, len + 1);
336 read(controlsocket_df, pit->addr, len);
337 aliased = 1;
341 * Increment semaphore because we stopped
342 * communicating with dotforwardhandler()
344 sema.sem_op = 1;
345 semop(semkey, &sema, 1);
348 } else {
349 it->local = 0;
352 return(0);
354 out:
355 free(it->addr);
356 free(it);
357 return(-1);
360 static void
361 deltmp(void)
363 struct stritem *t;
365 SLIST_FOREACH(t, &tmpfs, next) {
366 unlink(t->str);
370 static int
371 gentempf(struct queue *queue)
373 char fn[PATH_MAX+1];
374 struct stritem *t;
375 int fd;
377 if (snprintf(fn, sizeof(fn), "%s/%s", config->spooldir, "tmp_XXXXXXXXXX") <= 0)
378 return(-1);
379 fd = mkstemp(fn);
380 if (fd < 0)
381 return(-1);
382 queue->mailfd = fd;
384 queue->tmpf = strdup(fn);
385 if (queue->tmpf == NULL) {
386 unlink(fn);
387 return(-1);
389 t = malloc(sizeof(*t));
390 if (t != NULL) {
391 t->str = queue->tmpf;
392 SLIST_INSERT_HEAD(&tmpfs, t, next);
394 return(0);
398 * spool file format:
400 * envelope-from
401 * queue-id1 envelope-to1
402 * queue-id2 envelope-to2
403 * ...
404 * <empty line>
405 * mail data
407 * queue ids are unique, formed from the inode of the spool file
408 * and a unique identifier.
410 static int
411 preparespool(struct queue *queue, const char *sender)
413 char line[1000]; /* by RFC2822 */
414 struct stat st;
415 int error;
416 struct qitem *it;
417 FILE *queuef;
418 off_t hdrlen;
420 error = snprintf(line, sizeof(line), "%s\n", sender);
421 if (error < 0 || (size_t)error >= sizeof(line)) {
422 errno = E2BIG;
423 return(-1);
425 if (write(queue->mailfd, line, error) != error)
426 return(-1);
428 queuef = fdopen(queue->mailfd, "r+");
429 if (queuef == NULL)
430 return(-1);
433 * Assign queue id to each dest.
435 if (fstat(queue->mailfd, &st) != 0)
436 return(-1);
437 queue->id = st.st_ino;
438 LIST_FOREACH(it, &queue->queue, next) {
439 if (asprintf(&it->queueid, "%"PRIxMAX".%"PRIxPTR,
440 queue->id, (uintptr_t)it) <= 0)
441 return(-1);
442 if (asprintf(&it->queuefn, "%s/%s",
443 config->spooldir, it->queueid) <= 0)
444 return(-1);
445 /* File may already exist */
446 if (stat(it->queuefn, &st) == 0) {
447 warn("Spoolfile already exists: %s", it->queuefn);
448 return(-1);
450 /* Reset errno to avoid confusion */
451 errno = 0;
452 it->queuef = queuef;
453 error = snprintf(line, sizeof(line), "%s %s\n",
454 it->queueid, it->addr);
455 if (error < 0 || (size_t)error >= sizeof(line))
456 return(-1);
457 if (write(queue->mailfd, line, error) != error)
458 return(-1);
460 line[0] = '\n';
461 if (write(queue->mailfd, line, 1) != 1)
462 return(-1);
464 hdrlen = lseek(queue->mailfd, 0, SEEK_CUR);
465 LIST_FOREACH(it, &queue->queue, next) {
466 it->hdrlen = hdrlen;
468 return(0);
471 static char *
472 rfc822date(void)
474 static char str[50];
475 size_t error;
476 time_t now;
478 now = time(NULL);
479 error = strftime(str, sizeof(str), "%a, %d %b %Y %T %z",
480 localtime(&now));
481 if (error == 0)
482 strcpy(str, "(date fail)");
483 return(str);
486 static int
487 readmail(struct queue *queue, const char *sender, int nodot)
489 char line[1000]; /* by RFC2822 */
490 size_t linelen;
491 int error;
493 error = snprintf(line, sizeof(line), "\
494 Received: from %s (uid %d)\n\
495 \t(envelope-from %s)\n\
496 \tid %"PRIxMAX"\n\
497 \tby %s (%s)\n\
498 \t%s\n",
499 getlogin(), getuid(),
500 sender,
501 queue->id,
502 hostname(), VERSION,
503 rfc822date());
504 if (error < 0 || (size_t)error >= sizeof(line))
505 return(-1);
506 if (write(queue->mailfd, line, error) != error)
507 return(-1);
509 while (!feof(stdin)) {
510 if (fgets(line, sizeof(line), stdin) == NULL)
511 break;
512 linelen = strlen(line);
513 if (linelen == 0 || line[linelen - 1] != '\n') {
514 errno = EINVAL; /* XXX mark permanent errors */
515 return(-1);
517 if (!nodot && linelen == 2 && line[0] == '.')
518 break;
519 if ((size_t)write(queue->mailfd, line, linelen) != linelen)
520 return(-1);
522 if (fsync(queue->mailfd) != 0)
523 return(-1);
524 return(0);
527 static int
528 linkspool(struct queue *queue)
530 struct qitem *it;
533 * Only if it is not a pipe delivery
534 * pipe deliveries are only tried once so there
535 * is no need for a spool-file, they use the
536 * original tempfile
539 LIST_FOREACH(it, &queue->queue, next) {
541 * There shall be no files for pipe deliveries since not all
542 * information is saved in the header, so pipe delivery is
543 * tried once and forgotten thereafter.
545 if (it->local == 2)
546 continue;
547 if (link(queue->tmpf, it->queuefn) != 0)
548 goto delfiles;
550 return(0);
552 delfiles:
553 LIST_FOREACH(it, &queue->queue, next) {
555 * There are no files for pipe delivery, so they can't be
556 * deleted.
558 if (it->local == 2)
559 continue;
560 unlink(it->queuefn);
562 return(-1);
565 static void
566 go_background(struct queue *queue, int leavesemaphore)
568 struct sigaction sa;
569 struct qitem *it;
570 pid_t pid;
571 int seen_remote_address = 0;
573 if (daemonize && daemon(0, 0) != 0) {
574 syslog(LOG_ERR, "[go_background] can not daemonize: %m");
575 exit(1);
577 daemonize = 0;
578 bzero(&sa, sizeof(sa));
579 sa.sa_flags = SA_NOCLDWAIT;
580 sa.sa_handler = SIG_IGN;
581 sigaction(SIGCHLD, &sa, NULL);
584 LIST_FOREACH(it, &queue->queue, next) {
586 * If smarthost is enabled, the address is remote
587 * set smarthost delivery flag, otherwise deliver it 'normal'.
589 if (config->smarthost != NULL && strlen(config->smarthost) > 0
590 && it->local == 0
592 seen_remote_address = 1;
594 * if it is not the last entry, continue
595 * (if it is the last, start delivery in parent
597 if (LIST_NEXT(it, next) != NULL) {
598 continue;
600 } else {
602 * If item is local, we do not need it in the list any
603 * more, so delete it.
605 LIST_REMOVE(it, next);
607 pid = fork();
608 switch (pid) {
609 case -1:
610 syslog(LOG_ERR, "can not fork: %m");
611 exit(1);
612 break;
614 case 0:
616 * Child:
618 * return and deliver mail
621 if (config->smarthost == NULL || strlen(config->smarthost) == 0 || it->local)
622 if (LIST_NEXT(it, next) == NULL && !seen_remote_address)
623 /* if there is no smarthost-delivery and we are the last item */
624 deliver(it, leavesemaphore);
625 else
626 deliver(it, 0);
627 else
628 _exit(0);
630 default:
632 * Parent:
634 * fork next child
637 * If it is the last loop and there were remote
638 * addresses, start smarthost delivery.
639 * No need to doublecheck if smarthost is
640 * activated in config file.
642 if (LIST_NEXT(it, next) == NULL) {
643 if (seen_remote_address) {
644 deliver_smarthost(queue, leavesemaphore);
645 } else {
646 _exit(0);
649 break;
653 syslog(LOG_CRIT, "reached dead code");
654 exit(1);
657 static void
658 bounce(struct qitem *it, const char *reason, int leavesemaphore)
660 struct queue bounceq;
661 struct qitem *bit;
662 char line[1000];
663 int error;
664 struct sembuf sema;
666 /* Don't bounce bounced mails */
667 if (it->sender[0] == 0) {
669 * If we are the last bounce, then decrement semaphore
670 * and release children.
672 if (leavesemaphore) {
673 /* semaphore-- (MUST NOT BLOCK BECAUSE ITS POSITIVE) */
674 sema.sem_num = SEM_SIGHUP;
675 sema.sem_op = -1;
676 sema.sem_flg = IPC_NOWAIT;
677 if (semop(semkey, &sema, 1) == -1) {
678 err(1, "[deliver] semaphore decrement failed");
680 /* release child processes */
681 release_children();
683 syslog(LOG_CRIT, "%s: delivery panic: can't bounce a bounce",
684 it->queueid);
685 exit(1);
688 syslog(LOG_ERR, "%s: delivery failed, bouncing",
689 it->queueid);
691 LIST_INIT(&bounceq.queue);
692 if (add_recp(&bounceq, it->sender, "", 1) != 0)
693 goto fail;
694 if (gentempf(&bounceq) != 0)
695 goto fail;
696 if (preparespool(&bounceq, "") != 0)
697 goto fail;
699 bit = LIST_FIRST(&bounceq.queue);
700 error = fprintf(bit->queuef, "\
701 Received: from MAILER-DAEMON\n\
702 \tid %"PRIxMAX"\n\
703 \tby %s (%s)\n\
704 \t%s\n\
705 X-Original-To: <%s>\n\
706 From: MAILER-DAEMON <>\n\
707 To: %s\n\
708 Subject: Mail delivery failed\n\
709 Message-Id: <%"PRIxMAX"@%s>\n\
710 Date: %s\n\
712 This is the %s at %s.\n\
714 There was an error delivering your mail to <%s>.\n\
716 %s\n\
718 Message headers follow.\n\
721 bounceq.id,
722 hostname(), VERSION,
723 rfc822date(),
724 it->addr,
725 it->sender,
726 bounceq.id, hostname(),
727 rfc822date(),
728 VERSION, hostname(),
729 it->addr,
730 reason);
731 if (error < 0)
732 goto fail;
733 if (fflush(bit->queuef) != 0)
734 goto fail;
736 if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0)
737 goto fail;
738 while (!feof(it->queuef)) {
739 if (fgets(line, sizeof(line), it->queuef) == NULL)
740 break;
741 if (line[0] == '\n')
742 break;
743 write(bounceq.mailfd, line, strlen(line));
745 if (fsync(bounceq.mailfd) != 0)
746 goto fail;
747 if (linkspool(&bounceq) != 0)
748 goto fail;
749 /* bounce is safe */
751 unlink(it->queuefn);
752 fclose(it->queuef);
754 go_background(&bounceq, leavesemaphore);
755 /* NOTREACHED */
757 fail:
759 * If we are the last bounce, then decrement semaphore
760 * and release children.
762 if (leavesemaphore) {
763 /* semaphore-- (MUST NOT BLOCK BECAUSE ITS POSITIVE) */
764 sema.sem_num = SEM_SIGHUP;
765 sema.sem_op = -1;
766 sema.sem_flg = IPC_NOWAIT;
767 if (semop(semkey, &sema, 1) == -1) {
768 err(1, "[deliver] semaphore decrement failed");
770 /* release child processes */
771 release_children();
773 syslog(LOG_CRIT, "%s: error creating bounce: %m", it->queueid);
774 unlink(it->queuefn);
775 exit(1);
778 static int
779 deliver_local(struct qitem *it, const char **errmsg)
781 char line[1000];
782 char fn[PATH_MAX+1];
783 int len;
784 uint8_t mode = 0, fail = 0;
785 ssize_t linelen;
786 time_t now = time(NULL);
787 char *username = NULL;
788 struct sembuf sema;
792 * Try to decrement semaphore as we start communicating with
793 * write_to_local_user()
795 sema.sem_num = SEM_WL;
796 sema.sem_op = -1;
797 sema.sem_flg = 0;
798 if (semop(semkey, &sema, 1) == -1) {
799 err(1, "semaphore decrement failed");
803 /* Tell write_to_local_user() the username to drop the privileges */
804 if (it->local == 1) { /* mailbox delivery */
805 username = it->addr;
806 } else if (it->local == 2) { /* pipe delivery */
807 username = it->pipeuser;
809 len = strlen(username);
810 write(controlsocket_wl, &len, sizeof(len));
811 write(controlsocket_wl, username, len);
812 read(controlsocket_wl, &fail, sizeof(fail));
813 if (fail) {
814 syslog(LOG_ERR,
815 "%s: local delivery deferred: can not fork and drop privileges `%s': %m",
816 it->queueid, username);
818 * Increment semaphore because we stopped communicating with
819 * write_to_local_user().
821 sema.sem_op = 1;
822 semop(semkey, &sema, 1);
823 return(1);
827 /* Tell write_to_local_user() the delivery mode (write to mailbox || pipe) */
828 if (it->local == 1) { /* mailbox delivery */
829 mode = ISMAILBOX;
830 len = snprintf(fn, sizeof(fn), "%s/%s", _PATH_MAILDIR, it->addr);
831 if (len < 0 || (size_t)len >= sizeof(fn)) {
832 syslog(LOG_ERR, "%s: local delivery deferred: %m",
833 it->queueid);
835 * Increment semaphore because we stopped communicating
836 * with write_to_local_user().
838 sema.sem_op = 1;
839 semop(semkey, &sema, 1);
840 return(1);
842 } else if (it->local == 2) { /* pipe delivery */
843 mode = ISPIPE;
844 strncpy(fn, it->addr, sizeof(fn));
845 len = strlen(fn);
847 write(controlsocket_wl, &len, sizeof(len));
848 write(controlsocket_wl, fn, len);
849 write(controlsocket_wl, &mode, sizeof(mode));
850 read(controlsocket_wl, &fail, sizeof(fail));
851 if (fail) {
852 errno = fail;
853 syslog(LOG_ERR,
854 "%s: local delivery deferred: can not (p)open `%s': %m",
855 it->queueid, it->addr);
857 * Increment semaphore because we stopped communicating
858 * with write_to_local_user().
860 sema.sem_op = 1;
861 semop(semkey, &sema, 1);
862 return(1);
866 /* Prepare transfer of mail-data */
867 if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0) {
868 syslog(LOG_ERR, "%s: local delivery deferred: can not seek: %m",
869 it->queueid);
871 * Increment semaphore because we stopped communicating
872 * with write_to_local_user().
874 sema.sem_op = 1;
875 semop(semkey, &sema, 1);
876 return(1);
880 /* Send first header line. */
881 linelen = snprintf(line, sizeof(line), "From %s\t%s", it->sender, ctime(&now));
882 if (linelen < 0 || (size_t)linelen >= sizeof(line)) {
883 syslog(LOG_ERR, "%s: local delivery deferred: can not write header: %m",
884 it->queueid);
886 * Increment semaphore because we stopped communicating
887 * with write_to_local_user().
889 sema.sem_op = 1;
890 semop(semkey, &sema, 1);
891 return(1);
894 write(controlsocket_wl, &linelen, sizeof(linelen));
895 write(controlsocket_wl, line, linelen);
897 read(controlsocket_wl, &fail, sizeof(fail));
898 if (fail) {
899 goto wrerror;
903 /* Read mail data and transfer it to write_to_local_user(). */
904 while (!feof(it->queuef)) {
905 if (fgets(line, sizeof(line), it->queuef) == NULL)
906 break;
907 linelen = strlen(line);
908 if (linelen == 0 || line[linelen - 1] != '\n') {
909 syslog(LOG_CRIT,
910 "%s: local delivery failed: corrupted queue file",
911 it->queueid);
912 *errmsg = "corrupted queue file";
913 len = -1;
914 /* break receive and write loop at write_to_local_user() */
915 linelen = 0;
916 write(controlsocket_wl, &linelen, sizeof(linelen));
917 /* and send error state */
918 linelen = 1;
919 write(controlsocket_wl, &linelen, sizeof(linelen));
920 goto chop;
923 if (strncmp(line, "From ", 5) == 0) {
924 const char *gt = ">";
925 size_t sizeofchar = 1;
927 write(controlsocket_wl, &sizeofchar, sizeof(sizeofchar));
928 write(controlsocket_wl, gt, 1);
929 read(controlsocket_wl, &fail, sizeof(fail));
930 if (fail) {
931 goto wrerror;
934 write(controlsocket_wl, &linelen, sizeof(linelen));
935 write(controlsocket_wl, line, linelen);
936 read(controlsocket_wl, &fail, sizeof(fail));
937 if (fail) {
938 goto wrerror;
942 /* Send final linebreak */
943 line[0] = '\n';
944 linelen = 1;
945 write(controlsocket_wl, &linelen, sizeof(linelen));
946 write(controlsocket_wl, line, linelen);
947 read(controlsocket_wl, &fail, sizeof(fail));
948 if (fail) {
949 goto wrerror;
953 /* break receive and write loop in write_to_local_user() */
954 linelen = 0;
955 /* send '0' twice, because above we send '0' '1' in case of error */
956 write(controlsocket_wl, &linelen, sizeof(linelen));
957 write(controlsocket_wl, &linelen, sizeof(linelen));
958 read(controlsocket_wl, &fail, sizeof(fail));
959 if (fail) {
960 goto wrerror;
965 * Increment semaphore because we stopped communicating
966 * with write_to_local_user().
968 sema.sem_op = 1;
969 semop(semkey, &sema, 1);
970 return(0);
972 wrerror:
973 errno = fail;
974 syslog(LOG_ERR, "%s: local delivery failed: write error: %m",
975 it->queueid);
976 len = 1;
977 chop:
978 read(controlsocket_wl, &fail, sizeof(fail));
979 if (fail == 2) {
980 syslog(LOG_WARNING, "%s: error recovering mbox `%s': %m",
981 it->queueid, fn);
984 * Increment semaphore because we stopped communicating
985 * with write_to_local_user().
987 sema.sem_op = 1;
988 semop(semkey, &sema, 1);
989 return(len);
992 static void
993 deliver(struct qitem *it, int leavesemaphore)
995 int error;
996 unsigned int backoff = MIN_RETRY;
997 const char *errmsg = "unknown bounce reason";
998 struct timeval now;
999 struct stat st;
1000 struct sembuf sema;
1002 if (it->local == 2) {
1003 syslog(LOG_INFO, "%s: mail from=<%s> to=<%s> command=<%s>",
1004 it->queueid, it->sender, it->pipeuser, it->addr);
1005 } else {
1006 syslog(LOG_INFO, "%s: mail from=<%s> to=<%s>",
1007 it->queueid, it->sender, it->addr);
1010 retry:
1011 syslog(LOG_INFO, "%s: trying delivery",
1012 it->queueid);
1015 * Only increment semaphore, if we are not the last bounce
1016 * because there is still a incremented semaphore from
1017 * the bounced delivery
1019 if (!leavesemaphore) {
1021 * Increment semaphore for each mail we try to deliver.
1022 * When completing the transmit, the semaphore is decremented.
1023 * If the semaphore is zero the other childs know that they
1024 * can terminate.
1026 sema.sem_num = SEM_SIGHUP;
1027 sema.sem_op = 1;
1028 sema.sem_flg = 0;
1029 if (semop(semkey, &sema, 1) == -1) {
1030 err(1, "[deliver] semaphore increment failed");
1033 if (it->local) {
1034 error = deliver_local(it, &errmsg);
1035 } else {
1036 error = deliver_remote(it, &errmsg, NULL);
1039 switch (error) {
1040 case 0:
1041 /* semaphore-- (MUST NOT BLOCK BECAUSE ITS POSITIVE) */
1042 sema.sem_num = SEM_SIGHUP;
1043 sema.sem_op = -1;
1044 sema.sem_flg = IPC_NOWAIT;
1045 if (semop(semkey, &sema, 1) == -1) {
1046 err(1, "[deliver] semaphore decrement failed");
1048 /* release child processes */
1049 release_children();
1050 /* Do not try to delete the spool file: pipe mode */
1051 if (it->local != 2)
1052 unlink(it->queuefn);
1053 syslog(LOG_INFO, "%s: delivery successful",
1054 it->queueid);
1055 exit(0);
1057 case 1:
1058 /* pipe delivery only tries once, then gives up */
1059 if (it->local == 2) {
1060 /* decrement-- (MUST NOT BLOCK BECAUSE ITS POSITIVE) */
1061 sema.sem_num = SEM_SIGHUP;
1062 sema.sem_op = -1;
1063 sema.sem_flg = IPC_NOWAIT;
1064 if (semop(semkey, &sema, 1) == -1) {
1065 err(1, "[deliver] semaphore decrement failed");
1067 /* release child processes */
1068 release_children();
1069 syslog(LOG_ERR, "%s: delivery to pipe `%s' failed, giving up",
1070 it->queueid, it->addr);
1071 exit(1);
1073 if (stat(it->queuefn, &st) != 0) {
1074 /* semaphore-- (MUST NOT BLOCK BECAUSE ITS POSITIVE) */
1075 sema.sem_num = SEM_SIGHUP;
1076 sema.sem_op = -1;
1077 sema.sem_flg = IPC_NOWAIT;
1078 if (semop(semkey, &sema, 1) == -1) {
1079 err(1, "[deliver] semaphore decrement failed");
1081 /* release child processes */
1082 release_children();
1083 syslog(LOG_ERR, "%s: lost queue file `%s'",
1084 it->queueid, it->queuefn);
1085 exit(1);
1087 if (gettimeofday(&now, NULL) == 0 &&
1088 (now.tv_sec - st.st_mtimespec.tv_sec > MAX_TIMEOUT)) {
1089 char *msg;
1091 if (asprintf(&msg,
1092 "Could not deliver for the last %d seconds. Giving up.",
1093 MAX_TIMEOUT) > 0)
1094 errmsg = msg;
1095 goto bounce;
1097 sleep(backoff);
1098 backoff *= 2;
1099 if (backoff > MAX_RETRY)
1100 backoff = MAX_RETRY;
1101 goto retry;
1103 case -1:
1104 default:
1105 break;
1108 bounce:
1109 bounce(it, errmsg, 1);
1110 /* NOTREACHED */
1114 * deliver_smarthost() is similar to deliver(), but has some differences:
1115 * -deliver_smarthost() works with a queue
1116 * -each entry in this queue has a corresponding file in the spooldir
1117 * -if the mail is sent correctly to a address, delete the corresponding file,
1118 * even if there were errors with other addresses
1119 * -so deliver_remote must tell deliver_smarthost to which addresses it has
1120 * successfully sent the mail
1121 * -this can be done with 3 queues:
1122 * -one queue for sent mails
1123 * -one queue for 4xx addresses (tempfail)
1124 * -one queue for 5xx addresses (permfail)
1125 * -the sent mails are deleted
1126 * -the 4xx are tried again
1127 * -the 5xx are bounced
1130 static void
1131 deliver_smarthost(struct queue *queue, int leavesemaphore)
1133 int error, bounces = 0;
1134 unsigned int backoff = MIN_RETRY;
1135 const char *errmsg = "unknown bounce reason";
1136 struct timeval now;
1137 struct stat st;
1138 struct sembuf sema;
1139 struct qitem *it, *tit;
1140 struct queue *queues[4], *bouncequeue, successqueue, tempfailqueue,
1141 permfailqueue;
1144 * only increment semaphore, if we are not the last bounce
1145 * because there is still a incremented semaphore from
1146 * the bounced delivery
1148 if (!leavesemaphore) {
1150 * Increment semaphore for each mail we try to deliver.
1151 * When completing the transmit, the semaphore is decremented.
1152 * If the semaphore is zero the other childs know that they
1153 * can terminate.
1155 sema.sem_num = SEM_SIGHUP;
1156 sema.sem_op = 1;
1157 sema.sem_flg = 0;
1158 if (semop(semkey, &sema, 1) == -1) {
1159 err(1, "[deliver] semaphore increment failed");
1163 queues[0] = queue;
1164 queues[1] = &successqueue;
1165 queues[2] = &tempfailqueue;
1166 queues[3] = &permfailqueue;
1168 retry:
1169 /* initialise 3 empty queues and link it in queues[] */
1170 LIST_INIT(&queues[1]->queue); /* successful sent items */
1171 LIST_INIT(&queues[2]->queue); /* temporary error items */
1172 LIST_INIT(&queues[3]->queue); /* permanent error items */
1174 it = LIST_FIRST(&queues[0]->queue);
1176 syslog(LOG_INFO, "%s: trying delivery",
1177 it->queueid);
1179 /* if queuefile of first qitem is gone, the mail can't be sended out */
1180 if (stat(it->queuefn, &st) != 0) {
1181 syslog(LOG_ERR, "%s: lost queue file `%s'",
1182 it->queueid, it->queuefn);
1183 /* semaphore-- (MUST NOT BLOCK BECAUSE ITS POSITIVE) */
1184 sema.sem_num = SEM_SIGHUP;
1185 sema.sem_op = -1;
1186 sema.sem_flg = IPC_NOWAIT;
1187 if (semop(semkey, &sema, 1) == -1) {
1188 err(1, "[deliver] semaphore decrement failed");
1190 release_children();
1191 exit(1);
1194 error = deliver_remote(it, &errmsg, queues);
1196 /* if there was an error, do nothing with the other 3 queues! */
1197 if (error == 0) {
1200 * If there are permanent errors, bounce items in permanent
1201 * error queue.
1203 if (!LIST_EMPTY(&queues[3]->queue)) {
1204 bounces = 1;
1205 pid_t pid;
1206 pid = fork();
1207 switch (pid) {
1208 case -1:
1209 syslog(LOG_ERR, "can not fork: %m");
1210 exit(1);
1211 break;
1213 case 0:
1215 * Child:
1217 * Tell which queue to bounce and set
1218 * errmsg. Child will exit as soon as
1219 * all childs for bounces are spawned.
1220 * So no need to set up a signal handler.
1222 bouncequeue = queues[3];
1223 errmsg = "smarthost sent permanent error (5xx)";
1224 goto bounce;
1226 default:
1228 * Parent:
1230 * continue with stuff
1232 break;
1236 /* delete successfully sent items */
1237 if (!LIST_EMPTY(&queues[1]->queue)) {
1238 LIST_FOREACH(tit, &queues[1]->queue, next) {
1239 unlink(tit->queuefn);
1240 LIST_REMOVE(tit, next);
1241 syslog(LOG_INFO, "%s: delivery successful",
1242 tit->queueid);
1247 /* If the temporary error queue is empty and there was no error, finish */
1248 if (LIST_EMPTY(&queues[2]->queue) && error == 0) {
1249 /* only decrement semaphore if there were no bounces! */
1250 if (!bounces) {
1251 /* semaphore-- (MUST NOT BLOCK BECAUSE ITS POSITIVE) */
1252 sema.sem_num = SEM_SIGHUP;
1253 sema.sem_op = -1;
1254 sema.sem_flg = IPC_NOWAIT;
1255 if (semop(semkey, &sema, 1) == -1) {
1256 err(1, "[deliver] semaphore decrement failed");
1258 /* release child processes */
1259 release_children();
1261 exit(0);
1263 /* if there are remaining items, set up retry timer */
1264 } else {
1267 * if there was an error, do not touch queues[0]!
1268 * and try to deliver all items again
1271 if (!error) {
1272 /* wipe out old queue */
1273 if (!LIST_EMPTY(&queues[0]->queue)) {
1274 LIST_FOREACH(tit, &queues[0]->queue, next) {
1275 unlink(tit->queuefn);
1276 LIST_REMOVE(tit, next);
1278 LIST_INIT(&queues[0]->queue);
1280 /* link temporary error queue to queues[0] */
1281 queues[0] = &tempfailqueue;
1282 /* and link queues[2] to wiped out queue */
1283 queues[2] = queue;
1286 if (gettimeofday(&now, NULL) == 0 &&
1287 (now.tv_sec - st.st_mtimespec.tv_sec > MAX_TIMEOUT)) {
1288 char *msg;
1290 if (asprintf(&msg,
1291 "Could not deliver for the last %d seconds. Giving up.",
1292 MAX_TIMEOUT) > 0) {
1293 errmsg = msg;
1295 /* bounce remaining items which have temporary errors */
1296 bouncequeue = queues[2];
1297 goto bounce;
1299 sleep(backoff);
1300 backoff *= 2;
1301 if (backoff > MAX_RETRY)
1302 backoff = MAX_RETRY;
1303 goto retry;
1306 bounce:
1307 LIST_FOREACH(tit, &bouncequeue->queue, next) {
1308 struct sigaction sa;
1309 pid_t pid;
1310 bzero(&sa, sizeof(sa));
1311 sa.sa_flags = SA_NOCLDWAIT;
1312 sa.sa_handler = SIG_IGN;
1313 sigaction(SIGCHLD, &sa, NULL);
1315 /* fork is needed, because bounce() does not return */
1316 pid = fork();
1317 switch (pid) {
1318 case -1:
1319 syslog(LOG_ERR, "can not fork: %m");
1320 exit(1);
1321 break;
1323 case 0:
1325 * Child:
1327 * bounce mail
1330 LIST_REMOVE(tit, next);
1331 if (LIST_NEXT(tit, next) == NULL) {
1333 * For the last bounce, do not increment
1334 * the semaphore when delivering the
1335 * bounce.
1337 bounce(tit, errmsg, 1);
1338 } else {
1339 bounce(tit, errmsg, 0);
1341 /* NOTREACHED */
1343 default:
1345 * Parent:
1347 break;
1351 /* last parent shall exit, too */
1352 _exit(0);
1353 /* NOTREACHED */
1356 static void
1357 load_queue(struct queue *queue)
1359 struct stat st;
1360 struct qitem *it;
1361 //struct queue queue, itmqueue;
1362 struct queue itmqueue;
1363 DIR *spooldir;
1364 struct dirent *de;
1365 char line[1000];
1366 char *fn;
1367 FILE *queuef;
1368 char *sender;
1369 char *addr;
1370 char *queueid;
1371 char *queuefn;
1372 off_t hdrlen;
1373 int fd;
1375 LIST_INIT(&queue->queue);
1377 spooldir = opendir(config->spooldir);
1378 if (spooldir == NULL)
1379 err(1, "reading queue");
1381 while ((de = readdir(spooldir)) != NULL) {
1382 sender = NULL;
1383 queuef = NULL;
1384 queueid = NULL;
1385 queuefn = NULL;
1386 fn = NULL;
1387 LIST_INIT(&itmqueue.queue);
1389 /* ignore temp files */
1390 if (strncmp(de->d_name, "tmp_", 4) == 0 ||
1391 de->d_type != DT_REG)
1392 continue;
1393 if (asprintf(&queuefn, "%s/%s", config->spooldir, de->d_name) < 0)
1394 goto fail;
1395 fd = open(queuefn, O_RDONLY|O_EXLOCK|O_NONBLOCK);
1396 if (fd < 0) {
1397 /* Ignore locked files */
1398 if (errno == EWOULDBLOCK)
1399 continue;
1400 goto skip_item;
1403 queuef = fdopen(fd, "r");
1404 if (queuef == NULL)
1405 goto skip_item;
1406 if (fgets(line, sizeof(line), queuef) == NULL ||
1407 line[0] == 0)
1408 goto skip_item;
1409 line[strlen(line) - 1] = 0; /* chop newline */
1410 sender = strdup(line);
1411 if (sender == NULL)
1412 goto skip_item;
1414 for (;;) {
1415 if (fgets(line, sizeof(line), queuef) == NULL ||
1416 line[0] == 0)
1417 goto skip_item;
1418 if (line[0] == '\n')
1419 break;
1420 line[strlen(line) - 1] = 0;
1421 queueid = strdup(line);
1422 if (queueid == NULL)
1423 goto skip_item;
1424 addr = strchr(queueid, ' ');
1425 if (addr == NULL)
1426 goto skip_item;
1427 *addr++ = 0;
1428 if (fn != NULL)
1429 free(fn);
1430 if (asprintf(&fn, "%s/%s", config->spooldir, queueid) < 0)
1431 goto skip_item;
1432 /* Item has already been delivered? */
1433 if (stat(fn, &st) != 0)
1434 continue;
1435 if (add_recp(&itmqueue, addr, sender, 0) != 0)
1436 goto skip_item;
1437 it = LIST_FIRST(&itmqueue.queue);
1438 it->queuef = queuef;
1439 it->queueid = queueid;
1440 it->queuefn = fn;
1441 fn = NULL;
1443 if (LIST_EMPTY(&itmqueue.queue)) {
1444 warnx("queue file without items: `%s'", queuefn);
1445 goto skip_item2;
1447 hdrlen = ftell(queuef);
1448 while ((it = LIST_FIRST(&itmqueue.queue)) != NULL) {
1449 it->hdrlen = hdrlen;
1450 LIST_REMOVE(it, next);
1451 LIST_INSERT_HEAD(&queue->queue, it, next);
1453 continue;
1455 skip_item:
1456 warn("reading queue: `%s'", queuefn);
1457 skip_item2:
1458 if (sender != NULL)
1459 free(sender);
1460 if (queuefn != NULL)
1461 free(queuefn);
1462 if (fn != NULL)
1463 free(fn);
1464 if (queueid != NULL)
1465 free(queueid);
1466 close(fd);
1468 closedir(spooldir);
1469 return;
1471 fail:
1472 err(1, "reading queue");
1475 static void
1476 run_queue(struct queue *queue)
1478 if (LIST_EMPTY(&queue->queue))
1479 return;
1481 go_background(queue, 0);
1482 /* NOTREACHED */
1485 static void
1486 show_queue(struct queue *queue)
1488 struct qitem *it;
1490 if (LIST_EMPTY(&queue->queue)) {
1491 printf("Mail queue is empty\n");
1492 return;
1495 LIST_FOREACH(it, &queue->queue, next) {
1496 printf("\
1497 ID\t: %s\n\
1498 From\t: %s\n\
1499 To\t: %s\n--\n", it->queueid, it->sender, it->addr);
1504 * TODO:
1506 * - alias processing
1507 * - use group permissions
1508 * - proper sysexit codes
1511 static int
1512 parseandexecute(int argc, char **argv)
1514 char *sender = NULL;
1515 char tag[255];
1516 struct queue queue;
1517 struct queue lqueue;
1518 int i, ch;
1519 int nodot = 0, doqueue = 0, showq = 0;
1521 atexit(deltmp);
1522 LIST_INIT(&queue.queue);
1523 snprintf(tag, 254, "dma");
1525 opterr = 0;
1526 while ((ch = getopt(argc, argv, "A:b:Df:iL:o:O:q:r:")) != -1) {
1527 switch (ch) {
1528 case 'A':
1529 /* -AX is being ignored, except for -A{c,m} */
1530 if (optarg[0] == 'c' || optarg[0] == 'm') {
1531 break;
1533 /* else FALLTRHOUGH */
1534 case 'b':
1535 /* -bX is being ignored, except for -bp */
1536 if (optarg[0] == 'p') {
1537 showq = 1;
1538 break;
1540 /* else FALLTRHOUGH */
1541 case 'D':
1542 daemonize = 0;
1543 break;
1544 case 'L':
1545 if (optarg != NULL)
1546 snprintf(tag, 254, "%s", optarg);
1547 break;
1548 case 'f':
1549 case 'r':
1550 sender = optarg;
1551 break;
1553 case 'o':
1554 /* -oX is being ignored, except for -oi */
1555 if (optarg[0] != 'i')
1556 break;
1557 /* else FALLTRHOUGH */
1558 case 'O':
1559 break;
1560 case 'i':
1561 nodot = 1;
1562 break;
1564 case 'q':
1565 doqueue = 1;
1566 break;
1568 default:
1569 release_children();
1570 exit(1);
1573 argc -= optind;
1574 argv += optind;
1575 opterr = 1;
1577 openlog(tag, LOG_PID | LOG_PERROR, LOG_MAIL);
1579 config = malloc(sizeof(struct config));
1580 if (config == NULL)
1581 errx(1, "Cannot allocate enough memory");
1583 memset(config, 0, sizeof(struct config));
1584 if (parse_conf(CONF_PATH, config) < 0) {
1585 free(config);
1586 release_children();
1587 errx(1, "reading config file");
1590 if (config->features & VIRTUAL)
1591 if (parse_virtuser(config->virtualpath) < 0) {
1592 release_children();
1593 errx(1, "error reading virtual user file: %s",
1594 config->virtualpath);
1597 if (parse_authfile(config->authpath) < 0) {
1598 release_children();
1599 err(1, "reading SMTP authentication file");
1602 if (showq) {
1603 if (argc != 0)
1604 errx(1, "sending mail and displaying queue is"
1605 " mutually exclusive");
1606 load_queue(&lqueue);
1607 show_queue(&lqueue);
1608 return(0);
1611 if (doqueue) {
1612 if (argc != 0)
1613 errx(1, "sending mail and queue pickup is mutually exclusive");
1614 load_queue(&lqueue);
1615 run_queue(&lqueue);
1616 return(0);
1619 if (read_aliases() != 0) {
1620 release_children();
1621 err(1, "reading aliases");
1624 if ((sender = set_from(sender)) == NULL) {
1625 release_children();
1626 err(1, "setting from address");
1629 if (gentempf(&queue) != 0) {
1630 release_children();
1631 err(1, "create temp file");
1634 for (i = 0; i < argc; i++) {
1635 if (add_recp(&queue, argv[i], sender, 1) != 0) {
1636 release_children();
1637 errx(1, "invalid recipient `%s'\n", argv[i]);
1641 if (LIST_EMPTY(&queue.queue)) {
1642 release_children();
1643 errx(1, "no recipients");
1646 if (preparespool(&queue, sender) != 0) {
1647 release_children();
1648 err(1, "creating spools (1)");
1651 if (readmail(&queue, sender, nodot) != 0) {
1652 release_children();
1653 err(1, "reading mail");
1656 if (linkspool(&queue) != 0) {
1657 release_children();
1658 err(1, "creating spools (2)");
1661 /* From here on the mail is safe. */
1663 if (config->features & DEFER)
1664 return(0);
1666 go_background(&queue, 0);
1668 /* NOTREACHED */
1670 return(0);
1674 * dotforwardhandler() waits for incoming username
1675 * for each username, the .forward file is read and parsed
1676 * earch entry is given back to add_recp which communicates
1677 * with dotforwardhandler()
1679 static int
1680 dotforwardhandler(void)
1682 pid_t pid;
1683 fd_set rfds;
1684 int ret;
1685 uint8_t stmt, namelength;
1687 FD_ZERO(&rfds);
1688 FD_SET(clientsocket_df, &rfds);
1690 /* wait for incoming usernames */
1691 ret = select(clientsocket_df + 1, &rfds, NULL, NULL, NULL);
1692 if (ret == -1) {
1693 return(-1);
1695 while (read(clientsocket_df, &namelength, sizeof(namelength))) {
1696 char *username;
1697 struct passwd *userentry;
1698 if (namelength == 0) {
1699 /* there will be no more usernames, we can terminate */
1700 break;
1702 /* read username and get homedir */
1703 username = calloc(1, namelength + 1);
1704 read(clientsocket_df, username, namelength);
1705 userentry = getpwnam(username);
1706 endpwent();
1708 pid = fork();
1709 if (pid == 0) { /* child */
1710 FILE *forward;
1711 char *dotforward;
1712 /* drop privileges to user */
1713 if (chdir("/"))
1714 return(-1);
1715 if (initgroups(username, userentry->pw_gid))
1716 return(-1);
1717 if (setgid(userentry->pw_gid))
1718 return(-1);
1719 if (setuid(userentry->pw_uid))
1720 return(-1);
1722 /* read ~/.forward */
1723 dotforward = strdup(userentry->pw_dir);
1724 forward = fopen(strcat(dotforward, "/.forward"), "r");
1725 if (forward == NULL) { /* no dotforward */
1726 stmt = ENDOFDOTFORWARD;
1727 write(clientsocket_df, &stmt, 1);
1728 continue;
1732 /* parse ~/.forward */
1733 while (!feof(forward)) { /* each line in ~/.forward */
1734 char *target = NULL;
1735 /* 255 Bytes should be enough for a pipe and a emailaddress */
1736 uint8_t len;
1737 char line[2048];
1738 memset(line, 0, 2048);
1739 fgets(line, sizeof(line), forward);
1740 /* FIXME allow comments? */
1741 if (((target = strtok(line, "\t\n")) != NULL) &&
1742 (strncmp(target, "|", 1) == 0)) {
1743 /* if first char is a '|', the line is a pipe */
1744 stmt = ISPIPE;
1745 write(clientsocket_df, &stmt, 1);
1746 len = strlen(target);
1747 /* remove the '|' */
1748 len--;
1749 /* send result back to add_recp */
1750 write(clientsocket_df, &len, sizeof(len));
1751 write(clientsocket_df, target + 1, len);
1752 } else {
1753 /* if first char is not a '|', the line is a mailbox */
1754 stmt = ISMAILBOX;
1755 write(clientsocket_df, &stmt, 1);
1756 len = strlen(target);
1757 /* send result back to add_recp */
1758 write(clientsocket_df, &len, sizeof(len));
1759 write(clientsocket_df, target, len);
1762 stmt = ENDOFDOTFORWARD;
1763 /* send end of .forward to add_recp */
1764 write(clientsocket_df, &stmt, 1);
1765 _exit(0);
1766 } else if (pid < 0) { /* fork failed */
1767 return(1);
1768 } else { /* parent */
1769 /* parent waits while child is processing .forward */
1770 waitpid(-1, NULL, 0);
1773 return(0);
1777 * write_to_local_user() writes to a mailbox or
1778 * to a pipe in a user context and communicates with deliver_local()
1780 static int
1781 write_to_local_user(void)
1783 pid_t pid;
1784 int length;
1785 size_t linelen;
1787 /* wait for incoming targets */
1788 while (read(clientsocket_wl, &length, sizeof(length))) {
1789 char *target;
1790 uint8_t mode, fail = 0;
1791 char line[1000];
1792 int mbox = 0;
1793 off_t mboxlen = 0;
1794 FILE *mypipe = NULL;
1795 struct passwd *userentry;
1797 target = calloc(1, length + 1);
1798 if (length == 0) {
1799 struct sembuf sema;
1800 int retval;
1801 /* check if semaphore is '0' */
1802 sema.sem_num = SEM_SIGHUP;
1803 sema.sem_op = 0;
1804 sema.sem_flg = IPC_NOWAIT;
1805 retval = semop(semkey, &sema, 1);
1806 if (retval == 0 || errno == EINVAL) {
1808 * if semaphore is '0' then the last mail is sent
1809 * and there is no need for a write_to_local_user()
1810 * so we can exit
1812 * if errno is EINVAL, then someone has removed the semaphore, so we shall exit, too
1814 break;
1815 } else {
1816 continue;
1819 /* read username and get uid/gid */
1820 read(clientsocket_wl, target, length);
1822 userentry = getpwnam(target);
1823 endpwent();
1825 pid = fork();
1826 if (pid == 0) { /* child */
1827 /* drop privileges to user and tell if there is something wrong */
1828 if (chdir("/")) {
1829 fail = errno;
1830 write(clientsocket_wl, &fail, sizeof(fail));
1831 fail = 0;
1832 write(clientsocket_wl, &fail, sizeof(fail));
1833 free(target);
1834 _exit(1);
1836 if (initgroups(target, userentry->pw_gid)) {
1837 fail = errno;
1838 write(clientsocket_wl, &fail, sizeof(fail));
1839 fail = 0;
1840 write(clientsocket_wl, &fail, sizeof(fail));
1841 free(target);
1842 _exit(1);
1844 if (setgid(userentry->pw_gid)) {
1845 fail = errno;
1846 write(clientsocket_wl, &fail, sizeof(fail));
1847 fail = 0;
1848 write(clientsocket_wl, &fail, sizeof(fail));
1849 free(target);
1850 _exit(1);
1852 if (setuid(userentry->pw_uid)) {
1853 fail = errno;
1854 write(clientsocket_wl, &fail, sizeof(fail));
1855 fail = 0;
1856 write(clientsocket_wl, &fail, sizeof(fail));
1857 free(target);
1858 _exit(1);
1860 /* and go on with execution outside of if () */
1861 } else if (pid < 0) { /* fork failed */
1862 fail = errno;
1863 write(clientsocket_wl, &fail, sizeof(fail));
1864 fail = 0;
1865 write(clientsocket_wl, &fail, sizeof(fail));
1866 free(target);
1867 _exit(1);
1868 } else { /* parent */
1869 struct sembuf sema;
1870 int retval;
1871 /* wait for child to finish and continue loop */
1872 waitpid(-1, NULL, 0);
1873 /* check if semaphore is '0' */
1874 sema.sem_num = SEM_SIGHUP;
1875 sema.sem_op = 0;
1876 sema.sem_flg = IPC_NOWAIT;
1877 retval = semop(semkey, &sema, 1);
1878 if (retval == 0 || errno == EINVAL) {
1880 * if semaphore is '0' then the last mail is sent
1881 * and there is no need for a write_to_local_user()
1882 * so we can exit
1884 * if errno is EINVAL, then someone has removed the semaphore, so we shall exit, too
1886 break;
1887 } else if (errno != EAGAIN) {
1888 err(1, "[write_to_local_user] semop_op = 0 failed");
1890 continue;
1892 /* child code again here */
1893 /* send ack, we are ready to go on with mode and target */
1894 write(clientsocket_wl, &fail, sizeof(fail));
1896 read(clientsocket_wl, &length, sizeof(length));
1897 target = realloc(target, length + 1);
1898 memset(target, 0, length + 1);
1899 read(clientsocket_wl, target, length);
1900 read(clientsocket_wl, &mode, sizeof(mode));
1901 if (mode & ISMAILBOX) {
1902 /* if mode is mailbox, open mailbox */
1903 /* mailx removes users mailspool file if empty, so open with O_CREAT */
1904 mbox = open(target, O_WRONLY | O_EXLOCK | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
1905 if (mbox < 0) {
1906 fail = errno;
1907 write(clientsocket_wl, &fail, sizeof(fail));
1908 fail = 0;
1909 write(clientsocket_wl, &fail, sizeof(fail));
1910 _exit(1);
1912 mboxlen = lseek(mbox, 0, SEEK_CUR);
1913 } else if (mode & ISPIPE) {
1914 /* if mode is mailbox, popen pipe */
1915 fflush(NULL);
1916 if ((mypipe = popen(target, "w")) == NULL) {
1917 fail = errno;
1918 write(clientsocket_wl, &fail, sizeof(fail));
1919 fail = 0;
1920 write(clientsocket_wl, &fail, sizeof(fail));
1921 _exit(1);
1924 /* send ack, we are ready to receive mail contents */
1925 write(clientsocket_wl, &fail, sizeof(fail));
1927 /* write to file/pipe loop */
1928 while (read(clientsocket_wl, &linelen, sizeof(linelen))) {
1929 if (linelen == 0) {
1930 read(clientsocket_wl, &linelen, sizeof(linelen));
1931 if (linelen == 0) {
1932 break;
1933 } else {
1934 /* if linelen != 0, then there is a error on sender side */
1935 goto chop;
1938 /* receive line */
1939 read(clientsocket_wl, line, linelen);
1941 /* write line to target */
1942 if (mode & ISMAILBOX) { /* mailbox delivery */
1943 if ((size_t)write(mbox, line, linelen) != linelen) {
1944 goto failure;
1946 } else if (mode & ISPIPE) { /* pipe delivery */
1947 if (fwrite(line, 1, linelen, mypipe) != linelen) {
1948 goto failure;
1951 /* send ack */
1952 write(clientsocket_wl, &fail, sizeof(fail));
1955 /* close target after succesfully written last line */
1956 if (mode & ISMAILBOX) { /* mailbox delivery */
1957 close(mbox);
1958 } else if (mode & ISPIPE) { /* pipe delivery */
1959 pclose(mypipe);
1961 /* send ack and exit */
1962 write(clientsocket_wl, &fail, sizeof(fail));
1963 _exit(0);
1964 failure:
1965 fail = errno;
1966 write(clientsocket_wl, &fail, sizeof(fail));
1967 chop:
1968 fail = 0;
1969 /* reset mailbox if there was something wrong */
1970 if (mode & ISMAILBOX && ftruncate(mbox, mboxlen) != 0) {
1971 fail = 2;
1973 write(clientsocket_wl, &fail, sizeof(fail));
1974 if (mode & ISMAILBOX) { /* mailbox delivery */
1975 close(mbox);
1976 } else if (mode & ISPIPE) { /* pipe delivery */
1977 pclose(mypipe);
1979 _exit(1);
1981 uint8_t null = 0;
1982 /* release dotforwardhandler out of loop */
1983 write(controlsocket_df, &null, sizeof(null));
1984 /* we do not need the semaphores any more */
1985 semctl(semkey, 0, IPC_RMID, 0);
1986 _exit(0);
1990 main(int argc, char **argv)
1992 pid_t pid;
1993 int sockets1[2], sockets2[2];
1994 struct sembuf sema;
1995 struct ipc_perm semperm;
1997 if (geteuid() != 0) {
1998 fprintf(stderr, "This executable must be set setuid root!\n");
1999 return(-1);
2002 /* create socketpair for dotforwardhandler() communication */
2003 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets1) != 0) {
2004 err(1,"Socketpair1 creation failed!\n");
2006 /* df is short for DotForwardhandler */
2007 controlsocket_df = sockets1[0];
2008 clientsocket_df = sockets1[1];
2010 /* create socketpair for write_to_local_user() communication */
2011 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets2) != 0) {
2012 err(1,"Socketpair2 creation failed!\n");
2014 /* wl is short for Write_to_Local_user */
2015 controlsocket_wl = sockets2[0];
2016 clientsocket_wl = sockets2[1];
2019 * create semaphores:
2020 * -one for exclusive dotforwardhandler communication
2021 * -one for exclusive write_to_local_user communication
2022 * -another for signaling that the queue is completely processed
2024 semkey = semget(IPC_PRIVATE, 3, IPC_CREAT | IPC_EXCL | 0660);
2025 if (semkey == -1) {
2026 err(1,"[main] Creating semaphores failed");
2029 /* adjust privileges of semaphores */
2030 struct passwd *pw;
2031 if ((pw = getpwnam("nobody")) == NULL)
2032 err(1, "Can't get uid of user 'nobody'");
2033 endpwent();
2035 struct group *grp;
2036 if ((grp = getgrnam("mail")) == NULL)
2037 err(1, "Can't get gid of group 'mail'");
2038 endgrent();
2040 semperm.uid = pw->pw_uid;
2041 semperm.gid = grp->gr_gid;
2042 semperm.mode = 0660;
2043 if (semctl(semkey, SEM_DF, IPC_SET, &semperm) == -1) {
2044 err(1, "[main] semctl(SEM_DF)");
2046 if (semctl(semkey, SEM_WL, IPC_SET, &semperm) == -1) {
2047 err(1, "[main] semctl(SEM_WL)");
2049 if (semctl(semkey, SEM_SIGHUP, IPC_SET, &semperm) == -1) {
2050 err(1, "[main] semctl(SEM_SIGHUP)");
2053 sema.sem_num = SEM_DF;
2054 sema.sem_op = 1;
2055 sema.sem_flg = 0;
2056 if (semop(semkey, &sema, 1) == -1) {
2057 err(1, "[main] increment semaphore SEM_DF");
2060 sema.sem_num = SEM_WL;
2061 sema.sem_op = 1;
2062 sema.sem_flg = 0;
2063 if (semop(semkey, &sema, 1) == -1) {
2064 err(1, "[main] increment semaphore SEM_WL");
2067 pid = fork();
2068 if (pid == 0) { /* part _WITH_ root privileges */
2069 /* fork another process which goes into background */
2070 if (daemonize && daemon(0, 0) != 0) {
2071 syslog(LOG_ERR, "[main] can not daemonize: %m");
2072 exit(1);
2074 pid = fork();
2075 /* both processes are running simultaneousily */
2076 if (pid == 0) { /* child */
2077 /* this process handles .forward read requests */
2078 dotforwardhandler();
2079 _exit(0);
2080 } else if (pid < 0) {
2081 err(1, "[main] Fork failed!\n");
2082 return(-1);
2083 } else { /* parent */
2084 /* this process writes to mailboxes if needed */
2085 write_to_local_user();
2086 _exit(0);
2088 } else if (pid < 0) {
2089 err(1, "Fork failed!\n");
2090 return(-1);
2091 } else { /* part _WITHOUT_ root privileges */
2092 /* drop privileges */
2093 /* FIXME to user mail? */
2094 chdir("/");
2095 if (initgroups("nobody", pw->pw_gid) != 0)
2096 err(1, "initgroups");
2097 #if 0
2098 if (setgid(grp->gr_gid) != 0) /* set to group 'mail' */
2099 #else
2100 /* FIXME */
2101 if (setgid(6) != 0) /* set to group 'mail' */
2102 #endif
2103 err(1, "setgid");
2104 if (setuid(pw->pw_uid) != 0) /* set to user 'nobody' */
2105 err(1, "setuid");
2107 /* parse command line and execute main mua code */
2108 parseandexecute(argc, argv);
2110 /* release child processes */
2111 release_children();
2114 return(0);