nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 7d8b5c08a9e5e1a792650fb945ed9efc1b104ec0
parent 7347f3a3a5b9163b3904c004b2a825db89a852ec
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date:   Tue, 25 Oct 2022 15:53:35 +0000

text changes, indent roughshodding

Diffstat:
M.gitignore | 1+
MMakefile.legacy | 4++--
Minclude/alloc.h | 12+++++++++++-
Msrc/fmtqfn.c | 46+++++++++++++++++++++++++++++++++++-----------
Msrc/hier.c | 1+
Msrc/mxf-send/cleanup.c | 108+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/mxf-send/comm.c | 149+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/mxf-send/deliveries.c | 374++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Asrc/mxf-send/fnmake.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mxf-send/mxf-send-globals.h | 10++++++++++
Msrc/mxf-send/mxf-send-protos.h | 3+++
Msrc/mxf-send/mxf-send.c | 1272++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/mxf-send/mxf-send.h | 7+++++++
Msrc/mxf-send/pass.c | 436++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Asrc/mxf-send/rewrite.c | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mxf-send/todo.c | 456++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/nmail-smtpd.c | 1227++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/qmail-clean.c | 8++++----
Msrc/qmail-local.c | 2+-
Msrc/qmail-queue.c | 6++++--
Msrc/qmail-remote.c | 793++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/qmail-send.c | 1+
Asrc/qmail-start-np.c | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/qmail-start.c | 15++++++++++-----
24 files changed, 3257 insertions(+), 2064 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -384,3 +384,4 @@ src/mxf-remote/mxf-remote src/netstrings.o src/nmail-smtpd.o nmail-smtpd +*.BAK diff --git a/Makefile.legacy b/Makefile.legacy @@ -1339,11 +1339,11 @@ doc/man/qmail-qstat.0: \ doc/man/qmail-qstat.8 qmail-queue: \ -load src/qmail-queue.o src/triggerpull.o src/fmtqfn.o src/date822fmt.o \ +load src/qmail-queue.o src/triggerpull.o src/fmtqfn.o src/date822fmt.o src/stralloc.a \ src/datetime.a src/ndelay.a src/open.a src/sig.a src/substdio.a src/error.a \ src/str.a src/fs.a src/auto_qmail.o src/auto_split.o src/ids.a ./load qmail-queue src/triggerpull.o src/fmtqfn.o \ - src/date822fmt.o src/datetime.a src/ndelay.a src/open.a src/sig.a \ + src/date822fmt.o src/stralloc.a src/datetime.a src/ndelay.a src/open.a src/sig.a \ src/auto_qmail.o src/auto_split.o src/ids.a \ src/substdio.a src/error.a src/str.a src/fs.a diff --git a/include/alloc.h b/include/alloc.h @@ -2,10 +2,20 @@ #define ALLOC_H #include <stdlib.h> +#include <string.h> #include "deprecated.h" -#define alloc(x) malloc(x) +//#define alloc(x) malloc(x) + +// secure alloc(): zero bytes before use. +// prevents heartbleed. Hilda +static inline void *alloc(size_t x) +{ + void *y = malloc(x); + if (!(y == NULL)) memset(y, 0, x); + return y; +} #define alloc_free(x) free(x) #ifdef DEPRECATED_FUNCTIONS_AVAILABLE diff --git a/src/fmtqfn.c b/src/fmtqfn.c @@ -1,25 +1,49 @@ #include "fmtqfn.h" - +#include "stralloc.h" +#include <errno.h> +#define AMNESIAC (errno = ENOMEM, -1) #include "fmt.h" #include "auto_split.h" -unsigned int fmtqfn(s,dirslash,id,flagsplit) -char *s; -char *dirslash; -unsigned long id; -int flagsplit; +/* ForMaT Queue FileName + * The bleak Bernstein original. + * Requires a buffer long enough for it. + * Always no more than FMTQFN bytes (40 if + * len(dirslash) le 10) + * given inode numbers generally can't exceed 18446744073709551616, + * a 20 character string at any rate, this is futureproof. + */ +unsigned int fmtqfn(char *s, char *dirslash, + unsigned long id, int flagsplit) { unsigned int len; unsigned int i; len = 0; i = fmt_str(s,dirslash); len += i; if (s) s += i; - if (flagsplit) - { - i = fmt_ulong(s,id % auto_split); len += i; if (s) s += i; - i = fmt_str(s,"/"); len += i; if (s) s += i; + if (0 != flagsplit) { + i = fmt_ulong(s,id % auto_split); // generate the hash + len += i; + if (s) s += i; // slide the pointer cursor up one byte + i = fmt_str(s,"/"); + len += i; + if (s) s += i; // CURSOR } - i = fmt_ulong(s,id); len += i; if (s) s += i; + i = fmt_ulong(s,id); + len += i; + if (s) s += i; if (s) *s++ = 0; ++len; return len; } + +/* ForMaT Queue FileName StrAlloc + * This allows passing in a SA to write the filename to. + * This unavoidably calls fmtqfn twice. + */ +int fmtqfnsa (stralloc *sa, char *dirslash, + unsigned long id, int flagsplit) +{ + if (!stralloc_ready(sa, fmtqfn(NULL, dirslash, id, flagsplit))) return AMNESIAC; + if (!fmtqfn(sa->s, dirslash, id, flagsplit)) return AMNESIAC; + return sa->len; +} diff --git a/src/hier.c b/src/hier.c @@ -133,6 +133,7 @@ void hier() c(auto_qmail,"bin",/*"src"*/".","qmail-qmqpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin",/*"src"*/".","qmail-qmtpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin",/*"src"*/".","qmail-smtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin",/*"src"*/".","nmail-smtpd",auto_uido,auto_gidq,0755); // the alternative SMTPD c(auto_qmail,"bin",/*"src"*/".","sendmail",auto_uido,auto_gidq,0755); c(auto_qmail,"bin",/*"src"*/".","tcp-env",auto_uido,auto_gidq,0755); c(auto_qmail,"bin",/*"src"*/".","qreceipt",auto_uido,auto_gidq,0755); diff --git a/src/mxf-send/cleanup.c b/src/mxf-send/cleanup.c @@ -1,60 +1,76 @@ #include "mxf-send.h" /* this file is too long ------------------------------------------ CLEANUPS */ -int flagcleanup; /* if 1, cleanupdir is initialized and ready */ +int flagcleanup; /* if 1, cleanupdir is initialized and ready */ readsubdir cleanupdir; datetime_sec cleanuptime; -void cleanup_init() +void +cleanup_init() { - flagcleanup = 0; - cleanuptime = now(); + flagcleanup = 0; + cleanuptime = now(); } -void cleanup_selprep(datetime_sec *wakeup) +void +cleanup_selprep(datetime_sec * wakeup) { - if (flagcleanup) *wakeup = 0; - if (*wakeup > cleanuptime) *wakeup = cleanuptime; + if (flagcleanup) + *wakeup = 0; + if (*wakeup > cleanuptime) + *wakeup = cleanuptime; } -void cleanup_do() +void +cleanup_do() { - char ch; - struct stat st; - unsigned long id; - - if (!flagcleanup) - { - if (recent < cleanuptime) return; - readsubdir_init(&cleanupdir,"mess",pausedir); - flagcleanup = 1; - } - - switch(readsubdir_next(&cleanupdir,&id)) - { - case 1: - break; - case 0: - flagcleanup = 0; - cleanuptime = recent + SLEEP_CLEANUP; - default: - return; - } - - fnmake_mess(id); - if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */ - if (recent <= st.st_atime + OSSIFIED) return; - - fnmake_info(id); - if (stat(fn.s,&st) == 0) return; - if (errno != error_noent) return; - fnmake_todo(id); - if (stat(fn.s,&st) == 0) return; - if (errno != error_noent) return; - - fnmake_foop(id); - if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } - if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } - if (ch != '+') - log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); + char ch; + struct stat st; + unsigned long id; + + if (!flagcleanup) { + if (recent < cleanuptime) + return; + readsubdir_init(&cleanupdir, "mess", pausedir); + flagcleanup = 1; + } + + switch (readsubdir_next(&cleanupdir, &id)) { + case 1: + break; + case 0: + flagcleanup = 0; + cleanuptime = recent + SLEEP_CLEANUP; + default: + return; + } + + fnmake_mess(id); + if (stat(fn.s, &st) == -1) + return; /* probably qmail-queue deleted it */ + if (recent <= st.st_atime + OSSIFIED) + return; + + fnmake_info(id); + if (stat(fn.s, &st) == 0) + return; + if (errno != error_noent) + return; + fnmake_todo(id); + if (stat(fn.s, &st) == 0) + return; + if (errno != error_noent) + return; + + fnmake_foop(id); + if (substdio_putflush(&sstoqc, fn.s, fn.len) == -1) { + cleandied(); + return; + } + if (substdio_get(&ssfromqc, &ch, 1) != 1) { + cleandied(); + return; + } + if (ch != '+') + log3("warning: qmail-clean unable to clean up ", fn.s, "\n"); } diff --git a/src/mxf-send/comm.c b/src/mxf-send/comm.c @@ -1,83 +1,104 @@ #include "mxf-send.h" /* this file is too long ------------------------------------- COMMUNICATION */ -substdio sstoqc; char sstoqcbuf[1024]; -substdio ssfromqc; char ssfromqcbuf[1024]; -stralloc comm_buf[CHANNELS] = { {0}, {0} }; +// Why's cleanup a separate function? it should just be a channel... +substdio sstoqc; +char sstoqcbuf[1024]; +substdio ssfromqc; +char ssfromqcbuf[1024]; + +stralloc comm_buf[CHANNELS] = {{0}, {0}}; int comm_pos[CHANNELS]; -void comm_init() +void +comm_init() { - int c; - substdio_fdbuf(&sstoqc,write,5,sstoqcbuf,sizeof(sstoqcbuf)); - substdio_fdbuf(&ssfromqc,read,6,ssfromqcbuf,sizeof(ssfromqcbuf)); - for (c = 0;c < CHANNELS;++c) - if (ndelay_on(chanfdout[c]) == -1) - /* this is so stupid: NDELAY semantics should be default on write */ - spawndied(c); /* drastic, but better than risking deadlock */ + int c; + // special casing... + // this also relies on those FDs being, well... + substdio_fdbuf(&sstoqc, write, 5, sstoqcbuf, sizeof(sstoqcbuf)); + substdio_fdbuf(&ssfromqc, read, 6, ssfromqcbuf, sizeof(ssfromqcbuf)); + for (c = 0; c < CHANNELS; ++c) + if (ndelay_on(chanfdout[c]) == -1) + /* + * this is so stupid: NDELAY semantics should be + * default on write + */ + spawndied(c); /* drastic, but better than risking + * deadlock */ } -int comm_canwrite(int c) +int +comm_canwrite(int c) { - /* XXX: could allow a bigger buffer; say 10 recipients */ - if (comm_buf[c].s && comm_buf[c].len) return 0; - return 1; + /* XXX: could allow a bigger buffer; say 10 recipients */ + if (comm_buf[c].s && comm_buf[c].len) + return 0; + return 1; } -void comm_write(int c, int delnum, unsigned long id, char *sender, char *recip) +void +comm_write(int c, int delnum, unsigned long id, char *sender, char *recip) { - char ch; - if (comm_buf[c].s && comm_buf[c].len) return; - while (!stralloc_copys(&comm_buf[c],"")) nomem(); - ch = delnum; - while (!stralloc_append(&comm_buf[c],&ch)) nomem(); - fnmake_split(id); - while (!stralloc_cats(&comm_buf[c],fn.s)) nomem(); - while (!stralloc_0(&comm_buf[c])) nomem(); - senderadd(&comm_buf[c],sender,recip); - while (!stralloc_0(&comm_buf[c])) nomem(); - while (!stralloc_cats(&comm_buf[c],recip)) nomem(); - while (!stralloc_0(&comm_buf[c])) nomem(); - comm_pos[c] = 0; + char ch; + if (comm_buf[c].s && comm_buf[c].len) + return; + while (!stralloc_copys(&comm_buf[c], "")) + nomem(); + ch = delnum; + while (!stralloc_append(&comm_buf[c], &ch)) + nomem(); + fnmake_split(id); + while (!stralloc_cats(&comm_buf[c], fn.s)) + nomem(); + while (!stralloc_0(&comm_buf[c])) + nomem(); + senderadd(&comm_buf[c], sender, recip); + while (!stralloc_0(&comm_buf[c])) + nomem(); + while (!stralloc_cats(&comm_buf[c], recip)) + nomem(); + while (!stralloc_0(&comm_buf[c])) + nomem(); + comm_pos[c] = 0; } -void comm_selprep(int *nfds, fd_set *wfds) +void +comm_selprep(int *nfds, fd_set * wfds) { - int c; - for (c = 0;c < CHANNELS;++c) - if (flagspawnalive[c]) - if (comm_buf[c].s && comm_buf[c].len) - { - FD_SET(chanfdout[c],wfds); - if (*nfds <= chanfdout[c]) - *nfds = chanfdout[c] + 1; - } + int c; + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) + if (comm_buf[c].s && comm_buf[c].len) { + FD_SET(chanfdout[c], wfds); // if we had iolib, we could do this better. + if (*nfds <= chanfdout[c]) + *nfds = chanfdout[c] + 1; + } } -void comm_do(fd_set *wfds) +void +comm_do(fd_set * wfds) { - int c; - for (c = 0;c < CHANNELS;++c) - if (flagspawnalive[c]) - if (comm_buf[c].s && comm_buf[c].len) - if (FD_ISSET(chanfdout[c],wfds)) - { - int w; - int len; - len = comm_buf[c].len; - w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]); - if (w == 0 || w == -1) - { - if ((w == -1) && (errno == error_pipe)) - spawndied(c); - else - continue; /* kernel select() bug; can't avoid busy-looping */ - } - else - { - comm_pos[c] += w; - if (comm_pos[c] == len) - comm_buf[c].len = 0; - } - } + int c; + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) + if (comm_buf[c].s && comm_buf[c].len) + if (FD_ISSET(chanfdout[c], wfds)) { + int w; // isn't this just a nonblocking substdio?! + int len; + len = comm_buf[c].len; + w = write(chanfdout[c], comm_buf[c].s + comm_pos[c], len - comm_pos[c]); + if (w == 0 || w == -1) { + if ((w == -1) && (errno == error_pipe)) + spawndied(c); + else + continue; /* kernel select() bug; + * can't avoid + * busy-looping */ + } else { + comm_pos[c] += w; + if (comm_pos[c] == len) + comm_buf[c].len = 0; + } + } } diff --git a/src/mxf-send/deliveries.c b/src/mxf-send/deliveries.c @@ -1,201 +1,245 @@ #include "mxf-send.h" /* this file is too long ---------------------------------------- DELIVERIES */ -struct del - { - int used; - int j; - unsigned long delid; - seek_pos mpos; - stralloc recip; - } +struct del { + int used; + int j; + unsigned long delid; + seek_pos mpos; + stralloc recip; +} ; unsigned long masterdelid = 1; -unsigned int concurrency[CHANNELS] = { 10, 20 }; -unsigned int concurrencyused[CHANNELS] = { 0, 0 }; +unsigned int concurrency[CHANNELS] = {10, 20}; +unsigned int concurrencyused[CHANNELS] = {0, 0}; struct del *d[CHANNELS]; stralloc dline[CHANNELS]; char delbuf[2048]; -void del_status() +void +del_status() { - int c; - - log1("status:"); - for (c = 0;c < CHANNELS;++c) { - strnum2[fmt_ulong(strnum2,(unsigned long) concurrencyused[c])] = 0; - strnum3[fmt_ulong(strnum3,(unsigned long) concurrency[c])] = 0; - qslog2(chanstatusmsg[c],strnum2); - qslog2("/",strnum3); - } - if (flagexitasap) log1(" exitasap"); - log1("\n"); + int c; + + log1("status:"); + for (c = 0; c < CHANNELS; ++c) { + strnum2[fmt_ulong(strnum2, (unsigned long)concurrencyused[c])] = 0; + strnum3[fmt_ulong(strnum3, (unsigned long)concurrency[c])] = 0; + qslog2(chanstatusmsg[c], strnum2); + qslog2("/", strnum3); + } + if (flagexitasap) + log1(" exitasap"); + log1("\n"); } -void del_init() +void +del_init() { - int c; - unsigned int i; - for (c = 0;c < CHANNELS;++c) - { - flagspawnalive[c] = 1; - while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del)))) - nomem(); - for (i = 0;i < concurrency[c];++i) - { d[c][i].used = 0; d[c][i].recip.s = 0; } - dline[c].s = 0; - while (!stralloc_copys(&dline[c],"")) nomem(); - } - del_status(); + int c; + unsigned int i; + for (c = 0; c < CHANNELS; ++c) { + flagspawnalive[c] = 1; + while (!(d[c] = (struct del *)alloc(concurrency[c] * sizeof(struct del)))) + nomem(); + for (i = 0; i < concurrency[c]; ++i) { + d[c][i].used = 0; + d[c][i].recip.s = 0; + } + dline[c].s = 0; + while (!stralloc_copys(&dline[c], "")) + nomem(); + } + del_status(); } -int del_canexit() +int +del_canexit() { - int c; - for (c = 0;c < CHANNELS;++c) - if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */ - if (concurrencyused[c]) return 0; - return 1; + int c; + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) /* if dead, nothing we can do about + * its jobs */ + if (concurrencyused[c]) + return 0; + return 1; } -int del_avail(int c) { - return flagspawnalive[c] && comm_canwrite(c) && (concurrencyused[c] < concurrency[c]); +int +del_avail(int c) +{ + return flagspawnalive[c] && comm_canwrite(c) && (concurrencyused[c] < concurrency[c]); } -void del_start(int j, seek_pos mpos, char *recip) +void +del_start(int j, seek_pos mpos, char *recip) { - unsigned int i; - int c; - - c = jo[j].channel; - if (!flagspawnalive[c]) return; - if (!comm_canwrite(c)) return; - - for (i = 0;i < concurrency[c];++i) if (!d[c][i].used) break; - if (i == concurrency[c]) return; - - if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; } - if (!stralloc_0(&d[c][i].recip)) { nomem(); return; } - d[c][i].j = j; ++jo[j].refs; - d[c][i].delid = masterdelid++; - d[c][i].mpos = mpos; - d[c][i].used = 1; ++concurrencyused[c]; - - comm_write(c,i,jo[j].id,jo[j].sender.s,recip); - - strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0; - strnum3[fmt_ulong(strnum3,jo[j].id)] = 0; - qslog2("starting delivery ",strnum2); - log3(": msg ",strnum3,tochan[c]); - logsafe(recip); - log1("\n"); - del_status(); + unsigned int i; + int c; + + c = jo[j].channel; + if (!flagspawnalive[c]) + return; + if (!comm_canwrite(c)) + return; + + for (i = 0; i < concurrency[c]; ++i) + if (!d[c][i].used) + break; + if (i == concurrency[c]) + return; + + if (!stralloc_copys(&d[c][i].recip, recip)) { + nomem(); + return; + } + if (!stralloc_0(&d[c][i].recip)) { + nomem(); + return; + } + d[c][i].j = j; + ++jo[j].refs; + d[c][i].delid = masterdelid++; + d[c][i].mpos = mpos; + d[c][i].used = 1; + ++concurrencyused[c]; + + comm_write(c, i, jo[j].id, jo[j].sender.s, recip); + + strnum2[fmt_ulong(strnum2, d[c][i].delid)] = 0; + strnum3[fmt_ulong(strnum3, jo[j].id)] = 0; + qslog2("starting delivery ", strnum2); + log3(": msg ", strnum3, tochan[c]); + logsafe(recip); + log1("\n"); + del_status(); } -void markdone(int c, unsigned long id, seek_pos pos) +void +markdone(int c, unsigned long id, seek_pos pos) { - struct stat st; - int fd; - fnmake_chanaddr(id,c); - for (;;) - { - fd = open_write(fn.s); - if (fd == -1) break; - if (fstat(fd,&st) == -1) { close(fd); break; } - if (seek_set(fd,pos) == -1) { close(fd); break; } - if (write(fd,"D",1) != 1) { close(fd); break; } - /* further errors -> double delivery without us knowing about it, oh well */ - close(fd); - return; - } - log3("warning: trouble marking ",fn.s,"; message will be delivered twice!\n"); + struct stat st; + int fd; + fnmake_chanaddr(id, c); + for (;;) { + fd = open_write(fn.s); + if (fd == -1) + break; + if (fstat(fd, &st) == -1) { + close(fd); + break; + } + if (seek_set(fd, pos) == -1) { + close(fd); + break; + } + if (write(fd, "D", 1) != 1) { + close(fd); + break; + } + /* + * further errors -> double delivery without us knowing about + * it, oh well + */ + close(fd); + return; + } + log3("warning: trouble marking ", fn.s, "; message will be delivered twice!\n"); } -void del_dochan(int c) +void +del_dochan(int c) { - int r; - char ch; - int i; - int delnum; - r = read(chanfdin[c],delbuf,sizeof(delbuf)); - if (r == -1) return; - if (r == 0) { spawndied(c); return; } - for (i = 0;i < r;++i) - { - ch = delbuf[i]; - while (!stralloc_append(&dline[c],&ch)) nomem(); - if (dline[c].len > REPORTMAX) - dline[c].len = REPORTMAX; - /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */ - /* but from a security point of view, we don't trust rspawn */ - if (!ch && (dline[c].len > 1)) - { - delnum = (unsigned int) (unsigned char) dline[c].s[0]; - if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) - log1("warning: internal error: delivery report out of range\n"); - else - { - strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; - if (dline[c].s[1] == 'Z') - if (jo[d[c][delnum].j].flagdying) - { - dline[c].s[1] = 'D'; - --dline[c].len; - while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); - while (!stralloc_0(&dline[c])) nomem(); - } - switch(dline[c].s[1]) - { - case 'K': - log3("delivery ",strnum3,": success: "); - logsafe(dline[c].s + 2); - log1("\n"); - markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); - --jo[d[c][delnum].j].numtodo; - break; - case 'Z': - log3("delivery ",strnum3,": deferral: "); - logsafe(dline[c].s + 2); - log1("\n"); - break; - case 'D': - log3("delivery ",strnum3,": failure: "); - logsafe(dline[c].s + 2); - log1("\n"); - addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); - markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); - --jo[d[c][delnum].j].numtodo; - break; - default: - log3("delivery ",strnum3,": report mangled, will defer\n"); + int r; + char ch; + int i; + int delnum; + r = read(chanfdin[c], delbuf, sizeof(delbuf)); + if (r == -1) + return; + if (r == 0) { + spawndied(c); + return; + } + for (i = 0; i < r; ++i) { + ch = delbuf[i]; + while (!stralloc_append(&dline[c], &ch)) + nomem(); + if (dline[c].len > REPORTMAX) + dline[c].len = REPORTMAX; + /* + * qmail-lspawn and qmail-rspawn are responsible for keeping + * it short + */ + /* but from a security point of view, we don't trust rspawn */ + if (!ch && (dline[c].len > 1)) { + delnum = (unsigned int)(unsigned char)dline[c].s[0]; + if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) + log1("warning: internal error: delivery report out of range\n"); + else { + strnum3[fmt_ulong(strnum3, d[c][delnum].delid)] = 0; + if (dline[c].s[1] == 'Z') + if (jo[d[c][delnum].j].flagdying) { + dline[c].s[1] = 'D'; + --dline[c].len; + while (!stralloc_cats(&dline[c], "I'm not going to try again; this message has been in the queue too long.\n")) + nomem(); + while (!stralloc_0(&dline[c])) + nomem(); + } + switch (dline[c].s[1]) { + case 'K': + log3("delivery ", strnum3, ": success: "); + logsafe(dline[c].s + 2); + log1("\n"); + markdone(c, jo[d[c][delnum].j].id, d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; + case 'Z': + log3("delivery ", strnum3, ": deferral: "); + logsafe(dline[c].s + 2); + log1("\n"); + break; + case 'D': + log3("delivery ", strnum3, ": failure: "); + logsafe(dline[c].s + 2); + log1("\n"); + addbounce(jo[d[c][delnum].j].id, d[c][delnum].recip.s, dline[c].s + 2); + markdone(c, jo[d[c][delnum].j].id, d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; + default: + log3("delivery ", strnum3, ": report mangled, will defer\n"); + } + job_close(d[c][delnum].j); + d[c][delnum].used = 0; + --concurrencyused[c]; + del_status(); + } + dline[c].len = 0; + } } - job_close(d[c][delnum].j); - d[c][delnum].used = 0; --concurrencyused[c]; - del_status(); - } - dline[c].len = 0; - } - } } -void del_selprep(int *nfds, fd_set *rfds) +void +del_selprep(int *nfds, fd_set * rfds) { - int c; - for (c = 0;c < CHANNELS;++c) - if (flagspawnalive[c]) - { - FD_SET(chanfdin[c],rfds); - if (*nfds <= chanfdin[c]) - *nfds = chanfdin[c] + 1; - } + int c; + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) { + FD_SET(chanfdin[c], rfds); + if (*nfds <= chanfdin[c]) + *nfds = chanfdin[c] + 1; + } } -void del_do(fd_set *rfds) +void +del_do(fd_set * rfds) { - int c; - for (c = 0;c < CHANNELS;++c) - if (flagspawnalive[c]) - if (FD_ISSET(chanfdin[c],rfds)) - del_dochan(c); + int c; + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) + if (FD_ISSET(chanfdin[c], rfds)) + del_dochan(c); } diff --git a/src/mxf-send/fnmake.c b/src/mxf-send/fnmake.c @@ -0,0 +1,51 @@ +#include "mxf-send.h" +#include "mxf-send-protos.h" +/* this file is too long ----------------------------------------- FILENAMES */ + +stralloc fn = {0}; +stralloc fn2 = {0}; +char fnmake_strnum[FMT_ULONG]; + +/* Daniel, if you're up in my source code and reading this, I want to + * look you straight in the eyes and tell you this: + * All of these procedures, to a letter, should not have used globals. + * Macros should then have been defined to define the default stralloc + * to use in a particular file. + * When it comes to chanaddr, I understand why you'd use a global. That + * makes perfect sense - chanaddr is effectively write-once memory for + * channels. + */ +void +fnmake_init() +{ + while (!stralloc_ready(&fn, FMTQFN)) + nomem(); + while (!stralloc_ready(&fn2, FMTQFN)) + nomem(); +} + +void fnmake_info(unsigned long id){ + fn.len = fmtqfn(fn.s, "info/", id, 1); +} +void fnmake_todo(unsigned long id){ + fn.len = fmtqfn(fn.s, "todo/", id, 0); +} +void fnmake_mess(unsigned long id){ + fn.len = fmtqfn(fn.s, "mess/", id, 1); +} +void fnmake_foop(unsigned long id){ + fn.len = fmtqfn(fn.s, "foop/", id, 0); +} +void fnmake_split(unsigned long id){ + fn.len = fmtqfn(fn.s, "", id, 1); +} +void +fnmake2_bounce(unsigned long id) +{ + fn2.len = fmtqfn(fn2.s, "bounce/", id, 0); +} +void +fnmake_chanaddr(unsigned long id, int c) +{ + fn.len = fmtqfn(fn.s, chanaddr[c], id, 1); +} diff --git a/src/mxf-send/mxf-send-globals.h b/src/mxf-send/mxf-send-globals.h @@ -0,0 +1,10 @@ +#ifndef MXFSENDGLOBALS_H +#define MXFSENDGLOBALS_H + +// FILENAMES (fnmake.c) +extern stralloc fn, fn2; +extern char fnmake_strnum[FMT_ULONG]; + +// REWRITE (rewrite.c) +extern stralloc rwline; +#endif // MXFSENDGLOBALS_H diff --git a/src/mxf-send/mxf-send-protos.h b/src/mxf-send/mxf-send-protos.h @@ -1,3 +1,5 @@ +#ifndef MXFSEND_PROTOTYPES +#define MXFSEND_PROTOTYPES extern void fnmake_init(); extern void fnmake_info(unsigned long id); extern void fnmake_todo(unsigned long id); @@ -65,3 +67,4 @@ extern void todo_init(); extern void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup); extern void todo_do(fd_set *rfds); extern void todo_dochan(int c); +#endif // MXFSEND_PROTOTYPES diff --git a/src/mxf-send/mxf-send.c b/src/mxf-send/mxf-send.c @@ -48,12 +48,13 @@ /* critical timing feature #1: if not triggered, do not busy-loop */ /* critical timing feature #2: if triggered, respond within fixed time */ /* important timing feature: when triggered, respond instantly */ -#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ -#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ -#define SLEEP_FOREVER 86400 /* absolute maximum time spent in iopause() */ -#define SLEEP_CLEANUP 76431 /* time between cleanups */ +#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ +#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ +#define SLEEP_FOREVER 86400 /* absolute maximum time spent in iopause() */ +#define SLEEP_CLEANUP 76431 /* time between cleanups */ #define SLEEP_SYSFAIL 123 -#define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */ +#define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 + * hours) */ int lifetime = 604800; @@ -73,480 +74,499 @@ char strnum2[FMT_ULONG]; char strnum3[FMT_ULONG]; #define CHANNELS 2 -char *chanaddr[CHANNELS] = { "local/", "remote/" }; -char *chanstatusmsg[CHANNELS] = { " local ", " remote " }; -char *tochan[CHANNELS] = { " to local ", " to remote " }; -int chanfdout[CHANNELS] = { 1, 3 }; -int chanfdin[CHANNELS] = { 2, 4 }; -int chanskip[CHANNELS] = { 10, 20 }; - -int flagexitasap = 0; void sigterm() { flagexitasap = 1; } -int flagrunasap = 0; void sigalrm() { flagrunasap = 1; } -int flagreadasap = 0; void sighup() { flagreadasap = 1; } +char *chanaddr[CHANNELS] = {"local/", "remote/"}; +char *chanstatusmsg[CHANNELS] = {" local ", " remote "}; +char *tochan[CHANNELS] = {" to local ", " to remote "}; +int chanfdout[CHANNELS] = {1, 3}; +int chanfdin[CHANNELS] = {2, 4}; +int chanskip[CHANNELS] = {10, 20}; + +int flagexitasap = 0; +void +sigterm() +{ + flagexitasap = 1; +} +int flagrunasap = 0; +void +sigalrm() +{ + flagrunasap = 1; +} +int flagreadasap = 0; +void +sighup() +{ + flagreadasap = 1; +} -void cleandied() { log1("alert: oh no! lost qmail-clean connection! dying...\n"); - flagexitasap = 1; } +void cleandied() { + log1("alert: oh no! lost qmail-clean connection! dying...\n"); + flagexitasap = 1; +} int flagspawnalive[CHANNELS]; -void spawndied(int c) { log1("alert: oh no! lost spawn connection! dying...\n"); - flagspawnalive[c] = 0; flagexitasap = 1; } +void spawndied(int c){ + log1("alert: oh no! lost spawn connection! dying...\n"); + flagspawnalive[c] = 0; + flagexitasap = 1; +} #define REPORTMAX 10000 datetime_sec recent; -/* this file is too long ----------------------------------------- FILENAMES */ +/* this file is too long ----------------------------------------- FILENAMES stralloc fn = {0}; stralloc fn2 = {0}; char fnmake_strnum[FMT_ULONG]; -void fnmake_init() +void +fnmake_init() { - while (!stralloc_ready(&fn,FMTQFN)) nomem(); - while (!stralloc_ready(&fn2,FMTQFN)) nomem(); + while (!stralloc_ready(&fn, FMTQFN)) + nomem(); + while (!stralloc_ready(&fn2, FMTQFN)) + nomem(); } -void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } -void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,0); } -void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } -void fnmake_foop(unsigned long id) { fn.len = fmtqfn(fn.s,"foop/",id,0); } -void fnmake_split(unsigned long id) { fn.len = fmtqfn(fn.s,"",id,1); } -void fnmake2_bounce(unsigned long id) -{ fn2.len = fmtqfn(fn2.s,"bounce/",id,0); } -void fnmake_chanaddr(unsigned long id, int c) -{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } - - -/* this file is too long ----------------------------------------- REWRITING */ - -stralloc rwline = {0}; - -/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */ -/* may trash recip. must set up rwline, between a T and a \0. */ -int rewrite(char *recip) +void fnmake_info(unsigned long id){ + fn.len = fmtqfn(fn.s, "info/", id, 1); +} +void fnmake_todo(unsigned long id){ + fn.len = fmtqfn(fn.s, "todo/", id, 0); +} +void fnmake_mess(unsigned long id){ + fn.len = fmtqfn(fn.s, "mess/", id, 1); +} +void fnmake_foop(unsigned long id){ + fn.len = fmtqfn(fn.s, "foop/", id, 0); +} +void fnmake_split(unsigned long id){ + fn.len = fmtqfn(fn.s, "", id, 1); +} +void +fnmake2_bounce(unsigned long id) { - unsigned int i; - char *x; - static stralloc addr = {0}; - unsigned int at; - - if (!stralloc_copys(&rwline,"T")) return 0; - if (!stralloc_copys(&addr,recip)) return 0; - - i = byte_rchr(addr.s,addr.len,'@'); - if (i == addr.len) { - if (!stralloc_cats(&addr,"@")) return 0; - if (!stralloc_cat(&addr,&envnoathost)) return 0; - } - - while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { - unsigned int j = byte_rchr(addr.s,i,'%'); - if (j == i) break; - addr.len = i; - i = j; - addr.s[i] = '@'; - } - - at = byte_rchr(addr.s,addr.len,'@'); - - if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { - if (!stralloc_cat(&rwline,&addr)) return 0; - if (!stralloc_0(&rwline)) return 0; - return 1; - } - - for (i = 0;i <= addr.len;++i) - if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) - if ((x = constmap(&mapvdoms,addr.s + i,addr.len - i))) { - if (!*x) break; - if (!stralloc_cats(&rwline,x)) return 0; - if (!stralloc_cats(&rwline,"-")) return 0; - if (!stralloc_cat(&rwline,&addr)) return 0; - if (!stralloc_0(&rwline)) return 0; - return 1; - } - - if (!stralloc_cat(&rwline,&addr)) return 0; - if (!stralloc_0(&rwline)) return 0; - return 2; + fn2.len = fmtqfn(fn2.s, "bounce/", id, 0); } - -void senderadd(stralloc *sa, char *sender, char *recip) +void +fnmake_chanaddr(unsigned long id, int c) { - unsigned int i; - - i = str_len(sender); - if (i >= 4) - if (str_equal(sender + i - 4,"-@[]")) - { - unsigned int j = byte_rchr(sender,i - 4,'@'); - unsigned int k = str_rchr(recip,'@'); - if (recip[k] && (j + 5 <= i)) - { - /* owner-@host-@[] -> owner-recipbox=reciphost@host */ - while (!stralloc_catb(sa,sender,j)) nomem(); - while (!stralloc_catb(sa,recip,k)) nomem(); - while (!stralloc_cats(sa,"=")) nomem(); - while (!stralloc_cats(sa,recip + k + 1)) nomem(); - while (!stralloc_cats(sa,"@")) nomem(); - while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem(); - return; - } - } - while (!stralloc_cats(sa,sender)) nomem(); + fn.len = fmtqfn(fn.s, chanaddr[c], id, 1); } +*/ +/* this file is too long ----------------------------------------- REWRITING */ +/* the future is here! the rewriting routines have been moved to rewrite.c. */ /* this file is too long ---------------------------------------------- INFO */ -int getinfo(stralloc *sa, datetime_sec *dt, unsigned long id) +int +getinfo(stralloc * sa, datetime_sec * dt, unsigned long id) { - int fdinfo; - struct stat st; - static stralloc line = {0}; - int match; - substdio ss; - char buf[128]; - - fnmake_info(id); - fdinfo = open_read(fn.s); - if (fdinfo == -1) return 0; - if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; } - substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf)); - if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; } - close(fdinfo); - if (!match) return 0; - if (line.s[0] != 'F') return 0; - - *dt = st.st_mtime; - while (!stralloc_copys(sa,line.s + 1)) nomem(); - while (!stralloc_0(sa)) nomem(); - return 1; + int fdinfo; + struct stat st; + static stralloc line = { + 0 + }; + int match; + substdio ss; + char buf[128]; + + fnmake_info(id); + fdinfo = open_read(fn.s); + if (fdinfo == -1) + return 0; + if (fstat(fdinfo, &st) == -1) { + close(fdinfo); + return 0; + } + substdio_fdbuf(&ss, read, fdinfo, buf, sizeof(buf)); + if (getln(&ss, &line, &match, '\0') == -1) { + close(fdinfo); + return 0; + } + close(fdinfo); + if (!match) + return 0; + if (line.s[0] != 'F') + return 0; + + *dt = st.st_mtime; + while (!stralloc_copys(sa, line.s + 1)) + nomem(); + while (!stralloc_0(sa)) + nomem(); + return 1; } /* this file is too long ----------------------------------- PRIORITY QUEUES */ -prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */ -prioq pqchan[CHANNELS] = { {0}, {0} }; +prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */ +prioq pqchan[CHANNELS] = {{0}, {0}}; /* pqchan 0: -todo +info +local ?remote */ /* pqchan 1: -todo +info ?local +remote */ -prioq pqfail = {0}; /* stat() failure; has to be pqadded again */ +prioq pqfail = {0}; /* stat() failure; has to be pqadded again */ -void pqadd(unsigned long id) +void +pqadd(unsigned long id) { - struct prioq_elt pe; - struct prioq_elt pechan[CHANNELS]; - int flagchan[CHANNELS]; - struct stat st; - int c; + struct prioq_elt pe; + struct prioq_elt pechan[CHANNELS]; + int flagchan[CHANNELS]; + struct stat st; + int c; #define CHECKSTAT if (errno != error_noent) goto fail; - fnmake_info(id); - if (stat(fn.s,&st) == -1) - { - CHECKSTAT - return; /* someone yanking our chain */ - } - - fnmake_todo(id); - if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */ - CHECKSTAT - - for (c = 0;c < CHANNELS;++c) - { - fnmake_chanaddr(id,c); - if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT } - else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; } - } - - for (c = 0;c < CHANNELS;++c) - if (flagchan[c]) - while (!prioq_insert(&pqchan[c],&pechan[c])) nomem(); - - for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; - if (c == CHANNELS) - { - pe.id = id; pe.dt = now(); - while (!prioq_insert(&pqdone,&pe)) nomem(); - } - - return; - - fail: - log3("warning: unable to stat ",fn.s,"; will try again later\n"); - pe.id = id; pe.dt = now() + SLEEP_SYSFAIL; - while (!prioq_insert(&pqfail,&pe)) nomem(); + fnmake_info(id); + if (stat(fn.s, &st) == -1) { + CHECKSTAT + return; /* someone yanking our chain */ + } + + fnmake_todo(id); + if (stat(fn.s, &st) != -1) + return; /* look, ma, dad crashed writing info! */ + CHECKSTAT + + for (c = 0; c < CHANNELS; ++c) { + fnmake_chanaddr(id, c); + if (stat(fn.s, &st) == -1) { + flagchan[c] = 0; + CHECKSTAT + } else { + flagchan[c] = 1; + pechan[c].id = id; + pechan[c].dt = st.st_mtime; + } + } + + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c], &pechan[c])) + nomem(); + + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) + break; + if (c == CHANNELS) { + pe.id = id; + pe.dt = now(); + while (!prioq_insert(&pqdone, &pe)) + nomem(); + } + + return; + +fail: + log3("warning: unable to stat ", fn.s, "; will try again later\n"); + pe.id = id; + pe.dt = now() + SLEEP_SYSFAIL; + while (!prioq_insert(&pqfail, &pe)) + nomem(); } -void pqstart() +void +pqstart() { - readsubdir rs; - int x; - unsigned long id; + readsubdir rs; + int x; + unsigned long id; - readsubdir_init(&rs,"info",pausedir); + readsubdir_init(&rs, "info", pausedir); - while ((x = readsubdir_next(&rs,&id))) - if (x > 0) - pqadd(id); + while ((x = readsubdir_next(&rs, &id))) + if (x > 0) + pqadd(id); } -void pqfinish() +void +pqfinish() { - int c; - struct prioq_elt pe; - struct timeval ut[2] = { 0 }; - - for (c = 0;c < CHANNELS;++c) - while (prioq_min(&pqchan[c],&pe)) - { - prioq_delmin(&pqchan[c]); - fnmake_chanaddr(pe.id,c); - ut[0].tv_sec = ut[1].tv_sec = pe.dt; - if (utimes(fn.s,ut) == -1) - log3("warning: unable to utime ",fn.s,"; message will be retried too soon\n"); - } + int c; + struct prioq_elt pe; + struct timeval ut[2] = {0}; + + for (c = 0; c < CHANNELS; ++c) + while (prioq_min(&pqchan[c], &pe)) { + prioq_delmin(&pqchan[c]); + fnmake_chanaddr(pe.id, c); + ut[0].tv_sec = ut[1].tv_sec = pe.dt; + if (utimes(fn.s, ut) == -1) + log3("warning: unable to utime ", fn.s, "; message will be retried too soon\n"); + } } -void pqrun() +void +pqrun() { - int c; - unsigned int i; - for (c = 0;c < CHANNELS;++c) - if (pqchan[c].p) - if (pqchan[c].len) - for (i = 0;i < pqchan[c].len;++i) - pqchan[c].p[i].dt = recent; + int c; + unsigned int i; + for (c = 0; c < CHANNELS; ++c) + if (pqchan[c].p) + if (pqchan[c].len) + for (i = 0; i < pqchan[c].len; ++i) + pqchan[c].p[i].dt = recent; } /* this file is too long ---------------------------------------------- JOBS */ -struct job - { - int refs; /* if 0, this struct is unused */ - unsigned long id; - int channel; - datetime_sec retry; - stralloc sender; - int numtodo; - int flaghiteof; - int flagdying; - } +struct job { + int refs; /* if 0, this struct is unused */ + unsigned long id; + int channel; + datetime_sec retry; + stralloc sender; + int numtodo; + int flaghiteof; + int flagdying; +} ; int numjobs; struct job *jo; -void job_init() +void +job_init() { - int j; - while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem(); - for (j = 0;j < numjobs;++j) - { - jo[j].refs = 0; - jo[j].sender.s = 0; - } + int j; + while (!(jo = (struct job *)alloc(numjobs * sizeof(struct job)))) + nomem(); + for (j = 0; j < numjobs; ++j) { + jo[j].refs = 0; + jo[j].sender.s = 0; + } } -int job_avail() +int +job_avail() { - int j; - for (j = 0;j < numjobs;++j) if (!jo[j].refs) return 1; - return 0; + int j; + for (j = 0; j < numjobs; ++j) + if (!jo[j].refs) + return 1; + return 0; } -int job_open(unsigned long id, int channel) +int +job_open(unsigned long id, int channel) { - int j; - for (j = 0;j < numjobs;++j) if (!jo[j].refs) break; - if (j == numjobs) return -1; - jo[j].refs = 1; - jo[j].id = id; - jo[j].channel = channel; - jo[j].numtodo = 0; - jo[j].flaghiteof = 0; - return j; + int j; + for (j = 0; j < numjobs; ++j) + if (!jo[j].refs) + break; + if (j == numjobs) + return -1; + jo[j].refs = 1; + jo[j].id = id; + jo[j].channel = channel; + jo[j].numtodo = 0; + jo[j].flaghiteof = 0; + return j; } -void job_close(int j) +void +job_close(int j) { - struct prioq_elt pe; - struct stat st; - - if (0 < --jo[j].refs) return; - - pe.id = jo[j].id; - pe.dt = jo[j].retry; - if (jo[j].flaghiteof && !jo[j].numtodo) - { - fnmake_chanaddr(jo[j].id,jo[j].channel); - if (unlink(fn.s) == -1) - { - log3("warning: unable to unlink ",fn.s,"; will try again later\n"); - pe.dt = now() + SLEEP_SYSFAIL; - } - else - { - int c; - for (c = 0;c < CHANNELS;++c) if (c != jo[j].channel) - { - fnmake_chanaddr(jo[j].id,c); - if (stat(fn.s,&st) == 0) return; /* more channels going */ - if (errno != error_noent) - { - log3("warning: unable to stat ",fn.s,"\n"); - break; /* this is the only reason for HOPEFULLY */ + struct prioq_elt pe; + struct stat st; + + if (0 < --jo[j].refs) + return; + + pe.id = jo[j].id; + pe.dt = jo[j].retry; + if (jo[j].flaghiteof && !jo[j].numtodo) { + fnmake_chanaddr(jo[j].id, jo[j].channel); + if (unlink(fn.s) == -1) { + log3("warning: unable to unlink ", fn.s, "; will try again later\n"); + pe.dt = now() + SLEEP_SYSFAIL; + } else { + int c; + for (c = 0; c < CHANNELS; ++c) + if (c != jo[j].channel) { + fnmake_chanaddr(jo[j].id, c); + if (stat(fn.s, &st) == 0) + return; /* more channels going */ + if (errno != error_noent) { + log3("warning: unable to stat ", fn.s, "\n"); + break; /* this is the only + * reason for HOPEFULLY */ + } + } + pe.dt = now(); + while (!prioq_insert(&pqdone, &pe)) + nomem(); + return; + } } - } - pe.dt = now(); - while (!prioq_insert(&pqdone,&pe)) nomem(); - return; - } - } - - while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem(); + + while (!prioq_insert(&pqchan[jo[j].channel], &pe)) + nomem(); } /* this file is too long ------------------------------------------- BOUNCES */ -char *stripvdomprepend(char *recip) +char * +stripvdomprepend(char *recip) { - unsigned int i; - char *domain; - unsigned int domainlen; - char *prepend; - - i = str_rchr(recip,'@'); - if (!recip[i]) return recip; - domain = recip + i + 1; - domainlen = str_len(domain); - - for (i = 0;i <= domainlen;++i) - if ((i == 0) || (i == domainlen) || (domain[i] == '.')) - if ((prepend = constmap(&mapvdoms,domain + i,domainlen - i))) - { - if (!*prepend) break; - i = str_len(prepend); - if (str_diffn(recip,prepend,i)) break; - if (recip[i] != '-') break; - return recip + i + 1; - } - return recip; + unsigned int i; + char *domain; + unsigned int domainlen; + char *prepend; + + i = str_rchr(recip, '@'); + if (!recip[i]) + return recip; + domain = recip + i + 1; + domainlen = str_len(domain); + + for (i = 0; i <= domainlen; ++i) + if ((i == 0) || (i == domainlen) || (domain[i] == '.')) + if ((prepend = constmap(&mapvdoms, domain + i, domainlen - i))) { + if (!*prepend) + break; + i = str_len(prepend); + if (str_diffn(recip, prepend, i)) + break; + if (recip[i] != '-') + break; + return recip + i + 1; + } + return recip; } stralloc bouncetext = {0}; -void addbounce(unsigned long id, char *recip, char *report) +void +addbounce(unsigned long id, char *recip, char *report) { - int fd; - unsigned int pos; - int w; - while (!stralloc_copys(&bouncetext,"<")) nomem(); - while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem(); - for (pos = 0;pos < bouncetext.len;++pos) - if (bouncetext.s[pos] == '\n') - bouncetext.s[pos] = '_'; - while (!stralloc_cats(&bouncetext,">:\n")) nomem(); - while (!stralloc_cats(&bouncetext,report)) nomem(); - if (report[0]) - if (report[str_len(report) - 1] != '\n') - while (!stralloc_cats(&bouncetext,"\n")) nomem(); - for (pos = bouncetext.len - 2;pos > 0;--pos) - if (bouncetext.s[pos] == '\n') - if (bouncetext.s[pos - 1] == '\n') - bouncetext.s[pos] = '/'; - while (!stralloc_cats(&bouncetext,"\n")) nomem(); - fnmake2_bounce(id); - for (;;) - { - fd = open_append(fn2.s); - if (fd != -1) break; - log1("alert: unable to append to bounce message; HELP! sleeping...\n"); - sleep(10); - } - pos = 0; - while (pos < bouncetext.len) - { - w = write(fd,bouncetext.s + pos,bouncetext.len - pos); - if (w == 0 || w == -1) - { - log1("alert: unable to append to bounce message; HELP! sleeping...\n"); - sleep(10); - } - else - pos += w; - } - close(fd); + int fd; + unsigned int pos; + int w; + while (!stralloc_copys(&bouncetext, "<")) + nomem(); + while (!stralloc_cats(&bouncetext, stripvdomprepend(recip))) + nomem(); + for (pos = 0; pos < bouncetext.len; ++pos) + if (bouncetext.s[pos] == '\n') + bouncetext.s[pos] = '_'; + while (!stralloc_cats(&bouncetext, ">:\n")) + nomem(); + while (!stralloc_cats(&bouncetext, report)) + nomem(); + if (report[0]) + if (report[str_len(report) - 1] != '\n') + while (!stralloc_cats(&bouncetext, "\n")) + nomem(); + for (pos = bouncetext.len - 2; pos > 0; --pos) + if (bouncetext.s[pos] == '\n') + if (bouncetext.s[pos - 1] == '\n') + bouncetext.s[pos] = '/'; + while (!stralloc_cats(&bouncetext, "\n")) + nomem(); + fnmake2_bounce(id); + for (;;) { + fd = open_append(fn2.s); + if (fd != -1) + break; + log1("alert: unable to append to bounce message; HELP! sleeping...\n"); + sleep(10); + } + pos = 0; + while (pos < bouncetext.len) { + w = write(fd, bouncetext.s + pos, bouncetext.len - pos); + if (w == 0 || w == -1) { + log1("alert: unable to append to bounce message; HELP! sleeping...\n"); + sleep(10); + } else + pos += w; + } + close(fd); } -int injectbounce(unsigned long id) +int +injectbounce(unsigned long id) { - struct qmail qqt; - struct stat st; - char *bouncesender; - char *bouncerecip; - int r; - int fd; - substdio ssread; - char buf[128]; - char inbuf[128]; - static stralloc sender = {0}; - static stralloc quoted = {0}; - datetime_sec birth; - unsigned long qp; - - if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */ - - /* owner-@host-@[] -> owner-@host */ - if (sender.len >= 5) - if (str_equal(sender.s + sender.len - 5,"-@[]")) - { - sender.len -= 4; - sender.s[sender.len - 1] = 0; - } - - fnmake2_bounce(id); - fnmake_mess(id); - if (stat(fn2.s,&st) == -1) - { - if (errno == error_noent) - return 1; - log3("warning: unable to stat ",fn2.s,"\n"); - return 0; - } - if (str_equal(sender.s,"#@[]")) - log3("triple bounce: discarding ",fn2.s,"\n"); - else - { - if (qmail_open(&qqt) == -1) - { log1("warning: unable to start qmail-queue, will try later\n"); return 0; } - qp = qmail_qp(&qqt); - - if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; } - else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; } - - while (!newfield_datemake(now())) nomem(); - qmail_put(&qqt,newfield_date.s,newfield_date.len); - qmail_puts(&qqt,"From: "); - while (!quote(&quoted,&bouncefrom)) nomem(); - qmail_put(&qqt,quoted.s,quoted.len); - qmail_puts(&qqt,"@"); - qmail_put(&qqt,bouncehost.s,bouncehost.len); - qmail_puts(&qqt,"\nTo: "); - while (!quote2(&quoted,bouncerecip)) nomem(); - qmail_put(&qqt,quoted.s,quoted.len); - qmail_puts(&qqt,"\n\ + struct qmail qqt; + struct stat st; + char *bouncesender; + char *bouncerecip; + int r; + int fd; + substdio ssread; + char buf[128]; + char inbuf[128]; + static stralloc sender = { + 0 + }; + static stralloc quoted = { + 0 + }; + datetime_sec birth; + unsigned long qp; + + if (!getinfo(&sender, &birth, id)) + return 0; /* XXX: print warning */ + + /* owner-@host-@[] -> owner-@host */ + if (sender.len >= 5) + if (str_equal(sender.s + sender.len - 5, "-@[]")) { + sender.len -= 4; + sender.s[sender.len - 1] = 0; + } + + fnmake2_bounce(id); + fnmake_mess(id); + if (stat(fn2.s, &st) == -1) { + if (errno == error_noent) + return 1; + log3("warning: unable to stat ", fn2.s, "\n"); + return 0; + } + if (str_equal(sender.s, "#@[]")) + log3("triple bounce: discarding ", fn2.s, "\n"); + else { + if (qmail_open(&qqt) == -1) { + log1("warning: unable to start qmail-queue, will try later\n"); + return 0; + } + qp = qmail_qp(&qqt); + + if (*sender.s) { + bouncesender = ""; + bouncerecip = sender.s; + } else { + bouncesender = "#@[]"; + bouncerecip = doublebounceto.s; + } + + while (!newfield_datemake(now())) + nomem(); + qmail_put(&qqt, newfield_date.s, newfield_date.len); + qmail_puts(&qqt, "From: "); + while (!quote(&quoted, &bouncefrom)) + nomem(); + qmail_put(&qqt, quoted.s, quoted.len); + qmail_puts(&qqt, "@"); + qmail_put(&qqt, bouncehost.s, bouncehost.len); + qmail_puts(&qqt, "\nTo: "); + while (!quote2(&quoted, bouncerecip)) + nomem(); + qmail_put(&qqt, quoted.s, quoted.len); + qmail_puts(&qqt, "\n\ Subject: Failure Notice\n\ \n\ Hi. This is the qmail-send (NightmareMail queue manager) program at "); - qmail_put(&qqt,bouncehost.s,bouncehost.len); - qmail_puts(&qqt,*sender.s ? ".\n\ + qmail_put(&qqt, bouncehost.s, bouncehost.len); + qmail_puts(&qqt, *sender.s ? ".\n\ I'm afraid I wasn't able to deliver your message to the following addresses.\n\ This is a permanent error. I've given up. Sorry it didn't work out.\n\ For further assistance, mail the postmaster.\n\ @@ -556,225 +576,281 @@ I tried to deliver a bounce message to this address, but the bounce bounced!\n\ \n\ "); - fd = open_read(fn2.s); - if (fd == -1) - qmail_fail(&qqt); - else - { - substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); - while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) - qmail_put(&qqt,buf,r); - close(fd); - if (r == -1) - qmail_fail(&qqt); - } - - qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n"); - qmail_puts(&qqt,"Return-Path: <"); - while (!quote2(&quoted,sender.s)) nomem(); - qmail_put(&qqt,quoted.s,quoted.len); - qmail_puts(&qqt,">\n"); - - fd = open_read(fn.s); - if (fd == -1) - qmail_fail(&qqt); - else - { - substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); - while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) - qmail_put(&qqt,buf,r); - close(fd); - if (r == -1) - qmail_fail(&qqt); - } - - qmail_from(&qqt,bouncesender); - qmail_to(&qqt,bouncerecip); - if (*qmail_close(&qqt)) - { log1("warning: trouble injecting bounce message, will try later\n"); return 0; } - - strnum2[fmt_ulong(strnum2,id)] = 0; - qslog2("bounce msg ",strnum2); - strnum2[fmt_ulong(strnum2,qp)] = 0; - log3(" qp ",strnum2,"\n"); - } - if (unlink(fn2.s) == -1) - { - log3("warning: unable to unlink ",fn2.s,"\n"); - return 0; - } - return 1; + fd = open_read(fn2.s); + if (fd == -1) + qmail_fail(&qqt); + else { + substdio_fdbuf(&ssread, read, fd, inbuf, sizeof(inbuf)); + while ((r = substdio_get(&ssread, buf, sizeof(buf))) > 0) + qmail_put(&qqt, buf, r); + close(fd); + if (r == -1) + qmail_fail(&qqt); + } + + qmail_puts(&qqt, *sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n"); + qmail_puts(&qqt, "Return-Path: <"); + while (!quote2(&quoted, sender.s)) + nomem(); + qmail_put(&qqt, quoted.s, quoted.len); + qmail_puts(&qqt, ">\n"); + + fd = open_read(fn.s); + if (fd == -1) + qmail_fail(&qqt); + else { + substdio_fdbuf(&ssread, read, fd, inbuf, sizeof(inbuf)); + while ((r = substdio_get(&ssread, buf, sizeof(buf))) > 0) + qmail_put(&qqt, buf, r); + close(fd); + if (r == -1) + qmail_fail(&qqt); + } + + qmail_from(&qqt, bouncesender); + qmail_to(&qqt, bouncerecip); + if (*qmail_close(&qqt)) { + log1("warning: trouble injecting bounce message, will try later\n"); + return 0; + } + + strnum2[fmt_ulong(strnum2, id)] = 0; + qslog2("bounce msg ", strnum2); + strnum2[fmt_ulong(strnum2, qp)] = 0; + log3(" qp ", strnum2, "\n"); + } + if (unlink(fn2.s) == -1) { + log3("warning: unable to unlink ", fn2.s, "\n"); + return 0; + } + return 1; } /* this file is too long ---------------------------------------------- MAIN */ -int getcontrols() { if (control_init() == -1) return 0; - if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; - if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; - if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; - if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; - if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0; - if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0; - if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0; - if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0; - if (!stralloc_cats(&doublebounceto,"@")) return 0; - if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0; - if (!stralloc_0(&doublebounceto)) return 0; - if (control_readfile(&locals,"control/locals",1) != 1) return 0; - if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; - switch(control_readfile(&percenthack,"control/percenthack",0)) - { - case -1: return 0; - case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; - case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; - } - switch(control_readfile(&vdoms,"control/virtualdomains",0)) - { - case -1: return 0; - case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; - case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; - } - return 1; } +int +getcontrols() +{ + if (control_init() == -1) + return 0; + if (control_readint(&lifetime, "control/queuelifetime") == -1) + return 0; + if (control_readint(&concurrency[0], "control/concurrencylocal") == -1) + return 0; + if (control_readint(&concurrency[1], "control/concurrencyremote") == -1) + return 0; + if (control_rldef(&envnoathost, "control/envnoathost", 1, "envnoathost") != 1) + return 0; + if (control_rldef(&bouncefrom, "control/bouncefrom", 0, "MAILER-DAEMON") != 1) + return 0; + if (control_rldef(&bouncehost, "control/bouncehost", 1, "bouncehost") != 1) + return 0; + if (control_rldef(&doublebouncehost, "control/doublebouncehost", 1, "doublebouncehost") != 1) + return 0; + if (control_rldef(&doublebounceto, "control/doublebounceto", 0, "postmaster") != 1) + return 0; + if (!stralloc_cats(&doublebounceto, "@")) + return 0; + if (!stralloc_cat(&doublebounceto, &doublebouncehost)) + return 0; + if (!stralloc_0(&doublebounceto)) + return 0; + if (control_readfile(&locals, "control/locals", 1) != 1) + return 0; + if (!constmap_init(&maplocals, locals.s, locals.len, 0)) + return 0; + switch (control_readfile(&percenthack, "control/percenthack", 0)) { + case -1: + return 0; + case 0: + if (!constmap_init(&mappercenthack, "", 0, 0)) + return 0; + break; + case 1: + if (!constmap_init(&mappercenthack, percenthack.s, percenthack.len, 0)) + return 0; + break; + } + switch (control_readfile(&vdoms, "control/virtualdomains", 0)) { + case -1: + return 0; + case 0: + if (!constmap_init(&mapvdoms, "", 0, 1)) + return 0; + break; + case 1: + if (!constmap_init(&mapvdoms, vdoms.s, vdoms.len, 1)) + return 0; + break; + } + return 1; +} stralloc newlocals = {0}; stralloc newvdoms = {0}; -void regetcontrols() +void +regetcontrols() { - int r; - - if (control_readfile(&newlocals,"control/locals",1) != 1) - { log1("alert: unable to reread control/locals\n"); return; } - r = control_readfile(&newvdoms,"control/virtualdomains",0); - if (r == -1) - { log1("alert: unable to reread control/virtualdomains\n"); return; } - - constmap_free(&maplocals); - constmap_free(&mapvdoms); - - while (!stralloc_copy(&locals,&newlocals)) nomem(); - while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); - - if (r) - { - while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); - while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); - } - else - while (!constmap_init(&mapvdoms,"",0,1)) nomem(); + int r; + + if (control_readfile(&newlocals, "control/locals", 1) != 1) { + log1("alert: unable to reread control/locals\n"); + return; + } + r = control_readfile(&newvdoms, "control/virtualdomains", 0); + if (r == -1) { + log1("alert: unable to reread control/virtualdomains\n"); + return; + } + + constmap_free(&maplocals); + constmap_free(&mapvdoms); + + while (!stralloc_copy(&locals, &newlocals)) + nomem(); + while (!constmap_init(&maplocals, locals.s, locals.len, 0)) + nomem(); + + if (r) { + while (!stralloc_copy(&vdoms, &newvdoms)) + nomem(); + while (!constmap_init(&mapvdoms, vdoms.s, vdoms.len, 1)) + nomem(); + } else + while (!constmap_init(&mapvdoms, "", 0, 1)) + nomem(); } -void reread() +void +reread() { - if (chdir(auto_qmail) == -1) - { - log1("alert: unable to reread controls: unable to switch to home directory\n"); - return; - } - regetcontrols(); - while (chdir("queue") == -1) - { - log1("alert: unable to switch back to queue directory; HELP! sleeping...\n"); - sleep(10); - } + if (chdir(auto_qmail) == -1) { + log1("alert: unable to reread controls: unable to switch to home directory\n"); + return; + } + regetcontrols(); + while (chdir("queue") == -1) { + log1("alert: unable to switch back to queue directory; HELP! sleeping...\n"); + sleep(10); + } } -int main(void) +int +main(void) { - int fd; - datetime_sec wakeup; - fd_set rfds; - fd_set wfds; - int nfds; - struct timeval tv; - int c; - - if (chdir(auto_qmail) == -1) - { log1("alert: cannot start: unable to switch to home directory\n"); _exit(111); } - if (!getcontrols()) - { log1("alert: cannot start: unable to read controls\n"); _exit(111); } - if (chdir("queue") == -1) - { log1("alert: cannot start: unable to switch to queue directory\n"); _exit(111); } - sig_pipeignore(); - sig_termcatch(sigterm); - sig_alarmcatch(sigalrm); - sig_hangupcatch(sighup); - sig_childdefault(); - umask(077); - - fd = open_write("lock/sendmutex"); - if (fd == -1) - { log1("alert: cannot start: unable to open mutex\n"); _exit(111); } - if (lock_exnb(fd) == -1) - { log1("alert: cannot start: qmail-send is already running\n"); _exit(111); } - - numjobs = 0; - for (c = 0;c < CHANNELS;++c) - { - char ch; - int u; - int r; - do - r = read(chanfdin[c],&ch,1); - while ((r == -1) && (errno == error_intr)); - if (r < 1) - { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } - u = (unsigned int) (unsigned char) ch; - if (concurrency[c] > u) concurrency[c] = u; - numjobs += concurrency[c]; - } - - fnmake_init(); - - comm_init(); - - pqstart(); - job_init(); - del_init(); - pass_init(); - todo_init(); - cleanup_init(); - - while (!flagexitasap || !del_canexit()) - { - recent = now(); - - if (flagrunasap) { flagrunasap = 0; pqrun(); } - if (flagreadasap) { flagreadasap = 0; reread(); } - - wakeup = recent + SLEEP_FOREVER; - FD_ZERO(&rfds); - FD_ZERO(&wfds); - nfds = 1; - - comm_selprep(&nfds,&wfds); - del_selprep(&nfds,&rfds); - pass_selprep(&wakeup); - todo_selprep(&nfds,&rfds,&wakeup); - cleanup_selprep(&wakeup); - - if (wakeup <= recent) tv.tv_sec = 0; - else tv.tv_sec = wakeup - recent + SLEEP_FUZZ; - tv.tv_usec = 0; - - if (select(nfds,&rfds,&wfds,NULL,&tv) == -1) - if (errno == error_intr) - ; - else - log1("warning: trouble in select\n"); - else - { - recent = now(); - - // XXX: should have a queue structure - comm_do(&wfds); - del_do(&rfds); - todo_do(&rfds); - pass_do(); - cleanup_do(); - } - } - pqfinish(); - log1("status: exiting\n"); - _exit(0); + int fd; + datetime_sec wakeup; + fd_set rfds; + fd_set wfds; + int nfds; + struct timeval tv; + int c; + + if (chdir(auto_qmail) == -1) { + log1("alert: cannot start: unable to switch to home directory\n"); + _exit(111); + } + if (!getcontrols()) { + log1("alert: cannot start: unable to read controls\n"); + _exit(111); + } + if (chdir("queue") == -1) { + log1("alert: cannot start: unable to switch to queue directory\n"); + _exit(111); + } + sig_pipeignore(); + sig_termcatch(sigterm); + sig_alarmcatch(sigalrm); + sig_hangupcatch(sighup); + sig_childdefault(); + umask(077); + + fd = open_write("lock/sendmutex"); + if (fd == -1) { + log1("alert: cannot start: unable to open mutex\n"); + _exit(111); + } + if (lock_exnb(fd) == -1) { + log1("alert: cannot start: qmail-send is already running\n"); + _exit(111); + } + + numjobs = 0; + for (c = 0; c < CHANNELS; ++c) { + char ch; + int u; + int r; + do + r = read(chanfdin[c], &ch, 1); + while ((r == -1) && (errno == error_intr)); + if (r < 1) { + log1("alert: cannot start: hath the daemon spawn no fire?\n"); + _exit(111); + } + u = (unsigned int)(unsigned char)ch; + if (concurrency[c] > u) + concurrency[c] = u; + numjobs += concurrency[c]; + } + + fnmake_init(); + + comm_init(); + + pqstart(); + job_init(); + del_init(); + pass_init(); + todo_init(); + cleanup_init(); + + while (!flagexitasap || !del_canexit()) { + recent = now(); + + if (flagrunasap) { + flagrunasap = 0; + pqrun(); + } + if (flagreadasap) { + flagreadasap = 0; + reread(); + } + + wakeup = recent + SLEEP_FOREVER; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + nfds = 1; + + comm_selprep(&nfds, &wfds); + del_selprep(&nfds, &rfds); + pass_selprep(&wakeup); + todo_selprep(&nfds, &rfds, &wakeup); + cleanup_selprep(&wakeup); + + if (wakeup <= recent) + tv.tv_sec = 0; + else + tv.tv_sec = wakeup - recent + SLEEP_FUZZ; + tv.tv_usec = 0; + + if (select(nfds, &rfds, &wfds, NULL, &tv) == -1) + if (errno == error_intr) + ; + else + log1("warning: trouble in select\n"); + else { + recent = now(); + + //XXX: should have a queue structure + comm_do(&wfds); + del_do(&rfds); + todo_do(&rfds); + pass_do(); + cleanup_do(); + } + } + pqfinish(); + log1("status: exiting\n"); + _exit(0); }; diff --git a/src/mxf-send/mxf-send.h b/src/mxf-send/mxf-send.h @@ -1,3 +1,5 @@ +#ifndef MXFSEND_H +#define MXFSEND_H #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> @@ -40,6 +42,7 @@ #include "fmtqfn.h" #include "readsubdir.h" +#define AMNESIAC (errno = ENOMEM, -1) #define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ #define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ #define SLEEP_FOREVER 86400 /* absolute maximum time spent in iopause() */ @@ -49,3 +52,7 @@ #define CHANNELS 2 #define REPORTMAX 10000 #define CHECKSTAT if (errno != error_noent) goto fail; + +#include "mxf-send-protos.h" +#include "mxf-send-globals.h" +#endif // MXFSEND_H diff --git a/src/mxf-send/pass.c b/src/mxf-send/pass.c @@ -2,227 +2,255 @@ /* this file is too long -------------------------------------------- PASSES */ -struct - { - unsigned long id; /* if 0, need a new pass */ - int j; /* defined if id; job number */ - int fd; /* defined if id; reading from {local,remote} */ - seek_pos mpos; /* defined if id; mark position */ - substdio ss; - char buf[128]; - } -pass[CHANNELS]; - -void pass_init() -{ - int c; - for (c = 0;c < CHANNELS;++c) pass[c].id = 0; +struct { + unsigned long id; /* if 0, need a new pass */ + int j; /* defined if id; job number */ + int fd; /* defined if id; reading from {local,remote} */ + seek_pos mpos; /* defined if id; mark position */ + substdio ss; + char buf[128]; } + pass[CHANNELS]; -void pass_selprep(datetime_sec *wakeup) +void +pass_init() { - int c; - struct prioq_elt pe; - if (flagexitasap) return; - for (c = 0;c < CHANNELS;++c) - if (pass[c].id) - if (del_avail(c)) - { *wakeup = 0; return; } - if (job_avail()) - for (c = 0;c < CHANNELS;++c) - if (!pass[c].id) - if (prioq_min(&pqchan[c],&pe)) - if (*wakeup > pe.dt) - *wakeup = pe.dt; - if (prioq_min(&pqfail,&pe)) - if (*wakeup > pe.dt) - *wakeup = pe.dt; - if (prioq_min(&pqdone,&pe)) - if (*wakeup > pe.dt) - *wakeup = pe.dt; + int c; + for (c = 0; c < CHANNELS; ++c) + pass[c].id = 0; } -static datetime_sec squareroot(datetime_sec x) -/* result^2 <= x < (result + 1)^2 */ -/* assuming: >= 0 */ +void +pass_selprep(datetime_sec * wakeup) { - datetime_sec y; - datetime_sec yy; - datetime_sec y21; - int j; - - y = 0; yy = 0; - for (j = 15;j >= 0;--j) - { - y21 = (y << (j + 1)) + (1 << (j + j)); - if (y21 <= x - yy) { y += (1 << j); yy += y21; } - } - return y; + int c; + struct prioq_elt pe; + if (flagexitasap) + return; + for (c = 0; c < CHANNELS; ++c) + if (pass[c].id) + if (del_avail(c)) { + *wakeup = 0; + return; + } + if (job_avail()) + for (c = 0; c < CHANNELS; ++c) + if (!pass[c].id) + if (prioq_min(&pqchan[c], &pe)) + if (*wakeup > pe.dt) + *wakeup = pe.dt; + if (prioq_min(&pqfail, &pe)) + if (*wakeup > pe.dt) + *wakeup = pe.dt; + if (prioq_min(&pqdone, &pe)) + if (*wakeup > pe.dt) + *wakeup = pe.dt; +} + +static datetime_sec squareroot(datetime_sec x) { + /* result^2 <= x < (result + 1)^2 */ + /* assuming: >= 0 */ + datetime_sec y; + datetime_sec yy; + datetime_sec y21; + int j; + + y = 0; + yy = 0; + for (j = 15; j >= 0; --j) { + y21 = (y << (j + 1)) + (1 << (j + j)); + if (y21 <= x - yy) { + y += (1 << j); + yy += y21; + } + } + return y; } -datetime_sec nextretry(datetime_sec birth, int c) +datetime_sec +nextretry(datetime_sec birth, int c) { - datetime_sec n; + datetime_sec n; - if (birth > recent) n = 0; - else n = squareroot(recent - birth); /* no need to add fuzz to recent */ - n += chanskip[c]; - return birth + n * n; + if (birth > recent) + n = 0; + else + n = squareroot(recent - birth); /* no need to add fuzz to + * recent */ + n += chanskip[c]; + return birth + n * n; } -void pass_dochan(int c) +void +pass_dochan(int c) { - datetime_sec birth; - struct prioq_elt pe; - static stralloc line = {0}; - int match; - - if (flagexitasap) return; - - if (!pass[c].id) - { - if (!job_avail()) return; - if (!prioq_min(&pqchan[c],&pe)) return; - if (pe.dt > recent) return; - fnmake_chanaddr(pe.id,c); - - prioq_delmin(&pqchan[c]); - pass[c].mpos = 0; - pass[c].fd = open_read(fn.s); - if (pass[c].fd == -1) goto trouble; - if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; } - pass[c].id = pe.id; - substdio_fdbuf(&pass[c].ss,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf)); - pass[c].j = job_open(pe.id,c); - jo[pass[c].j].retry = nextretry(birth,c); - jo[pass[c].j].flagdying = (recent > birth + lifetime); - while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem(); - } - - if (!del_avail(c)) return; - - if (getln(&pass[c].ss,&line,&match,'\0') == -1) - { - fnmake_chanaddr(pass[c].id,c); - log3("warning: trouble reading ",fn.s,"; will try again later\n"); - close(pass[c].fd); - job_close(pass[c].j); - pass[c].id = 0; - return; - } - if (!match) - { - close(pass[c].fd); - jo[pass[c].j].flaghiteof = 1; - job_close(pass[c].j); - pass[c].id = 0; - return; - } - switch(line.s[0]) - { - case 'T': - ++jo[pass[c].j].numtodo; - del_start(pass[c].j,pass[c].mpos,line.s + 1); - break; - case 'D': - break; - default: - fnmake_chanaddr(pass[c].id,c); - log3("warning: unknown record type in ",fn.s,"!\n"); - close(pass[c].fd); - job_close(pass[c].j); - pass[c].id = 0; - return; - } - - pass[c].mpos += line.len; - return; - - trouble: - log3("warning: trouble opening ",fn.s,"; will try again later\n"); - pe.dt = recent + SLEEP_SYSFAIL; - while (!prioq_insert(&pqchan[c],&pe)) nomem(); + datetime_sec birth; + struct prioq_elt pe; + static stralloc line = { + 0 + }; + int match; + + if (flagexitasap) + return; + + if (!pass[c].id) { + if (!job_avail()) + return; + if (!prioq_min(&pqchan[c], &pe)) + return; + if (pe.dt > recent) + return; + fnmake_chanaddr(pe.id, c); + + prioq_delmin(&pqchan[c]); + pass[c].mpos = 0; + pass[c].fd = open_read(fn.s); + if (pass[c].fd == -1) + goto trouble; + if (!getinfo(&line, &birth, pe.id)) { + close(pass[c].fd); + goto trouble; + } + pass[c].id = pe.id; + substdio_fdbuf(&pass[c].ss, read, pass[c].fd, pass[c].buf, sizeof(pass[c].buf)); + pass[c].j = job_open(pe.id, c); + jo[pass[c].j].retry = nextretry(birth, c); + jo[pass[c].j].flagdying = (recent > birth + lifetime); + while (!stralloc_copy(&jo[pass[c].j].sender, &line)) + nomem(); + } + + if (!del_avail(c)) + return; + + if (getln(&pass[c].ss, &line, &match, '\0') == -1) { + fnmake_chanaddr(pass[c].id, c); + log3("warning: trouble reading ", fn.s, "; will try again later\n"); + close(pass[c].fd); + job_close(pass[c].j); + pass[c].id = 0; + return; + } + if (!match) { + close(pass[c].fd); + jo[pass[c].j].flaghiteof = 1; + job_close(pass[c].j); + pass[c].id = 0; + return; + } + switch (line.s[0]) { + case 'T': + ++jo[pass[c].j].numtodo; + del_start(pass[c].j, pass[c].mpos, line.s + 1); + break; + case 'D': + break; + default: + fnmake_chanaddr(pass[c].id, c); + log3("warning: unknown record type in ", fn.s, "!\n"); + close(pass[c].fd); + job_close(pass[c].j); + pass[c].id = 0; + return; + } + + pass[c].mpos += line.len; + return; + +trouble: + log3("warning: trouble opening ", fn.s, "; will try again later\n"); + pe.dt = recent + SLEEP_SYSFAIL; + while (!prioq_insert(&pqchan[c], &pe)) + nomem(); } -void messdone(unsigned long id) +void +messdone(unsigned long id) { - char ch; - int c; - struct prioq_elt pe; - struct stat st; - - for (c = 0;c < CHANNELS;++c) - { - fnmake_chanaddr(id,c); - if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */ - if (errno != error_noent) - { - log3("warning: unable to stat ",fn.s,"; will try again later\n"); - goto fail; - } - } - - fnmake_todo(id); - if (stat(fn.s,&st) == 0) return; - if (errno != error_noent) - { - log3("warning: unable to stat ",fn.s,"; will try again later\n"); - goto fail; - } - - fnmake_info(id); - if (stat(fn.s,&st) == -1) - { - if (errno == error_noent) return; - log3("warning: unable to stat ",fn.s,"; will try again later\n"); - goto fail; - } - - /* -todo +info -local -remote ?bounce */ - if (!injectbounce(id)) - goto fail; /* injectbounce() produced error message */ - - strnum3[fmt_ulong(strnum3,id)] = 0; - log3("end msg ",strnum3,"\n"); - - /* -todo +info -local -remote -bounce */ - fnmake_info(id); - if (unlink(fn.s) == -1) - { - log3("warning: unable to unlink ",fn.s,"; will try again later\n"); - goto fail; - } - - /* -todo -info -local -remote -bounce; we can relax */ - fnmake_foop(id); - if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } - if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } - if (ch != '+') - log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); - - return; - - fail: - pe.id = id; pe.dt = now() + SLEEP_SYSFAIL; - while (!prioq_insert(&pqdone,&pe)) nomem(); + char ch; + int c; + struct prioq_elt pe; + struct stat st; + + for (c = 0; c < CHANNELS; ++c) { + fnmake_chanaddr(id, c); + if (stat(fn.s, &st) == 0) + return; /* false alarm; consequence of HOPEFULLY */ + if (errno != error_noent) { + log3("warning: unable to stat ", fn.s, "; will try again later\n"); + goto fail; + } + } + + fnmake_todo(id); + if (stat(fn.s, &st) == 0) + return; + if (errno != error_noent) { + log3("warning: unable to stat ", fn.s, "; will try again later\n"); + goto fail; + } + + fnmake_info(id); + if (stat(fn.s, &st) == -1) { + if (errno == error_noent) + return; + log3("warning: unable to stat ", fn.s, "; will try again later\n"); + goto fail; + } + + /* -todo +info -local -remote ?bounce */ + if (!injectbounce(id)) + goto fail; /* injectbounce() produced error message */ + + strnum3[fmt_ulong(strnum3, id)] = 0; + log3("end msg ", strnum3, "\n"); + + /* -todo +info -local -remote -bounce */ + fnmake_info(id); + if (unlink(fn.s) == -1) { + log3("warning: unable to unlink ", fn.s, "; will try again later\n"); + goto fail; + } + + /* -todo -info -local -remote -bounce; we can relax */ + fnmake_foop(id); + if (substdio_putflush(&sstoqc, fn.s, fn.len) == -1) { + cleandied(); + return; + } + if (substdio_get(&ssfromqc, &ch, 1) != 1) { + cleandied(); + return; + } + if (ch != '+') + log3("warning: qmail-clean unable to clean up ", fn.s, "\n"); + + return; + +fail: + pe.id = id; + pe.dt = now() + SLEEP_SYSFAIL; + while (!prioq_insert(&pqdone, &pe)) + nomem(); } -void pass_do() +void +pass_do() { - int c; - struct prioq_elt pe; - - for (c = 0;c < CHANNELS;++c) pass_dochan(c); - if (prioq_min(&pqfail,&pe)) - if (pe.dt <= recent) - { - prioq_delmin(&pqfail); - pqadd(pe.id); - } - if (prioq_min(&pqdone,&pe)) - if (pe.dt <= recent) - { - prioq_delmin(&pqdone); - messdone(pe.id); - } + int c; + struct prioq_elt pe; + + for (c = 0; c < CHANNELS; ++c) + pass_dochan(c); + if (prioq_min(&pqfail, &pe)) + if (pe.dt <= recent) { + prioq_delmin(&pqfail); + pqadd(pe.id); + } + if (prioq_min(&pqdone, &pe)) + if (pe.dt <= recent) { + prioq_delmin(&pqdone); + messdone(pe.id); + } } diff --git a/src/mxf-send/rewrite.c b/src/mxf-send/rewrite.c @@ -0,0 +1,133 @@ +#include "mxf-send.h" +/* this file is too long ----------------------------------------- REWRITING */ + +stralloc rwline = {0}; // XXX should be passed into rewrite, not a global + +#define AMNESIAC (errno = ENOMEM, -1) +/* MXF TODO: change semantics to + * -1 ENOMEM if forgetful, + * -1 ENOENT if can't find channel, + * channel number if going to a channel. currently just 0 for local and 1 for remote + * MXF TODO: if slots are available, callers should automatically + * try to reach that channel to schedule a delivery after calling + * this. + * MXF TODO: callers should barf if -1 and errno isn't enomem or enoent. + */ +/* may trash recip. must set up rwline, between a T and a \0. */ +int +rewrite(char *recip) +{ + unsigned int i; + char *x; + static stralloc addr = TA_ZERO; + unsigned int at; + + if (!stralloc_copys(&rwline, "T")) + return AMNESIAC; + if (!stralloc_copys(&addr, recip)) + return AMNESIAC; + + i = byte_rchr(addr.s, addr.len, '@'); + if (i == addr.len) { + if (!stralloc_cats(&addr, "@")) + return AMNESIAC; + if (!stralloc_cat(&addr, &envnoathost)) + return AMNESIAC; + } + + /* MXF NOTE: Double tap now if you'd scrunkly the when! + * This makes no sense. This is a crock. Nobody actually uses the + * percent hack. + * A respectable site with UUCP source routing allows local parts + * to contain exclamatories. + */ + while (constmap(&mappercenthack, addr.s + i + 1, addr.len - i - 1)) { + unsigned int j = byte_rchr(addr.s, i, '%'); + if (j == i) + break; + addr.len = i; + i = j; + addr.s[i] = '@'; + } + + at = byte_rchr(addr.s, addr.len, '@'); + + /* MXF NOTE: so what's happening here is that addr, our static, + * the value to the right of @ is being checked against + * constmap maplocals. Return 1 if "by land" (local). + */ + if (constmap(&maplocals, addr.s + at + 1, addr.len - at - 1)) { + if (!stralloc_cat(&rwline, &addr)) + return AMNESIAC; + if (!stralloc_0(&rwline)) + return AMNESIAC; + return 0; + } + + /* MXF NOTE: so what's happening here is that addr, our static, + * if start of addrlen, or if one after the @, or if at end, + * or if cursor>at and addr.s[cursor] is an ascii dot, + * then if constmap returns anything useful, + * prepend the virtual prepend (XXX uses "-" even if different + * dash is used), and return 1 since by land (local). + * TODO: turn to different channel idea + */ + for (i = 0; i <= addr.len; ++i) + if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) + if ((x = constmap(&mapvdoms, addr.s + i, addr.len - i))) { + if (!*x) + break; + if (!stralloc_cats(&rwline, x)) + return AMNESIAC; + if (!stralloc_cats(&rwline, "-")) + return AMNESIAC; + if (!stralloc_cat(&rwline, &addr)) + return AMNESIAC; + if (!stralloc_0(&rwline)) + return AMNESIAC; + return 0; + } + + if (!stralloc_cat(&rwline, &addr)) + return AMNESIAC; + if (!stralloc_0(&rwline)) + return AMNESIAC; + /* MXF NOTE: it isn't going down channel 0, so it's going down channel 1. + */ + return 1; +} + +// QUERY should return AMNESIAC? +void +senderadd(stralloc * sa, char *sender, char *recip) +{ + unsigned int i; + + i = str_len(sender); + if (i >= 4) + if (str_equal(sender + i - 4, "-@[]")) { + unsigned int j = byte_rchr(sender, i - 4, '@'); + unsigned int k = str_rchr(recip, '@'); + if (recip[k] && (j + 5 <= i)) { + /* + * owner-@host-@[] -> + * owner-recipbox=reciphost@host + */ + while (!stralloc_catb(sa, sender, j)) + nomem(); + while (!stralloc_catb(sa, recip, k)) + nomem(); + while (!stralloc_cats(sa, "=")) + nomem(); + while (!stralloc_cats(sa, recip + k + 1)) + nomem(); + while (!stralloc_cats(sa, "@")) + nomem(); + while (!stralloc_catb(sa, sender + j + 1, i - 5 - j)) + nomem(); + return; + } + } + while (!stralloc_cats(sa, sender)) + nomem(); +} diff --git a/src/mxf-send/todo.c b/src/mxf-send/todo.c @@ -2,221 +2,271 @@ /* this file is too long ---------------------------------------------- TODO */ datetime_sec nexttodorun; -DIR *tododir; /* if 0, have to opendir again */ +DIR *tododir; /* if 0, have to opendir again */ stralloc todoline = {0}; char todobuf[SUBSTDIO_INSIZE]; char todobufinfo[512]; char todobufchan[CHANNELS][1024]; -void todo_init() +void +todo_init() { - tododir = 0; - nexttodorun = now(); - trigger_set(); + tododir = 0; + nexttodorun = now(); + trigger_set(); } -void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup) +void +todo_selprep(int *nfds, fd_set * rfds, datetime_sec * wakeup) { - if (flagexitasap) return; - trigger_selprep(nfds,rfds); - if (tododir) *wakeup = 0; - if (*wakeup > nexttodorun) *wakeup = nexttodorun; + if (flagexitasap) + return; + trigger_selprep(nfds, rfds); + if (tododir) + *wakeup = 0; + if (*wakeup > nexttodorun) + *wakeup = nexttodorun; } -// XXX XXX - need to refactor this into a todo_dochan -void todo_do(fd_set *rfds) +//XXX XXX - need to refactor this into a todo_doone +void +todo_do(fd_set * rfds) { - struct stat st; - substdio ss; int fd; - substdio ssinfo; int fdinfo; - substdio sschan[CHANNELS]; - int fdchan[CHANNELS]; - int flagchan[CHANNELS]; - struct prioq_elt pe; - char ch; - int match; - unsigned long id; - unsigned int len; - direntry *dent; - int c; - unsigned long uid; - unsigned long pid; - - fd = -1; - fdinfo = -1; - for (c = 0;c < CHANNELS;++c) fdchan[c] = -1; - - if (flagexitasap) return; - - if (!tododir) - { - if (!trigger_pulled(rfds)) - if (recent < nexttodorun) - return; - trigger_set(); - tododir = opendir("todo"); - if (!tododir) - { - pausedir("todo"); - return; - } - nexttodorun = recent + SLEEP_TODO; - } - - dent = readdir(tododir); - if (!dent) - { - closedir(tododir); - tododir = 0; - return; - } - if (str_equal(dent->d_name,".")) return; - if (str_equal(dent->d_name,"..")) return; - len = scan_ulong(dent->d_name,&id); - if (!len || dent->d_name[len]) return; - - fnmake_todo(id); - - fd = open_read(fn.s); - if (fd == -1) { log3("warning: unable to open ",fn.s,"\n"); return; } - - fnmake_mess(id); - /* just for the statistics */ - if (stat(fn.s,&st) == -1) - { log3("warning: unable to stat ",fn.s,"\n"); goto fail; } - - for (c = 0;c < CHANNELS;++c) - { - fnmake_chanaddr(id,c); - if (unlink(fn.s) == -1) if (errno != error_noent) - { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; } - } - - fnmake_info(id); - if (unlink(fn.s) == -1) if (errno != error_noent) - { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; } - - fdinfo = open_excl(fn.s); - if (fdinfo == -1) - { log3("warning: unable to create ",fn.s,"\n"); goto fail; } - - strnum3[fmt_ulong(strnum3,id)] = 0; - log3("new msg ",strnum3,"\n"); - - for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; - - substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf)); - substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo)); - - uid = 0; - pid = 0; - - for (;;) - { - if (getln(&ss,&todoline,&match,'\0') == -1) - { - /* perhaps we're out of memory, perhaps an I/O error */ - fnmake_todo(id); - log3("warning: trouble reading ",fn.s,"\n"); goto fail; - } - if (!match) break; - - switch(todoline.s[0]) - { - case 'u': - scan_ulong(todoline.s + 1,&uid); - break; - case 'p': - scan_ulong(todoline.s + 1,&pid); - break; - case 'F': - if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1) - { - fnmake_info(id); - log3("warning: trouble writing to ",fn.s,"\n"); goto fail; + struct stat st; + substdio ss; + int fd; + substdio ssinfo; + int fdinfo; + substdio sschan[CHANNELS]; + int fdchan[CHANNELS]; + int flagchan[CHANNELS]; + struct prioq_elt pe; + char ch; + int match; + unsigned long id; + unsigned int len; + direntry *dent; + int c; + unsigned long uid; + unsigned long pid; + + fd = -1; + fdinfo = -1; + for (c = 0; c < CHANNELS; ++c) + fdchan[c] = -1; + + if (flagexitasap) + return; + + if (!tododir) { + if (!trigger_pulled(rfds)) + if (recent < nexttodorun) + return; + trigger_set(); + tododir = opendir("todo"); + if (!tododir) { + pausedir("todo"); + return; + } + nexttodorun = recent + SLEEP_TODO; + } + + dent = readdir(tododir); + if (!dent) { + closedir(tododir); + tododir = 0; + return; + } + if (str_equal(dent->d_name, ".")) + return; + if (str_equal(dent->d_name, "..")) + return; + len = scan_ulong(dent->d_name, &id); + if (!len || dent->d_name[len]) + return; + + fnmake_todo(id); + + fd = open_read(fn.s); + if (fd == -1) { + log3("warning: unable to open ", fn.s, "\n"); + return; + } + + fnmake_mess(id); + /* just for the statistics */ + if (stat(fn.s, &st) == -1) { + log3("warning: unable to stat ", fn.s, "\n"); + goto fail; + } + + for (c = 0; c < CHANNELS; ++c) { + fnmake_chanaddr(id, c); + if (unlink(fn.s) == -1) + if (errno != error_noent) { + log3("warning: unable to unlink ", fn.s, "\n"); + goto fail; + } + } + + fnmake_info(id); + if (unlink(fn.s) == -1) + if (errno != error_noent) { + log3("warning: unable to unlink ", fn.s, "\n"); + goto fail; + } + + fdinfo = open_excl(fn.s); + if (fdinfo == -1) { + log3("warning: unable to create ", fn.s, "\n"); + goto fail; + } + + strnum3[fmt_ulong(strnum3, id)] = 0; + log3("new msg ", strnum3, "\n"); + + for (c = 0; c < CHANNELS; ++c) + flagchan[c] = 0; + + substdio_fdbuf(&ss, read, fd, todobuf, sizeof(todobuf)); + substdio_fdbuf(&ssinfo, write, fdinfo, todobufinfo, sizeof(todobufinfo)); + + uid = 0; + pid = 0; + + for (;;) { + if (getln(&ss, &todoline, &match, '\0') == -1) { + /* perhaps we're out of memory, perhaps an I/O error */ + fnmake_todo(id); + log3("warning: trouble reading ", fn.s, "\n"); + goto fail; + } + if (!match) + break; + + switch (todoline.s[0]) { + case 'u': + scan_ulong(todoline.s + 1, &uid); + break; + case 'p': + scan_ulong(todoline.s + 1, &pid); + break; + case 'F': + if (substdio_putflush(&ssinfo, todoline.s, todoline.len) == -1) { + fnmake_info(id); + log3("warning: trouble writing to ", fn.s, "\n"); + goto fail; + } + qslog2("info msg ", strnum3); + strnum2[fmt_ulong(strnum2, (unsigned long)st.st_size)] = 0; + qslog2(": bytes ", strnum2); + log1(" from <"); + logsafe(todoline.s + 1); + strnum2[fmt_ulong(strnum2, pid)] = 0; + qslog2("> qp ", strnum2); + strnum2[fmt_ulong(strnum2, uid)] = 0; + qslog2(" uid ", strnum2); + log1("\n"); + break; + case 'T': + switch (c = rewrite(todoline.s + 1)) { + case -1: + if (errno == ENOMEM) nomem(); + goto fail; + default: + //c = 0; + break; + } + if (fdchan[c] == -1) { + fnmake_chanaddr(id, c); + fdchan[c] = open_excl(fn.s); + if (fdchan[c] == -1) { + log3("warning: unable to create ", fn.s, "\n"); + goto fail; + } + substdio_fdbuf(&sschan[c] + ,write, fdchan[c], todobufchan[c], sizeof(todobufchan[c])); + flagchan[c] = 1; + } + if (substdio_bput(&sschan[c], rwline.s, rwline.len) == -1) { + fnmake_chanaddr(id, c); + log3("warning: trouble writing to ", fn.s, "\n"); + goto fail; + } + break; + default: + fnmake_todo(id); + log3("warning: unknown record type in ", fn.s, "\n"); + goto fail; + } } - qslog2("info msg ",strnum3); - strnum2[fmt_ulong(strnum2,(unsigned long) st.st_size)] = 0; - qslog2(": bytes ",strnum2); - log1(" from <"); logsafe(todoline.s + 1); - strnum2[fmt_ulong(strnum2,pid)] = 0; - qslog2("> qp ",strnum2); - strnum2[fmt_ulong(strnum2,uid)] = 0; - qslog2(" uid ",strnum2); - log1("\n"); - break; - case 'T': - switch(rewrite(todoline.s + 1)) - { - case 0: nomem(); goto fail; - case 2: c = 1; break; - default: c = 0; break; - } - if (fdchan[c] == -1) - { - fnmake_chanaddr(id,c); - fdchan[c] = open_excl(fn.s); - if (fdchan[c] == -1) - { log3("warning: unable to create ",fn.s,"\n"); goto fail; } - substdio_fdbuf(&sschan[c] - ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c])); - flagchan[c] = 1; + + close(fd); + fd = -1; + + fnmake_info(id); + if (substdio_flush(&ssinfo) == -1) { + log3("warning: trouble writing to ", fn.s, "\n"); + goto fail; } - if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1) - { - fnmake_chanaddr(id,c); - log3("warning: trouble writing to ",fn.s,"\n"); goto fail; - } - break; - default: - fnmake_todo(id); - log3("warning: unknown record type in ",fn.s,"\n"); goto fail; - } - } - - close(fd); fd = -1; - - fnmake_info(id); - if (substdio_flush(&ssinfo) == -1) - { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; } - if (fsync(fdinfo) == -1) - { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; } - close(fdinfo); fdinfo = -1; - - for (c = 0;c < CHANNELS;++c) - if (fdchan[c] != -1) - { - fnmake_chanaddr(id,c); - if (substdio_flush(&sschan[c]) == -1) - { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; } - if (fsync(fdchan[c]) == -1) - { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; } - close(fdchan[c]); fdchan[c] = -1; - } - - fnmake_todo(id); - if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } - if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } - if (ch != '+') - { - log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); - return; - } - - pe.id = id; pe.dt = now(); - for (c = 0;c < CHANNELS;++c) - if (flagchan[c]) - while (!prioq_insert(&pqchan[c],&pe)) nomem(); - - for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; - if (c == CHANNELS) - while (!prioq_insert(&pqdone,&pe)) nomem(); - - return; - - fail: - if (fd != -1) close(fd); - if (fdinfo != -1) close(fdinfo); - for (c = 0;c < CHANNELS;++c) - if (fdchan[c] != -1) close(fdchan[c]); + if (fsync(fdinfo) == -1) { + log3("warning: trouble fsyncing ", fn.s, "\n"); + goto fail; + } + close(fdinfo); + fdinfo = -1; + + for (c = 0; c < CHANNELS; ++c) + if (fdchan[c] != -1) { + fnmake_chanaddr(id, c); + if (substdio_flush(&sschan[c]) == -1) { + log3("warning: trouble writing to ", fn.s, "\n"); + goto fail; + } + if (fsync(fdchan[c]) == -1) { + log3("warning: trouble fsyncing ", fn.s, "\n"); + goto fail; + } + close(fdchan[c]); + fdchan[c] = -1; + } + + fnmake_todo(id); + if (substdio_putflush(&sstoqc, fn.s, fn.len) == -1) { + cleandied(); + return; + } + if (substdio_get(&ssfromqc, &ch, 1) != 1) { + cleandied(); + return; + } + if (ch != '+') { + log3("warning: qmail-clean unable to clean up ", fn.s, "\n"); + return; + } + + pe.id = id; + pe.dt = now(); + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c], &pe)) + nomem(); + + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) + break; + if (c == CHANNELS) + while (!prioq_insert(&pqdone, &pe)) + nomem(); + + return; + +fail: + if (fd != -1) + close(fd); + if (fdinfo != -1) + close(fdinfo); + for (c = 0; c < CHANNELS; ++c) + if (fdchan[c] != -1) + close(fdchan[c]); } diff --git a/src/nmail-smtpd.c b/src/nmail-smtpd.c @@ -33,66 +33,170 @@ unsigned int databytes = 0; int timeout = 1200; -GEN_SAFE_TIMEOUTWRITE(safewrite,timeout,fd,_exit(10)) +GEN_SAFE_TIMEOUTWRITE(safewrite, timeout, fd, _exit(10)) char ssoutbuf[512]; -substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof(ssoutbuf)); +substdio ssout = SUBSTDIO_FDBUF(safewrite, 1, ssoutbuf, sizeof(ssoutbuf)); -void flush() { substdio_flush(&ssout); } -void out(s) char *s; { substdio_puts(&ssout,s); } +void flush() { + substdio_flush(&ssout); +} +void out(s) +char *s; { + substdio_puts(&ssout, s); +} -void die_read() { _exit(1); } -void die_alarm() { out("451 4.4.2 Timeout\r\n"); flush(); _exit(2); } -void die_nomem() { out("421 4.3.0 Out of memory\r\n"); flush(); _exit(3); } -void die_control() { out("421 4.3.0 Unable to read controls\r\n"); flush(); _exit(4); } -void die_ipme() { out("421 4.3.0 Unable to figure out my IP addresses\r\n"); flush(); _exit(5); } -void die_ip6me() { out("421 4.3.0 Unable to figure out my IPv6 addresses\r\n"); flush(); _exit(6); } -void straynewline() { out("451 You sent a stray newline. See https://cr.yp.to/docs/smtplf.html.\r\n"); flush(); _exit(1); } +void die_read() { + _exit(1); +} +void die_alarm() { + out("451 4.4.2 Timeout\r\n"); + flush(); + _exit(2); +} +void die_nomem() { + out("421 4.3.0 Out of memory\r\n"); + flush(); + _exit(3); +} +void die_control() { + out("421 4.3.0 Unable to read controls\r\n"); + flush(); + _exit(4); +} +void die_ipme() { + out("421 4.3.0 Unable to figure out my IP addresses\r\n"); + flush(); + _exit(5); +} +void die_ip6me() { + out("421 4.3.0 Unable to figure out my IPv6 addresses\r\n"); + flush(); + _exit(6); +} +void straynewline() { + out("451-You sent a stray newline. See https://cr.yp.to/docs/smtplf.html.\r\n451 This error should be going away soon."); + flush(); + _exit(1); +} char ssebuf[1024]; -substdio sse = SUBSTDIO_FDBUF(write,2,ssebuf,sizeof(ssebuf)); -void err(char *s) { substdio_putsflush(&sse,s); } - -void err_bmf() { out("553 5.7.1 Sorry, your envelope sender is in my badmailfrom list.\r\n"); } -void err_nogateway() { out("553 5.7.1 Sorry, that domain isn't in my list of allowed rcpthosts\r\n"); } -void err_unimpl(char *arg) { out("502 5.5.1 Unimplemented\r\n"); } -void err_syntax() { out("555 5.5.4 Syntax error\r\n"); } -void die_wtf(int d, char *arg) { out("421 4.3.2 inconsistent state: "); out(arg); out(" - shutting down!\r\n"); flush(); -err("421 4.3.2 inconsistent state: "); err(arg); err(" - shutting down!\r\n"); _exit(d); } -void err_wantmail() { out("503 5.5.1 You must execute MAIL first\r\n"); } -void err_wantrcpt() { out("503 5.5.1 You must execute RCPT first\r\n"); } -void err_notlshere() { out("454 4.7.6 STARTTLS is not available at this site.\r\n"); } -void err_alreadytlshere() { out("454 5.7.6 STARTTLS is already active on this connection.\r\n"); } -void die_cdb() { out("421 4.3.0 Unable to read cdb user database\r\n"); flush(); _exit(7); } -void die_sys() { out("421 4.3.0 Unable to read system user database\r\n"); flush(); _exit(8); } -void err_noop(char *arg) { out("250 As requested, no operation performed.\r\n"); } -void err_vrfy(char *arg) { out("252 Send something and we'll give it a shot.\r\n"); } -void err_qqt() { out("451 4.3.0 Temporarily unavailable: cannot inject message into queue.\r\n"); } -void die_dnsbl(char *arg) +substdio sse = SUBSTDIO_FDBUF(write, 2, ssebuf, sizeof(ssebuf)); +void err(char *s){ + substdio_putsflush(&sse, s); +} + +void err_bmf() { + out("553 5.7.1 Sorry, your envelope sender is in my badmailfrom list.\r\n"); + flush(); +} +void err_nogateway() { + out("553 5.7.1 Sorry, that domain isn't in my list of allowed rcpthosts\r\n"); + flush(); +} +void err_unimpl(char *arg){ + out("502 5.5.1 Unimplemented\r\n"); + flush(); +} +void err_syntax() { + out("555 5.5.4 Syntax error\r\n"); + flush(); +} +void die_wtf(int d, char *arg){ + out("421 4.3.2 inconsistent state: "); + out(arg); + out(" - shutting down!\r\n"); + flush(); + err("421 4.3.2 inconsistent state: "); + err(arg); + err(" - shutting down!\r\n"); + _exit(d); +} +void err_wantmail() { + out("503 5.5.1 You must execute MAIL first\r\n"); + flush(); +} +void err_wantrcpt() { + out("503 5.5.1 You must execute RCPT first\r\n"); + flush(); +} +void err_notlshere() { + out("454 4.7.6 STARTTLS is not available at this site.\r\n"); + flush(); +} +void err_alreadytlshere() { + out("454 5.7.6 STARTTLS is already active on this connection.\r\n"); + flush(); +} +void die_cdb() { + out("421 4.3.0 Unable to read cdb user database\r\n"); + flush(); + _exit(7); +} +void die_sys() { + out("421 4.3.0 Unable to read system user database\r\n"); + flush(); + _exit(8); +} +void die_addresses() { + out("501 5.3.0 That's too many addresses!\r\n"); + flush(); + _exit(11); +} +void err_addresses() { + out("501 5.3.0 That's too many addresses!\r\n"); + flush(); +} +void err_noop(char *arg){ + out("250 As requested, no operation performed.\r\n"); + flush(); +} +void err_vrfy(char *arg){ + out("252 Send something and we'll give it a shot.\r\n"); + flush(); +} +void err_qqt() { + out("451 4.3.0 Temporarily unavailable: cannot inject message into queue.\r\n"); + flush(); +} +void +die_dnsbl(char *arg) { - out("421 Your IP is currently blacklisted. If available at this site, auth first. ("); out(arg); out(")\r\n"); - flush(); - _exit(9); + out("421 Your IP is currently blacklisted. If available at this site, auth first. ("); + out(arg); + out(")\r\n"); + flush(); + _exit(9); } stralloc greeting = {0}; int starttlsready = 0, sslctlfd = -1, sslrfd = -1, sslwfd = -1, isstarttls = 0, isesmtp = 0; -void smtp_greet(code) char *code; +void +smtp_greet(code) + char *code; { - substdio_puts(&ssout,code); - substdio_put(&ssout,greeting.s,greeting.len); + substdio_puts(&ssout, code); + substdio_put(&ssout, greeting.s, greeting.len); } -void smtp_help(char *arg) +void +smtp_help(char *arg) { - out("214-NightmareMail home page: <https://umbrellix.net./software/nightmaremail/index.html>\r\n"); - out("214-NightmareMail is based on notqmail. notqmail home page: <https://notqmail.org>\r\n"); - out("214-We accept the following commands: HELO, EHLO, (SEND/SOML/SAML/MAIL) FROM:<, RCPT TO:<, DATA, RSET"); if (starttlsready) out(", STARTTLS"); out("\r\n"); - out("214 Note that SMTP is not a user interface. It should only be used thus to verify correct operation of the mail system.\r\n"); + out("214-NightmareMail home page: <https://umbrellix.net./software/nightmaremail/index.html>\r\n"); + out("214-NightmareMail is based on notqmail. notqmail home page: <https://notqmail.org>\r\n"); + out("214-We accept the following commands: HELO, EHLO, (SEND/SOML/SAML/MAIL) FROM:<, RCPT TO:<, DATA, RSET"); + if (starttlsready) + out(", STARTTLS"); + out("\r\n"); + out("214 Note that SMTP is not a user interface. It should only be used thus to verify correct operation of the mail system.\r\n"); } -void smtp_quit(char *arg) +void +smtp_quit(char *arg) { - smtp_greet("221 "); out("\r\n"); flush(); _exit(0); + smtp_greet("221 "); + out("\r\n"); + flush(); + _exit(0); } char *remoteip; @@ -103,12 +207,16 @@ char *relayclient; char *dnsblskip; stralloc helohost = {0}; -char *fakehelo; /* pointer into helohost, or 0 */ +char *fakehelo; /* pointer into helohost, or 0 */ -void dohelo(char *arg) { - if (!stralloc_copys(&helohost,arg)) die_nomem(); - if (!stralloc_0(&helohost)) die_nomem(); - fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; +void +dohelo(char *arg) +{ + if (!stralloc_copys(&helohost, arg)) + die_nomem(); + if (!stralloc_0(&helohost)) + die_nomem(); + fakehelo = case_diffs(remotehost, helohost.s) ? helohost.s : 0; } int liphostok = 0; @@ -117,64 +225,102 @@ int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; -void setuptls() +void +setuptls() { - char *x; - unsigned long u; - - x = env_get("SSLCTLFD"); - if (x) { scan_ulong(x,&u); sslctlfd = u; } else return; - x = env_get("SSLREADFD"); - if (x) { scan_ulong(x,&u); sslrfd = u; } else return; - x = env_get("SSLWRITEFD"); - if (x) { scan_ulong(x,&u); sslwfd = u; } else return; - ndelay_on(sslctlfd); - //ndelay_on(sslrfd); - //ndelay_on(sslwfd); - starttlsready = 1; // if reached, then we can do starttls - return; -} - -void setup() + char *x; + unsigned long u; + + x = env_get("SSLCTLFD"); + if (x) { + scan_ulong(x, &u); + sslctlfd = u; + } else + return; + x = env_get("SSLREADFD"); + if (x) { + scan_ulong(x, &u); + sslrfd = u; + } else + return; + x = env_get("SSLWRITEFD"); + if (x) { + scan_ulong(x, &u); + sslwfd = u; + } else + return; + ndelay_on(sslctlfd); + /* ndelay_on(sslrfd); */ + /* ndelay_on(sslwfd); */ + starttlsready = 1; /* when reached, then we can do starttls */ + return; +} + +int seenmail = 0; +int flagbarf; /* defined when seenmail */ +stralloc mailfrom = {0}; +stralloc rcptto = {0}; +int rcpttos = 0, maxrcpttos = 5; + +void +setup() { - char *x; - unsigned long u; - - if (control_init() == -1) die_control(); - if (control_rldef(&greeting,"control/smtpgreeting",1,NULL) != 1) - die_control(); - liphostok = control_rldef(&liphost,"control/localiphost",1,NULL); - if (liphostok == -1) die_control(); - if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); - if (timeout <= 0) timeout = 1; - - if (rcpthosts_init() == -1) die_control(); - - bmfok = control_readfile(&bmf,"control/badmailfrom",0); - if (bmfok == -1) die_control(); - if (bmfok) - if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); - - // manually merged from prj's realrcptto patch - realrcptto_init(); - - if (control_readint(&databytes,"control/databytes") == -1) die_control(); - x = env_get("DATABYTES"); - if (x) { scan_ulong(x,&u); databytes = u; } - if (!(databytes + 1)) --databytes; - setuptls(); - - remoteip = env_get("TCPREMOTEIP"); - if (!remoteip) remoteip = "unknown"; - local = env_get("TCPLOCALHOST"); - if (!local) local = env_get("TCPLOCALIP"); - if (!local) local = "unknown"; - remotehost = env_get("TCPREMOTEHOST"); - if (!remotehost) remotehost = "unknown"; - remoteinfo = env_get("TCPREMOTEINFO"); - relayclient = env_get("RELAYCLIENT"); - dnsblskip = env_get("DNSBLSKIP"); - dohelo(remotehost); + char *x; + unsigned long u; + + if (control_init() == -1) + die_control(); + if (control_rldef(&greeting, "control/smtpgreeting", 1, NULL) != 1) + die_control(); + liphostok = control_rldef(&liphost, "control/localiphost", 1, NULL); + if (liphostok == -1) + die_control(); + if (control_readint(&timeout, "control/timeoutsmtpd") == -1) + die_control(); + if (timeout <= 0) + timeout = 1; + + if (rcpthosts_init() == -1) + die_control(); + + bmfok = control_readfile(&bmf, "control/badmailfrom", 0); + if (bmfok == -1) + die_control(); + if (bmfok) + if (!constmap_init(&mapbmf, bmf.s, bmf.len, 0)) + die_nomem(); + + /* manually merged from prj's realrcptto patch */ + realrcptto_init(); + + if (control_readint(&databytes, "control/databytes") == -1) + die_control(); + if (control_readint(&maxrcpttos, "control/maxrcpts") == -1) + maxrcpttos = 128; /* proceed with this limit */ + x = env_get("DATABYTES"); + if (x) { + scan_ulong(x, &u); + databytes = u; + } + if (!(databytes + 1)) + --databytes; + setuptls(); + + remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) + remoteip = "unknown"; + local = env_get("TCPLOCALHOST"); + if (!local) + local = env_get("TCPLOCALIP"); + if (!local) + local = "unknown"; + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) + remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); + dnsblskip = env_get("DNSBLSKIP"); + dohelo(remotehost); } extern void realrcptto_init(); @@ -182,405 +328,590 @@ extern void realrcptto_start(); extern int realrcptto(); extern int realrcptto_deny(); -stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ +stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ -int addrparse(char *arg) +int +addrparse(char *arg) { - int i; - char ch; - char terminator; - struct ip_address ip; - int flagesc; - int flagquoted; - - terminator = '>'; - i = str_chr(arg,'<'); - if (arg[i]) - arg += i + 1; - else { /* partner should go read rfc 821 */ - terminator = ' '; - arg += str_chr(arg,':'); - if (*arg == ':') ++arg; - while (*arg == ' ') ++arg; - } - - /* strip source route */ - if (*arg == '@') while (*arg) if (*arg++ == ':') break; - - if (!stralloc_copys(&addr,"")) die_nomem(); - flagesc = 0; - flagquoted = 0; - for (i = 0;(ch = arg[i]);++i) { /* copy arg to addr, stripping quotes */ - if (flagesc) { - if (!stralloc_append(&addr,&ch)) die_nomem(); - flagesc = 0; - } - else { - if (!flagquoted && (ch == terminator)) break; - switch(ch) { - case '\\': flagesc = 1; break; - case '"': flagquoted = !flagquoted; break; - default: if (!stralloc_append(&addr,&ch)) die_nomem(); - } - } - } - /* could check for termination failure here, but why bother? */ - if (!stralloc_append(&addr,"")) die_nomem(); - - if (liphostok) { - i = byte_rchr(addr.s,addr.len,'@'); - if (i < addr.len) /* if not, partner should go read rfc 821 */ - if (addr.s[i + 1] == '[') - if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)]) - if (ipme_is(&ip)) { - addr.len = i + 1; - if (!stralloc_cat(&addr,&liphost)) die_nomem(); - if (!stralloc_0(&addr)) die_nomem(); - } - } - - if (addr.len > 900) return 0; - return 1; + int i; + char ch; + char terminator; + struct ip_address ip; + int flagesc; + int flagquoted; + + terminator = '>'; + i = str_chr(arg, '<'); + if (arg[i]) + arg += i + 1; + else { /* partner should go read rfc 821 */ + terminator = ' '; + arg += str_chr(arg, ':'); + if (*arg == ':') + ++arg; + while (*arg == ' ') + ++arg; + } + + /* strip source route */ + if (*arg == '@') + while (*arg) + if (*arg++ == ':') + break; + + if (!stralloc_copys(&addr, "")) + die_nomem(); + flagesc = 0; + flagquoted = 0; + for (i = 0; (ch = arg[i]); ++i) { /* copy arg to addr, stripping quotes */ + if (flagesc) { + if (!stralloc_append(&addr, &ch)) + die_nomem(); + flagesc = 0; + } else { + if (!flagquoted && (ch == terminator)) + break; + switch (ch) { + case '\\': + flagesc = 1; + break; + case '"': + flagquoted = !flagquoted; + break; + default: + if (!stralloc_append(&addr, &ch)) + die_nomem(); + } + } + } + /* could check for termination failure here, but why bother? */ + if (!stralloc_append(&addr, "")) + die_nomem(); + + if (liphostok) { + i = byte_rchr(addr.s, addr.len, '@'); + if (i < addr.len) /* if not, partner should go read rfc 821 */ + if (addr.s[i + 1] == '[') + if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1, &ip)]) + if (ipme_is(&ip)) { + addr.len = i + 1; + if (!stralloc_cat(&addr, &liphost)) + die_nomem(); + if (!stralloc_0(&addr)) + die_nomem(); + } + } + + if (addr.len > 900) + return 0; + return 1; } -int bmfcheck() +int +bmfcheck() { - int j; - if (!bmfok) return 0; - if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; - j = byte_rchr(addr.s,addr.len,'@'); - if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; - return 0; + int j; + if (!bmfok) + return 0; + if (constmap(&mapbmf, addr.s, addr.len - 1)) + return 1; + j = byte_rchr(addr.s, addr.len, '@'); + if (j < addr.len) + if (constmap(&mapbmf, addr.s + j, addr.len - j - 1)) + return 1; + return 0; } -int addrallowed() +int +addrallowed() { - int r; - r = rcpthosts(addr.s,str_len(addr.s)); - if (r == -1) die_control(); - return r; + int r; + r = rcpthosts(addr.s, str_len(addr.s)); + if (r == -1) + die_control(); + return r; } int flagdnsbl = 0; stralloc dnsblhost = {0}; -int dnsblcheck() +int +dnsblcheck() { - char *ch; - static stralloc dnsblbyte = {0}; - static stralloc dnsblrev = {0}; - static ipalloc dnsblip = {0}; - static ip6alloc dnsblip6 = {0}; - static stralloc dnsbllist = {0}; - - ch = remoteip; - if(control_readfile(&dnsbllist,"control/dnsbllist",0) != 1) return 0; - - if (!stralloc_copys(&dnsblrev,"")) return 0; - for (;;) { - if (!stralloc_copys(&dnsblbyte,"")) return 0; - while (ch[0] && (ch[0] != '.')) { - if (!stralloc_append(&dnsblbyte,ch)) return 0; - ch++; - } - if (!stralloc_append(&dnsblbyte,".")) return 0; - if (!stralloc_cat(&dnsblbyte,&dnsblrev)) return 0; - if (!stralloc_copy(&dnsblrev,&dnsblbyte)) return 0; - - if (!ch[0]) break; - ch++; - } - - flagdnsbl = 1; - ch = dnsbllist.s; - while (ch < (dnsbllist.s + dnsbllist.len)) { - if (!stralloc_copy(&dnsblhost,&dnsblrev)) return 0; - if (!stralloc_cats(&dnsblhost,ch)) return 0; - if (!stralloc_0(&dnsblhost)) return 0; - - if (!dns_ip(&dnsblip,&dnsblhost)) return 1; - while (*ch++); - } - - return 0; + char *ch; + static stralloc dnsblbyte = { + 0 + }; + static stralloc dnsblrev = { + 0 + }; + static ipalloc dnsblip = { + 0 + }; + static ip6alloc dnsblip6 = { + 0 + }; + static stralloc dnsbllist = { + 0 + }; + + ch = remoteip; + if (control_readfile(&dnsbllist, "control/dnsbllist", 0) != 1) + return 0; + + if (!stralloc_copys(&dnsblrev, "")) + return 0; + for (;;) { + if (!stralloc_copys(&dnsblbyte, "")) + return 0; + while (ch[0] && (ch[0] != '.')) { + if (!stralloc_append(&dnsblbyte, ch)) + return 0; + ch++; + } + if (!stralloc_append(&dnsblbyte, ".")) + return 0; + if (!stralloc_cat(&dnsblbyte, &dnsblrev)) + return 0; + if (!stralloc_copy(&dnsblrev, &dnsblbyte)) + return 0; + + if (!ch[0]) + break; + ch++; + } + + flagdnsbl = 1; + ch = dnsbllist.s; + while (ch < (dnsbllist.s + dnsbllist.len)) { + if (!stralloc_copy(&dnsblhost, &dnsblrev)) + return 0; + if (!stralloc_cats(&dnsblhost, ch)) + return 0; + if (!stralloc_0(&dnsblhost)) + return 0; + + if (!dns_ip(&dnsblip, &dnsblhost)) + return 1; + while (*ch++); + } + + return 0; } -int seenmail = 0; -int flagbarf; /* defined if seenmail */ -stralloc mailfrom = {0}; -stralloc rcptto = {0}; - -ssize_t saferead(int fd, void *buf, size_t len) +ssize_t +saferead(int fd, void *buf, size_t len) { - ssize_t r; - r = timeoutread(timeout,fd,buf,len); - if (r == -1) if (errno == error_timeout) die_alarm(); - if (r == 0 || r == -1) die_read(); - return r; + ssize_t r; + r = timeoutread(timeout, fd, buf, len); + if (r == -1) + if (errno == error_timeout) + die_alarm(); + if (r == 0 || r == -1) + die_read(); + return r; } char ssinbuf[1024]; -substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof(ssinbuf)); +substdio ssin = SUBSTDIO_FDBUF(saferead, 0, ssinbuf, sizeof(ssinbuf)); -void smtp_starttls(char *arg) +void +smtp_starttls(char *arg) { - char fdou[1920]; // compare skarnet s-s-proxy: OUTSIZE 1920. Me thinks I cribbed too much. - ssize_t hsread = 0, hsr = 0; - if (str_len(arg) != 0) { err_syntax(); return; } - if (!starttlsready) { err_notlshere(); return; } - if (timeoutwrite(1, sslctlfd, "Y", 1) != 1) { die_wtf(errno, "could not cut over to TLS in one second"); } - out("220 2.7.0 Ready to start TLS\r\n"); - substdio_flush(&ssout); - for (;;) { - hsread += (hsr = timeoutread(1, sslctlfd, fdou, 1920)); - if (hsr < 0) { if (errno != EAGAIN) die_wtf(errno, "could not cut over to TLS in 1 second"); } - if (!hsr) break; - isstarttls = 1; // just use the global variable (TODO: make TL when npthread) - } - if (!isstarttls) { _exit(10); } - // At this point, we are starttls. We must now cut sslwfd to fd 1 and sslrfd to fd 0. - ssout.fd = sslwfd; - ssin.fd = sslrfd; - substdio_flush(&ssout); - substdio_flush(&sse); -} -void smtp_helo(char *arg) + char fdou[1920]; /* compare skarnet s-s-proxy: OUTSIZE 1920. Me thinks I cribbed too much. */ + ssize_t hsread = 0, hsr = 0; + if (str_len(arg) != 0) { + err_syntax(); + return; + } + if (!starttlsready) { + err_notlshere(); + return; + } + if (timeoutwrite(1, sslctlfd, "Y", 1) != 1) { + die_wtf(errno, "could not cut over to TLS in one second"); + } + out("220 2.7.0 Ready to start TLS\r\n"); + substdio_flush(&ssout); + for (;;) { + hsread += (hsr = timeoutread(1, sslctlfd, fdou, 1920)); + if (hsr < 0) { + if (errno != EAGAIN) + die_wtf(errno, "could not cut over to TLS in 1 second"); + } + if (!hsr) + break; + isstarttls = 1; /* just use the global variable (TODO: make TL when npthread) */ + } + if (!isstarttls) { + _exit(10); + } + /* At this point, we are starttls. We must now cut sslwfd to fd 1 and sslrfd to fd 0. */ + ssout.fd = sslwfd; + ssin.fd = sslrfd; + substdio_flush(&ssout); + substdio_flush(&sse); +} +void +smtp_helo(char *arg) +{ + smtp_greet("250 "); + out("\r\n"); + seenmail = 0; + dohelo(arg); +} +void +smtp_ehlo(char *arg) { - smtp_greet("250 "); out("\r\n"); - seenmail = 0; dohelo(arg); + isesmtp = 1; + smtp_greet("250-"); + out("\r\n"); + out("250-PIPELINING\r\n"); + out("250-ENHANCEDSTATUSCODES\r\n"); + /* TODO: add startTLS cutover support */ + if (starttlsready && !isstarttls) { + out("250-8BITMIME\r\n"); + out("250 STARTTLS\r\n"); + } else + out("250 8BITMIME\r\n"); + seenmail = 0; + dohelo(arg); } -void smtp_ehlo(char *arg) +void +smtp_rset(char *arg) { - isesmtp = 1; - smtp_greet("250-"); out("\r\n"); - out("250-PIPELINING\r\n"); - out("250-ENHANCEDSTATUSCODES\r\n"); - // TODO: add startTLS cutover support - if (starttlsready && !isstarttls) { - out("250-8BITMIME\r\n"); - out("250 STARTTLS\r\n"); - } else out("250 8BITMIME\r\n"); - seenmail = 0; dohelo(arg); -} -void smtp_rset(char *arg) + seenmail = 0; + out("250 Envelope flushed\r\n"); +} +void +smtp_mail(char *arg) { - seenmail = 0; - out("250 Envelope flushed\r\n"); + if (!addrparse(arg)) { + err_syntax(); + return; + } + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto, "")) + die_nomem(); + if (!stralloc_copys(&mailfrom, addr.s)) + die_nomem(); + if (!stralloc_0(&mailfrom)) + die_nomem(); + realrcptto_start(); + out("250 Go on...\r\n"); } -void smtp_mail(char *arg) +void +smtp_rcpt(char *arg) { - if (!addrparse(arg)) { err_syntax(); return; } - flagbarf = bmfcheck(); - seenmail = 1; - if (!stralloc_copys(&rcptto,"")) die_nomem(); - if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); - if (!stralloc_0(&mailfrom)) die_nomem(); - realrcptto_start(); - out("250 Go on...\r\n"); -} -void smtp_rcpt(char *arg) { - if (!seenmail) { err_wantmail(); return; } - if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } - if (relayclient) { - --addr.len; - if (!stralloc_cats(&addr,relayclient)) die_nomem(); - if (!stralloc_0(&addr)) die_nomem(); - } - else - if (!addrallowed()) { err_nogateway(); return; } - if (!realrcptto(addr.s)) { - out("550 5.1.1 That user has elected not to receive emails.\r\n"); - return; - } - if (!(relayclient || dnsblskip || flagdnsbl)) - if (dnsblcheck()) die_dnsbl(dnsblhost.s); - if (!stralloc_cats(&rcptto,"T")) die_nomem(); - if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); - if (!stralloc_0(&rcptto)) die_nomem(); - out("250 Go on...\r\n"); + if (!seenmail) { + err_wantmail(); + return; + } + if (!addrparse(arg)) { + err_syntax(); + return; + } + if (flagbarf) { + err_bmf(); + return; + } + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr, relayclient)) + die_nomem(); + if (!stralloc_0(&addr)) + die_nomem(); + } else if (!addrallowed()) { + err_nogateway(); + return; + } + if (rcpttos > maxrcpttos) { + err_addresses(); + return; + } + if (!realrcptto(addr.s)) { + out("550 5.1.1 That user has elected not to receive emails.\r\n"); + return; + } + if (!(relayclient || dnsblskip || flagdnsbl)) + if (dnsblcheck()) + die_dnsbl(dnsblhost.s); + if (!stralloc_cats(&rcptto, "T")) + die_nomem(); + if (!stralloc_cats(&rcptto, addr.s)) + die_nomem(); + if (!stralloc_0(&rcptto)) + die_nomem(); + ++rcpttos; + out("250 Go on...\r\n"); } struct qmail qqt; unsigned int bytestooverflow = 0; -void put(ch) -char *ch; +void +put(char *ch) { - if (bytestooverflow) - if (!--bytestooverflow) - qmail_fail(&qqt); - qmail_put(&qqt,ch,1); + if (bytestooverflow) + if (!--bytestooverflow) + qmail_fail(&qqt); + qmail_put(&qqt, ch, 1); } -void blast(hops) -int *hops; +void +blast(int *hops) { - char ch; - int state; - int flaginheader; - int pos; /* number of bytes since most recent \n, if fih */ - int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ - int flagmaybey; /* 1 if this line might match \r\n, if fih */ - int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ - - state = 1; - *hops = 0; - flaginheader = 1; - pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; - for (;;) { - /* This isn't very optimized -- Amelia B */ - substdio_get(&ssin,&ch,1); - if (flaginheader) { - if (pos < 9) { // something about method and madness? - if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; - if (flagmaybez) if (pos == 8) ++*hops; - if (pos < 8) - if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0; - if (flagmaybex) if (pos == 7) ++*hops; - if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; - if (flagmaybey) if (pos == 1) flaginheader = 0; - ++pos; - } - if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } - } - switch(state) { - case 0: - if (ch == '\n') straynewline(); - if (ch == '\r') { state = 4; continue; } - break; - case 1: /* \r\n */ - if (ch == '\n') straynewline(); - if (ch == '.') { state = 2; continue; } - if (ch == '\r') { state = 4; continue; } - state = 0; - break; - case 2: /* \r\n + . */ - if (ch == '\n') straynewline(); - if (ch == '\r') { state = 3; continue; } - state = 0; - break; - case 3: /* \r\n + .\r */ - if (ch == '\n') return; - put("."); - put("\r"); - if (ch == '\r') { state = 4; continue; } - state = 0; - break; - case 4: /* + \r */ - if (ch == '\n') { state = 1; break; } - if (ch != '\r') { put("\r"); state = 0; } - } - put(&ch); - } + char ch; + int state; + int flaginheader; + int pos; /* number of bytes since most recent \n, if fih */ + int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ + int flagmaybey; /* 1 if this line might match \r\n, if fih */ + int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ + + state = 1; + *hops = 0; + flaginheader = 1; + pos = 0; + flagmaybex = flagmaybey = flagmaybez = 1; + for (;;) { + /* This isn't very optimized -- Amelia B */ + substdio_get(&ssin, &ch, 1); + if (flaginheader) { + if (pos >= 0 && pos < 9) { /* something about method and madness? bro literally just use strcmp it won't hurt you */ + if (ch != "delivered"[pos]) + if (ch != "DELIVERED"[pos]) + flagmaybez = 0; + if (flagmaybez) + if (pos == 8) + ++* hops; + if (pos < 8) + if (ch != "received"[pos]) + if (ch != "RECEIVED"[pos]) + flagmaybex = 0; + if (flagmaybex) + if (pos == 7) + ++* hops; + if (pos < 2) + if (ch != "\r\n"[pos]) + flagmaybey = 0; + if (flagmaybey) + if (pos == 1) + flaginheader = 0; + ++pos; + } + if (ch == '\n') { + pos = 0; + flagmaybex = flagmaybey = flagmaybez = 1; + } + } + switch (state) { + case 0: + if (ch == '\n') + straynewline(); + if (ch == '\r') { + state = 4; + continue; + } + break; + case 1: /* \r\n */ + if (ch == '\n') + straynewline(); + if (ch == '.') { + state = 2; + continue; + } + if (ch == '\r') { + state = 4; + continue; + } + state = 0; + break; + case 2: /* \r\n + . */ + if (ch == '\n') + straynewline(); + if (ch == '\r') { + state = 3; + continue; + } + state = 0; + break; + case 3: /* \r\n + .\r */ + if (ch == '\n') + return; + put("."); + put("\r"); + if (ch == '\r') { + state = 4; + continue; + } + state = 0; + break; + case 4: /* + \r */ + if (ch == '\n') { + state = 1; + break; + } + if (ch != '\r') { + put("\r"); + state = 0; + } + } + put(&ch); + } } char accept_buf[FMT_ULONG]; -void acceptmessage(qp) unsigned long qp; +void +acceptmessage(qp) + unsigned long qp; { - datetime_sec when; - when = now(); - out("250 Accepted responsibility at time "); - accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; - out(accept_buf); - out(" queue process "); - accept_buf[fmt_ulong(accept_buf,qp)] = 0; - out(accept_buf); - out(" - we'll do our best!\r\n"); - flush(); + datetime_sec when; + when = now(); + out("250 Accepted responsibility at time "); + accept_buf[fmt_ulong(accept_buf, (unsigned long)when)] = 0; + out(accept_buf); + out(" queue process "); + accept_buf[fmt_ulong(accept_buf, qp)] = 0; + out(accept_buf); + out(" - we'll do our best!\r\n"); + flush(); } /* unsigned long alen, plen, len; if (databytes && ((alen = str_len(arg)) != 0)) { - // We now accept the optional argument to DATA which is a ulong which must < databytes + 1. + / We now accept the optional argument to DATA which is a ulong which must < databytes + 1. plen = scan_ulong(arg, &len); if (alen != plen) { err_syntax(); return; } // crap at the end of the argument if (len > databytes) { out("552 5.3.4 This message is too big for me (rapid rejection).\r\n"); return; } } */ -void smtp_data(char *arg) { - int hops; - unsigned long qp; - char *qqx; - char *esmtps; - - if (!seenmail) { err_wantmail(); return; } - if (!rcptto.len) { err_wantrcpt(); return; } - if (realrcptto_deny()) { out("554 5.1.1 That user has elected not to receive emails.\r\n"); return; } - seenmail = 0; - if (databytes) bytestooverflow = databytes + 1; - if (qmail_open(&qqt) == -1) { err_qqt(); return; } - qp = qmail_qp(&qqt); - out("354 go ahead\r\n"); - substdio_flush(&ssout); - - switch ((isesmtp & 1)+((isstarttls & 1) << 1)+((relayclient != NULL) << 2)) { - case 7: esmtps = "ESMTPSA"; - break; - case 6: esmtps = "SMTPSA"; // Compliant clients should never do this, but it's not pathological. - break; - case 5: esmtps = "ESMTPA"; - break; - case 4: esmtps = "SMTPA"; // Clients, at all, will never do this, as we don't process SMTP AUTH. - break; - case 3: esmtps = "ESMTPS"; - break; - case 2: esmtps = "SMTPS"; // Compliant clients should never do this, but it's not pathological. - break; - case 1: esmtps = "ESMTP"; - break; - default: esmtps = "SMTP"; - } - received(&qqt,esmtps,local,remoteip,remotehost,remoteinfo,fakehelo); - blast(&hops); - hops = (hops >= MAXHOPS); - if (hops) qmail_fail(&qqt); - qmail_from(&qqt,mailfrom.s); - qmail_put(&qqt,rcptto.s,rcptto.len); - - qqx = qmail_close(&qqt); - if (!*qqx) { acceptmessage(qp); return; } - if (hops) { out("554 5.4.6 This message has gone through too many SMTP, QMTP or other mail protocol servers; it may be looping. It ends with me.\r\n"); return; } - if (databytes) if (!bytestooverflow) { out("552 5.3.4 This message is too big for me.\r\n"); return; } - if (*qqx == 'D') out("554 "); else out("451 "); - out(qqx + 1); - out("\r\n"); +void +smtp_data(char *arg) +{ + int hops; + unsigned long qp; + char *qqx; + char *esmtps; + + if (!seenmail) { + err_wantmail(); + return; + } + if (!rcptto.len) { + err_wantrcpt(); + return; + } + if (realrcptto_deny()) { + out("554 5.1.1 That user has elected not to receive emails.\r\n"); + return; + } + seenmail = 0; + if (databytes) + bytestooverflow = databytes + 1; + if (qmail_open(&qqt) == -1) { + err_qqt(); + return; + } + qp = qmail_qp(&qqt); + out("354 go ahead\r\n"); + substdio_flush(&ssout); + + switch ((isesmtp & 1) + ((isstarttls & 1) << 1) + ((relayclient != NULL) << 2)) { + case 7: + esmtps = "ESMTPSA"; + break; + case 6: + esmtps = "SMTPSA"; /* Compliant clients should never do this, but it's not pathological. */ + break; + case 5: + esmtps = "ESMTPA"; + break; + case 4: + esmtps = "SMTPA"; /* Clients, at all, will never do this, as we don't process SMTP AUTH. */ + break; + case 3: + esmtps = "ESMTPS"; + break; + case 2: + esmtps = "SMTPS"; /* Compliant clients should never do this, but it's not pathological. */ + break; + case 1: + esmtps = "ESMTP"; + break; + default: + esmtps = "SMTP"; + } + received(&qqt, esmtps, local, remoteip, remotehost, remoteinfo, fakehelo); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) + qmail_fail(&qqt); + qmail_from(&qqt, mailfrom.s); + qmail_put(&qqt, rcptto.s, rcptto.len); + + qqx = qmail_close(&qqt); + if (!*qqx) { + acceptmessage(qp); + return; + } + if (hops) { + out("554 5.4.6 This message has gone through too many SMTP, QMTP or other mail protocol servers; it may be looping. It ends with me.\r\n"); + return; + } + if (databytes) + if (!bytestooverflow) { + out("552 5.3.4 This message is too big for me.\r\n"); + return; + } + if (*qqx == 'D') + out("554 "); + else + out("451 "); + out(qqx + 1); + out("\r\n"); } struct commands smtpcommands[] = { - { "rcpt", smtp_rcpt, flush } -, { "mail", smtp_mail, flush } -, { "send", smtp_mail, flush } // not recommended -, { "soml", smtp_mail, flush } // not recommended -, { "saml", smtp_mail, flush } // not recommended -, { "data", smtp_data, flush } -, { "quit", smtp_quit, flush } -, { "helo", smtp_helo, flush } -, { "ehlo", smtp_ehlo, flush } -, { "rset", smtp_rset, flush } -, { "help", smtp_help, flush } -, { "starttls", smtp_starttls, flush } -, { "noop", err_noop, flush } -, { "vrfy", err_vrfy, flush } -, { 0, err_unimpl, flush } -} ; - -int main(void) + {"rcpt", smtp_rcpt, flush} + ,{"mail", smtp_mail, flush} + ,{"send", smtp_mail, flush} /* not recommended */ + ,{"soml", smtp_mail, flush} /* not recommended */ + ,{"saml", smtp_mail, flush} /* not recommended */ + ,{"data", smtp_data, flush} + ,{"quit", smtp_quit, flush} + ,{"helo", smtp_helo, flush} + ,{"ehlo", smtp_ehlo, flush} + ,{"rset", smtp_rset, flush} + ,{"help", smtp_help, flush} + ,{"starttls", smtp_starttls, flush} + ,{"noop", err_noop, flush} + ,{"vrfy", err_vrfy, flush} + ,{0, err_unimpl, flush} +}; + +int +main(void) { - //ndelay_on(0); - //ndelay_on(1); - sig_pipeignore(); - if (chdir(auto_qmail) == -1) die_control(); - setup(); - if (ipme_init() != 1) die_ipme(); - if (ip6me_init() != 1) die_ip6me(); - smtp_greet("220 "); - out(" ESMTP\r\n"); - substdio_flush(&ssout); - if (commands(&ssin,&smtpcommands) == 0) die_read(); - die_nomem(); + /* ndelay_on(0); */ + /* ndelay_on(1); */ + maxrcpttos = 5; // will be overwritten + sig_pipeignore(); + if (chdir(auto_qmail) == -1) + die_control(); + setup(); + if (ipme_init() != 1) + die_ipme(); + if (ip6me_init() != 1) + die_ip6me(); + smtp_greet("220 "); + out(" ESMTP\r\n"); + substdio_flush(&ssout); + if (commands(&ssin, &smtpcommands) == 0) + die_read(); + die_nomem(); } diff --git a/src/qmail-clean.c b/src/qmail-clean.c @@ -69,14 +69,14 @@ int main(void) for (;;) { if (cleanuploop) --cleanuploop; else { cleanuppid(); cleanuploop = 30; } - if (getln(subfdinsmall,&line,&match,'\0') == -1) break; + if (getln(subfdinsmall,&line,&match,'\0') == -1) break; //blocking read stdin because we do nowt else if (!match) break; - if (line.len < 7) { respond("x"); continue; } - if (line.len > 100) { respond("x"); continue; } + if (line.len < 7) { respond("x"); continue; } // must be at least 7 chars + if (line.len > 100) { respond("x"); continue; } // must be under 101 chars if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */ for (i = 5;i < line.len - 1;++i) if ((unsigned char) (line.s[i] - '0') > 9) - { respond("x"); continue; } + { respond("x"); continue; } // NaN if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; } if (byte_equal(line.s,5,"foop/")) { diff --git a/src/qmail-local.c b/src/qmail-local.c @@ -43,7 +43,7 @@ void _noreturn_ temp_slowlock() void _noreturn_ temp_qmail(char *fn) { strerr_die5x(111,"4.3.0 Unable to open ",fn,": ",error_str(errno)," ."); } -int flagdoit = 0; +int flagdoit = 0, flagprobe = 0; int flag99; char *user; diff --git a/src/qmail-queue.c b/src/qmail-queue.c @@ -203,7 +203,7 @@ int main(void) die(64); } if (unlink(pidfn) == -1) die(63); - flagmademess = 1; + flagmademess = 1; // created pid file, linked it to mesg file, unlinked pid file. substdio_fdbuf(&ssout,write,messfd,outbuf,sizeof(outbuf)); substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf)); @@ -233,6 +233,8 @@ int main(void) if (substdio_bput(&ssout,"p",1) == -1) die_write(); if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,mypid)) == -1) die_write(); if (substdio_bput(&ssout,"",1) == -1) die_write(); + // it's not immediately clear that fmt_ulong actually runs first out of + // each invokation of bput containing it if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (ch != 'F') die(91); @@ -267,6 +269,6 @@ int main(void) if (link(intdfn,todofn) == -1) die(66); - triggerpull(); + triggerpull(); // xxx should be a triggerpull on the appropriate queue. return 0; } diff --git a/src/qmail-remote.c b/src/qmail-remote.c @@ -35,381 +35,554 @@ #define HUGESMTPTEXT 5000 -#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ +#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ unsigned long port = PORT_SMTP; -GEN_ALLOC_typedef(saa,stralloc,sa,len,a) -GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,10,saa_readyplus) -static stralloc sauninit = {0}; +GEN_ALLOC_typedef(saa, stralloc, sa, len, a) +GEN_ALLOC_readyplus(saa, stralloc, sa, len, a, 10, saa_readyplus) +static stralloc sauninit = STRALLOC_ZERO; stralloc helohost = {0}; stralloc routes = {0}; struct constmap maproutes; stralloc host = {0}; stralloc sender = {0}; +int pedanticsmtp = 1; saa reciplist = {0}; struct ip_address partner; -void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } -void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } -void _noreturn_ zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } -void outsafe(sa) stralloc *sa; { int i; char ch; -for (i = 0;i < sa->len;++i) { -ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?'; -if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } } - -void _noreturn_ temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); } -void _noreturn_ temp_oserr() { out("Z\ -System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); } -void _noreturn_ temp_noconn() { out("Z\ -Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); } -void _noreturn_ temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); } -void _noreturn_ temp_dnscanon() { out("Z\ -CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); } -void _noreturn_ temp_dns() { out("Z\ -Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); } -void _noreturn_ temp_chdir() { out("Z\ -Unable to switch to home directory. (#4.3.0)\n"); zerodie(); } -void _noreturn_ temp_control() { out("Z\ -Unable to read control files. (#4.3.0)\n"); zerodie(); } -void _noreturn_ perm_partialline() { out("D\ -SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); } -void _noreturn_ perm_usage() { out("D\ -I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); } -void _noreturn_ perm_dns() { out("D\ -Sorry, I couldn't find any host named "); -outsafe(&host); -out(". (#5.1.2)\n"); zerodie(); } -void _noreturn_ perm_nomx() { out("D\ -Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n"); -zerodie(); } -void _noreturn_ perm_ambigmx() { out("D\ +void out(s) +char *s; { + if (substdio_puts(subfdoutsmall, s) == -1) + _exit(0); +} +void zero() { + if (substdio_put(subfdoutsmall, "\0", 1) == -1) + _exit(0); +} +void _noreturn_ zerodie() { + zero(); + substdio_flush(subfdoutsmall); + _exit(0); +} +void +outsafe(sa) stralloc * sa; +{ + int i; + char ch; + for (i = 0; i < sa->len; ++i) { + ch = sa->s[i]; + if (ch < 33) + ch = '?'; + if (ch > 126) + ch = '?'; + if (substdio_put(subfdoutsmall, &ch, 1) == -1) + _exit(0); + } +} + +void _noreturn_ temp_nomem() { + out("ZOut of memory. (#4.3.0)\n"); + zerodie(); +} +void _noreturn_ +temp_oserr() +{ + out("ZSystem resources temporarily unavailable. (#4.3.0)\n"); + zerodie(); +} +void _noreturn_ +temp_noconn() +{ + out("ZSorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); + zerodie(); +} +void _noreturn_ temp_read() { + out("ZUnable to read message. (#4.3.0)\n"); + zerodie(); +} +void _noreturn_ +temp_dnscanon() +{ + out("ZCNAME lookup failed temporarily. (#4.4.3)\n"); + zerodie(); +} +void _noreturn_ +temp_dns() +{ + out("ZSorry, I couldn't find any host by that name. (#4.1.2)\n"); + zerodie(); +} +void _noreturn_ +temp_chdir() +{ + out("ZUnable to switch to home directory. (#4.3.0)\n"); + zerodie(); +} +void _noreturn_ +temp_control() +{ + out("ZUnable to read control files. (#4.3.0)\n"); + zerodie(); +} +void _noreturn_ +perm_partialline() +{ + out("DSMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); + zerodie(); +} +void _noreturn_ +perm_usage() +{ + out("DI (qmail-remote) was invoked improperly. (#5.3.5)\n"); + zerodie(); +} +void _noreturn_ +perm_dns() +{ + out("D5.1.2 Sorry, I couldn't find any host named "); + outsafe(&host); + out(".\n"); + zerodie(); +} +void _noreturn_ +perm_nomx() +{ + out("D5.4.4 Sorry, I couldn't find a mail exchanger or IP address.\n"); + zerodie(); +} +void _noreturn_ +perm_ambigmx() +{ + out("D\ Sorry. Although I'm listed as a best-preference MX or A for that host,\n\ it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); -zerodie(); } + zerodie(); +} -void outhost() +void +outhost() { - char x[IPFMT]; - if (substdio_put(subfdoutsmall,x,ip_fmt(x,&partner)) == -1) _exit(0); + char x[IPFMT]; + if (substdio_put(subfdoutsmall, x, ip_fmt(x, &partner)) == -1) + _exit(0); } int flagcritical = 0; -void _noreturn_ dropped() { - out("ZConnected to "); - outhost(); - out(" but connection died. "); - if (flagcritical) out("Possible duplicate! "); - out("(#4.4.2)\n"); - zerodie(); +void _noreturn_ +dropped() +{ + out("ZConnected to "); + outhost(); + out(" but connection died. "); + if (flagcritical) + out("Possible duplicate! "); + out("(#4.4.2)\n"); + zerodie(); } int timeoutconnect = 60; int smtpfd; int timeout = 1200; -GEN_SAFE_TIMEOUTREAD(saferead,timeout,smtpfd,dropped()) -GEN_SAFE_TIMEOUTWRITE(safewrite,timeout,smtpfd,dropped()) +GEN_SAFE_TIMEOUTREAD(saferead, timeout, smtpfd, dropped()) +GEN_SAFE_TIMEOUTWRITE(safewrite, timeout, smtpfd, dropped()) char inbuf[1024]; -substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf)); +substdio ssin = SUBSTDIO_FDBUF(read, 0, inbuf, sizeof(inbuf)); char smtptobuf[1024]; -substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof(smtptobuf)); +substdio smtpto = SUBSTDIO_FDBUF(safewrite, -1, smtptobuf, sizeof(smtptobuf)); char smtpfrombuf[128]; -substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof(smtpfrombuf)); +substdio smtpfrom = SUBSTDIO_FDBUF(saferead, -1, smtpfrombuf, sizeof(smtpfrombuf)); stralloc smtptext = {0}; -static void get(unsigned char *uc) +static void +get(unsigned char *uc) { - char *ch = (char *)uc; - substdio_get(&smtpfrom,ch,1); - if (*ch != '\r') - if (smtptext.len < HUGESMTPTEXT) + char *ch = (char *)uc; + substdio_get(&smtpfrom, ch, 1); + if (*ch != '\r') + if (smtptext.len < HUGESMTPTEXT) #ifdef USING_SKALIBS - // skalibs is different on this - if (!stralloc_append(&smtptext,*ch)) temp_nomem(); + //skalibs is different on this + if (!stralloc_append(&smtptext, *ch)) + temp_nomem(); #else - if (!stralloc_append(&smtptext,ch)) temp_nomem(); + if (!stralloc_append(&smtptext, ch)) + temp_nomem(); #endif } -unsigned long smtpcode() +unsigned long +smtpcode() { - unsigned char ch; - unsigned long code; - - if (!stralloc_copys(&smtptext,"")) temp_nomem(); - - get(&ch); code = ch - '0'; - get(&ch); code = code * 10 + (ch - '0'); - get(&ch); code = code * 10 + (ch - '0'); - for (;;) { - get(&ch); - if (ch != '-') break; - while (ch != '\n') get(&ch); - get(&ch); - get(&ch); - get(&ch); - } - while (ch != '\n') get(&ch); - - return code; + unsigned char ch = 0; + unsigned long code = 0; + + if (!stralloc_copys(&smtptext, "")) + temp_nomem(); + + get(&ch); + code = ch - '0'; + get(&ch); + code = code * 10 + (ch - '0'); + get(&ch); + code = code * 10 + (ch - '0'); + for (;;) { + get(&ch); + if (ch != '-') + break; + while (ch != '\n') + get(&ch); + get(&ch); + get(&ch); + get(&ch); + } + while (ch != '\n') + get(&ch); + + return code; } -void outsmtptext() +void +outsmtptext() { - int i; - if (smtptext.s) if (smtptext.len) { - out("Remote host said: "); - for (i = 0;i < smtptext.len;++i) - if (!smtptext.s[i]) smtptext.s[i] = '?'; - if (substdio_put(subfdoutsmall,smtptext.s,smtptext.len) == -1) _exit(0); - smtptext.len = 0; - } + int i; + if (smtptext.s) + if (smtptext.len) { + out("Remote host said: "); + for (i = 0; i < smtptext.len; ++i) + if (!smtptext.s[i]) + smtptext.s[i] = '?'; + if (substdio_put(subfdoutsmall, smtptext.s, smtptext.len) == -1) + _exit(0); + smtptext.len = 0; + } } -void quit(prepend,append) -char *prepend; -char *append; +void +quit(char *prepend, + char *append) { - substdio_putsflush(&smtpto,"QUIT\r\n"); - /* waiting for remote side is just too ridiculous */ - out(prepend); - outhost(); - out(append); - out(".\n"); - outsmtptext(); - zerodie(); + substdio_putsflush(&smtpto, "QUIT\r\n"); + /* waiting for remote side is just too ridiculous + * do it anyway if we are pedanticsmtp (the default) */ + out(prepend); + outhost(); + out(append); + out(".\n"); + outsmtptext(); + if (pedanticsmtp) smtpcode(); /* Discard */ + zerodie(); } -void blast() +void +blastsmtp() { - int r; - char ch; - - for (;;) { - r = substdio_get(&ssin,&ch,1); - if (r == 0) break; - if (r == -1) temp_read(); - if (ch == '.') - substdio_put(&smtpto,".",1); - while (ch != '\n') { - if (ch == '\r') { - r = substdio_get(&ssin, &ch, 1); - if (r == 0) - break; - if (r == -1) temp_read(); - if (ch != '\n') { - substdio_put(&smtpto, "\r\n", 2); - } else - break; - } - substdio_put(&smtpto,&ch,1); - r = substdio_get(&ssin,&ch,1); - if (r == 0) perm_partialline(); - if (r == -1) temp_read(); - } - substdio_put(&smtpto,"\r\n",2); - } - - flagcritical = 1; - substdio_put(&smtpto,".\r\n",3); - substdio_flush(&smtpto); + int r; + char ch; + + for (;;) { + r = substdio_get(&ssin, &ch, 1); + if (r == 0) + break; + if (r == -1) + temp_read(); + if (ch == '.') + substdio_put(&smtpto, ".", 1); + while (ch != '\n') { + if (ch == '\r') { + r = substdio_get(&ssin, &ch, 1); + if (r == 0) + break; + if (r == -1) + temp_read(); + if (ch != '\n') { + substdio_put(&smtpto, "\r\n", 2); + } else + break; + } + substdio_put(&smtpto, &ch, 1); + r = substdio_get(&ssin, &ch, 1); + if (r == 0) + perm_partialline(); + if (r == -1) + temp_read(); + } + substdio_put(&smtpto, "\r\n", 2); + } + + flagcritical = 1; + substdio_put(&smtpto, ".\r\n", 3); + substdio_flush(&smtpto); } stralloc recip = {0}; +int esmtp = 1; -void smtp() +void +smtp() { - unsigned long code; - int flagbother; - int i; - - if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); - - substdio_puts(&smtpto,"HELO "); - substdio_put(&smtpto,helohost.s,helohost.len); - substdio_puts(&smtpto,"\r\n"); - substdio_flush(&smtpto); - if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); - - substdio_puts(&smtpto,"MAIL FROM:<"); - substdio_put(&smtpto,sender.s,sender.len); - substdio_puts(&smtpto,">\r\n"); - substdio_flush(&smtpto); - code = smtpcode(); - if (code >= 500) quit("DConnected to "," but sender was rejected"); - if (code >= 400) quit("ZConnected to "," but sender was rejected"); - - flagbother = 0; - for (i = 0;i < reciplist.len;++i) { - substdio_puts(&smtpto,"RCPT TO:<"); - substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len); - substdio_puts(&smtpto,">\r\n"); - substdio_flush(&smtpto); - code = smtpcode(); - if (code >= 500) { - out("h"); outhost(); out(" does not like recipient.\n"); - outsmtptext(); zero(); - } - else if (code >= 400) { - out("s"); outhost(); out(" does not like recipient.\n"); - outsmtptext(); zero(); - } - else { - out("r"); zero(); - flagbother = 1; - } - } - if (!flagbother) quit("DGiving up on ",""); - - substdio_putsflush(&smtpto,"DATA\r\n"); - code = smtpcode(); - if (code >= 500) quit("D"," failed on DATA command"); - if (code >= 400) quit("Z"," failed on DATA command"); - - blast(); - code = smtpcode(); - flagcritical = 0; - if (code >= 500) quit("D"," failed after I sent the message"); - if (code >= 400) quit("Z"," failed after I sent the message"); - quit("K"," accepted message"); + unsigned long code; + int flagbother; + int i; + + //XXX commented example(straight BER flow) is RFC non-compliant. + // if (smtpcode() != 220) quit("ZConnected to ", " but greeting failed"); + //Use theory of codes instead - any 2xx code is fine + code = smtpcode(); + if (code < 200 || code > 299) { + substdio_putsflush(&smtpto, "QUIT\r\n"); + smtpcode(); /* Return value deliberately unused; just get us to EPIPE. Hildie */ + close(smtpfd); + return; + } + + substdio_puts(&smtpto, "EHLO "); + substdio_put(&smtpto, helohost.s, helohost.len); + substdio_puts(&smtpto, "\r\n"); + substdio_flush(&smtpto); + code = smtpcode(); + if (code < 250 || code > 259) { + esmtp = 0; + /* Old far side doesn't understand EHLO. That's fine, we don't use any ESMTP extensions anyway. */ + substdio_puts(&smtpto, "HELO "); + substdio_put(&smtpto, helohost.s, helohost.len); + substdio_puts(&smtpto, "\r\n"); + substdio_flush(&smtpto); + code = smtpcode(); + if (code < 250 || code > 259) + quit("ZConnected to ", " but my name was rejected"); + } + + substdio_puts(&smtpto, "MAIL FROM:<"); + substdio_put(&smtpto, sender.s, sender.len); + substdio_puts(&smtpto, ">\r\n"); + substdio_flush(&smtpto); + code = smtpcode(); + if (code >= 500) + quit("DConnected to ", " but sender was rejected"); + if (code >= 400) + quit("ZConnected to ", " but sender was rejected"); + + flagbother = 0; + for (i = 0; i < reciplist.len; ++i) { + substdio_puts(&smtpto, "RCPT TO:<"); + substdio_put(&smtpto, reciplist.sa[i].s, reciplist.sa[i].len); + substdio_puts(&smtpto, ">\r\n"); + substdio_flush(&smtpto); + code = smtpcode(); + if (code >= 500) { + out("h"); + outhost(); + out(" does not like recipient.\n"); + outsmtptext(); + zero(); + } else if (code >= 400) { + out("s"); + outhost(); + out(" does not like recipient.\n"); + outsmtptext(); + zero(); + } else { + out("r"); + zero(); + flagbother = 1; + } + } + if (!flagbother) + quit("DGiving up on ", ""); + + substdio_putsflush(&smtpto, "DATA\r\n"); + code = smtpcode(); + if (code >= 500) + quit("D", " failed on DATA command"); + if (code >= 400) + quit("Z", " failed on DATA command"); + + blastsmtp(); + code = smtpcode(); + flagcritical = 0; + if (code >= 500) + quit("D", " failed after I sent the message"); + if (code >= 400) + quit("Z", " failed after I sent the message"); + if (esmtp) + quit("K", " accepted message with ESMTP"); + else quit("K", " accepted message with standard SMTP"); } stralloc canonhost = {0}; stralloc canonbox = {0}; -void addrmangle(stralloc *saout, char *s) +void +addrmangle(stralloc * saout, char *s) { - int j; - - j = str_rchr(s,'@'); - if (!s[j]) { - if (!stralloc_copys(saout,s)) temp_nomem(); - return; - } - if (!stralloc_copys(&canonbox,s)) temp_nomem(); - canonbox.len = j; - /* box has to be quoted */ - if (!quote(saout,&canonbox)) temp_nomem(); - if (!stralloc_cats(saout,"@")) temp_nomem(); - - if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem(); - - if (!stralloc_cat(saout,&canonhost)) temp_nomem(); + int j; + + j = str_rchr(s, '@'); + if (!s[j]) { + if (!stralloc_copys(saout, s)) + temp_nomem(); + return; + } + if (!stralloc_copys(&canonbox, s)) + temp_nomem(); + canonbox.len = j; + /* box has to be quoted */ + if (!quote(saout, &canonbox)) + temp_nomem(); + if (!stralloc_cats(saout, "@")) + temp_nomem(); + + if (!stralloc_copys(&canonhost, s + j + 1)) + temp_nomem(); + + if (!stralloc_cat(saout, &canonhost)) + temp_nomem(); } -void getcontrols() +void +getcontrols() { - if (control_init() == -1) temp_control(); - if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); - if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) - temp_control(); - if (control_rldef(&helohost,"control/helohost",1,NULL) != 1) - temp_control(); - switch(control_readfile(&routes,"control/smtproutes",0)) { - case -1: - temp_control(); - case 0: - if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break; - case 1: - if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; - } + if (control_init() == -1) + temp_control(); + if (control_readint(&pedanticsmtp, "control/pedanticsmtp") == -1) + pedanticsmtp = 1; // default to pedantic + if (control_readint(&timeout, "control/timeoutremote") == -1) + temp_control(); + if (control_readint(&timeoutconnect, "control/timeoutconnect") == -1) + temp_control(); + if (control_rldef(&helohost, "control/helohost", 1, NULL) != 1) + temp_control(); + switch (control_readfile(&routes, "control/smtproutes", 0)) { + case -1: + temp_control(); + case 0: + if (!constmap_init(&maproutes, "", 0, 1)) + temp_nomem(); + break; + case 1: + if (!constmap_init(&maproutes, routes.s, routes.len, 1)) + temp_nomem(); + break; + } } -int main(int argc, char **argv) +int +main(int argc, char **argv) { - static ipalloc ip = {0}; - int i; - unsigned long random; - char **recips; - unsigned long prefme; - char *relayhost; - - sig_pipeignore(); - if (argc < 4) perm_usage(); - if (chdir(auto_qmail) == -1) temp_chdir(); - getcontrols(); - - - if (!stralloc_copys(&host,argv[1])) temp_nomem(); - - relayhost = 0; - for (i = 0;i <= host.len;++i) - if ((i == 0) || (i == host.len) || (host.s[i] == '.')) - if ((relayhost = constmap(&maproutes,host.s + i,host.len - i))) - break; - if (relayhost && !*relayhost) relayhost = 0; - - if (relayhost) { - i = str_chr(relayhost,':'); - if (relayhost[i]) { - scan_ulong(relayhost + i + 1,&port); - relayhost[i] = 0; - } - if (!stralloc_copys(&host,relayhost)) temp_nomem(); - } - - - addrmangle(&sender,argv[2]); - - if (!saa_readyplus(&reciplist,0)) temp_nomem(); - if (ipme_init() != 1) temp_oserr(); - - recips = argv + 3; - while (*recips) { - if (!saa_readyplus(&reciplist,1)) temp_nomem(); - reciplist.sa[reciplist.len] = sauninit; - addrmangle(reciplist.sa + reciplist.len,*recips); - ++reciplist.len; - ++recips; - } - - - random = now() + (getpid() << 16); - switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) { - case DNS_MEM: temp_nomem(); - case DNS_SOFT: temp_dns(); - case DNS_HARD: perm_dns(); - case 1: - if (ip.len <= 0) temp_dns(); - } - - if (ip.len <= 0) perm_nomx(); - - prefme = 100000; - for (i = 0;i < ip.len;++i) - if (ipme_is(&ip.ix[i].ip)) - if (ip.ix[i].pref < prefme) - prefme = ip.ix[i].pref; - - if (relayhost) prefme = 300000; - - for (i = 0;i < ip.len;++i) - if (ip.ix[i].pref < prefme) - break; - - if (i >= ip.len) - perm_ambigmx(); - - for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) { - if (tcpto(&ip.ix[i].ip)) continue; - - smtpfd = socket(AF_INET,SOCK_STREAM,0); - if (smtpfd == -1) temp_oserr(); - - if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { - tcpto_err(&ip.ix[i].ip,0); - partner = ip.ix[i].ip; - smtp(); /* does not return */ - } - tcpto_err(&ip.ix[i].ip,errno == error_timeout); - close(smtpfd); - } - - temp_noconn(); + static ipalloc ip = { + 0 + }; + int i; + unsigned long random; + char **recips; + unsigned long prefme; + char *relayhost; + + sig_pipeignore(); + if (argc < 4) + perm_usage(); + if (chdir(auto_qmail) == -1) + temp_chdir(); + getcontrols(); + + + if (!stralloc_copys(&host, argv[1])) + temp_nomem(); + + relayhost = 0; + for (i = 0; i <= host.len; ++i) + if ((i == 0) || (i == host.len) || (host.s[i] == '.')) + if ((relayhost = constmap(&maproutes, host.s + i, host.len - i))) + break; + if (relayhost && !*relayhost) + relayhost = 0; + + if (relayhost) { + i = str_chr(relayhost, ':'); + if (relayhost[i]) { + scan_ulong(relayhost + i + 1, &port); + relayhost[i] = 0; + } + if (!stralloc_copys(&host, relayhost)) + temp_nomem(); + } + + + addrmangle(&sender, argv[2]); + + if (!saa_readyplus(&reciplist, 0)) + temp_nomem(); + if (ipme_init() != 1) + temp_oserr(); + + recips = argv + 3; + while (*recips) { + if (!saa_readyplus(&reciplist, 1)) + temp_nomem(); + reciplist.sa[reciplist.len] = sauninit; + addrmangle(reciplist.sa + reciplist.len, *recips); + ++reciplist.len; + ++recips; + } + + + random = now() + (getpid() << 16); + switch (relayhost ? dns_ip(&ip, &host) : dns_mxip(&ip, &host, random)) { + case DNS_MEM: + temp_nomem(); + case DNS_SOFT: + temp_dns(); + case DNS_HARD: + perm_dns(); + case 1: + if (ip.len <= 0) + temp_dns(); + } + + if (ip.len <= 0) + perm_nomx(); + + prefme = 100000; + for (i = 0; i < ip.len; ++i) + if (ipme_is(&ip.ix[i].ip)) + if (ip.ix[i].pref < prefme) + prefme = ip.ix[i].pref; + + if (relayhost) + prefme = 300000; + + for (i = 0; i < ip.len; ++i) + if (ip.ix[i].pref < prefme) + break; + + if (i >= ip.len) + perm_ambigmx(); + + for (i = 0; i < ip.len; ++i) + if (ip.ix[i].pref < prefme) { + if (tcpto(&ip.ix[i].ip)) + continue; + + smtpfd = socket(AF_INET, SOCK_STREAM, 0); + if (smtpfd == -1) + temp_oserr(); + + if (timeoutconn(smtpfd, &ip.ix[i].ip, (unsigned int)port, timeoutconnect) == 0) { + tcpto_err(&ip.ix[i].ip, 0); + partner = ip.ix[i].ip; + smtp(); /* does not return, unless greeting + * wrong */ + } + tcpto_err(&ip.ix[i].ip, errno == error_timeout); + close(smtpfd); + } + + temp_noconn(); } diff --git a/src/qmail-send.c b/src/qmail-send.c @@ -112,6 +112,7 @@ void fnmake_chanaddr(id,c) unsigned long id; int c; stralloc rwline = {0}; /* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */ +/* Daniel.. by sea? Don't you mean by air? */ /* may trash recip. must set up rwline, between a T and a \0. */ int rewrite(recip) char *recip; diff --git a/src/qmail-start-np.c b/src/qmail-start-np.c @@ -0,0 +1,206 @@ +#include <sys/stat.h> +#include <unistd.h> +#include "fd.h" +#include "prot.h" +#include "exit.h" +#include "fork.h" +#include "noreturn.h" +#include "uidgid.h" +#include "auto_uids.h" +#include "auto_users.h" +#include "auto_qmail.h" // auto_qmail.o +#include "select.h" // ...? +#include "fifo.h" // fifo.something +#include "open.h" // open.a +#include "hasnpbg1.h" + +/* MXF NOTES: + * This qmail-start is special. It creates named pipes for channel + * drivers (to wit, lspawn and rspawn) and the logger, and fires off + * an extra process that creates a triggerpull which qmsend should + * yank when it has picked up the stdin pipes of clean, logger and + * channel drivers. + */ + +#define RDEND 0 +#define WREND 1 + +char *(qsargs[]) = { "qmail-send", 0 }; +char *(qcargs[]) = { "qmail-clean", 0 }; +char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 }; +char *(qrargs[]) = { "qmail-rspawn", 0 }; + +void _noreturn_ die() { _exit(111); } + +int pi0[2]; +int pi1[2]; +int pi2[2]; +int pi3[2]; +int pi4[2]; +int pi5[2]; +int pi6[2]; + +uid_t auto_uidl; +uid_t auto_uidq; +uid_t auto_uidr; +uid_t auto_uids; + +gid_t auto_gidn; +gid_t auto_gidq; + +void close23456() { close(2); close(3); close(4); close(5); close(6); } + +// sort of same semantics as pipe() except that it returns the pipe instead of setting memory +int makeorusenp(char *n, int *f) { + if (fifo_make(n, 0600) == -1) { + if (errno != EEXIST) { + return -1; + } + } + f[0] = open_read(n); + if (f[0] == -1) return -1; + fchown(f[0], auto_uids); + fchown(f[0], auto_gidq); + fchmod(f[0], 0600); + f[1] = open_write(n); + if (f[1] == -1) return -1; + return 0; +} + +void closepipes() { + close(pi1[RDEND]); close(pi1[WREND]); close(pi2[RDEND]); close(pi2[WREND]); + close(pi3[RDEND]); close(pi3[WREND]); close(pi4[RDEND]); close(pi4[WREND]); + close(pi5[RDEND]); close(pi5[WREND]); close(pi6[RDEND]); close(pi6[WREND]); +} + +int main(int argc, char **argv) +{ + if (chdir("/") == -1) die(); + umask(077); + + auto_uidl = inituid(auto_userl); + auto_uidq = inituid(auto_userq); + auto_uidr = inituid(auto_userr); + auto_uids = inituid(auto_users); + + auto_gidn = initgid(auto_groupn); + auto_gidq = initgid(auto_groupq); + + if (prot_gid(auto_gidq) == -1) die(); + + if (fd_copy(2,0) == -1) die(); + if (fd_copy(3,0) == -1) die(); + if (fd_copy(4,0) == -1) die(); + if (fd_copy(5,0) == -1) die(); + if (fd_copy(6,0) == -1) die(); + + if (argv[1]) { + qlargs[1] = argv[1]; + ++argv; + } + + if (chdir(auto_qmail) == -1) die(); + + // logger + if (argv[1]) { + if (makeorusenp("queue/lock/loggersin", &pi0) == -1) die(); + switch(fork()) { + case -1: + die(); + case 0: + if (prot_gid(auto_gidn) == -1) die(); + if (prot_uid(auto_uidl) == -1) die(); + close(pi0[WREND]); + if (fd_move(0,pi0[RDEND]) == -1) die(); + close23456(); + execvp(argv[1],argv + 1); + die(); + } + close(pi0[RDEND]); + if (fd_move(1,pi0[WREND]) == -1) die(); + } + + if (makeorusenp("queue/lock/chan0sin", &pi1) == -1) die(); // si [ + if (makeorusenp("queue/lock/chan0sout", &pi2) == -1) die(); // so ] lspawn + if (makeorusenp("queue/lock/chan1sin", &pi3) == -1) die(); // si [ + if (makeorusenp("queue/lock/chan1sout", &pi4) == -1) die(); // so ] rspawn + if (makeorusenp("queue/lock/cleansin", &pi5) == -1) die(); // si [ + if (makeorusenp("queue/lock/cleansout", &pi6) == -1) die(); // so ] cleanup + + // channelspawn: local + switch(fork()) { + case -1: die(); + case 0: + if (fd_copy(0,pi1[RDEND]) == -1) die(); + if (fd_copy(1,pi2[WREND]) == -1) die(); + close23456(); + closepipes(); + execvp(*qlargs,qlargs); + die(); + } + + // channelspawn: remote + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidr) == -1) die(); + if (fd_copy(0,pi3[RDEND]) == -1) die(); + if (fd_copy(1,pi4[WREND]) == -1) die(); + close23456(); + closepipes(); + execvp(*qrargs,qrargs); + die(); + } + + // cleanup + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidq) == -1) die(); + if (fd_copy(0,pi5[RDEND]) == -1) die(); + if (fd_copy(1,pi6[WREND]) == -1) die(); + close23456(); + closepipes(); + execvp(*qcargs,qcargs); + die(); + } + + // readiness + switch (fork()) { + case -1: die(); + case 0: + close23456(); +#ifdef HASNAMEDPIPEBUG1 + if (fd_copy(2,pi1[WREND]) == -1) die(); + if (fd_copy(3,pi3[WREND]) == -1) die(); + if (fd_copy(4,pi5[WREND]) == -1) die(); + // on some systems, named pipes that are only open at one end + // constantly check readability. This turns their selects into + // a spinlock. Prevent this by keeping the WRENDs of pipes whose + // RDENDs are passed into the above processes open. +#endif + closepipes(); + if (makeorusenp("queue/lock/sendready", &pi0) == -1) die() + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(pi0[0], &rfds); + if (select(pi0[0] + 1, &rfds, NULL, NULL, NULL) > 0) { + // this can only mean one thing: someone wrote to our readiness FD. + // that means they now have the WRENDs of the loggersin and chanNsin + // pipes. + } + } + + // exec into qmsend + if (prot_uid(auto_uids) == -1) die(); + if (fd_copy(0,1) == -1) die(); + if (fd_copy(1,pi1[WREND]) == -1) die(); + if (fd_copy(2,pi2[RDEND]) == -1) die(); + if (fd_copy(3,pi3[WREND]) == -1) die(); + if (fd_copy(4,pi4[RDEND]) == -1) die(); + if (fd_copy(5,pi5[WREND]) == -1) die(); + if (fd_copy(6,pi6[RDEND]) == -1) die(); + closepipes(); + execvp(*qsargs,qsargs); + die(); +} diff --git a/src/qmail-start.c b/src/qmail-start.c @@ -65,6 +65,7 @@ int main(int argc, char **argv) ++argv; } + // logger if (argv[1]) { if (pipe(pi0) == -1) die(); switch(fork()) { @@ -82,14 +83,15 @@ int main(int argc, char **argv) close(pi0[0]); if (fd_move(1,pi0[1]) == -1) die(); } - + if (pipe(pi1) == -1) die(); if (pipe(pi2) == -1) die(); if (pipe(pi3) == -1) die(); if (pipe(pi4) == -1) die(); if (pipe(pi5) == -1) die(); if (pipe(pi6) == -1) die(); - + + // channelspawn: local switch(fork()) { case -1: die(); case 0: @@ -100,7 +102,8 @@ int main(int argc, char **argv) execvp(*qlargs,qlargs); die(); } - + + // channelspawn: remote switch(fork()) { case -1: die(); case 0: @@ -112,7 +115,8 @@ int main(int argc, char **argv) execvp(*qrargs,qrargs); die(); } - + + // cleanup switch(fork()) { case -1: die(); case 0: @@ -124,7 +128,8 @@ int main(int argc, char **argv) execvp(*qcargs,qcargs); die(); } - + + // exec into qmsend if (prot_uid(auto_uids) == -1) die(); if (fd_copy(0,1) == -1) die(); if (fd_copy(1,pi1[1]) == -1) die();