nightmaremail

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

commit 0a0d284a19a487555ee1a16d1db13c2a4aeaa4ba
parent 7d8b5c08a9e5e1a792650fb945ed9efc1b104ec0
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date:   Thu, 27 Oct 2022 19:54:05 +0000

a lot has changed, not all of it good

Diffstat:
M.gitignore | 3+++
MMakefile.legacy | 30+++++++++++++++---------------
Mdoc/man/dot-qmail.9 | 21++++++++++++++++++---
Minclude/fmt.h | 1+
Minclude/ip.h | 11++++++-----
Minclude/ipme.h | 8+++++---
Minclude/scan.h | 1+
Msrc/dns.c | 17+++++++++--------
Msrc/error_str.c | 19+++++++++++++------
Msrc/error_temp.c | 10++++++----
Msrc/fmt_ulong.c | 13+++++++++++++
Msrc/ip.c | 158++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/ipme.c | 45+++++++++++++++++++++++++++++++--------------
Msrc/qmail-local.c | 1361+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/qmail-lspawn.c | 464+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/qmail-remote.c | 292++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/scan_ulong.c | 30++++++++++++++++++++++++++++++
Msrc/slurpclose.c | 8++++----
Msrc/tcpto.c | 359++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/timeoutconn.c | 70+++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
20 files changed, 1792 insertions(+), 1129 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -385,3 +385,6 @@ src/netstrings.o src/nmail-smtpd.o nmail-smtpd *.BAK +test.c +test.o +teste diff --git a/Makefile.legacy b/Makefile.legacy @@ -419,10 +419,10 @@ compile src/trydrent.c include/direntry.h1 include/direntry.h2 rm -f src/trydrent.o dns.lib: \ -src/tryrsolv.c compile load socket.lib src/dns.o src/ipalloc.o src/ip.o src/stralloc.a \ +src/tryrsolv.c compile load socket.lib src/dns.o src/ipalloc.o src/ip.o src/case.a src/stralloc.a \ src/error.a src/fs.a src/str.a ( ( ./compile src/tryrsolv.c && ./load tryrsolv src/dns.o \ - src/ipalloc.o src/ip.o src/stralloc.a src/error.a src/fs.a src/str.a \ + src/ipalloc.o src/ip.o src/case.a src/stralloc.a src/error.a src/fs.a src/str.a \ -lresolv `cat socket.lib` ) >/dev/null 2>&1 \ && echo -lresolv || exit 0 ) > dns.lib rm -f src/tryrsolv.o tryrsolv @@ -437,9 +437,9 @@ compile src/dnsdoe.c include/substdio.h include/subfd.h include/substdio.h inclu ./compile src/dnsdoe.c dnsfq: \ -load src/dnsfq.o src/dns.o src/dnsdoe.o src/ip.o src/ipalloc.o src/stralloc.a \ +load src/dnsfq.o src/dns.o src/dnsdoe.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a dns.lib socket.lib - ./load dnsfq src/dns.o src/dnsdoe.o src/ip.o src/ipalloc.o src/stralloc.a \ + ./load dnsfq src/dns.o src/dnsdoe.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a `cat dns.lib` `cat socket.lib` src/dnsfq.o: \ @@ -448,9 +448,9 @@ include/dns.h include/dnsdoe.h include/ip.h include/ipalloc.h include/ip.h inclu ./compile src/dnsfq.c dnsip: \ -load src/dnsip.o src/dns.o src/dnsdoe.o src/ip.o src/ipalloc.o src/stralloc.a \ +load src/dnsip.o src/dns.o src/dnsdoe.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a dns.lib socket.lib - ./load dnsip src/dns.o src/dnsdoe.o src/ip.o src/ipalloc.o src/stralloc.a \ + ./load dnsip src/dns.o src/dnsdoe.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a `cat dns.lib` `cat socket.lib` src/dnsip.o: \ @@ -459,9 +459,9 @@ include/dns.h include/dnsdoe.h include/ip.h include/ipalloc.h include/ip.h inclu ./compile src/dnsip.c dnsptr: \ -load src/dnsptr.o src/dns.o src/dnsdoe.o src/ip.o src/ipalloc.o src/stralloc.a \ +load src/dnsptr.o src/dns.o src/dnsdoe.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a dns.lib socket.lib - ./load dnsptr src/dns.o src/dnsdoe.o src/ip.o src/ipalloc.o src/stralloc.a \ + ./load dnsptr src/dns.o src/dnsdoe.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a `cat dns.lib` `cat socket.lib` src/dnsptr.o: \ @@ -760,9 +760,9 @@ include/stralloc.h include/gen_alloc.h include/ipme.h include/ip.h include/ipall ./compile src/ipme.c ipmeprint: \ -load src/ipmeprint.o src/ipme.o src/ip.o src/ipalloc.o src/stralloc.a src/substdio.a \ +load src/ipmeprint.o src/ipme.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a src/substdio.a \ src/error.a src/str.a src/fs.a socket.lib - ./load ipmeprint src/ipme.o src/ip.o src/ipalloc.o src/stralloc.a \ + ./load ipmeprint src/ipme.o src/ip.o src/case.a src/ipalloc.o src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a `cat socket.lib` src/ipmeprint.o: \ @@ -776,7 +776,7 @@ qmail-clean qmail-send qmail-start splogger qmail-queue qmail-inject \ predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \ qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ -qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast \ +qmail-smtpd nmail-smtpd sendmail tcp-env qmail-newmrh config config-fast \ dnsptr dnsip dnsfq hostname ipmeprint qreceipt qbiff \ forward preline condredirect bouncesaying except maildirmake \ maildir2mbox install instpackage instqueue instchown \ @@ -1255,10 +1255,10 @@ include/auto_users.h include/byte.h qmail-qmqpc: \ load src/qmail-qmqpc.o src/slurpclose.o src/timeoutread.o src/timeoutwrite.o \ -src/timeoutconn.o src/ip.o src/control.o src/auto_qmail.o src/sig.a src/ndelay.a src/open.a \ +src/timeoutconn.o src/ip.o src/case.a src/control.o src/auto_qmail.o src/sig.a src/ndelay.a src/open.a \ src/getln.a src/substdio.a src/stralloc.a src/error.a src/str.a src/fs.a socket.lib ./load qmail-qmqpc src/slurpclose.o src/timeoutread.o \ - src/timeoutwrite.o src/timeoutconn.o src/ip.o src/control.o src/auto_qmail.o \ + src/timeoutwrite.o src/timeoutconn.o src/ip.o src/case.a src/control.o src/auto_qmail.o \ src/sig.a src/ndelay.a src/open.a src/getln.a src/substdio.a src/stralloc.a \ src/error.a src/str.a src/fs.a `cat socket.lib` @@ -1535,9 +1535,9 @@ include/auto_qmail.h ./compile src/qmail-tcpok.c qmail-tcpto: \ -load src/qmail-tcpto.o src/ip.o src/open.a src/lock.a src/substdio.a src/error.a src/str.a \ +load src/qmail-tcpto.o src/ip.o src/case.a src/open.a src/lock.a src/substdio.a src/error.a src/str.a \ src/fs.a src/auto_qmail.o - ./load qmail-tcpto src/ip.o src/open.a src/lock.a src/substdio.a \ + ./load qmail-tcpto src/ip.o src/case.a src/open.a src/lock.a src/substdio.a \ src/error.a src/str.a src/fs.a src/auto_qmail.o doc/man/qmail-tcpto.0: \ diff --git a/doc/man/dot-qmail.9 b/doc/man/dot-qmail.9 @@ -42,6 +42,21 @@ A comment line begins with a number sign: ignores the line. .TP 5 (2) +A pragma line begins with a plus sign: + +.EX + +list +.EE + +This +.B qmail-local +only understands one pragma at this time, namely "list" (as demonstrated above). +No line following a +list line is permitted to be a file or program delivery - it must be a forward. +Please note that this mechanism of handling mailing lists is long deprecated. qlist switched to setting the executable bit on .qmail files, which this +.B qmail-local +treats the same way, all the way back in 1996 CE, which is before the git history of this suite begins. It exists to aid migration on systems that, after 26 years, still have qlist-maintained mailing lists. +.TP 5 +(3) A program line begins with a vertical bar: .EX @@ -55,7 +70,7 @@ See .B qmail-command(8) for further information. .TP 5 -(3) +(4) A forward line begins with an ampersand: .EX @@ -95,7 +110,7 @@ omits its new .B Return-Path line when forwarding messages. .TP 5 -(4) +(5) An .I mbox line begins with a slash or dot, @@ -134,7 +149,7 @@ it will truncate the file back to its original length. However, it cannot prevent mailbox corruption if the system crashes during delivery. .TP 5 -(5) +(6) A .I maildir line begins with a slash or dot, diff --git a/include/fmt.h b/include/fmt.h @@ -9,6 +9,7 @@ extern unsigned int fmt_uint(char *, unsigned int); extern unsigned int fmt_uint0(char *, unsigned int, unsigned int); extern unsigned int fmt_ulong(char *, unsigned long); +extern unsigned int fmt_ulongalphabet(char *, unsigned long, char *, unsigned long); extern unsigned int fmt_ulonglong(char *, unsigned long long); extern unsigned int fmt_str(char *, char *); diff --git a/include/ip.h b/include/ip.h @@ -1,12 +1,13 @@ #ifndef IP_H #define IP_H -struct ip_address { unsigned char d[4]; } ; +struct ip4_address { unsigned char d[4]; } ; struct ip6_address { unsigned char d[16]; } ; +struct ip_address { unsigned int is6; union { struct ip4_address a; struct ip6_address aaaa; } ip; }; /* blatantly the way that skalibs does it */ -extern unsigned int ip_fmt(); -#define IPFMT 19 -extern unsigned int ip_scan(); -extern unsigned int ip_scanbracket(); +extern unsigned int ip_fmt(char *, struct ip_address *); +#define IPFMT 41 +extern unsigned int ip_scan(char *, struct ip_address *); +extern unsigned int ip_scanbracket(char *, struct ip_address *); #endif diff --git a/include/ipme.h b/include/ipme.h @@ -5,11 +5,13 @@ #include "ipalloc.h" extern ipalloc ipme; -extern ip6alloc ip6me; +extern ipalloc ip6me; -extern int ipme_init(); +extern int ip4me_init(); +extern int ip4me_is(); extern int ip6me_init(); -extern int ipme_is(); extern int ip6me_is(); +extern int ipme_init(); +extern int ipme_is(); #endif diff --git a/include/scan.h b/include/scan.h @@ -2,6 +2,7 @@ #define SCAN_H extern unsigned int scan_ulong(char *, unsigned long *); +extern unsigned int scan_ulongalphabet(char *, unsigned long *, char *, unsigned long); extern unsigned int scan_8long(char *, unsigned long *); #endif diff --git a/src/dns.c b/src/dns.c @@ -156,10 +156,11 @@ static int findip(int wanttype) { if (rrdlen < 4) return DNS_SOFT; - ip.d[0] = responsepos[0]; - ip.d[1] = responsepos[1]; - ip.d[2] = responsepos[2]; - ip.d[3] = responsepos[3]; + ip.is6 = 0; + ip.ip.a.d[0] = responsepos[0]; + ip.ip.a.d[1] = responsepos[1]; + ip.ip.a.d[2] = responsepos[2]; + ip.ip.a.d[3] = responsepos[3]; responsepos += rrdlen; return 1; } @@ -219,13 +220,13 @@ static int iaafmt(char *s, struct ip_address *ipa) unsigned int i; unsigned int len; len = 0; - i = fmt_ulong(s,(unsigned long) ipa->d[3]); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[3]); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; - i = fmt_ulong(s,(unsigned long) ipa->d[2]); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[2]); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; - i = fmt_ulong(s,(unsigned long) ipa->d[1]); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[1]); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; - i = fmt_ulong(s,(unsigned long) ipa->d[0]); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[0]); len += i; if (s) s += i; i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i; return len; } diff --git a/src/error_str.c b/src/error_str.c @@ -1,11 +1,15 @@ #include <errno.h> #include "error.h" +#include <string.h> -#define X(e,s) if (i == e) return s; +extern const int sys_nerr; +#define X(e,s) case e: return s; break; +#define XI(e,s) if (i == e) return s; -char *error_str(i) -int i; +char *error_str(int i) { + char *err = NULL; + switch (i) { X(0,"no error") X(error_intr,"interrupted system call") X(error_nomem,"out of memory") @@ -16,7 +20,6 @@ int i; X(error_timeout,"timed out") X(error_inprogress,"operation in progress") X(error_again,"temporary failure") - X(error_wouldblock,"input/output would block") X(error_pipe,"broken pipe") X(error_perm,"permission denied") X(error_acces,"access denied") @@ -72,7 +75,7 @@ int i; X(EMFILE,"process cannot open more files") #endif #ifdef ENOTTY - X(ENOTTY,"not a tty") + X(ENOTTY,"inappropriate ioctl for file or device") #endif #ifdef EFBIG X(EFBIG,"file too big") @@ -272,5 +275,9 @@ int i; #ifdef EREMCHG X(EREMCHG,"remote address changed") #endif - return "unknown error"; + default: /* take advantage of system strerror as a last resort */ + XI(error_wouldblock,"input/output would block") + if (i < sys_nerr) err = strerror(i); + return (err == NULL) ? "unknown error" : err; + } } diff --git a/src/error_temp.c b/src/error_temp.c @@ -1,17 +1,17 @@ #include <errno.h> #include "error.h" -#define X(n) if (e == n) return 1; +#define X(n) case n: return 1; break; +#define XI(n,s) if (n == e) return 1; -int error_temp(e) -int e; +int error_temp(int e) { + switch (e) { X(error_intr) X(error_nomem) X(error_txtbsy) X(error_io) X(error_timeout) - X(error_wouldblock) X(error_again) #ifdef EDEADLK X(EDEADLK) @@ -76,5 +76,7 @@ int e; #ifdef ENOLCK X(ENOLCK) #endif + } + XI(error_wouldblock, e) return 0; } diff --git a/src/fmt_ulong.c b/src/fmt_ulong.c @@ -12,6 +12,19 @@ unsigned int fmt_ulong(char *s, unsigned long u) return len; } +/* fmt_ulongalphabet is used if you have special numeral needs*/ +unsigned int fmt_ulongalphabet(char *s, unsigned long u, char *alphabet, unsigned long alphabetlen) +{ + unsigned int len; unsigned long q; + len = 1; q = u; + while (q >= alphabetlen) { ++len; q /= alphabetlen; } + if (s && alphabet) { + s += len; + do { *--s = alphabet[(u % alphabetlen)]; u /= alphabetlen; } while(u); /* handles u == 0 */ + } + return len; +} + unsigned int fmt_ulonglong(char *s, unsigned long long u) { unsigned int len; unsigned long long q; diff --git a/src/ip.c b/src/ip.c @@ -1,15 +1,17 @@ +#include <string.h> #include "ip.h" #include "fmt.h" #include "scan.h" +#include "case.h" -unsigned int ip_fmt(s,ip) -char *s; -struct ip_address *ip; +/* ip4 functions */ +static +unsigned int ip4_fmt(char *s, struct ip4_address *ip) { unsigned int len; unsigned int i; - + len = 0; i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; @@ -21,14 +23,13 @@ struct ip_address *ip; return len; } -unsigned int ip_scan(s,ip) -char *s; -struct ip_address *ip; +static +unsigned int ip4_scan(char *s, struct ip4_address *ip) { unsigned int i; unsigned int len; unsigned long u; - + len = 0; i = scan_ulong(s,&u); if (!i) return 0; ip->d[0] = u; s += i; len += i; if (*s != '.') return 0; ++s; ++len; @@ -40,15 +41,146 @@ struct ip_address *ip; return len; } -unsigned int ip_scanbracket(s,ip) -char *s; -struct ip_address *ip; +static +unsigned int ip4_scanbracket(char *s, struct ip4_address *ip) { unsigned int len; - + if (*s != '[') return 0; - len = ip_scan(s + 1,ip); + len = ip4_scan(s + 1,ip); if (!len) return 0; if (s[len + 1] != ']') return 0; return len + 2; } + +/* ip6 functions */ +static +unsigned int ip6_fmt(char *s, struct ip6_address *ip) +{ + unsigned int len; + unsigned int i, j, k, seenearliest; + unsigned long word; + unsigned int zf[8]; /* !0 means a word is all 0s */ + unsigned int hwm = 0; /* length of longest run */ + len = j = k = seenearliest = 0; + for (; j < 8; ++j) { + word = (unsigned long) ((ip->d[(j*2)]) << 8) + (ip->d[(j*2)+1]); + k = (word != 0) ? 0 : k + 1; /* length of this run */ + zf[j] = (word != 0) ? 0 : k; + if (k > hwm) hwm = k; + } + k = 0; + for (j = 0; j < 7; ++j) { + if (j+hwm > 7) break; /* continuation makes no sense because we are past the longest run */ + if (!seenearliest && zf[j] != 0 && zf[j+hwm] == hwm) { + seenearliest = 1; + while (zf[j] != 0 && j < 8) zf[j++] = hwm; + while (j < 8) zf[j++] = 0; /* at this point we don't care about runs, they should be encoded as 0, not "" */ + } + } + for (j = 0; j < 8; ++j) { + word = (unsigned long) ((ip->d[(j*2)]) << 8) + (ip->d[(j*2)+1]); + i = fmt_ulongalphabet(s, word, "0123456789abcdef", 16); + len += i; + if (s) s += i; + if ((j + 1) < 8) { + i = fmt_str(s,":"); + len += i; + if (s) s += i; + } + } + return len; +} + +static +unsigned int ip6_scan(char *s, struct ip6_address *ip) +{ + unsigned int i, j, k; + unsigned int len; + unsigned long u; + char *st = s; + unsigned char ebuf[16]; + + len = i = j = k = 0; + /* left side */ + for (j = 0; j < 16; ++j) { + ip->d[j] = 0; + } + for (j = 0; j < 8; ++j) { + i = scan_ulongalphabet(s,&u, "0123456789abcdef", 16); + /* if (!i) return 0; this could yet be valid!.. */ + if (!i) u = 0x0U; + ip->d[(j*2)] = (u & 0xff00) >> 8; + ip->d[(j*2)+1] = (u & 0x00ff); + s += i; + len += i; + if (*s == ':') if (*(s + 1) == ':') { + ++s; ++len; k = 1; break; + } + if (*s != ':') return 0; + ++s; ++len; + } + /* right side */ + if (k) { + for (j = 0; j < 8; ++j) { + i = scan_ulong(s,&u); + /* if (!i) return 0; this could yet be valid!.. */ + if (!i) u = 0x0U; + ebuf[(j*2)] = (u & 0xff00) >> 8; + ebuf[(j*2)+1] = (u & 0x00ff); + s += i; + len += i; + if (*s == ':') if (*(s + 1) == ':') { + break; + } + } + for (k = 0; k < (j * 2); ++k) { + ip->d[15-k] = ebuf[(j * 2)-k]; + } + } + return len; +} + +unsigned int ip6_scanbracket(char *s, struct ip_address *ip) +{ + /* SMTP-specific scanbracket. */ + unsigned int len; + + if (*s != '[') return 0; + if (*++s != 'I' && *s != 'i') return 0; + if (*++s != 'P' && *s != 'p') return 0; + if (*++s != 'V' && *s != 'v') return 0; + if (*++s != '6') return 0; + if (*++s != ':') return 0; + len = ip_scan(s + 1,ip); /* IPv6:::ffff:127.0.0.1 ... it's 2022, you never know ~H */ + if (!len) return 0; + if (s[len + 1] != ']') return 0; + return len + 7; +} + +unsigned int ip_fmt(char *s, struct ip_address *ip) +{ + return (ip->is6 ? + ip6_fmt(s, &(ip->ip.aaaa)) : + ip4_fmt(s, &(ip->ip.a)) + ); +} + +unsigned int ip_scan(char *s, struct ip_address *ip) +{ + unsigned int r; + /* this won't catch all cases of 6map4, but it's good enough. ~Hildie */ + if (case_starts(s, "::ffff:")) if ((r = ip4_scan(s + 7, &(ip->ip.a)))) return (ip->is6 = 0, r); + if (case_starts(s, "0::ffff:")) if ((r = ip4_scan(s + 8, &(ip->ip.a)))) return (ip->is6 = 0, r); + if ((r = ip6_scan(s, &(ip->ip.aaaa)))) return (ip->is6 = 1, r); + if ((r = ip4_scan(s, &(ip->ip.a)))) return (ip->is6 = 0, r); + return 0; +} + +unsigned int ip_scanbracket(char *s, struct ip_address *ip) +{ + unsigned int r; + if ((r = ip6_scanbracket(s, ip))) return (ip->is6 = 1, r); + if ((r = ip4_scanbracket(s, &(ip->ip.a)))) return (ip->is6 = 0, r); + return (ip->is6 = 0, 0); +} diff --git a/src/ipme.c b/src/ipme.c @@ -24,21 +24,22 @@ static int ipmeok = 0; static int ip6meok = 0; ipalloc ipme = {0}; -ip6alloc ip6me = {0}; +ipalloc ip6me = {0}; -int ipme_is(struct ip_address *ip) +int ip4me_is(struct ip_address *ip) { int i; - if (ipme_init() != 1) return -1; + if (ip4me_init() != 1) return -1; + if (ip->is6) return 0; for (i = 0;i < ipme.len;++i) - if (byte_equal(&ipme.ix[i].ip,4,ip)) + if (byte_equal(&(ipme.ix[i].ip.ip.a),4,&(ip->ip.a))) return 1; return 0; } static stralloc buf = {0}; -int ipme_init() +int ip4me_init() { struct ifconf ifc; char *x; @@ -56,7 +57,8 @@ int ipme_init() /* 0.0.0.0 is a special address which always refers to * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a. */ - byte_copy(&ix.ip,4,"\0\0\0\0"); + memset(&ix.ip,0,sizeof(struct ip_address)); + ix.ip.is6 = 0; if (!ipalloc_append(&ipme,&ix)) { return 0; } if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1; @@ -99,9 +101,11 @@ int ipme_init() #else len = sizeof(*ifr); #endif + memset(&ix.ip,0,sizeof(struct ip_address)); if (ifr->ifr_addr.sa_family == AF_INET) { sin = (struct sockaddr_in *) &ifr->ifr_addr; - byte_copy(&ix.ip,4,&sin->sin_addr); + ix.ip.is6 = 0; + byte_copy(&ix.ip.ip.a.d,4,&sin->sin_addr); if (ioctl(s,SIOCGIFFLAGS,x) == 0) if (ifr->ifr_flags & IFF_UP) if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; } @@ -113,12 +117,12 @@ int ipme_init() return 1; } -int ip6me_is(struct ip6_address *ip) +int ip6me_is(struct ip_address *ip) { int i; if (ip6me_init() != 1) return -1; for (i = 0;i < ip6me.len;++i) - if (byte_equal(&ip6me.ix[i].ip,4,ip)) + if (byte_equal(&(ip6me.ix[i].ip.ip.aaaa.d),16,&(ip->ip.aaaa.d))) return 1; return 0; } @@ -131,7 +135,7 @@ int ip6me_init() struct sockaddr_in6 *sin; int len; int s; - struct ip6_mx ix; + struct ip_mx ix; if (ip6meok) return 1; if (!ipalloc_readyplus(&ip6me,0)) return 0; @@ -140,8 +144,9 @@ int ip6me_init() /* :: is a special address which is specifically unspecified. */ - byte_copy(&ix.ip,16,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); - if (!ip6alloc_append(&ip6me,&ix)) { return 0; } + memset(&ix.ip,0,sizeof(struct ip_address)); + ix.ip.is6 = 1; + if (!ipalloc_append(&ip6me,&ix)) { return 0; } if ((s = socket(AF_INET6,SOCK_STREAM,0)) == -1) return -1; ifc.ifc_buf = 0; @@ -183,9 +188,11 @@ int ip6me_init() #else len = sizeof(*ifr); #endif - if (ifr->ifr_addr.sa_family == AF_INET) { + memset(&ix.ip,0,sizeof(struct ip_address)); + if (ifr->ifr_addr.sa_family == AF_INET6) { sin = (struct sockaddr_in6 *) &ifr->ifr_addr; - byte_copy(&ix.ip,16,&sin->sin6_addr); + ix.ip.is6 = 1; + byte_copy(&ix.ip.ip.aaaa,16,&sin->sin6_addr); if (ioctl(s,SIOCGIFFLAGS,x) == 0) if (ifr->ifr_flags & IFF_UP) if (!ip6alloc_append(&ip6me,&ix)) { close(s); return 0; } @@ -196,3 +203,13 @@ int ip6me_init() ip6meok = 1; return 1; } + +int ipme_is(struct ip_address *ip) { + return (ip->is6 ? ip6me_is(ip) : ip4me_is(ip)); +} + +int ipme_init() { + if (!ip4me_init()) return 0; + if (!ip6me_init()) return 0; + return 1; +} diff --git a/src/qmail-local.c b/src/qmail-local.c @@ -31,17 +31,35 @@ #include "gfrom.h" #include "auto_patrn.h" -void _noreturn_ usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nNT ] user homedir local dash ext domain sender aliasempty"); } - -void _noreturn_ temp_nomem() { strerr_die1x(111,"4.3.0 Out of memory."); } -void _noreturn_ temp_rewind() { strerr_die1x(111,"4.3.0 Unable to rewind message."); } -void _noreturn_ temp_childcrashed() { strerr_die1x(111,"4.3.0 Aack, child crashed."); } -void _noreturn_ temp_fork() { strerr_die3x(111,"4.3.0 Unable to fork: ",error_str(errno),"."); } -void _noreturn_ temp_read() { strerr_die3x(111,"4.3.0 Unable to read message: ",error_str(errno),"."); } -void _noreturn_ temp_slowlock() -{ strerr_die1x(111,"4.3.0 File has been locked for 30 seconds straight."); } -void _noreturn_ temp_qmail(char *fn) -{ strerr_die5x(111,"4.3.0 Unable to open ",fn,": ",error_str(errno)," ."); } +void _noreturn_ usage() { + strerr_die1x(100, "qmail-local: usage: qmail-local [ -nNT ] user homedir local dash ext domain sender aliasempty"); +} + +void _noreturn_ temp_nomem() { + strerr_die1x(111, "4.3.0 Out of memory."); +} +void _noreturn_ temp_rewind() { + strerr_die1x(111, "4.3.0 Unable to rewind message."); +} +void _noreturn_ temp_childcrashed() { + strerr_die1x(111, "4.3.0 Aack, child crashed."); +} +void _noreturn_ temp_fork() { + strerr_die3x(111, "4.3.0 Unable to fork: ", error_str(errno), "."); +} +void _noreturn_ temp_read() { + strerr_die3x(111, "4.3.0 Unable to read message: ", error_str(errno), "."); +} +void _noreturn_ +temp_slowlock() +{ + strerr_die1x(111, "4.3.0 File has been locked for 30 seconds straight."); +} +void _noreturn_ +temp_qmail(char *fn) +{ + strerr_die5x(111, "4.3.0 Unable to open ", fn, ": ", error_str(errno), " ."); +} int flagdoit = 0, flagprobe = 0; int flag99; @@ -73,304 +91,379 @@ char outbuf[1024]; char fntmptph[80 + FMT_ULONG * 2]; char fnnewtph[80 + FMT_ULONG * 2]; -void tryunlinktmp() { unlink(fntmptph); } -void sigalrm() { tryunlinktmp(); _exit(3); } +void tryunlinktmp() { + unlink(fntmptph); +} +void sigalrm() { + tryunlinktmp(); + _exit(3); +} -void maildir_child(dir) -char *dir; +void +maildir_child(char *dir) { - unsigned long pid; - unsigned long time; - char myhost[64]; - char *s; - int loop; - int fd; - substdio ss; - substdio ssout; - - sig_alarmcatch(sigalrm); - if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); } - pid = getpid(); - myhost[0] = 0; - gethostname(myhost,sizeof(myhost)); - for (loop = 0;;++loop) - { - time = now(); - s = fntmptph; - s += fmt_str(s,"tmp/"); - s += fmt_ulong(s,time); *s++ = '.'; - s += fmt_ulong(s,pid); *s++ = '.'; - s += fmt_strn(s,myhost,sizeof(myhost)); *s++ = 0; - alarm(86400); - fd = open_excl(fntmptph); - if (fd >= 0) - break; - if (errno == error_exist) { - /* really should never get to this point */ - if (loop == 2) _exit(1); - sleep(2); - } else { - _exit(1); - } - } - str_copy(fnnewtph,fntmptph); - byte_copy(fnnewtph,3,"new"); - - substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); - substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); - if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail; - if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail; - - switch(substdio_copy(&ssout,&ss)) - { - case -2: tryunlinktmp(); _exit(4); - case -3: goto fail; - } - - if (substdio_flush(&ssout) == -1) goto fail; - if (fsync(fd) == -1) goto fail; - if (close(fd) == -1) goto fail; /* NFS dorks */ - - if (link(fntmptph,fnnewtph) == -1) goto fail; - /* if it was error_exist, almost certainly successful; i hate NFS */ - tryunlinktmp(); _exit(0); - - fail: tryunlinktmp(); _exit(1); + unsigned long pid; + unsigned long time; + char myhost[64]; + char *s; + int loop; + int fd; + substdio ss; + substdio ssout; + + sig_alarmcatch(sigalrm); + if (chdir(dir) == -1) { + if (error_temp(errno)) + _exit(1); + _exit(2); + } + pid = getpid(); + myhost[0] = 0; + gethostname(myhost, sizeof(myhost)); + for (loop = 0;; ++loop) { + time = now(); + s = fntmptph; + s += fmt_str(s, "tmp/"); + s += fmt_ulong(s, time); + *s++ = '.'; + s += fmt_ulong(s, pid); + *s++ = '.'; + s += fmt_strn(s, myhost, sizeof(myhost)); + *s++ = 0; + alarm(86400); + fd = open_excl(fntmptph); + if (fd >= 0) + break; + if (errno == error_exist) { + /* really should never get to this point */ + if (loop == 2) + _exit(1); + sleep(2); + } else { + _exit(1); + } + } + str_copy(fnnewtph, fntmptph); + byte_copy(fnnewtph, 3, "new"); + + substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); + substdio_fdbuf(&ssout, write, fd, outbuf, sizeof(outbuf)); + if (substdio_put(&ssout, rpline.s, rpline.len) == -1) + goto fail; + if (substdio_put(&ssout, dtline.s, dtline.len) == -1) + goto fail; + + switch (substdio_copy(&ssout, &ss)) { + case -2: + tryunlinktmp(); + _exit(4); + case -3: + goto fail; + } + + if (substdio_flush(&ssout) == -1) + goto fail; + if (fsync(fd) == -1) + goto fail; + if (close(fd) == -1) + goto fail; /* NFS dorks */ + + if (link(fntmptph, fnnewtph) == -1) + goto fail; + /* if it was error_exist, almost certainly successful; i hate NFS */ + tryunlinktmp(); + _exit(0); + +fail: tryunlinktmp(); + _exit(1); } /* end child process */ -void maildir(fn) -char *fn; +void +maildir(char *fn) { - int child; - int wstat; - - if (seek_begin(0) == -1) temp_rewind(); - - switch(child = fork()) - { - case -1: - temp_fork(); - case 0: - maildir_child(fn); - _exit(111); - } - - wait_pid(&wstat,child); - if (wait_crashed(wstat)) - temp_childcrashed(); - switch(wait_exitcode(wstat)) - { - case 0: break; - case 2: strerr_die1x(111,"4.2.1 Unable to chdir to maildir. "); - case 3: strerr_die1x(111,"4.3.0 Timeout on maildir delivery. "); - case 4: strerr_die1x(111,"4.3.0 Unable to read message. "); - default: strerr_die1x(111,"4.3.0 Temporary error on maildir delivery. "); - } + int child; + int wstat; + + if (seek_begin(0) == -1) + temp_rewind(); + + switch (child = fork()) { + case -1: + temp_fork(); + case 0: + maildir_child(fn); + _exit(111); + } + + wait_pid(&wstat, child); + if (wait_crashed(wstat)) + temp_childcrashed(); + switch (wait_exitcode(wstat)) { + case 0: + break; + case 2: + strerr_die1x(111, "4.2.1 Unable to chdir to maildir. "); + case 3: + strerr_die1x(111, "4.3.0 Timeout on maildir delivery. "); + case 4: + strerr_die1x(111, "4.3.0 Unable to read message. "); + default: + strerr_die1x(111, "4.3.0 Temporary error on maildir delivery. "); + } } -void mailfile(fn) -char *fn; +void +mailfile(char *fn) { - int fd; - substdio ss; - substdio ssout; - int match; - seek_pos pos; - int flaglocked; - - if (seek_begin(0) == -1) temp_rewind(); - - fd = open_append(fn); - if (fd == -1) - strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),"4.2.1 . "); - - sig_alarmcatch(temp_slowlock); - alarm(30); - flaglocked = (lock_ex(fd) != -1); - alarm(0); - sig_alarmdefault(); - - seek_end(fd); - pos = seek_cur(fd); - - substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); - substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); - if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs; - if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs; - if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs; - for (;;) - { - if (getln(&ss,&messline,&match,'\n') != 0) - { - strerr_warn3("Unable to read message: ",error_str(errno),"4.3.0 . ",0); - if (flaglocked) seek_trunc(fd,pos); close(fd); - _exit(111); - } - if (!match && !messline.len) break; - if (gfrom(messline.s,messline.len)) - if (substdio_bput(&ssout,">",1)) goto writeerrs; - if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs; - if (!match) - { - if (substdio_bputs(&ssout,"\n")) goto writeerrs; - break; - } - } - if (substdio_bputs(&ssout,"\n")) goto writeerrs; - if (substdio_flush(&ssout)) goto writeerrs; - if (fsync(fd) == -1) goto writeerrs; - close(fd); - return; - - writeerrs: - strerr_warn5("Unable to write ",fn,": ",error_str(errno),"4.3.0 . ",0); - if (flaglocked) seek_trunc(fd,pos); - close(fd); - _exit(111); + int fd; + substdio ss; + substdio ssout; + int match; + seek_pos pos; + int flaglocked; + + if (seek_begin(0) == -1) + temp_rewind(); + + fd = open_append(fn); + if (fd == -1) + strerr_die5x(111, "4.2.1 Unable to open ", fn, ": ", error_str(errno), "."); + + sig_alarmcatch(temp_slowlock); + alarm(30); + flaglocked = (lock_ex(fd) != -1); + alarm(0); + sig_alarmdefault(); + + seek_end(fd); + pos = seek_cur(fd); + + substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); + substdio_fdbuf(&ssout, write, fd, outbuf, sizeof(outbuf)); + if (substdio_put(&ssout, ufline.s, ufline.len)) + goto writeerrs; + if (substdio_put(&ssout, rpline.s, rpline.len)) + goto writeerrs; + if (substdio_put(&ssout, dtline.s, dtline.len)) + goto writeerrs; + for (;;) { + if (getln(&ss, &messline, &match, '\n') != 0) { + strerr_warn3("4.3.0 Unable to read message: ", error_str(errno), ".", 0); + if (flaglocked) + seek_trunc(fd, pos); + close(fd); + _exit(111); + } + if (!match && !messline.len) + break; + if (gfrom(messline.s, messline.len)) + if (substdio_bput(&ssout, ">", 1)) + goto writeerrs; + if (substdio_bput(&ssout, messline.s, messline.len)) + goto writeerrs; + if (!match) { + if (substdio_bputs(&ssout, "\n")) + goto writeerrs; + break; + } + } + if (substdio_bputs(&ssout, "\n")) + goto writeerrs; + if (substdio_flush(&ssout)) + goto writeerrs; + if (fsync(fd) == -1) + goto writeerrs; + close(fd); + return; + +writeerrs: + strerr_warn5("Unable to write ", fn, ": ", error_str(errno), "4.3.0 . ", 0); + if (flaglocked) + seek_trunc(fd, pos); + close(fd); + _exit(111); } -void mailprogram(prog) -char *prog; +void +mailprogram(char *prog) { - int child; - char *(args[4]); - int wstat; - - if (seek_begin(0) == -1) temp_rewind(); - - switch(child = fork()) - { - case -1: - temp_fork(); - case 0: - args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0; - sig_pipedefault(); - execv(*args,args); - strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno),"4.3.0 . "); - } - - wait_pid(&wstat,child); - if (wait_crashed(wstat)) - temp_childcrashed(); - switch(wait_exitcode(wstat)) - { - case 100: - case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100); - case 0: break; - case 99: flag99 = 1; break; - default: _exit(111); - } + int child; + char *(args[4]); + int wstat; + + if (seek_begin(0) == -1) + temp_rewind(); + + /* About here is where you'd add the logic for Courieresque pipe-pipe deliveries. */ + + switch (child = fork()) { + case -1: + temp_fork(); + case 0: + /* MXF XXX: Should add a control file, maybe with a per user override?, to allow this to be execlineb, instead. */ + args[0] = "/bin/sh"; + args[1] = "-c"; + args[2] = prog; + args[3] = 0; + sig_pipedefault(); + execv(*args, args); + strerr_die3x(111, "4.3.0 Unable to run /bin/sh: ", error_str(errno), "."); + } + + wait_pid(&wstat, child); + if (wait_crashed(wstat)) + temp_childcrashed(); + switch (wait_exitcode(wstat)) { + case 100: + case 64: + case 65: + case 70: + case 76: + case 77: + case 78: + case 112: + _exit(100); + case 0: + break; + case 99: + flag99 = 1; + break; + default: + _exit(111); + } } unsigned long mailforward_qp = 0; -void mailforward(recips) -char **recips; +void +mailforward(char **recips) { - struct qmail qqt; - char *qqx; - substdio ss; - int match; - - if (seek_begin(0) == -1) temp_rewind(); - substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); - - if (qmail_open(&qqt) == -1) temp_fork(); - mailforward_qp = qmail_qp(&qqt); - qmail_put(&qqt,dtline.s,dtline.len); - do - { - if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; } - qmail_put(&qqt,messline.s,messline.len); - } - while (match); - qmail_from(&qqt,ueo.s); - while (*recips) qmail_to(&qqt,*recips++); - qqx = qmail_close(&qqt); - if (!*qqx) return; - strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,"."); + struct qmail qqt; + char *qqx; + substdio ss; + int match; + + if (seek_begin(0) == -1) + temp_rewind(); + substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); + + if (qmail_open(&qqt) == -1) + temp_fork(); + mailforward_qp = qmail_qp(&qqt); + qmail_put(&qqt, dtline.s, dtline.len); + do { + if (getln(&ss, &messline, &match, '\n') != 0) { + qmail_fail(&qqt); + break; + } + qmail_put(&qqt, messline.s, messline.len); + } + while (match); + qmail_from(&qqt, ueo.s); + while (*recips) + qmail_to(&qqt, *recips++); + qqx = qmail_close(&qqt); + if (!*qqx) + return; + strerr_die3x(*qqx == 'D' ? 100 : 111, "Unable to forward message: ", qqx + 1, "."); } -void bouncexf() +void +bouncexf() { - int match; - substdio ss; - - if (seek_begin(0) == -1) temp_rewind(); - substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); - for (;;) - { - if (getln(&ss,&messline,&match,'\n') != 0) temp_read(); - if (!match) break; - if (messline.len <= 1) - break; - if (messline.len == dtline.len) - if (!str_diffn(messline.s,dtline.s,dtline.len)) - strerr_die1x(100,"5.4.6 This message is looping: it already has my Delivered-To line. "); - } + int match; + substdio ss; + + if (seek_begin(0) == -1) + temp_rewind(); + substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); + for (;;) { + if (getln(&ss, &messline, &match, '\n') != 0) + temp_read(); + if (!match) + break; + if (messline.len <= 1) + break; + if (messline.len == dtline.len) + if (!str_diffn(messline.s, dtline.s, dtline.len)) + strerr_die1x(100, "5.4.6 This message is looping: it already has my Delivered-To line. "); + } } -void checkhome() +void +checkhome() { - struct stat st; - - if (stat(".",&st) == -1) - strerr_die3x(111,"Unable to stat home directory: ",error_str(errno),"4.3.0 . "); - if (st.st_mode & auto_patrn) - strerr_die1x(111,"4.7.0 Uh-oh: home directory is writable. "); - if (st.st_mode & 01000) { - if (flagdoit) - strerr_die1x(111,"4.2.1 Home directory is sticky: user is editing their .qmail file. "); - else - strerr_warn1("Warning: home directory is sticky.",0); - } + struct stat st; + + if (stat(".", &st) == -1) + strerr_die3x(111, "Unable to stat home directory: ", error_str(errno), "4.3.0 . "); + if (st.st_mode & auto_patrn) + strerr_die1x(111, "4.7.0 Uh-oh: home directory is writable. "); + if (st.st_mode & 01000) { + if (flagdoit) + strerr_die1x(111, "4.2.1 Home directory is sticky: user is editing their .qmail file. "); + else + strerr_warn1("Warning: home directory is sticky.", 0); + } } -int qmeox(dashowner) -char *dashowner; +int +qmeox(dashowner) + char *dashowner; { - struct stat st; - - if (!stralloc_copys(&qme,".qmail")) temp_nomem(); - if (!stralloc_cats(&qme,dash)) temp_nomem(); - if (!stralloc_cat(&qme,&safeext)) temp_nomem(); - if (!stralloc_cats(&qme,dashowner)) temp_nomem(); - if (!stralloc_0(&qme)) temp_nomem(); - if (stat(qme.s,&st) == -1) - { - if (error_temp(errno)) temp_qmail(qme.s); - return -1; - } - return 0; + struct stat st; + + if (!stralloc_copys(&qme, ".qmail")) + temp_nomem(); + if (!stralloc_cats(&qme, dash)) + temp_nomem(); + if (!stralloc_cat(&qme, &safeext)) + temp_nomem(); + if (!stralloc_cats(&qme, dashowner)) + temp_nomem(); + if (!stralloc_0(&qme)) + temp_nomem(); + if (stat(qme.s, &st) == -1) { + if (error_temp(errno)) + temp_qmail(qme.s); + return -1; + } + return 0; } -int qmeexists(fd,cutable) -int *fd; -int *cutable; +/* MXF NOTES: int *cutable == "PBR flag executable" + */ +int +qmeexists(int *fd, int *cutable) { - struct stat st; - - if (!stralloc_0(&qme)) temp_nomem(); - - *fd = open_read(qme.s); - if (*fd == -1) { - if (error_temp(errno)) temp_qmail(qme.s); - if (errno == error_perm) temp_qmail(qme.s); - if (errno == error_acces) temp_qmail(qme.s); - return 0; - } - - if (fstat(*fd,&st) == -1) temp_qmail(qme.s); - if ((st.st_mode & S_IFMT) == S_IFREG) { - if (st.st_mode & auto_patrn) - strerr_die1x(111,"4.7.0 Uh-oh: .qmail file is writable. "); - *cutable = !!(st.st_mode & 0100); - return 1; - } - close(*fd); - return 0; + struct stat st; + + if (!stralloc_0(&qme)) + temp_nomem(); + + *fd = open_read(qme.s); + if (*fd == -1) { + if (error_temp(errno)) + temp_qmail(qme.s); + if (errno == error_perm) + temp_qmail(qme.s); + if (errno == error_acces) + temp_qmail(qme.s); + return 0; + } + + if (fstat(*fd, &st) == -1) + temp_qmail(qme.s); + if ((st.st_mode & S_IFMT) == S_IFREG) { + if (st.st_mode & auto_patrn) + strerr_die1x(111, "4.7.0 Uh-oh: .qmail file is writable. "); + *cutable = !!(st.st_mode & 0100); + return 1; + } + close(*fd); + return 0; } /* "" "": "" */ @@ -380,40 +473,49 @@ int *cutable; /* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */ /* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */ /* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */ - -void qmesearch(fd,cutable) -int *fd; -int *cutable; +/* MXF NOTE: notably, this does not support using custom dashes anywhere + * except the first split of the address. */ +void +qmesearch(int *fd, int *cutable) { - int i; - - if (!stralloc_copys(&qme,".qmail")) temp_nomem(); - if (!stralloc_cats(&qme,dash)) temp_nomem(); - if (!stralloc_cat(&qme,&safeext)) temp_nomem(); - if (qmeexists(fd,cutable)) { - if (safeext.len >= 7) { - i = safeext.len - 7; - if (byte_equal("default",7,safeext.s + i)) - if (i <= str_len(ext)) /* paranoia */ - if (!env_put2("DEFAULT",ext + i)) temp_nomem(); - } - return; - } - - for (i = safeext.len;i >= 0;--i) - if (!i || (safeext.s[i - 1] == '-')) { - if (!stralloc_copys(&qme,".qmail")) temp_nomem(); - if (!stralloc_cats(&qme,dash)) temp_nomem(); - if (!stralloc_catb(&qme,safeext.s,i)) temp_nomem(); - if (!stralloc_cats(&qme,"default")) temp_nomem(); - if (qmeexists(fd,cutable)) { - if (i <= str_len(ext)) /* paranoia */ - if (!env_put2("DEFAULT",ext + i)) temp_nomem(); - return; - } - } - - *fd = -1; + int i; + + if (!stralloc_copys(&qme, ".qmail")) + temp_nomem(); + if (!stralloc_cats(&qme, dash)) + temp_nomem(); + if (!stralloc_cat(&qme, &safeext)) + temp_nomem(); + if (qmeexists(fd, cutable)) { + if (safeext.len >= 7) { + i = safeext.len - 7; + if (byte_equal("default", 7, safeext.s + i)) + if (i <= str_len(ext)) /* paranoia */ + if (!env_put2("DEFAULT", ext + i)) + temp_nomem(); + } + return; + } + + for (i = safeext.len; i >= 0; --i) + if (!i || (safeext.s[i - 1] == '-')) { + if (!stralloc_copys(&qme, ".qmail")) + temp_nomem(); + if (!stralloc_cats(&qme, dash)) + temp_nomem(); + if (!stralloc_catb(&qme, safeext.s, i)) + temp_nomem(); + if (!stralloc_cats(&qme, "default")) + temp_nomem(); + if (qmeexists(fd, cutable)) { + if (i <= str_len(ext)) /* paranoia */ + if (!env_put2("DEFAULT", ext + i)) + temp_nomem(); + return; + } + } + + *fd = -1; } unsigned long count_file = 0; @@ -421,279 +523,380 @@ unsigned long count_forward = 0; unsigned long count_program = 0; char count_buf[FMT_ULONG]; -void count_print() +void +count_print() { - substdio_puts(subfdoutsmall,"did "); - substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_file)); - substdio_puts(subfdoutsmall,"+"); - substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_forward)); - substdio_puts(subfdoutsmall,"+"); - substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program)); - substdio_puts(subfdoutsmall,"\n"); - if (mailforward_qp) - { - substdio_puts(subfdoutsmall,"qp "); - substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp)); - substdio_puts(subfdoutsmall,"\n"); - } - substdio_flush(subfdoutsmall); + substdio_puts(subfdoutsmall, "did "); + substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, count_file)); + substdio_puts(subfdoutsmall, "+"); + substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, count_forward)); + substdio_puts(subfdoutsmall, "+"); + substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, count_program)); + substdio_puts(subfdoutsmall, "\n"); + if (mailforward_qp) { + substdio_puts(subfdoutsmall, "qp "); + substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, mailforward_qp)); + substdio_puts(subfdoutsmall, "\n"); + } + substdio_flush(subfdoutsmall); } -void sayit(char *type, char *cmd, unsigned int len) +void +sayit(char *type, char *cmd, unsigned int len) { - substdio_puts(subfdoutsmall,type); - substdio_put(subfdoutsmall,cmd,len); - substdio_putsflush(subfdoutsmall,"\n"); + substdio_puts(subfdoutsmall, type); + substdio_put(subfdoutsmall, cmd, len); + substdio_putsflush(subfdoutsmall, "\n"); } -int main(int argc, char **argv) +int +main(int argc, char **argv) { - int opt; - unsigned int i; - unsigned int j; - int fd; - unsigned int numforward; - char **recips; - datetime_sec starttime; - int flagforwardonly; - char *x; - - umask(077); - sig_pipeignore(); - - if (!env_init()) temp_nomem(); - - flagdoit = 1; - while ((opt = getopt(argc,argv,"nNT")) != opteof) - switch(opt) - { - case 'n': flagdoit = 0; break; - case 'N': flagdoit = 1; break; - case 'T': flagdoit = 0; flagprobe = 1; break; - default: - usage(); - } - argc -= optind; - argv += optind; - - if (!(user = *argv++)) usage(); - if (!(homedir = *argv++)) usage(); - if (!(local = *argv++)) usage(); - if (!(dash = *argv++)) usage(); - if (!(ext = *argv++)) usage(); - if (!(host = *argv++)) usage(); - if (!(sender = *argv++)) usage(); - if (!(aliasempty = *argv++)) usage(); - if (*argv) usage(); - - if (homedir[0] != '/') usage(); - if (chdir(homedir) == -1) - strerr_die5x(111,"4.3.0 Unable to switch to ",homedir,": ",error_str(errno),"."); - checkhome(); - - if (!env_put2("HOST",host)) temp_nomem(); - if (!env_put2("HOME",homedir)) temp_nomem(); - if (!env_put2("USER",user)) temp_nomem(); - if (!env_put2("LOCAL",local)) temp_nomem(); - - if (!stralloc_copys(&envrecip,local)) temp_nomem(); - if (!stralloc_cats(&envrecip,"@")) temp_nomem(); - if (!stralloc_cats(&envrecip,host)) temp_nomem(); - - if (!stralloc_copy(&foo,&envrecip)) temp_nomem(); - if (!stralloc_0(&foo)) temp_nomem(); - if (!env_put2("RECIPIENT",foo.s)) temp_nomem(); - - if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem(); - if (!stralloc_cat(&dtline,&envrecip)) temp_nomem(); - for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_'; - if (!stralloc_cats(&dtline,"\n")) temp_nomem(); - - if (!stralloc_copy(&foo,&dtline)) temp_nomem(); - if (!stralloc_0(&foo)) temp_nomem(); - if (!env_put2("DTLINE",foo.s)) temp_nomem(); - - if (flagdoit) - bouncexf(); - - if (!env_put2("SENDER",sender)) temp_nomem(); - - if (!quote2(&foo,sender)) temp_nomem(); - if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem(); - if (!stralloc_cat(&rpline,&foo)) temp_nomem(); - for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_'; - if (!stralloc_cats(&rpline,">\n")) temp_nomem(); - - if (!stralloc_copy(&foo,&rpline)) temp_nomem(); - if (!stralloc_0(&foo)) temp_nomem(); - if (!env_put2("RPLINE",foo.s)) temp_nomem(); - - if (!stralloc_copys(&ufline,"From ")) temp_nomem(); - if (*sender) - { - unsigned int len; - char ch; - - len = str_len(sender); - if (!stralloc_readyplus(&ufline,len)) temp_nomem(); - for (i = 0;i < len;++i) - { - ch = sender[i]; - if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-'; - ufline.s[ufline.len + i] = ch; - } - ufline.len += len; - } - else - if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem(); - if (!stralloc_cats(&ufline," ")) temp_nomem(); - starttime = now(); - if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem(); - - if (!stralloc_copy(&foo,&ufline)) temp_nomem(); - if (!stralloc_0(&foo)) temp_nomem(); - if (!env_put2("UFLINE",foo.s)) temp_nomem(); - - x = ext; - if (!env_put2("EXT",x)) temp_nomem(); - x += str_chr(x,'-'); if (*x) ++x; - if (!env_put2("EXT2",x)) temp_nomem(); - x += str_chr(x,'-'); if (*x) ++x; - if (!env_put2("EXT3",x)) temp_nomem(); - x += str_chr(x,'-'); if (*x) ++x; - if (!env_put2("EXT4",x)) temp_nomem(); - - if (!stralloc_copys(&safeext,ext)) temp_nomem(); - case_lowerb(safeext.s,safeext.len); - for (i = 0;i < safeext.len;++i) - if (safeext.s[i] == '.') - safeext.s[i] = ':'; - - i = str_len(host); - i = byte_rchr(host,i,'.'); - if (!stralloc_copyb(&foo,host,i)) temp_nomem(); - if (!stralloc_0(&foo)) temp_nomem(); - if (!env_put2("HOST2",foo.s)) temp_nomem(); - i = byte_rchr(host,i,'.'); - if (!stralloc_copyb(&foo,host,i)) temp_nomem(); - if (!stralloc_0(&foo)) temp_nomem(); - if (!env_put2("HOST3",foo.s)) temp_nomem(); - i = byte_rchr(host,i,'.'); - if (!stralloc_copyb(&foo,host,i)) temp_nomem(); - if (!stralloc_0(&foo)) temp_nomem(); - if (!env_put2("HOST4",foo.s)) temp_nomem(); - - flagforwardonly = 0; - qmesearch(&fd,&flagforwardonly); - if (fd == -1) - if (*dash) - strerr_die1x(100,"5.1.1 Sorry, no mailbox here by that name."); - - if (!stralloc_copys(&ueo,sender)) temp_nomem(); - if (str_diff(sender,"")) - if (str_diff(sender,"#@[]")) - if (qmeox("-owner") == 0) - { - if (qmeox("-owner-default") == 0) - { - if (!stralloc_copys(&ueo,local)) temp_nomem(); - if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem(); - if (!stralloc_cats(&ueo,host)) temp_nomem(); - if (!stralloc_cats(&ueo,"-@[]")) temp_nomem(); - } - else - { - if (!stralloc_copys(&ueo,local)) temp_nomem(); - if (!stralloc_cats(&ueo,"-owner@")) temp_nomem(); - if (!stralloc_cats(&ueo,host)) temp_nomem(); + int opt; + unsigned int i; + unsigned int j; + int fd; + unsigned int numforward; + char **recips; + datetime_sec starttime; + int flagforwardonly; + char *x; + + umask(077); + sig_pipeignore(); + + if (!env_init()) + temp_nomem(); + + flagdoit = 1; + while ((opt = getopt(argc, argv, "nNT")) != opteof) + switch (opt) { + case 'n': + flagdoit = 0; + break; + case 'N': + flagdoit = 1; + break; + case 'T': + flagdoit = 0; + flagprobe = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!(user = *argv++)) + usage(); + if (!(homedir = *argv++)) + usage(); + if (!(local = *argv++)) + usage(); + if (!(dash = *argv++)) + usage(); + if (!(ext = *argv++)) + usage(); + if (!(host = *argv++)) + usage(); + if (!(sender = *argv++)) + usage(); + if (!(aliasempty = *argv++)) + usage(); + if (*argv) + usage(); + + if (homedir[0] != '/') + usage(); + if (chdir(homedir) == -1) + strerr_die5x(111, "4.3.0 Unable to switch to ", homedir, ": ", error_str(errno), "."); + checkhome(); + + if (!env_put2("HOST", host)) + temp_nomem(); + if (!env_put2("HOME", homedir)) + temp_nomem(); + if (!env_put2("USER", user)) + temp_nomem(); + if (!env_put2("LOCAL", local)) + temp_nomem(); + + if (!stralloc_copys(&envrecip, local)) + temp_nomem(); + if (!stralloc_cats(&envrecip, "@")) + temp_nomem(); + if (!stralloc_cats(&envrecip, host)) + temp_nomem(); + + if (!stralloc_copy(&foo, &envrecip)) + temp_nomem(); + if (!stralloc_0(&foo)) + temp_nomem(); + if (!env_put2("RECIPIENT", foo.s)) + temp_nomem(); + + if (!stralloc_copys(&dtline, "Delivered-To: ")) + temp_nomem(); + if (!stralloc_cat(&dtline, &envrecip)) + temp_nomem(); + for (i = 0; i < dtline.len; ++i) + if (dtline.s[i] == '\n') + dtline.s[i] = '_'; + if (!stralloc_cats(&dtline, "\n")) + temp_nomem(); + + if (!stralloc_copy(&foo, &dtline)) + temp_nomem(); + if (!stralloc_0(&foo)) + temp_nomem(); + if (!env_put2("DTLINE", foo.s)) + temp_nomem(); + + if (flagdoit) + bouncexf(); + + if (!env_put2("SENDER", sender)) + temp_nomem(); + + if (!quote2(&foo, sender)) + temp_nomem(); + if (!stralloc_copys(&rpline, "Return-Path: <")) + temp_nomem(); + if (!stralloc_cat(&rpline, &foo)) + temp_nomem(); + for (i = 0; i < rpline.len; ++i) + if (rpline.s[i] == '\n') + rpline.s[i] = '_'; + if (!stralloc_cats(&rpline, ">\n")) + temp_nomem(); + + if (!stralloc_copy(&foo, &rpline)) + temp_nomem(); + if (!stralloc_0(&foo)) + temp_nomem(); + if (!env_put2("RPLINE", foo.s)) + temp_nomem(); + + if (!stralloc_copys(&ufline, "From ")) + temp_nomem(); + if (*sender) { + unsigned int len; + char ch; + + len = str_len(sender); + if (!stralloc_readyplus(&ufline, len)) + temp_nomem(); + for (i = 0; i < len; ++i) { + ch = sender[i]; + if ((ch == ' ') || (ch == '\t') || (ch == '\n')) + ch = '-'; + ufline.s[ufline.len + i] = ch; + } + ufline.len += len; + } else if (!stralloc_cats(&ufline, "MAILER-DAEMON")) + temp_nomem(); + if (!stralloc_cats(&ufline, " ")) + temp_nomem(); + starttime = now(); + if (!stralloc_cats(&ufline, myctime(starttime))) + temp_nomem(); + + if (!stralloc_copy(&foo, &ufline)) + temp_nomem(); + if (!stralloc_0(&foo)) + temp_nomem(); + if (!env_put2("UFLINE", foo.s)) + temp_nomem(); + + x = ext; + if (!env_put2("EXT", x)) + temp_nomem(); + x += str_chr(x, '-'); + if (*x) + ++x; + if (!env_put2("EXT2", x)) + temp_nomem(); + x += str_chr(x, '-'); + if (*x) + ++x; + if (!env_put2("EXT3", x)) + temp_nomem(); + x += str_chr(x, '-'); + if (*x) + ++x; + if (!env_put2("EXT4", x)) + temp_nomem(); + + if (!stralloc_copys(&safeext, ext)) + temp_nomem(); + case_lowerb(safeext.s, safeext.len); + for (i = 0; i < safeext.len; ++i) + if (safeext.s[i] == '.') + safeext.s[i] = ':'; + + i = str_len(host); + i = byte_rchr(host, i, '.'); + if (!stralloc_copyb(&foo, host, i)) + temp_nomem(); + if (!stralloc_0(&foo)) + temp_nomem(); + if (!env_put2("HOST2", foo.s)) + temp_nomem(); + i = byte_rchr(host, i, '.'); + if (!stralloc_copyb(&foo, host, i)) + temp_nomem(); + if (!stralloc_0(&foo)) + temp_nomem(); + if (!env_put2("HOST3", foo.s)) + temp_nomem(); + i = byte_rchr(host, i, '.'); + if (!stralloc_copyb(&foo, host, i)) + temp_nomem(); + if (!stralloc_0(&foo)) + temp_nomem(); + if (!env_put2("HOST4", foo.s)) + temp_nomem(); + + flagforwardonly = 0; + qmesearch(&fd, &flagforwardonly); + if (fd == -1) + if (*dash) + strerr_die1x(100, "5.1.1 Sorry, no mailbox here by that name."); + + if (!stralloc_copys(&ueo, sender)) + temp_nomem(); + if (str_diff(sender, "")) + if (str_diff(sender, "#@[]")) + if (qmeox("-owner") == 0) { + if (qmeox("-owner-default") == 0) { + if (!stralloc_copys(&ueo, local)) + temp_nomem(); + if (!stralloc_cats(&ueo, "-owner-@")) + temp_nomem(); + if (!stralloc_cats(&ueo, host)) + temp_nomem(); + if (!stralloc_cats(&ueo, "-@[]")) + temp_nomem(); + } else { + if (!stralloc_copys(&ueo, local)) + temp_nomem(); + if (!stralloc_cats(&ueo, "-owner@")) + temp_nomem(); + if (!stralloc_cats(&ueo, host)) + temp_nomem(); + } + } + if (!stralloc_0(&ueo)) + temp_nomem(); + if (!env_put2("NEWSENDER", ueo.s)) + temp_nomem(); + + if (!stralloc_ready(&cmds, 0)) + temp_nomem(); + cmds.len = 0; + /* The full .qmail file must fit into memory for qmail-local. */ + if (fd != -1) + if (slurpclose(fd, &cmds, 256) == -1) + temp_nomem(); + + if (!cmds.len) { + if (!stralloc_copys(&cmds, aliasempty)) + temp_nomem(); + flagforwardonly = 0; } - } - if (!stralloc_0(&ueo)) temp_nomem(); - if (!env_put2("NEWSENDER",ueo.s)) temp_nomem(); - - if (!stralloc_ready(&cmds,0)) temp_nomem(); - cmds.len = 0; - if (fd != -1) - if (slurpclose(fd,&cmds,256) == -1) temp_nomem(); - - if (!cmds.len) - { - if (!stralloc_copys(&cmds,aliasempty)) temp_nomem(); - flagforwardonly = 0; - } - if (!cmds.len || (cmds.s[cmds.len - 1] != '\n')) - if (!stralloc_cats(&cmds,"\n")) temp_nomem(); - - numforward = 0; - i = 0; - for (j = 0;j < cmds.len;++j) - if (cmds.s[j] == '\n') - { - switch(cmds.s[i]) { case '#': case '.': case '/': case '|': break; - default: ++numforward; } - i = j + 1; - } - - recips = calloc(numforward + 1, sizeof(char *)); - if (!recips) temp_nomem(); - numforward = 0; - - flag99 = 0; - - i = 0; - for (j = 0;j < cmds.len;++j) - if (cmds.s[j] == '\n') - { - unsigned int k = j; - cmds.s[j] = 0; - while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) - cmds.s[--k] = 0; - switch(cmds.s[i]) - { - case 0: /* k == i */ - if (i) break; - strerr_die1x(111,"4.2.1 Uh-oh: first line of .qmail file is blank."); - case '#': - break; - case '.': - case '/': - ++count_file; - if (flagforwardonly) strerr_die1x(111,"4.7.0 Uh-oh: .qmail has file delivery but has x bit set."); - if (cmds.s[k - 1] == '/') - if (flagdoit) maildir(cmds.s + i); - else sayit("maildir ",cmds.s + i,k - i); - else - if (flagdoit) mailfile(cmds.s + i); - else sayit("mbox ",cmds.s + i,k - i); - break; - case '|': - ++count_program; - if (flagforwardonly) strerr_die1x(111,"4.7.0 Uh-oh: .qmail has prog delivery but has x bit set."); - if (flagdoit) mailprogram(cmds.s + i + 1); - else sayit("program ",cmds.s + i + 1,k - i - 1); - break; - case '+': - if (str_equal(cmds.s + i + 1,"list")) - flagforwardonly = 1; - break; - case '&': - ++i; - default: - ++count_forward; - if (flagdoit) recips[numforward++] = cmds.s + i; - else sayit("forward ",cmds.s + i,k - i); - break; - } - i = j + 1; - if (flag99) break; - } - - if (numforward) if (flagdoit) - { - recips[numforward] = 0; - mailforward(recips); - } - - count_print(); - _exit(0); + if (!cmds.len || (cmds.s[cmds.len - 1] != '\n')) + if (!stralloc_cats(&cmds, "\n")) + temp_nomem(); + + numforward = 0; + i = 0; + for (j = 0; j < cmds.len; ++j) + if (cmds.s[j] == '\n') { + switch (cmds.s[i]) { + case '#': + case '.': + case '/': + case '|': + break; + default: + ++numforward; + } + i = j + 1; + } + + recips = calloc(numforward + 1, sizeof(char *)); + if (!recips) + temp_nomem(); + numforward = 0; + + flag99 = 0; + + i = 0; + /* Daniel, if you are reading, this is a crock. + * An LUT would only cost you 2 to 6 kB on amd64, and is more easily + * patched to extend. */ + for (j = 0; j < cmds.len; ++j) + if (cmds.s[j] == '\n') { + unsigned int k = j; + cmds.s[j] = 0; + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) + cmds.s[--k] = 0; /* I like this, though. Smart. */ + switch (cmds.s[i]) { + case 0: /* k == i */ + if (i) + break; + strerr_die1x(111, "4.2.1 Uh-oh: first line of .qmail file is blank."); + case '#': + break; + case '.': + case '/': + ++count_file; + if (flagforwardonly) + strerr_die1x(111, "4.7.0 Uh-oh: .qmail has file delivery but has x bit set."); + if (cmds.s[k - 1] == '/') + if (flagdoit) + maildir(cmds.s + i); + else + sayit("maildir ", cmds.s + i, k - i); + else if (flagdoit) + mailfile(cmds.s + i); + else + sayit("mboxrd ", cmds.s + i, k - i); + break; + case '|': + ++count_program; + if (flagforwardonly) + strerr_die1x(111, "4.7.0 Uh-oh: .qmail has prog delivery but has x bit set."); + if (flagdoit) + mailprogram(cmds.s + i + 1); + else + sayit("program ", cmds.s + i + 1, k - i - 1); + break; + case '+': + if (str_equal(cmds.s + i + 1, "list")) + flagforwardonly = 1; + break; + case '&': + ++i; + default: + ++count_forward; + if (flagdoit) + recips[numforward++] = cmds.s + i; + else + sayit("forward ", cmds.s + i, k - i); + break; + } + i = j + 1; + if (flag99) + break; + } + + if (numforward) + if (flagdoit) { + recips[numforward] = 0; + mailforward(recips); + } + + count_print(); + _exit(0); } diff --git a/src/qmail-lspawn.c b/src/qmail-lspawn.c @@ -25,224 +25,284 @@ uid_t auto_uidp; gid_t auto_gidn; -void initialize(argc,argv) -int argc; -char **argv; +void +initialize(int argc, char **argv) { - aliasempty = argv[1]; - if (!aliasempty) _exit(100); + aliasempty = argv[1]; + if (!aliasempty) + _exit(100); - auto_uidp = inituid(auto_userp); - auto_uidq = inituid(auto_userq); + auto_uidp = inituid(auto_userp); + auto_uidq = inituid(auto_userq); - auto_gidn = initgid(auto_groupn); + auto_gidn = initgid(auto_groupn); } int truncreport = 3000; -void report(ss,wstat,s,len) -substdio *ss; -int wstat; -char *s; -int len; +void +report(substdio * ss, int wstat, char *s, int len) { - int i; - if (wait_crashed(wstat)) - { substdio_puts(ss,"Zqmail-local crashed.\n"); return; } - switch(wait_exitcode(wstat)) - { - case QLX_CDB: - substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); return; - case QLX_NOMEM: - substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); return; - case QLX_SYS: - substdio_puts(ss,"ZTemporary failure in qmail-lspawn.\n"); return; - case QLX_NOALIAS: - substdio_puts(ss,"ZUnable to find alias user!\n"); return; - case QLX_ROOT: - substdio_puts(ss,"ZNot allowed to perform deliveries as root.\n"); return; - case QLX_USAGE: - substdio_puts(ss,"ZInternal qmail-lspawn bug.\n"); return; - case QLX_NFS: - substdio_puts(ss,"ZNFS failure in qmail-local.\n"); return; - case QLX_EXECHARD: - substdio_puts(ss,"DUnable to run qmail-local.\n"); return; - case QLX_EXECSOFT: - substdio_puts(ss,"ZUnable to run qmail-local.\n"); return; - case QLX_EXECPW: - substdio_puts(ss,"ZUnable to run qmail-getpw.\n"); return; - case 111: case 71: case 74: case 75: - substdio_put(ss,"Z",1); break; - case 0: - substdio_put(ss,"K",1); break; - case 100: - default: - substdio_put(ss,"D",1); break; - } - - for (i = 0;i < len;++i) if (!s[i]) break; - substdio_put(ss,s,i); + int i; + if (wait_crashed(wstat)) { + substdio_puts(ss, "Zqmail-local crashed.\n"); + return; + } + switch (wait_exitcode(wstat)) { + case QLX_CDB: + substdio_puts(ss, "ZTrouble reading users/cdb in qmail-lspawn.\n"); + return; + case QLX_NOMEM: + substdio_puts(ss, "ZOut of memory in qmail-lspawn.\n"); + return; + case QLX_SYS: + substdio_puts(ss, "ZTemporary failure in qmail-lspawn.\n"); + return; + case QLX_NOALIAS: + substdio_puts(ss, "ZUnable to find alias user!\n"); + return; + case QLX_ROOT: + substdio_puts(ss, "ZNot allowed to perform deliveries as root.\n"); + return; + case QLX_USAGE: + substdio_puts(ss, "ZInternal qmail-lspawn bug.\n"); + return; + case QLX_NFS: + substdio_puts(ss, "ZNFS failure in qmail-local.\n"); + return; + case QLX_EXECHARD: + substdio_puts(ss, "DUnable to run qmail-local.\n"); + return; + case QLX_EXECSOFT: + substdio_puts(ss, "ZUnable to run qmail-local.\n"); + return; + case QLX_EXECPW: + substdio_puts(ss, "ZUnable to run qmail-getpw.\n"); + return; + case 111: + case 71: + case 74: + case 75: + substdio_put(ss, "Z", 1); + break; + case 0: + substdio_put(ss, "K", 1); + break; + case 100: + default: + substdio_put(ss, "D", 1); + break; + } + + for (i = 0; i < len; ++i) + if (!s[i]) + break; + substdio_put(ss, s, i); } stralloc lower = {0}; stralloc nughde = {0}; stralloc wildchars = {0}; -void nughde_get(local) -char *local; +void +nughde_get(char *local) { - char *(args[3]); - int pi[2]; - int gpwpid; - int gpwstat; - int r; - int fd; - int flagwild; - - if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM); - if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM); - if (!stralloc_0(&lower)) _exit(QLX_NOMEM); - case_lowerb(lower.s,lower.len); - - if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM); - - fd = open_read("users/cdb"); - if (fd == -1) - if (errno != error_noent) - _exit(QLX_CDB); - - if (fd != -1) - { - uint32 dlen; - unsigned int i; - - r = cdb_seek(fd,"",0,&dlen); - if (r != 1) _exit(QLX_CDB); - if (!stralloc_ready(&wildchars,(unsigned int) dlen)) _exit(QLX_NOMEM); - wildchars.len = dlen; - if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) _exit(QLX_CDB); - - i = lower.len; - flagwild = 0; - - do - { - /* i > 0 */ - if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) - { - r = cdb_seek(fd,lower.s,i,&dlen); - if (r == -1) _exit(QLX_CDB); - if (r == 1) - { - if (!stralloc_ready(&nughde,(unsigned int) dlen)) _exit(QLX_NOMEM); - nughde.len = dlen; - if (cdb_bread(fd,nughde.s,nughde.len) == -1) _exit(QLX_CDB); - if (flagwild) - if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM); - if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); - close(fd); - return; - } - } - --i; - flagwild = 1; - } - while (i); - - close(fd); - } - - if (pipe(pi) == -1) _exit(QLX_SYS); - args[0] = "bin/qmail-getpw"; - args[1] = local; - args[2] = 0; - switch(gpwpid = fork()) - { - case -1: - _exit(QLX_SYS); - case 0: - if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE); - if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE); - close(pi[0]); - if (fd_move(1,pi[1]) == -1) _exit(QLX_SYS); - execv(*args,args); - _exit(QLX_EXECPW); - } - close(pi[1]); - - if (slurpclose(pi[0],&nughde,128) == -1) _exit(QLX_SYS); - - if (wait_pid(&gpwstat,gpwpid) != -1) - { - if (wait_crashed(gpwstat)) _exit(QLX_SYS); - if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat)); - } + char *(args[3]); + int pi[2]; + int gpwpid; + int gpwstat; + int r; + int fd; + int flagwild; + + if (!stralloc_copys(&lower, "!")) + _exit(QLX_NOMEM); + if (!stralloc_cats(&lower, local)) + _exit(QLX_NOMEM); + if (!stralloc_0(&lower)) + _exit(QLX_NOMEM); + case_lowerb(lower.s, lower.len); + + if (!stralloc_copys(&nughde, "")) + _exit(QLX_NOMEM); + + fd = open_read("users/cdb"); + if (fd == -1) + if (errno != error_noent) + _exit(QLX_CDB); + + if (fd != -1) { + uint32 dlen; + unsigned int i; + + r = cdb_seek(fd, "", 0, &dlen); + if (r != 1) + _exit(QLX_CDB); + if (!stralloc_ready(&wildchars, (unsigned int)dlen)) + _exit(QLX_NOMEM); + wildchars.len = dlen; + if (cdb_bread(fd, wildchars.s, wildchars.len) == -1) + _exit(QLX_CDB); + + i = lower.len; + flagwild = 0; + + do { + /* i > 0 */ + if (!flagwild || (i == 1) || (byte_chr(wildchars.s, wildchars.len, lower.s[i - 1]) < wildchars.len)) { + r = cdb_seek(fd, lower.s, i, &dlen); + if (r == -1) + _exit(QLX_CDB); + if (r == 1) { + if (!stralloc_ready(&nughde, (unsigned int)dlen)) + _exit(QLX_NOMEM); + nughde.len = dlen; + if (cdb_bread(fd, nughde.s, nughde.len) == -1) + _exit(QLX_CDB); + if (flagwild) + if (!stralloc_cats(&nughde, local + i - 1)) + _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) + _exit(QLX_NOMEM); + close(fd); + return; + } + } + --i; + flagwild = 1; + } + while (i); + + close(fd); + } + + if (pipe(pi) == -1) + _exit(QLX_SYS); + args[0] = "bin/qmail-getpw"; + args[1] = local; + args[2] = 0; + switch (gpwpid = fork()) { + case -1: + _exit(QLX_SYS); + case 0: + if (prot_gid(auto_gidn) == -1) + _exit(QLX_USAGE); + if (prot_uid(auto_uidp) == -1) + _exit(QLX_USAGE); + close(pi[0]); + if (fd_move(1, pi[1]) == -1) + _exit(QLX_SYS); + execv(*args, args); + _exit(QLX_EXECPW); + } + close(pi[1]); + + if (slurpclose(pi[0], &nughde, 128) == -1) + _exit(QLX_SYS); + + if (wait_pid(&gpwstat, gpwpid) != -1) { + if (wait_crashed(gpwstat)) + _exit(QLX_SYS); + if (wait_exitcode(gpwstat) != 0) + _exit(wait_exitcode(gpwstat)); + } } -int spawn(fdmess,fdout,s,r,at) -int fdmess; int fdout; -char *s; char *r; int at; +int +spawn(int fdmess, int fdout, char *s, char *r, int at) { - int f; - - if (!(f = fork())) - { - char *(args[11]); - unsigned long u; - int n; - uid_t uid; - gid_t gid; - char *x; - unsigned int xlen; - - r[at] = 0; - if (!r[0]) _exit(0); /* <> */ - - if (chdir(auto_qmail) == -1) _exit(QLX_USAGE); - - nughde_get(r); - - x = nughde.s; - xlen = nughde.len; - - args[0] = "bin/qmail-local"; - args[1] = "--"; - args[2] = x; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; - - scan_ulong(x,&u); - uid = u; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; - - scan_ulong(x,&u); - gid = u; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; - - args[3] = x; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; - - args[4] = r; - args[5] = x; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; - - args[6] = x; - n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; - - args[7] = r + at + 1; - args[8] = s; - args[9] = aliasempty; - args[10] = 0; - - if (fd_move(0,fdmess) == -1) _exit(QLX_SYS); - if (fd_move(1,fdout) == -1) _exit(QLX_SYS); - if (fd_copy(2,1) == -1) _exit(QLX_SYS); - if (prot_gid(gid) == -1) _exit(QLX_USAGE); - if (prot_uid(uid) == -1) _exit(QLX_USAGE); - if (!getuid()) _exit(QLX_ROOT); - - execv(*args,args); - if (error_temp(errno)) _exit(QLX_EXECSOFT); - _exit(QLX_EXECHARD); - } - return f; + int f; + + if (!(f = fork())) { + char *(args[11]); + unsigned long u; + int n; + uid_t uid; + gid_t gid; + char *x; + unsigned int xlen; + + r[at] = 0; + if (!r[0]) + _exit(0); /* <> */ + + if (chdir(auto_qmail) == -1) + _exit(QLX_USAGE); + + nughde_get(r); + + x = nughde.s; + xlen = nughde.len; + + args[0] = "bin/qmail-local"; + args[1] = "--"; + args[2] = x; + n = byte_chr(x, xlen, 0); + if (n++ == xlen) + _exit(QLX_USAGE); + x += n; + xlen -= n; + + scan_ulong(x, &u); + uid = u; + n = byte_chr(x, xlen, 0); + if (n++ == xlen) + _exit(QLX_USAGE); + x += n; + xlen -= n; + + scan_ulong(x, &u); + gid = u; + n = byte_chr(x, xlen, 0); + if (n++ == xlen) + _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[3] = x; + n = byte_chr(x, xlen, 0); + if (n++ == xlen) + _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[4] = r; + args[5] = x; + n = byte_chr(x, xlen, 0); + if (n++ == xlen) + _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[6] = x; + n = byte_chr(x, xlen, 0); + if (n++ == xlen) + _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[7] = r + at + 1; + args[8] = s; + args[9] = aliasempty; + args[10] = 0; /* Would need one additional arg to add an lda-prepend file */ + + if (fd_move(0, fdmess) == -1) + _exit(QLX_SYS); + if (fd_move(1, fdout) == -1) + _exit(QLX_SYS); + if (fd_copy(2, 1) == -1) + _exit(QLX_SYS); + if (prot_gid(gid) == -1) + _exit(QLX_USAGE); + if (prot_uid(uid) == -1) + _exit(QLX_USAGE); + if (!getuid()) + _exit(QLX_ROOT); + + execv(*args, args); + if (error_temp(errno)) + _exit(QLX_EXECSOFT); + _exit(QLX_EXECHARD); + } + return f; } diff --git a/src/qmail-remote.c b/src/qmail-remote.c @@ -34,7 +34,7 @@ #include "timeoutwrite.h" #define HUGESMTPTEXT 5000 - +/* ? Sort errors into netw and application */ #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ unsigned long port = PORT_SMTP; @@ -50,11 +50,16 @@ stralloc sender = {0}; int pedanticsmtp = 1; saa reciplist = {0}; +/* These defines make it easier to sort for future wreckage into app and net subprograms. */ +#define _err_app +#define _err_net +#define _call_app +#define _call_net -struct ip_address partner; +/* XXX: should be an ip46full-like object */ +_call_app struct ip_address partner; -void out(s) -char *s; { +void out(char *s) { if (substdio_puts(subfdoutsmall, s) == -1) _exit(0); } @@ -68,7 +73,7 @@ void _noreturn_ zerodie() { _exit(0); } void -outsafe(sa) stralloc * sa; +outsafe(stralloc * sa) { int i; char ch; @@ -83,85 +88,75 @@ outsafe(sa) stralloc * sa; } } -void _noreturn_ temp_nomem() { - out("ZOut of memory. (#4.3.0)\n"); +void _noreturn_ _err_app temp_nomem() { + out("Z4.3.0 Out of memory.\n"); zerodie(); } -void _noreturn_ -temp_oserr() +void _noreturn_ _err_net _err_app temp_oserr() { - out("ZSystem resources temporarily unavailable. (#4.3.0)\n"); + out("Z4.3.0 System resources temporarily unavailable.\n"); zerodie(); } -void _noreturn_ -temp_noconn() +void _noreturn_ _err_net temp_noconn() { - out("ZSorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); + out("Z4.4.1 Sorry, I wasn't able to establish an SMTP connection.\n"); zerodie(); } -void _noreturn_ temp_read() { - out("ZUnable to read message. (#4.3.0)\n"); +void _noreturn_ _err_app temp_read() { + out("Z4.3.0 Unable to read message.\n"); zerodie(); } -void _noreturn_ -temp_dnscanon() +void _noreturn_ _err_net temp_dnscanon() { - out("ZCNAME lookup failed temporarily. (#4.4.3)\n"); + out("Z4.4.3 CNAME lookup failed temporarily.\n"); zerodie(); } -void _noreturn_ -temp_dns() +void _noreturn_ _err_net temp_dns() { - out("ZSorry, I couldn't find any host by that name. (#4.1.2)\n"); + out("Z4.1.2 Sorry, I couldn't find any host by that name.\n"); zerodie(); } -void _noreturn_ -temp_chdir() +void _noreturn_ _err_app temp_chdir() { - out("ZUnable to switch to home directory. (#4.3.0)\n"); + out("Z4.3.0 Unable to switch to home directory.\n"); zerodie(); } -void _noreturn_ -temp_control() +void _noreturn_ _err_app temp_control() { - out("ZUnable to read control files. (#4.3.0)\n"); + out("Z4.3.0 Unable to read control files.\n"); zerodie(); } -void _noreturn_ -perm_partialline() +void _noreturn_ _err_app perm_partialline() { - out("DSMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); + out("D5.6.2 SMTP cannot transfer messages with partial final lines.\n"); zerodie(); } -void _noreturn_ -perm_usage() +void _noreturn_ _err_app _err_net perm_usage() { - out("DI (qmail-remote) was invoked improperly. (#5.3.5)\n"); + out("D5.3.5 I (qmail-remote) was invoked improperly.\n"); zerodie(); } -void _noreturn_ -perm_dns() +void _noreturn_ _err_net perm_dns() { out("D5.1.2 Sorry, I couldn't find any host named "); outsafe(&host); out(".\n"); zerodie(); } -void _noreturn_ -perm_nomx() +void _noreturn_ _err_net perm_nomx() { out("D5.4.4 Sorry, I couldn't find a mail exchanger or IP address.\n"); zerodie(); } -void _noreturn_ -perm_ambigmx() +void _noreturn_ _err_net 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"); + out("D5.4.6 Sorry. Although I'm listed as a best-preference MX, A or AAAA for that host,\n" + "it isn't in my control/locals file, so I don't treat it as local.\n"); zerodie(); } +int flagcritical = 0; + void outhost() { @@ -170,26 +165,25 @@ outhost() _exit(0); } -int flagcritical = 0; - void _noreturn_ dropped() { - out("ZConnected to "); + out("Z4.4.2 Connected to "); outhost(); - out(" but connection died. "); + out(" but connection died."); if (flagcritical) - out("Possible duplicate! "); - out("(#4.4.2)\n"); + out(" Possible duplicate!"); + out("\n"); zerodie(); } int timeoutconnect = 60; -int smtpfd; +int smtpfdin; +int smtpfdou; int timeout = 1200; -GEN_SAFE_TIMEOUTREAD(saferead, timeout, smtpfd, dropped()) -GEN_SAFE_TIMEOUTWRITE(safewrite, timeout, smtpfd, dropped()) +GEN_SAFE_TIMEOUTREAD(saferead, timeout, smtpfdin, dropped()) +GEN_SAFE_TIMEOUTWRITE(safewrite, timeout, smtpfdou, dropped()) char inbuf[1024]; substdio ssin = SUBSTDIO_FDBUF(read, 0, inbuf, sizeof(inbuf)); @@ -227,10 +221,13 @@ smtpcode() temp_nomem(); get(&ch); + if (ch < '0' || ch > '9') return 599; code = ch - '0'; get(&ch); + if (ch < '0' || ch > '9') return 599; code = code * 10 + (ch - '0'); get(&ch); + if (ch < '0' || ch > '9') return 599; code = code * 10 + (ch - '0'); for (;;) { get(&ch); @@ -248,6 +245,56 @@ smtpcode() return code; } +/* smtpcodetext + * globals: smtptext, both directly and indirectly + * inputs: a stralloc to scribble on + * outputs: a code + * side-effects: reads an SMTP response from the server, + */ +unsigned long +smtpcodetext(stralloc *sa) +{ + /* We assume we get a stralloc we can scribble on. Hildie */ + unsigned char ch = 0; + unsigned long code = 0; + + if (!stralloc_copys(&smtptext, "")) + temp_nomem(); + + if (sa != NULL) if (!stralloc_copys(sa, "")) + temp_nomem(); + + if (sa != NULL) stralloc_readyplus(sa, 256); /* It doesn't hurt to have a little extra space eh? Hildie */ + get(&ch); + if (ch < '0' || ch > '9') return 599; + code = ch - '0'; + get(&ch); + if (ch < '0' || ch > '9') return 599; + code = code * 10 + (ch - '0'); + get(&ch); + if (ch < '0' || ch > '9') return 599; + code = code * 10 + (ch - '0'); + for (;;) { + get(&ch); + if (ch != '-') /* If it is a dash, we keep iterating until we get */ + break; + while (ch != '\n') /* XXX - wouldn't strip \r Hildie */ { + if (sa != NULL) stralloc_cat(sa, &ch, 1); + get(&ch); + } + /* .. wouldn't ch be \n at this stage? Hildie */ + if (ch == '\n') stralloc_cat(sa, &ch, 1); + get(&ch); + get(&ch); + get(&ch); + } + while (ch != '\n') { + get(&ch); + } + + return code; +} + void outsmtptext() { @@ -264,7 +311,7 @@ outsmtptext() } } -void +void _call_app quit(char *prepend, char *append) { @@ -276,11 +323,12 @@ quit(char *prepend, out(append); out(".\n"); outsmtptext(); - if (pedanticsmtp) smtpcode(); /* Discard */ - zerodie(); + // if (pedanticsmtp) smtpcode(); + /* Discard; &smtptext will just be freed when we die */ + zerodie(); /* the qmail-remote-net program should cut if the qmail-remote-smtpc program dies zero (success or permfail) */ } -void +void _call_app blastsmtp() { int r; @@ -293,7 +341,7 @@ blastsmtp() if (r == -1) temp_read(); if (ch == '.') - substdio_put(&smtpto, ".", 1); + substdio_put(&smtpto, ".", 1); /* SMTP quoting crock. Only needed for DATA, not BDAT. Hildie */ while (ch != '\n') { if (ch == '\r') { r = substdio_get(&ssin, &ch, 1); @@ -302,18 +350,18 @@ blastsmtp() if (r == -1) temp_read(); if (ch != '\n') { - substdio_put(&smtpto, "\r\n", 2); + substdio_put(&smtpto, "\r\n", 2); /* Accepts both \r\n... */ } else break; } substdio_put(&smtpto, &ch, 1); r = substdio_get(&ssin, &ch, 1); if (r == 0) - perm_partialline(); + perm_partialline(); /* Daniel, this is a crock. remote should fastforward the file to the end, check it ends appropriately, and if not, fail fast. */ if (r == -1) temp_read(); } - substdio_put(&smtpto, "\r\n", 2); + substdio_put(&smtpto, "\r\n", 2); /* ... and \n in the queue. Why shouldn't we just store \r\n in the queue, as it's required by SMTP anyway and would allow us to give an accurate size description for SIZE= and BDAT=? */ } flagcritical = 1; @@ -337,8 +385,8 @@ smtp() 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); + close(smtpfdin); + close(smtpfdou); /* Already closed? Doesn't matter. Hildie */ return; } @@ -347,6 +395,7 @@ smtp() substdio_puts(&smtpto, "\r\n"); substdio_flush(&smtpto); code = smtpcode(); + esmtp = 1; 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. */ @@ -356,7 +405,7 @@ smtp() substdio_flush(&smtpto); code = smtpcode(); if (code < 250 || code > 259) - quit("ZConnected to ", " but my name was rejected"); + quit("ZConnected to ", " but my name was rejected, both for EHLO and HELO. I do not expect to be able to deliver mail here at this time."); } substdio_puts(&smtpto, "MAIL FROM:<"); @@ -367,7 +416,7 @@ smtp() if (code >= 500) quit("DConnected to ", " but sender was rejected"); if (code >= 400) - quit("ZConnected to ", " but sender was rejected"); + quit("ZConnected to ", " but sender was deferred"); flagbother = 0; for (i = 0; i < reciplist.len; ++i) { @@ -395,7 +444,7 @@ smtp() } } if (!flagbother) - quit("DGiving up on ", ""); + quit("DGiving up on ", " because remote server "); substdio_putsflush(&smtpto, "DATA\r\n"); code = smtpcode(); @@ -446,61 +495,75 @@ addrmangle(stralloc * saout, char *s) temp_nomem(); } -void -getcontrols() +void _call_app +getappcontrols() { - if (control_init() == -1) - temp_control(); - if (control_readint(&pedanticsmtp, "control/pedanticsmtp") == -1) + if (control_readint(&pedanticsmtp, "control/pedanticsmtp") == -1) _call_app pedanticsmtp = 1; // default to pedantic - if (control_readint(&timeout, "control/timeoutremote") == -1) + if (control_readint(&timeout, "control/timeoutremote") == -1) _call_app temp_control(); - if (control_readint(&timeoutconnect, "control/timeoutconnect") == -1) + if (control_rldef(&helohost, "control/helohost", 1, NULL) != 1) _call_app temp_control(); - if (control_rldef(&helohost, "control/helohost", 1, NULL) != 1) - temp_control(); - switch (control_readfile(&routes, "control/smtproutes", 0)) { - case -1: +} + +void _call_net +getnetcontrols() +{ + if (control_readint(&timeoutconnect, "control/timeoutconnect") == -1) _call_net 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; + switch (control_readfile(&routes, "control/smtproutes", 0)) { _call_net /* would be looked up by */ + 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; } } +void _call_app _call_net +getcontrols() +{ + if (control_init() == -1) + temp_control(); + getappcontrols(); + getnetcontrols(); +} + int main(int argc, char **argv) { - static ipalloc ip = { - 0 - }; int i; - unsigned long random; - char **recips; - unsigned long prefme; - char *relayhost; + _call_net static ipalloc ip = {0}; + _call_net unsigned long prefme; + _call_net char *relayhost; + _call_app unsigned long random; + _call_app char **recips; sig_pipeignore(); + if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); - getcontrols(); + getcontrols(); if (!stralloc_copys(&host, argv[1])) temp_nomem(); + _call_net // { + 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; @@ -513,17 +576,18 @@ main(int argc, char **argv) if (!stralloc_copys(&host, relayhost)) temp_nomem(); } + // } + _call_app addrmangle(&sender, argv[2]); - addrmangle(&sender, argv[2]); - - if (!saa_readyplus(&reciplist, 0)) + _call_app if (!saa_readyplus(&reciplist, 0)) temp_nomem(); - if (ipme_init() != 1) + + _call_net if (ipme_init() != 1) temp_oserr(); - recips = argv + 3; - while (*recips) { + _call_app recips = argv + 3; + _call_app while (*recips) { if (!saa_readyplus(&reciplist, 1)) temp_nomem(); reciplist.sa[reciplist.len] = sauninit; @@ -532,20 +596,20 @@ main(int argc, char **argv) ++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) + _call_net 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(); } + _call_net // { if (ip.len <= 0) perm_nomx(); @@ -565,23 +629,25 @@ main(int argc, char **argv) if (i >= ip.len) perm_ambigmx(); - for (i = 0; i < ip.len; ++i) + _call_net 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) + smtpfdin = socket(AF_INET, SOCK_STREAM, 0); + if (smtpfdin == -1) temp_oserr(); + smtpfdou = smtpfdin; - if (timeoutconn(smtpfd, &ip.ix[i].ip, (unsigned int)port, timeoutconnect) == 0) { + if (timeoutconn(smtpfdin, &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 + _call_app smtp(); /* does not return, unless greeting * wrong */ } tcpto_err(&ip.ix[i].ip, errno == error_timeout); - close(smtpfd); + close(smtpfdin); + close(smtpfdou); } temp_noconn(); diff --git a/src/scan_ulong.c b/src/scan_ulong.c @@ -9,3 +9,33 @@ unsigned int scan_ulong(char *s, unsigned long *u) { result = result * 10 + c; ++pos; } *u = result; return pos; } + +/* no quick LUTtery; specific functions could use a static LUT but that's out scope here */ +static inline long first(char x, char *iny, unsigned long ylen) +{ + int i; + /* iny can only be ascii characters unless you change char somehow */ + /* unsigned long y[256]; * oof, a whole kb */ + for (i = 0; i < ylen; ++i) + { + /* y[(iny[i])] = i; */ + if (x == iny[i]) return i; + } + return -1; +} + +/* kinda unavoidably inefficient ~Hildie */ +unsigned int scan_ulongalphabet(char *s, unsigned long *u, char *alphabet, unsigned long alphabetlen) +{ + unsigned int pos; + unsigned long result; + long c; + pos = 0; result = 0; + c = (long) (unsigned char) (first(s[pos], alphabet, alphabetlen)); + while (c < alphabetlen && c >= 0) + { + result = result * alphabetlen + c; ++pos; + c = (long) (unsigned char) (first(s[pos], alphabet, alphabetlen)); + } + *u = result; return pos; +} diff --git a/src/slurpclose.c b/src/slurpclose.c @@ -4,10 +4,10 @@ #include "readwrite.h" #include "error.h" -int slurpclose(fd,sa,bufsize) -int fd; -stralloc *sa; -int bufsize; +/* This Bernstein slurpclose strides across the input, + * at the pitch specified in bufsize. It returns the last + * return of read() so that that might be */ +int slurpclose(int fd, stralloc *sa, int bufsize) { int r; for (;;) { diff --git a/src/tcpto.c b/src/tcpto.c @@ -9,156 +9,233 @@ #include "datetime.h" #include "readwrite.h" -char tcpto_buf[1024]; +char tcpto_buf[2048]; static int flagwasthere; static int fdlock; -static int getbuf() +static int +getbuf() { - int r; - int fd; - - fdlock = open_write("queue/lock/tcpto"); - if (fdlock == -1) return 0; - fd = open_read("queue/lock/tcpto"); - if (fd == -1) { close(fdlock); return 0; } - if (lock_ex(fdlock) == -1) { close(fdlock); close(fd); return 0; } - r = read(fd,tcpto_buf,sizeof(tcpto_buf)); - close(fd); - if (r == -1) { close(fdlock); return 0; } - r >>= 4; - if (!r) close(fdlock); - return r; + int r; + int fd; + + fdlock = open_write("queue/lock/tcpto"); + if (fdlock == -1) + return 0; + fd = open_read("queue/lock/tcpto"); + if (fd == -1) { + close(fdlock); + return 0; + } + if (lock_ex(fdlock) == -1) { + close(fdlock); + close(fd); + return 0; + } + r = read(fd, tcpto_buf, sizeof(tcpto_buf)); + close(fd); + if (r == -1) { + close(fdlock); + return 0; + } + r >>= 5; + if (!r) + close(fdlock); + return r; } -int tcpto(ip) struct ip_address *ip; +int +tcpto(struct ip_address *ip) { - int n; - int i; - char *record; - - flagwasthere = 0; - - n = getbuf(); - if (!n) return 0; - close(fdlock); - - record = tcpto_buf; - for (i = 0;i < n;++i) - { - if (byte_equal(ip->d,4,record)) - { - flagwasthere = 1; - if (record[4] >= 2) - { - datetime_sec when = (unsigned long) (unsigned char) record[11]; - when = (when << 8) + (unsigned long) (unsigned char) record[10]; - when = (when << 8) + (unsigned long) (unsigned char) record[9]; - when = (when << 8) + (unsigned long) (unsigned char) record[8]; - - if (now() - when < ((60 + (getpid() & 31)) << 6)) - return 1; - } - return 0; - } - record += 16; - } - return 0; + int n; + int i; + char *record; + + flagwasthere = 0; + + n = getbuf(); + if (!n) + return 0; + close(fdlock); + + record = tcpto_buf; + for (i = 0; i < n; ++i) { + if (ip->is6) { + if (byte_equal(ip->ip.aaaa.d, 16, record)) { + flagwasthere = 1; + if (record[16] >= 2) { + datetime_sec when = (unsigned long)(unsigned char)record[11]; + when = (when << 8) + (unsigned long)(unsigned char)record[10]; + when = (when << 8) + (unsigned long)(unsigned char)record[9]; + when = (when << 8) + (unsigned long)(unsigned char)record[8]; + + if (now() - when < ((60 + (getpid() & 31)) << 6)) + return 1; + } + return 0; + } + } else { + if (byte_equal("\0\0\0\0\0\0\0\0\0\0\0", 12, record)) + if (byte_equal(ip->ip.a.d, 4, record + 12)) { + flagwasthere = 1; + if (record[16] >= 2) { + datetime_sec when = (unsigned long)(unsigned char)record[11]; + when = (when << 8) + (unsigned long)(unsigned char)record[10]; + when = (when << 8) + (unsigned long)(unsigned char)record[9]; + when = (when << 8) + (unsigned long)(unsigned char)record[8]; + + if (now() - when < ((60 + (getpid() & 31)) << 6)) + return 1; + } + return 0; + } + } + record += 12 + 16; // was 16, now 12 + inet6 + } + return 0; } -void tcpto_err(ip,flagerr) struct ip_address *ip; int flagerr; +void +tcpto_err(struct ip_address *ip, int flagerr) { - int n; - int i; - char *record; - datetime_sec when; - datetime_sec lastwhen; - - if (!flagerr) - if (!flagwasthere) - return; /* could have been added, but not worth the effort to check */ - - n = getbuf(); - if (!n) return; - - record = tcpto_buf; - for (i = 0;i < n;++i) - { - if (byte_equal(ip->d,4,record)) - { - if (!flagerr) - record[4] = 0; - else - { - lastwhen = (unsigned long) (unsigned char) record[11]; - lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[10]; - lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[9]; - lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[8]; - when = now(); - - if (record[4] && (when < 120 + lastwhen)) { close(fdlock); return; } - - if (++record[4] > 10) record[4] = 10; - record[8] = when; when >>= 8; - record[9] = when; when >>= 8; - record[10] = when; when >>= 8; - record[11] = when; - } - if (seek_set(fdlock,i << 4) == 0) - if (write(fdlock,record,16) < 16) - ; /*XXX*/ - close(fdlock); - return; - } - record += 16; - } - - if (!flagerr) { close(fdlock); return; } - - record = tcpto_buf; - for (i = 0;i < n;++i) - { - if (!record[4]) break; - record += 16; - } - - if (i >= n) - { - int firstpos = -1; - datetime_sec firstwhen; - record = tcpto_buf; - for (i = 0;i < n;++i) - { - when = (unsigned long) (unsigned char) record[11]; - when = (when << 8) + (unsigned long) (unsigned char) record[10]; - when = (when << 8) + (unsigned long) (unsigned char) record[9]; - when = (when << 8) + (unsigned long) (unsigned char) record[8]; - when += (record[4] << 10); - if ((firstpos < 0) || (when < firstwhen)) - { - firstpos = i; - firstwhen = when; - } - record += 16; - } - i = firstpos; - } - - if (i >= 0) - { - record = tcpto_buf + (i << 4); - byte_copy(record,4,ip->d); - when = now(); - record[8] = when; when >>= 8; - record[9] = when; when >>= 8; - record[10] = when; when >>= 8; - record[11] = when; - record[4] = 1; - if (seek_set(fdlock,i << 4) == 0) - if (write(fdlock,record,16) < 16) - ; /*XXX*/ - } - - close(fdlock); + int n; + int i; + char *record; + datetime_sec when; + datetime_sec lastwhen; + + if (!flagerr) + if (!flagwasthere) + return; /* could have been added, but not worth the effort to check */ + + n = getbuf(); + if (!n) + return; + + record = tcpto_buf; + for (i = 0; i < n; ++i) { + if (ip->is6) { + if (byte_equal(ip->ip.aaaa.d, 16, record)) { + if (!flagerr) + record[16] = 0; + else { + lastwhen = (unsigned long)(unsigned char)record[23]; + lastwhen = (lastwhen << 8) + (unsigned long)(unsigned char)record[22]; + lastwhen = (lastwhen << 8) + (unsigned long)(unsigned char)record[21]; + lastwhen = (lastwhen << 8) + (unsigned long)(unsigned char)record[20]; + when = now(); + + if (record[16] && (when < 120 + lastwhen)) { + close(fdlock); + return; + } + + if (++record[16] > 10) + record[16] = 10; + record[20] = when; + when >>= 8; + record[21] = when; + when >>= 8; + record[22] = when; + when >>= 8; + record[23] = when; + } + if (seek_set(fdlock, i << 4) == 0) + if (write(fdlock, record, 16) < 16) + ; /* XXX */ + close(fdlock); + return; + } + } else { + if (byte_equal("\0\0\0\0""\0\0\0\0""\0\0\0", 12, record)) + if (byte_equal(ip->ip.a.d, 4, record + 12)) { + if (!flagerr) + record[16] = 0; + else { + lastwhen = (unsigned long)(unsigned char)record[23]; + lastwhen = (lastwhen << 8) + (unsigned long)(unsigned char)record[22]; + lastwhen = (lastwhen << 8) + (unsigned long)(unsigned char)record[21]; + lastwhen = (lastwhen << 8) + (unsigned long)(unsigned char)record[20]; + when = now(); + + if (record[16] && (when < 120 + lastwhen)) { + close(fdlock); + return; + } + + if (++record[16] > 10) + record[16] = 10; + record[20] = when; + when >>= 8; + record[21] = when; + when >>= 8; + record[22] = when; + when >>= 8; + record[23] = when; + } + if (seek_set(fdlock, i << 4) == 0) + if (write(fdlock, record, 16) < 16) + ; /* XXX */ + close(fdlock); + return; + } + } + record += 12 + 16; + } + + if (!flagerr) { + close(fdlock); + return; + } + + record = tcpto_buf; + for (i = 0; i < n; ++i) { + if (!record[16]) + break; + record += 12 + 16; + } + + if (i >= n) { + int firstpos = -1; + datetime_sec firstwhen; + record = tcpto_buf; + for (i = 0; i < n; ++i) { + when = (unsigned long)(unsigned char)record[23]; + when = (when << 8) + (unsigned long)(unsigned char)record[22]; + when = (when << 8) + (unsigned long)(unsigned char)record[21]; + when = (when << 8) + (unsigned long)(unsigned char)record[20]; + when += (record[16] << 10); + if ((firstpos < 0) || (when < firstwhen)) { + firstpos = i; + firstwhen = when; + } + record += 12 + 16; + } + i = firstpos; + } + + if (i >= 0) { + record = tcpto_buf + (i << 5); + if (ip->is6) { + byte_copy(record, 16, ip->ip.aaaa.d); + } else { + byte_copy(record, 12, "\0\0\0\0""\0\0\0\0""\0\0\0"); + byte_copy(record + 12, 4, ip->ip.a.d); + } + when = now(); + record[20] = when; + when >>= 8; + record[21] = when; + when >>= 8; + record[22] = when; + when >>= 8; + record[23] = when; + record[16] = 1; + if (seek_set(fdlock, i << 5) == 0) + if (write(fdlock, record, 12 + 16) < (12 + 16)) + ; /* XXX */ + } + + close(fdlock); } diff --git a/src/timeoutconn.c b/src/timeoutconn.c @@ -11,38 +11,65 @@ #include "ip.h" #include "byte.h" -int timeoutconn(s,ip,port,timeout) -int s; -struct ip_address *ip; -unsigned int port; -int timeout; +int timeoutbindconn(int s, struct ip_address *ip, struct ip_address *bip, unsigned int port, int timeout) { char ch; - struct sockaddr_in sin; + int is6 = AF_INET; + union { + struct sockaddr_in a; + struct sockaddr_in6 aaaa; + } sin; + union { + struct sockaddr_in a; + struct sockaddr_in6 aaaa; + } bsin; char *x; fd_set wfds; struct timeval tv; - - byte_zero(&sin,sizeof(sin)); - byte_copy(&sin.sin_addr,4,ip); - x = (char *) &sin.sin_port; - x[1] = port; port >>= 8; x[0] = port; - sin.sin_family = AF_INET; - + + if (bip != NULL) if (bip->is6 != ip->is6) return (errno = EOPNOTSUPP, -1); /* erm... Reinhilde Bjornsdottir */ + + if (ip->is6) { + is6 = AF_INET6; + byte_zero(&sin,sizeof(sin)); + byte_copy(&sin.aaaa.sin6_addr,16,ip->ip.aaaa.d); + x = (char *) &sin.aaaa.sin6_port; + x[1] = port; port >>= 8; x[0] = port; + if (bip != NULL) { + byte_zero(&bsin,sizeof(bsin)); + byte_copy(&bsin.aaaa.sin6_addr,16,bip->ip.aaaa.d); + x = (char *) &bsin.aaaa.sin6_port; + x[1] = 0; x[0] = 0; + } + sin.aaaa.sin6_family = is6; + } else { + byte_zero(&sin,sizeof(sin)); + byte_copy(&sin.a.sin_addr,4,ip->ip.a.d); + x = (char *) &sin.a.sin_port; + x[1] = port; port >>= 8; x[0] = port; + if (bip != NULL) { + byte_zero(&bsin,sizeof(bsin)); + byte_copy(&bsin.a.sin_addr,4,bip->ip.a.d); + x = (char *) &bsin.a.sin_port; + x[1] = 0; x[0] = 0; + } + sin.a.sin_family = is6; + } + if (ndelay_on(s) == -1) return -1; - + /* XXX: could bind s */ - - if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) { + + if (connect(s,(struct sockaddr *) &sin,ip->is6 ? sizeof(sin.aaaa) : sizeof(sin.a)) == 0) { ndelay_off(s); return 0; } if ((errno != error_inprogress) && (errno != error_wouldblock)) return -1; - + FD_ZERO(&wfds); FD_SET(s,&wfds); tv.tv_sec = timeout; tv.tv_usec = 0; - + if (select(s + 1,NULL,&wfds,NULL,&tv) == -1) return -1; if (FD_ISSET(s,&wfds)) { unsigned int dummy; @@ -54,7 +81,12 @@ int timeout; ndelay_off(s); return 0; } - errno = error_timeout; /* note that connect attempt is continuing */ return -1; } + +int timeoutconn(int s, struct ip_address *ip, unsigned int port, int timeout) +{ + return timeoutbindconn(s, ip, NULL, port, timeout); +} +