summaryrefslogtreecommitdiff
path: root/sys/src/cmd/upas/smtp
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2017-03-12 17:15:03 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2017-03-12 17:15:03 +0100
commit963cfc9a6f6e721f52aa949e6d1af0c3e8dc2ecc (patch)
tree749b74875dbc49bcf6ed0776648b8f0ef9417407 /sys/src/cmd/upas/smtp
parent8177d20fb2709ba9290dfd41308b8e5bee4e00f8 (diff)
merging erik quanstros nupas
Diffstat (limited to 'sys/src/cmd/upas/smtp')
-rw-r--r--sys/src/cmd/upas/smtp/greylist.c2
-rw-r--r--sys/src/cmd/upas/smtp/mkfile30
-rw-r--r--sys/src/cmd/upas/smtp/mxdial.c534
-rw-r--r--sys/src/cmd/upas/smtp/parsetest.c86
-rw-r--r--sys/src/cmd/upas/smtp/rfc822.y82
-rw-r--r--sys/src/cmd/upas/smtp/rmtdns.c59
-rw-r--r--sys/src/cmd/upas/smtp/smtp.c292
-rw-r--r--sys/src/cmd/upas/smtp/smtp.h33
-rw-r--r--sys/src/cmd/upas/smtp/smtpd.c886
-rw-r--r--sys/src/cmd/upas/smtp/spam.c59
10 files changed, 1119 insertions, 944 deletions
diff --git a/sys/src/cmd/upas/smtp/greylist.c b/sys/src/cmd/upas/smtp/greylist.c
index b5cee5a6e..3bbe9f586 100644
--- a/sys/src/cmd/upas/smtp/greylist.c
+++ b/sys/src/cmd/upas/smtp/greylist.c
@@ -53,7 +53,7 @@ onwhitelist(void)
wl = Bopen(whitelist, OREAD);
if (wl == nil)
- return 1;
+ return 0;
while ((line = Brdline(wl, '\n')) != nil) {
lnlen = Blinelen(wl);
line[lnlen-1] = '\0'; /* clobber newline */
diff --git a/sys/src/cmd/upas/smtp/mkfile b/sys/src/cmd/upas/smtp/mkfile
index 10b312b90..9f02f8ae2 100644
--- a/sys/src/cmd/upas/smtp/mkfile
+++ b/sys/src/cmd/upas/smtp/mkfile
@@ -1,8 +1,12 @@
</$objtype/mkfile
+<../mkupas
TARG = smtpd\
smtp\
+TEST=\
+ parsetest
+
OFILES=
LIB=../common/libcommon.a$O\
@@ -12,13 +16,11 @@ HFILES=../common/common.h\
smtpd.h\
smtp.h\
-BIN=/$objtype/bin/upas
UPDATE=\
greylist.c\
mkfile\
mxdial.c\
rfc822.y\
- rmtdns.c\
smtpd.y\
spam.c\
$HFILES\
@@ -26,24 +28,30 @@ UPDATE=\
${TARG:%=%.c}\
</sys/src/cmd/mkmany
-CFLAGS=$CFLAGS -I../common -D'SPOOL="/mail"'
+CFLAGS=$CFLAGS -I../common
+
+$O.smtpd:\
+ smtpd.tab.$O\
+ spam.$O\
+ rfc822.tab.$O\
+ greylist.$O\
-$O.smtpd: smtpd.tab.$O rmtdns.$O spam.$O rfc822.tab.$O greylist.$O
$O.smtp: rfc822.tab.$O mxdial.$O
-smtpd.tab.c: smtpd.y
+smtpd.tab.c: smtpd.y
yacc -o xxx smtpd.y
sed 's/yy/zz/g' < xxx > $target
rm xxx
-rfc822.tab.c: rfc822.y
+rfc822.tab.c: rfc822.y
yacc -d -o $target rfc822.y
+$O.parsetest: rfc822.tab.$O
+
+parsetest.$O: rfc822.tab.$O
+
clean:V:
- rm -f *.[$OS] [$OS].$TARG smtpd.tab.c rfc822.tab.c y.tab.? y.debug $TARG
+ rm -f *.[$OS] [$OS].^($TARG $TEST) smtpd.tab.c rfc822.tab.c y.tab.? y.debug $TARG
../common/libcommon.a$O:
- @{
- cd ../common
- mk
- }
+ cd ../common && mk
diff --git a/sys/src/cmd/upas/smtp/mxdial.c b/sys/src/cmd/upas/smtp/mxdial.c
index c17e280c3..74516a030 100644
--- a/sys/src/cmd/upas/smtp/mxdial.c
+++ b/sys/src/cmd/upas/smtp/mxdial.c
@@ -1,60 +1,96 @@
#include "common.h"
+#include "smtp.h"
#include <ndb.h>
-#include <smtp.h> /* to publish dial_string_parse */
-
-enum
-{
- Nmx= 16,
- Maxstring= 256,
-};
-
-typedef struct Mx Mx;
-struct Mx
-{
- char host[256];
- char ip[24];
- int pref;
-};
char *bustedmxs[Maxbustedmx];
-Ndb *db;
-
-static int mxlookup(DS*, char*);
-static int mxlookup1(DS*, char*);
-static int compar(void*, void*);
-static int callmx(DS*, char*, char*);
-static void expand_meta(DS *ds);
-
-static Mx mx[Nmx];
-int
-mxdial(char *addr, char *ddomain, char *gdomain)
+static void
+expand(DS *ds)
{
- int fd;
- DS ds;
-
- addr = netmkaddr(addr, 0, "smtp");
- dial_string_parse(addr, &ds);
+ char *s;
+ Ndbtuple *t;
+
+ s = ds->host + 1;
+ t = csipinfo(ds->netdir, "sys", sysname(), &s, 1);
+ if(t != nil){
+ strecpy(ds->expand, ds->expand+sizeof ds->expand, t->val);
+ ds->host = ds->expand;
+ }
+ ndbfree(t);
+}
- /* try connecting to destination or any of it's mail routers */
- fd = callmx(&ds, addr, ddomain);
+/* break up an address to its component parts */
+void
+dialstringparse(char *str, DS *ds)
+{
+ char *p, *p2;
- /* try our mail gateway */
- if(fd < 0 && gdomain)
- fd = dial(netmkaddr(gdomain, 0, "smtp"), 0, 0, 0);
+ strecpy(ds->buf, ds->buf + sizeof ds->buf, str);
+ p = strchr(ds->buf, '!');
+ if(p == 0) {
+ ds->netdir = 0;
+ ds->proto = "net";
+ ds->host = ds->buf;
+ } else {
+ if(*ds->buf != '/'){
+ ds->netdir = 0;
+ ds->proto = ds->buf;
+ } else {
+ for(p2 = p; *p2 != '/'; p2--)
+ ;
+ *p2++ = 0;
+ ds->netdir = ds->buf;
+ ds->proto = p2;
+ }
+ *p = 0;
+ ds->host = p + 1;
+ }
+ ds->service = strchr(ds->host, '!');
+ if(ds->service)
+ *ds->service++ = 0;
+ if(*ds->host == '$')
+ expand(ds);
+}
- return fd;
+void
+mxtabfree(Mxtab *mx)
+{
+ free(mx->mx);
+ memset(mx, 0, sizeof *mx);
}
-static int
-busted(char *mx)
+static void
+mxtabrealloc(Mxtab *mx)
{
- char **bmp;
+ if(mx->nmx < mx->amx)
+ return;
+ if(mx->amx == 0)
+ mx->amx = 1;
+ mx->amx <<= 1;
+ mx->mx = realloc(mx->mx, sizeof mx->mx[0] * mx->amx);
+ if(mx->mx == nil)
+ sysfatal("no memory for mx");
+}
- for (bmp = bustedmxs; *bmp != nil; bmp++)
- if (strcmp(mx, *bmp) == 0)
- return 1;
- return 0;
+static void
+mxtabadd(Mxtab *mx, char *host, char *ip, char *net, int pref)
+{
+ int i;
+ Mx *x;
+
+ mxtabrealloc(mx);
+ x = mx->mx;
+ for(i = mx->nmx; i>0 && x[i-1].pref>pref && x[i-1].netdir == net; i--)
+ x[i] = x[i-1];
+ strecpy(x[i].host, x[i].host + sizeof x[i].host, host);
+ if(ip != nil)
+ strecpy(x[i].ip, x[i].ip + sizeof x[i].ip, ip);
+ else
+ x[i].ip[0] = 0;
+ x[i].netdir = net;
+ x[i].pref = pref;
+ x[i].valid = 1;
+ mx->nmx++;
}
static int
@@ -65,293 +101,235 @@ timeout(void*, char *msg)
return 0;
}
-long
+static long
timedwrite(int fd, void *buf, long len, long ms)
{
long n, oalarm;
atnotify(timeout, 1);
oalarm = alarm(ms);
- n = write(fd, buf, len);
+ n = pwrite(fd, buf, len, 0);
alarm(oalarm);
atnotify(timeout, 0);
return n;
}
-/*
- * take an address and return all the mx entries for it,
- * most preferred first
- */
static int
-callmx(DS *ds, char *dest, char *domain)
+dnslookup(Mxtab *mx, int fd, char *query, char *domain, char *net, int pref0)
{
- int fd, i, nmx;
- char addr[Maxstring];
-
- /* get a list of mx entries */
- nmx = mxlookup(ds, domain);
- if(nmx < 0){
- /* dns isn't working, don't just dial */
- return -1;
- }
- if(nmx == 0){
- if(debug)
- fprint(2, "mxlookup returns nothing\n");
- return dial(dest, 0, 0, 0);
- }
+ int n;
+ char buf[1024], *f[4];
- /* refuse to honor loopback addresses given by dns */
- for(i = 0; i < nmx; i++)
- if(strcmp(mx[i].ip, "127.0.0.1") == 0){
- if(debug)
- fprint(2, "mxlookup returns loopback\n");
- werrstr("illegal: domain lists 127.0.0.1 as mail server");
+ n = timedwrite(fd, query, strlen(query), 60*1000);
+ if(n < 0){
+ rerrstr(buf, sizeof buf);
+ dprint("dns: %s\n", buf);
+ if(strstr(buf, "dns failure")){
+ /* if dns fails for the mx lookup, we have to stop */
+ close(fd);
return -1;
}
+ return 0;
+ }
- /* sort by preference */
- if(nmx > 1)
- qsort(mx, nmx, sizeof(Mx), compar);
-
- /* dial each one in turn */
- for(i = 0; i < nmx; i++){
- if (busted(mx[i].host)) {
- if (debug)
- fprint(2, "mxdial skipping busted mx %s\n",
- mx[i].host);
+ seek(fd, 0, 0);
+ for(;;){
+ if((n = read(fd, buf, sizeof buf - 1)) < 1)
+ break;
+ buf[n] = 0;
+ // chat("dns: %s\n", buf);
+ n = tokenize(buf, f, nelem(f));
+ if(n < 2)
continue;
+ if(strcmp(f[1], "mx") == 0 && n == 4){
+ if(strchr(domain, '.') == 0)
+ strcpy(domain, f[0]);
+ mxtabadd(mx, f[3], nil, net, atoi(f[2]));
+ }
+ else if (strcmp(f[1], "ip") == 0 && n == 3){
+ if(strchr(domain, '.') == 0)
+ strcpy(domain, f[0]);
+ mxtabadd(mx, f[0], f[2], net, pref0);
}
- snprint(addr, sizeof(addr), "%s/%s!%s!%s", ds->netdir, ds->proto,
- mx[i].host, ds->service);
- if(debug)
- fprint(2, "mxdial trying %s\n", addr);
- atnotify(timeout, 1);
- alarm(10*1000);
- fd = dial(addr, 0, 0, 0);
- alarm(0);
- atnotify(timeout, 0);
- if(fd >= 0)
- return fd;
}
- return -1;
+
+ return 0;
}
-/*
- * call the dns process and have it try to resolve the mx request
- *
- * this routine knows about the firewall and tries inside and outside
- * dns's seperately.
- */
static int
-mxlookup(DS *ds, char *domain)
+busted(char *mx)
{
- int n;
+ char **bmp;
- /* just in case we find no domain name */
- strcpy(domain, ds->host);
-
- if(ds->netdir)
- n = mxlookup1(ds, domain);
- else {
- ds->netdir = "/net";
- n = mxlookup1(ds, domain);
- if(n <= 0) {
- ds->netdir = "/net.alt";
- n = mxlookup1(ds, domain);
- }
- }
+ for (bmp = bustedmxs; *bmp != nil; bmp++)
+ if (strcmp(mx, *bmp) == 0)
+ return 1;
+ return 0;
+}
- return n;
+static void
+complain(Mxtab *mx, char *domain)
+{
+ char buf[1024], *e, *p;
+ int i;
+
+ p = buf;
+ e = buf + sizeof buf;
+ for(i = 0; i < mx->nmx; i++)
+ p = seprint(p, e, "%s ", mx->mx[i].ip);
+ syslog(0, "smtpd.mx", "loopback for %s %s", domain, buf);
}
static int
-mxlookup1(DS *ds, char *domain)
+okaymx(Mxtab *mx, char *domain)
{
- int i, n, fd, nmx;
- char buf[1024], dnsname[Maxstring];
- char *fields[4];
+ int i;
+ Mx *x;
+
+ /* look for malicious dns entries; TODO use badcidr in ../spf/ to catch more than ip4 */
+ for(i = 0; i < mx->nmx; i++){
+ x = mx->mx + i;
+ if(x->valid && strcmp(x->ip, "127.0.0.1") == 0){
+ dprint("illegal: domain %s lists 127.0.0.1 as mail server", domain);
+ complain(mx, domain);
+ werrstr("illegal: domain %s lists 127.0.0.1 as mail server", domain);
+ return -1;
+ }
+ if(x->valid && busted(x->host)){
+ dprint("lookup: skipping busted mx %s\n", x->host);
+ x->valid = 0;
+ }
+ }
+ return 0;
+}
- snprint(dnsname, sizeof dnsname, "%s/dns", ds->netdir);
+static int
+lookup(Mxtab *mx, char *net, char *host, char *domain, char *type)
+{
+ char dns[128], buf[1024];
+ int fd, i;
+ Mx *x;
- fd = open(dnsname, ORDWR);
- if(fd < 0)
- return 0;
+ snprint(dns, sizeof dns, "%s/dns", net);
+ fd = open(dns, ORDWR);
+ if(fd == -1)
+ return -1;
- nmx = 0;
- snprint(buf, sizeof buf, "%s mx", ds->host);
- if(debug)
- fprint(2, "sending %s '%s'\n", dnsname, buf);
- /*
- * don't hang indefinitely in the write to /net/dns.
- */
- n = timedwrite(fd, buf, strlen(buf), 60*1000);
- if(n < 0){
- rerrstr(buf, sizeof buf);
- if(debug)
- fprint(2, "dns: %s\n", buf);
- if(strstr(buf, "dns failure")){
- /* if dns fails for the mx lookup, we have to stop */
- close(fd);
- return -1;
- }
- } else {
- /*
- * get any mx entries
- */
- seek(fd, 0, 0);
- while(nmx < Nmx && (n = read(fd, buf, sizeof buf-1)) > 0){
- buf[n] = 0;
- if(debug)
- fprint(2, "dns mx: %s\n", buf);
- n = getfields(buf, fields, 4, 1, " \t");
- if(n < 4)
- continue;
+ snprint(buf, sizeof buf, "%s %s", host, type);
+ dprint("sending %s '%s'\n", dns, buf);
+ dnslookup(mx, fd, buf, domain, net, 10000);
- if(strchr(domain, '.') == 0)
- strcpy(domain, fields[0]);
+ for(i = 0; i < mx->nmx; i++){
+ x = mx->mx + i;
+ if(x->ip[0] != 0)
+ continue;
+ x->valid = 0;
- strncpy(mx[nmx].host, fields[3], sizeof(mx[n].host)-1);
- mx[nmx].pref = atoi(fields[2]);
- nmx++;
- }
- if(debug)
- fprint(2, "dns mx; got %d entries\n", nmx);
+ snprint(buf, sizeof buf, "%s %s", x->host, "ip");
+ dprint("sending %s '%s'\n", dns, buf);
+ dnslookup(mx, fd, buf, domain, net, x->pref);
}
- /*
- * no mx record? try name itself.
- */
- /*
- * BUG? If domain has no dots, then we used to look up ds->host
- * but return domain instead of ds->host in the list. Now we return
- * ds->host. What will this break?
- */
- if(nmx == 0){
- mx[0].pref = 1;
- strncpy(mx[0].host, ds->host, sizeof(mx[0].host));
- nmx++;
- }
+ close(fd);
- /*
- * look up all ip addresses
- */
- for(i = 0; i < nmx; i++){
- seek(fd, 0, 0);
- snprint(buf, sizeof buf, "%s ip", mx[i].host);
- mx[i].ip[0] = 0;
- /*
- * don't hang indefinitely in the write to /net/dns.
- */
- if(timedwrite(fd, buf, strlen(buf), 60*1000) < 0)
- goto no;
- seek(fd, 0, 0);
- if((n = read(fd, buf, sizeof buf-1)) < 0)
- goto no;
- buf[n] = 0;
- if(getfields(buf, fields, 4, 1, " \t") < 3)
- goto no;
- strncpy(mx[i].ip, fields[2], sizeof(mx[i].ip)-1);
- continue;
-
- no:
- /* remove mx[i] and go around again */
- nmx--;
- mx[i] = mx[nmx];
- i--;
+ if(strcmp(type, "mx") == 0){
+ if(okaymx(mx, domain) == -1)
+ return -1;
+ for(i = 0; i < mx->nmx; i++){
+ x = mx->mx + i;
+ dprint("mx list: %s %d %s\n", x->host, x->pref, x->ip);
+ }
+ dprint("\n");
}
- return nmx;
-}
-static int
-compar(void *a, void *b)
-{
- return ((Mx*)a)->pref - ((Mx*)b)->pref;
+ return 0;
}
-/* break up an address to its component parts */
-void
-dial_string_parse(char *str, DS *ds)
+static int
+lookcall(Mxtab *mx, DS *d, char *domain, char *type)
{
- char *p, *p2;
-
- strncpy(ds->buf, str, sizeof(ds->buf));
- ds->buf[sizeof(ds->buf)-1] = 0;
+ char buf[1024];
+ int i;
+ Mx *x;
+
+ if(lookup(mx, d->netdir, d->host, domain, type) == -1){
+ for(i = 0; i < mx->nmx; i++)
+ if(mx->mx[i].netdir == d->netdir)
+ mx->mx[i].valid = 0;
+ return -1;
+ }
- p = strchr(ds->buf, '!');
- if(p == 0) {
- ds->netdir = 0;
- ds->proto = "net";
- ds->host = ds->buf;
- } else {
- if(*ds->buf != '/'){
- ds->netdir = 0;
- ds->proto = ds->buf;
- } else {
- for(p2 = p; *p2 != '/'; p2--)
- ;
- *p2++ = 0;
- ds->netdir = ds->buf;
- ds->proto = p2;
+ for(i = 0; i < mx->nmx; i++){
+ x = mx->mx + i;
+ if(x->ip[0] == 0 || x->valid == 0){
+ x->valid = 0;
+ continue;
}
- *p = 0;
- ds->host = p + 1;
+ snprint(buf, sizeof buf, "%s/%s!%s!%s", d->netdir, d->proto,
+ x->ip /*x->host*/, d->service);
+ dprint("mxdial trying %s [%s]\n", x->host, buf);
+ atnotify(timeout, 1);
+ alarm(10*1000);
+ mx->fd = dial(buf, 0, 0, 0);
+ alarm(0);
+ atnotify(timeout, 0);
+ if(mx->fd >= 0){
+ mx->pmx = i;
+ return mx->fd;
+ }
+ dprint(" failed %r\n");
+ x->valid = 0;
}
- ds->service = strchr(ds->host, '!');
- if(ds->service)
- *ds->service++ = 0;
- if(*ds->host == '$')
- expand_meta(ds);
+
+ return -1;
}
-static void
-expand_meta(DS *ds)
+int
+mxdial0(char *addr, char *ddomain, char *gdomain, Mxtab *mx)
{
- char buf[128], cs[128], *net, *p;
- int fd, n;
-
- net = ds->netdir;
- if(!net)
- net = "/net";
-
- if(debug)
- fprint(2, "expanding %s!%s\n", net, ds->host);
- snprint(cs, sizeof(cs), "%s/cs", net);
- if((fd = open(cs, ORDWR)) == -1){
- if(debug)
- fprint(2, "open %s: %r\n", cs);
- syslog(0, "smtp", "cannot open %s: %r", cs);
- return;
- }
+ int nd, i, j;
+ DS *d;
+ static char *tab[] = {"mx", "ip", };
- snprint(buf, sizeof buf, "!ipinfo %s", ds->host+1); // +1 to skip $
- if(write(fd, buf, strlen(buf)) <= 0){
- if(debug)
- fprint(2, "write %s: %r\n", cs);
- syslog(0, "smtp", "%s to %s - write failed: %r", buf, cs);
- close(fd);
- return;
+ dprint("mxdial(%s, %s, %s, mx)\n", addr, ddomain, gdomain);
+ memset(mx, 0, sizeof *mx);
+ addr = netmkaddr(addr, 0, "smtp");
+ d = mx->ds;
+ dialstringparse(addr, d + 0);
+ nd = 1;
+ if(d[0].netdir == nil){
+ d[1] = d[0];
+ d[0].netdir = "/net";
+ d[1].netdir = "/net.alt";
+ nd = 2;
}
- seek(fd, 0, 0);
- if((n = read(fd, ds->expand, sizeof(ds->expand)-1)) < 0){
- if(debug)
- fprint(2, "read %s: %r\n", cs);
- syslog(0, "smtp", "%s - read failed: %r", cs);
- close(fd);
- return;
+ /* search all networks for mx records; then ip records */
+ for(j = 0; j < nelem(tab); j++)
+ for(i = 0; i < nd; i++)
+ if(lookcall(mx, d + i, ddomain, tab[j]) != -1)
+ return mx->fd;
+
+ /* grotty: try gateway machine by ip only (fixme: try cs lookup) */
+ if(gdomain != nil){
+ dialstringparse(netmkaddr(gdomain, 0, "smtp"), d + 0);
+ if(lookcall(mx, d + 0, gdomain, "ip") != -1)
+ return mx->fd;
}
- close(fd);
- ds->expand[n] = 0;
- if((p = strchr(ds->expand, '=')) == nil){
- if(debug)
- fprint(2, "response %s: %s\n", cs, ds->expand);
- syslog(0, "smtp", "%q from %s - bad response: %r", ds->expand, cs);
- return;
- }
- ds->host = p+1;
+ return -1;
+}
- /* take only first one returned (quasi-bug) */
- if((p = strchr(ds->host, ' ')) != nil)
- *p = 0;
+int
+mxdial(char *addr, char *ddomain, char *gdomain, Mx *x)
+{
+ int fd;
+ Mxtab mx;
+
+ memset(x, 0, sizeof *x);
+ fd = mxdial0(addr, ddomain, gdomain, &mx);
+ if(fd >= 0 && mx.pmx >= 0)
+ *x = mx.mx[mx.pmx];
+ mxtabfree(&mx);
+ return fd;
}
diff --git a/sys/src/cmd/upas/smtp/parsetest.c b/sys/src/cmd/upas/smtp/parsetest.c
new file mode 100644
index 000000000..38e77fb9a
--- /dev/null
+++ b/sys/src/cmd/upas/smtp/parsetest.c
@@ -0,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include <String.h>
+#include <bio.h>
+#include "smtp.h"
+
+Biobuf o;
+
+void
+freefields(void)
+{
+ Field *f, *fn;
+ Node *n, *nn;
+
+ for(f = firstfield; f != nil; f = fn){
+ fn = f->next;
+ for(n = f->node; n != nil; n = nn){
+ nn = n->next;
+ s_free(n->s);
+ s_free(n->white);
+ free(n);
+ }
+ free(f);
+ }
+ firstfield = nil;
+}
+
+void
+printhdr(void)
+{
+ Field *f;
+ Node *n;
+
+ for(f = firstfield; f != nil; f = f->next){
+ for(n = f->node; n != nil; n = n->next){
+ if(n->s != nil)
+ Bprint(&o, "%s", s_to_c(n->s));
+ else
+ Bprint(&o, "%c", n->c);
+ if(n->white != nil)
+ Bprint(&o, "%s", s_to_c(n->white));
+ }
+ Bprint(&o, "\n");
+ }
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: parsetest file ...\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, fd, nbuf;
+ char *buf;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if(Binit(&o, 1, OWRITE) == -1)
+ sysfatal("Binit: %r");
+ for(i = 0; i < argc; i++){
+ fd = open(argv[i], OREAD);
+ if(fd == -1)
+ sysfatal("open: %r");
+ buf = malloc(128*1024);
+ if(buf == nil)
+ sysfatal("malloc: %r");
+ nbuf = read(fd, buf, 128*1024);
+ if(nbuf == -1)
+ sysfatal("read: %r");
+ close(fd);
+ yyinit(buf, nbuf);
+ yyparse();
+ printhdr();
+ freefields();
+ free(buf);
+ Bflush(&o);
+ }
+ exits("");
+}
diff --git a/sys/src/cmd/upas/smtp/rfc822.y b/sys/src/cmd/upas/smtp/rfc822.y
index b50f63eb1..065c15347 100644
--- a/sys/src/cmd/upas/smtp/rfc822.y
+++ b/sys/src/cmd/upas/smtp/rfc822.y
@@ -3,8 +3,6 @@
#include "smtp.h"
#include <ctype.h>
-#define YYMAXDEPTH 500 /* was default 150 */
-
char *yylp; /* next character to be lex'd */
int yydone; /* tell yylex to give up */
char *yybuffer; /* first parsed character */
@@ -54,10 +52,11 @@ int messageid;
msg : fields
| unixfrom '\n' fields
;
-fields : '\n'
+fields : fieldlist '\n'
{ yydone = 1; }
- | field '\n'
- | field '\n' fields
+ ;
+fieldlist : field '\n'
+ | fieldlist field '\n'
;
field : dates
{ date = 1; }
@@ -304,14 +303,12 @@ Keyword key[] = {
*/
yylex(void)
{
- String *t;
- int quoting;
- int escaping;
char *start;
+ int quoting, escaping, c, d;
+ String *t;
Keyword *kp;
- int c, d;
-/* print("lexing\n"); /**/
+// print("lexing\n");
if(yylp >= yyend)
return 0;
if(yydone)
@@ -331,15 +328,15 @@ yylex(void)
if(c == 0)
continue;
- if(escaping) {
+ if(escaping)
escaping = 0;
- } else if(quoting) {
+ else if(quoting){
switch(c){
case '\\':
escaping = 1;
break;
case '\n':
- d = (*(yylp+1))&0xff;
+ d = yylp[1] & 0xff;
if(d != ' ' && d != '\t'){
quoting = 0;
yylp--;
@@ -350,7 +347,7 @@ yylex(void)
quoting = 0;
break;
}
- } else {
+ }else{
switch(c){
case '\\':
escaping = 1;
@@ -363,7 +360,7 @@ yylex(void)
case '\n':
if(yylp == start){
yylp++;
-/* print("lex(c %c)\n", c); /**/
+// print("lex(c %c)\n", c);
yylval->end = yylp;
return yylval->c = c;
}
@@ -377,7 +374,7 @@ yylex(void)
if(yylp == start){
yylp++;
yylval->white = yywhite();
-/* print("lex(c %c)\n", c); /**/
+// print("lex(c %c)\n", c);
yylval->end = yylp;
return yylval->c = c;
}
@@ -395,25 +392,23 @@ yylex(void)
}
out:
yylval->white = yywhite();
- if(t) {
+ if(t)
s_terminate(t);
- } else /* message begins with white-space! */
+ else /* message begins with white-space! */
return yylval->c = '\n';
yylval->s = t;
for(kp = key; kp->val != WORD; kp++)
- if(cistrcmp(s_to_c(t), kp->rep)==0)
+ if(cistrcmp(s_to_c(t), kp->rep) == 0)
break;
-/* print("lex(%d) %s\n", kp->val-WORD, s_to_c(t)); /**/
+// print("lex(%d) %s\n", kp->val - WORD, s_to_c(t));
yylval->end = yylp;
return yylval->c = kp->val;
}
void
-yyerror(char *x)
+yyerror(char*)
{
- USED(x);
-
- /*fprint(2, "parse err: %s\n", x);/**/
+// fprint(2, "parse err: %s\n", x);
}
/*
@@ -423,9 +418,7 @@ String *
yywhite(void)
{
String *w;
- int clevel;
- int c;
- int escaping;
+ int clevel, c, escaping;
escaping = clevel = 0;
for(w = 0; yylp < yyend; yylp++){
@@ -435,15 +428,15 @@ yywhite(void)
if(c == 0)
continue;
- if(escaping){
+ if(escaping)
escaping = 0;
- } else if(clevel) {
+ else if(clevel){
switch(c){
case '\n':
/*
* look for multiline fields
*/
- if(*(yylp+1)==' ' || *(yylp+1)=='\t')
+ if(yylp[1] == ' ' || yylp[1] == '\t')
break;
else
goto out;
@@ -473,7 +466,7 @@ yywhite(void)
/*
* look for multiline fields
*/
- if(*(yylp+1)==' ' || *(yylp+1)=='\t')
+ if(yylp[1] == ' ' || yylp[1] == '\t')
break;
else
goto out;
@@ -533,7 +526,7 @@ colon(Node *p1, Node *p2)
if(p1->white){
if(p2->white)
s_append(p1->white, s_to_c(p2->white));
- } else {
+ }else{
p1->white = p2->white;
p2->white = 0;
}
@@ -573,7 +566,7 @@ concat(Node *p1, Node *p2)
if(p2->s)
s_append(p1->s, s_to_c(p2->s));
- else {
+ else{
buf[0] = p2->c;
buf[1] = 0;
s_append(p1->s, buf);
@@ -610,23 +603,6 @@ address(Node *p)
}
/*
- * case independent string compare
- */
-int
-cistrcmp(char *s1, char *s2)
-{
- int c1, c2;
-
- for(; *s1; s1++, s2++){
- c1 = isupper(*s1) ? tolower(*s1) : *s1;
- c2 = isupper(*s2) ? tolower(*s2) : *s2;
- if (c1 != c2)
- return -1;
- }
- return *s2;
-}
-
-/*
* free a node
*/
void
@@ -673,10 +649,10 @@ missing(Node *p)
start = yybuffer;
if(lastfield != nil){
for(np = lastfield->node; np; np = np->next)
- start = np->end+1;
+ start = np->end + 1;
}
- end = p->start-1;
+ end = p->start - 1;
if(end <= start)
return;
@@ -689,7 +665,7 @@ missing(Node *p)
np->end = end;
np->white = nil;
s = s_copy("BadHeader: ");
- np->s = s_nappend(s, start, end-start);
+ np->s = s_nappend(s, start, end - start);
np->next = nil;
f = malloc(sizeof(Field));
diff --git a/sys/src/cmd/upas/smtp/rmtdns.c b/sys/src/cmd/upas/smtp/rmtdns.c
deleted file mode 100644
index b74a1c90b..000000000
--- a/sys/src/cmd/upas/smtp/rmtdns.c
+++ /dev/null
@@ -1,59 +0,0 @@
-#include "common.h"
-#include <ndb.h>
-
-int
-rmtdns(char *net, char *path)
-{
- int fd, n, nb, r;
- char *domain, *cp, buf[1024];
-
- if(net == 0 || path == 0)
- return 0;
-
- domain = strdup(path);
- cp = strchr(domain, '!');
- if(cp){
- *cp = 0;
- n = cp-domain;
- } else
- n = strlen(domain);
-
- if(*domain == '[' && domain[n-1] == ']'){ /* accept [nnn.nnn.nnn.nnn] */
- domain[n-1] = 0;
- r = strcmp(ipattr(domain+1), "ip");
- domain[n-1] = ']';
- } else
- r = strcmp(ipattr(domain), "ip"); /* accept nnn.nnn.nnn.nnn */
- if(r == 0){
- free(domain);
- return 0;
- }
-
- snprint(buf, sizeof buf, "%s/dns", net);
- fd = open(buf, ORDWR); /* look up all others */
- if(fd < 0){ /* dns screw up - can't check */
- free(domain);
- return 0;
- }
-
- n = snprint(buf, sizeof buf, "%s all", domain);
- free(domain);
- seek(fd, 0, 0);
- nb = write(fd, buf, n);
- close(fd);
- if(nb != n){
- rerrstr(buf, sizeof buf);
- if (strcmp(buf, "dns: name does not exist") == 0)
- return -1;
- }
- return 0;
-}
-
-/*
-void
-main(int, char *argv[])
-{
- print("return = %d\n", rmtdns("/net.alt", argv[1]));
- exits(0);
-}
-*/
diff --git a/sys/src/cmd/upas/smtp/smtp.c b/sys/src/cmd/upas/smtp/smtp.c
index 96f2d65ab..99e5e631d 100644
--- a/sys/src/cmd/upas/smtp/smtp.c
+++ b/sys/src/cmd/upas/smtp/smtp.c
@@ -5,7 +5,7 @@
#include <libsec.h>
#include <auth.h>
-static char* connect(char*);
+static char* connect(char*, Mx*);
static char* wraptls(void);
static char* dotls(char*);
static char* doauth(char*);
@@ -15,7 +15,7 @@ String* bangtoat(char*);
String* convertheader(String*);
int dBprint(char*, ...);
int dBputc(int);
-char* data(String*, Biobuf*);
+char* data(String*, Biobuf*, Mx*);
char* domainify(char*, char*);
String* fixrouteaddr(String*, Node*, Node*);
char* getcrnl(String*);
@@ -27,7 +27,7 @@ int printheader(void);
void putcrnl(char*, int);
void quit(char*);
char* rcptto(char*);
-char* rewritezone(char *);
+char *rewritezone(char *);
#define Retry "Retry, Temporary Failure"
#define Giveup "Permanent Failure"
@@ -53,13 +53,33 @@ char *uneaten; /* first character after rfc822 headers */
char *farend; /* system we are trying to send to */
char *user; /* user we are authenticating as, if authenticating */
char hostdomain[256];
+Mx *tmmx; /* global for timeout */
Biobuf bin;
Biobuf bout;
Biobuf berr;
Biobuf bfile;
-static int bustedmx;
+int
+Dfmt(Fmt *fmt)
+{
+ Mx *mx;
+
+ mx = va_arg(fmt->args, Mx*);
+ if(mx == nil || mx->host[0] == 0)
+ return fmtstrcpy(fmt, "");
+ else
+ return fmtprint(fmt, "(%s:%s)", mx->host, mx->ip);
+}
+#pragma varargck type "D" Mx*
+
+char*
+deliverytype(void)
+{
+ if(ping)
+ return "ping";
+ return "delivery";
+}
void
usage(void)
@@ -70,10 +90,9 @@ usage(void)
}
int
-timeout(void *x, char *msg)
+timeout(void *, char *msg)
{
- USED(x);
- syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg);
+ syslog(0, "smtp.fail", "%s interrupt: %s: %s %D", deliverytype(), farend, msg, tmmx);
if(strstr(msg, "alarm")){
fprint(2, "smtp timeout: connection to %s timed out\n", farend);
if(quitting)
@@ -81,12 +100,12 @@ timeout(void *x, char *msg)
exits(Retry);
}
if(strstr(msg, "closed pipe")){
- /* call _exits() to prevent Bio from trying to flush closed pipe */
fprint(2, "smtp timeout: connection closed to %s\n", farend);
if(quitting){
- syslog(0, "smtp.fail", "closed pipe to %s", farend);
+ syslog(0, "smtp.fail", "%s closed pipe to %s %D", deliverytype(), farend, tmmx);
_exits(quitrv);
}
+ /* call _exits() to prevent Bio from trying to flush closed pipe */
_exits(Retry);
}
return 0;
@@ -95,7 +114,7 @@ timeout(void *x, char *msg)
void
removenewline(char *p)
{
- int n = strlen(p)-1;
+ int n = strlen(p) - 1;
if(n < 0)
return;
@@ -106,18 +125,21 @@ removenewline(char *p)
void
main(int argc, char **argv)
{
- int i, ok, rcvrs;
- char *addr, *rv, *trv, *host, *domain;
- char **errs;
- char hellodomain[256];
+ char *phase, *addr, *rv, *trv, *host, *domain;
+ char **errs, *p, *e, hellodomain[256], allrx[512];
+ int i, ok, rcvrs, bustedmx;
String *from, *fromm, *sender;
+ Mx mx;
alarmscale = 60*1000; /* minutes */
quotefmtinstall();
+ mailfmtinstall(); /* 2047 encoding */
+ fmtinstall('D', Dfmt);
fmtinstall('[', encodefmt);
errs = malloc(argc*sizeof(char*));
reply = s_new();
host = 0;
+ bustedmx = 0;
ARGBEGIN{
case 'a':
tryauth = 1;
@@ -128,7 +150,7 @@ main(int argc, char **argv)
autistic = 1;
break;
case 'b':
- if (bustedmx >= Maxbustedmx)
+ if(bustedmx >= Maxbustedmx)
sysfatal("more than %d busted mxs given", Maxbustedmx);
bustedmxs[bustedmx++] = EARGF(usage());
break;
@@ -172,7 +194,7 @@ main(int argc, char **argv)
/*
* get domain and add to host name
*/
- if(*argv && **argv=='.') {
+ if(*argv && **argv=='.'){
domain = *argv;
argv++; argc--;
} else
@@ -189,6 +211,11 @@ main(int argc, char **argv)
usage();
addr = *argv++; argc--;
farend = addr;
+ if((rv = strrchr(addr, '!')) && rv[1] == '['){
+ syslog(0, "smtp.fail", "%s to %s failed: illegal address",
+ deliverytype(), addr);
+ exits(Giveup);
+ }
/*
* get sender's machine.
@@ -209,30 +236,40 @@ main(int argc, char **argv)
/*
* send the mail
*/
+ rcvrs = 0;
+ phase = "";
+ USED(phase); /* just in case */
if(filter){
Binit(&bout, 1, OWRITE);
- rv = data(from, &bfile);
- if(rv != 0)
+ rv = data(from, &bfile, nil);
+ if(rv != 0){
+ phase = "filter";
goto error;
+ }
exits(0);
}
/* mxdial uses its own timeout handler */
- if((rv = connect(addr)) != 0)
+ if((rv = connect(addr, &mx)) != 0)
exits(rv);
+ tmmx = &mx;
/* 10 minutes to get through the initial handshake */
atnotify(timeout, 1);
alarm(10*alarmscale);
- if((rv = hello(hellodomain, 0)) != 0)
+ if((rv = hello(hellodomain, 0)) != 0){
+ phase = "hello";
goto error;
+ }
alarm(10*alarmscale);
- if((rv = mailfrom(s_to_c(from))) != 0)
+ if((rv = mailfrom(s_to_c(from))) != 0){
+ phase = "mailfrom";
goto error;
+ }
ok = 0;
- rcvrs = 0;
/* if any rcvrs are ok, we try to send the message */
+ phase = "rcptto";
for(i = 0; i < argc; i++){
if((trv = rcptto(argv[i])) != 0){
/* remember worst error */
@@ -248,6 +285,8 @@ main(int argc, char **argv)
}
/* if no ok rcvrs or worst error is retry, give up */
+ if(ok == 0 && rcvrs == 0)
+ phase = "rcptto; no addresses";
if(ok == 0 || rv == Retry)
goto error;
@@ -256,7 +295,7 @@ main(int argc, char **argv)
exits(0);
}
- rv = data(from, &bfile);
+ rv = data(from, &bfile, &mx);
if(rv != 0)
goto error;
quit(0);
@@ -266,10 +305,11 @@ main(int argc, char **argv)
/*
* here when some but not all rcvrs failed
*/
- fprint(2, "%s connect to %s:\n", thedate(), addr);
+ fprint(2, "%s connect to %s: %D %s:\n", thedate(), addr, &mx, phase);
for(i = 0; i < rcvrs; i++){
if(errs[i]){
- syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);
+ syslog(0, "smtp.fail", "delivery to %s at %s %D %s, failed: %s",
+ argv[i], addr, &mx, phase, errs[i]);
fprint(2, " mail to %s failed: %s", argv[i], errs[i]);
}
}
@@ -279,11 +319,19 @@ main(int argc, char **argv)
* here when all rcvrs failed
*/
error:
+ alarm(0);
removenewline(s_to_c(reply));
- syslog(0, "smtp.fail", "%s to %s failed: %s",
- ping ? "ping" : "delivery",
- addr, s_to_c(reply));
- fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
+ if(rcvrs > 0){
+ p = allrx;
+ e = allrx + sizeof allrx;
+ seprint(p, e, "to ");
+ for(i = 0; i < rcvrs - 1; i++)
+ p = seprint(p, e, "%s,", argv[i]);
+ seprint(p, e, "%s ", argv[i]);
+ }
+ syslog(0, "smtp.fail", "%s %s at %s %D %s failed: %s",
+ deliverytype(), allrx, addr, &mx, phase, s_to_c(reply));
+ fprint(2, "%s connect to %s %D %s:\n%s\n", thedate(), addr, &mx, phase, s_to_c(reply));
if(!filter)
quit(rv);
exits(rv);
@@ -293,17 +341,17 @@ error:
* connect to the remote host
*/
static char *
-connect(char* net)
+connect(char* net, Mx *mx)
{
- char buf[Errlen];
+ char buf[ERRMAX];
int fd;
- fd = mxdial(net, ddomain, gdomain);
+ fd = mxdial(net, ddomain, gdomain, mx);
if(fd < 0){
- rerrstr(buf, sizeof(buf));
- Bprint(&berr, "smtp: %s (%s)\n", buf, net);
- syslog(0, "smtp.fail", "%s (%s)", buf, net);
+ rerrstr(buf, sizeof buf);
+ Bprint(&berr, "smtp: %s (%s) %D\n", buf, net, mx);
+ syslog(0, "smtp.fail", "%s %s (%s) %D", deliverytype(), buf, net, mx);
if(strstr(buf, "illegal")
|| strstr(buf, "unknown")
|| strstr(buf, "can't translate"))
@@ -320,7 +368,20 @@ connect(char* net)
static char smtpthumbs[] = "/sys/lib/tls/smtp";
static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude";
-static char *
+static int
+tracetls(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ Bvprint(&berr, fmt, ap);
+ Bprint(&berr, "\n");
+ Bflush(&berr);
+ va_end(ap);
+ return 0;
+}
+
+static char*
wraptls(void)
{
TLSconn *c;
@@ -335,6 +396,9 @@ wraptls(void)
if (c == nil)
return err;
+ if (debug)
+ c->trace = tracetls;
+
fd = tlsClient(Bfildes(&bout), c);
if (fd < 0) {
syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
@@ -396,6 +460,36 @@ dotls(char *me)
return(hello(me, 1));
}
+static char*
+smtpcram(DS *ds)
+{
+ char *p, ch[128], usr[64], rbuf[128], ubuf[128], ebuf[192];
+ int i, n, l;
+
+ dBprint("AUTH CRAM-MD5\r\n");
+ if(getreply() != 3)
+ return Retry;
+ p = s_to_c(reply) + 4;
+ l = dec64((uchar*)ch, sizeof ch, p, strlen(p));
+ ch[l] = 0;
+ n = auth_respond(ch, l, usr, sizeof usr, rbuf, sizeof rbuf, auth_getkey,
+ user?"proto=cram role=client server=%q user=%q":"proto=cram role=client server=%q",
+ ds->host, user);
+ if(n == -1)
+ return "cannot find SMTP password";
+ if(usr[0] == 0)
+ return "cannot find user name";
+ for(i = 0; i < n; i++)
+ rbuf[i] = tolower(rbuf[i]);
+ l = snprint(ubuf, sizeof ubuf, "%s %.*s", usr, n, rbuf);
+ snprint(ebuf, sizeof ebuf, "%.*[", l, ubuf);
+
+ dBprint("%s\r\n", ch);
+ if(getreply() != 2)
+ return Retry;
+ return nil;
+}
+
static char *
doauth(char *methods)
{
@@ -404,14 +498,13 @@ doauth(char *methods)
int n;
DS ds;
- dial_string_parse(ddomain, &ds);
+ dialstringparse(ddomain, &ds);
+ if(strstr(methods, "CRAM-MD5"))
+ return smtpcram(&ds);
- if(user != nil)
- p = auth_getuserpasswd(nil,
- "proto=pass service=smtp server=%q user=%q", ds.host, user);
- else
- p = auth_getuserpasswd(nil,
- "proto=pass service=smtp server=%q", ds.host);
+ p = auth_getuserpasswd(nil,
+ user?"proto=pass service=smtp server=%q user=%q":"proto=pass service=smtp server=%q",
+ ds.host, user);
if (p == nil)
return Giveup;
@@ -455,14 +548,14 @@ out:
return err;
}
-char *
+char*
hello(char *me, int encrypted)
{
+ char *ret, *s, *t;
int ehlo;
String *r;
- char *ret, *s, *t;
- if (!encrypted) {
+ if(!encrypted){
if(trysecure > 1){
if((ret = wraptls()) != nil)
return ret;
@@ -474,7 +567,7 @@ hello(char *me, int encrypted)
* answers a call. Send a no-op in the hope of making it
* talk.
*/
- if (autistic) {
+ if(autistic){
dBprint("NOOP\r\n");
getreply(); /* consume the smtp greeting */
/* next reply will be response to noop */
@@ -495,7 +588,7 @@ hello(char *me, int encrypted)
dBprint("EHLO %s\r\n", me);
else
dBprint("HELO %s\r\n", me);
- switch (getreply()) {
+ switch(getreply()){
case 2:
break;
case 5:
@@ -514,17 +607,15 @@ hello(char *me, int encrypted)
/* Invariant: every line has a newline, a result of getcrlf() */
for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
*t = '\0';
- for (t = s; *t != '\0'; t++)
- *t = toupper(*t);
if(!encrypted && trysecure &&
- (strcmp(s, "250-STARTTLS") == 0 ||
- strcmp(s, "250 STARTTLS") == 0)){
+ (cistrcmp(s, "250-STARTTLS") == 0 ||
+ cistrcmp(s, "250 STARTTLS") == 0)){
s_free(r);
return dotls(me);
}
if(tryauth && (encrypted || insecure) &&
- (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
- strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
+ (cistrncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
+ cistrncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
ret = doauth(s + strlen("250 AUTH "));
s_free(r);
return ret;
@@ -542,20 +633,18 @@ mailfrom(char *from)
{
if(!returnable(from))
dBprint("MAIL FROM:<>\r\n");
- else
- if(strchr(from, '@'))
+ else if(strchr(from, '@'))
dBprint("MAIL FROM:<%s>\r\n", from);
else
dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
switch(getreply()){
case 2:
- break;
+ return 0;
case 5:
return Giveup;
default:
return Retry;
}
- return 0;
}
/*
@@ -597,13 +686,11 @@ static char hex[] = "0123456789abcdef";
* send the damn thing
*/
char *
-data(String *from, Biobuf *b)
+data(String *from, Biobuf *b, Mx *mx)
{
- char *buf, *cp;
+ char *buf, *cp, errmsg[ERRMAX], id[40];
int i, n, nbytes, bufsize, eof, r;
String *fromline;
- char errmsg[Errlen];
- char id[40];
/*
* input the header.
@@ -623,12 +710,12 @@ data(String *from, Biobuf *b)
break;
}
nbytes = Blinelen(b);
- buf = realloc(buf, n+nbytes+1);
+ buf = realloc(buf, n + nbytes + 1);
if(buf == 0){
s_append(s_restart(reply), "out of memory");
return Retry;
}
- strncpy(buf+n, cp, nbytes);
+ strncpy(buf + n, cp, nbytes);
n += nbytes;
if(nbytes == 1) /* end of header */
break;
@@ -669,10 +756,10 @@ data(String *from, Biobuf *b)
srand(truerand());
if(messageid == 0){
- for(i=0; i<16; i++){
- r = rand()&0xFF;
- id[2*i] = hex[r&0xF];
- id[2*i+1] = hex[(r>>4)&0xF];
+ for(i = 0; i < 16; i++){
+ r = rand() & 0xff;
+ id[2*i] = hex[r & 0xf];
+ id[2*i + 1] = hex[(r>>4) & 0xf];
}
id[2*i] = '\0';
nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
@@ -680,7 +767,7 @@ data(String *from, Biobuf *b)
Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
}
- if(originator==0){
+ if(originator == 0){
nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
if(debug)
Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
@@ -698,20 +785,20 @@ data(String *from, Biobuf *b)
Bprint(&berr, "To: %s\r\n", s_to_c(toline));
}
- if(date==0 && udate)
+ if(date == 0 && udate)
nbytes += printdate(udate);
- if (usys)
+ if(usys)
uneaten = usys->end + 1;
nbytes += printheader();
- if (*uneaten != '\n')
+ if(*uneaten != '\n')
putcrnl("\n", 1);
/*
* send body
*/
- putcrnl(uneaten, buf+n - uneaten);
- nbytes += buf+n - uneaten;
+ putcrnl(uneaten, buf + n - uneaten);
+ nbytes += buf + n - uneaten;
if(eof == 0){
for(;;){
n = Bread(b, buf, bufsize);
@@ -743,8 +830,8 @@ data(String *from, Biobuf *b)
default:
return Retry;
}
- syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
- nbytes, s_to_c(toline));/**/
+ syslog(0, "smtp", "%s sent %d bytes to %s %D", s_to_c(from),
+ nbytes, s_to_c(toline), mx);
}
return 0;
}
@@ -806,10 +893,9 @@ addhostdom(String *buf, char *host)
String *
bangtoat(char *addr)
{
- String *buf;
- register int i;
- int j, d;
char *field[128];
+ int i, j, d;
+ String *buf;
/* parse the '!' format address */
buf = s_new();
@@ -819,7 +905,7 @@ bangtoat(char *addr)
if(addr)
*addr++ = 0;
}
- if (i==1) {
+ if(i == 1){
s_append(buf, field[0]);
return buf;
}
@@ -827,8 +913,8 @@ bangtoat(char *addr)
/*
* count leading domain fields (non-domains don't count)
*/
- for(d = 0; d<i-1; d++)
- if(strchr(field[d], '.')==0)
+ for(d = 0; d < i - 1; d++)
+ if(strchr(field[d], '.') == 0)
break;
/*
* if there are more than 1 leading domain elements,
@@ -836,7 +922,7 @@ bangtoat(char *addr)
*/
if(d > 1){
addhostdom(buf, field[0]);
- for(j=1; j<d-1; j++){
+ for(j = 1; j< d - 1; j++){
s_append(buf, ",");
s_append(buf, "@");
s_append(buf, field[j]);
@@ -848,7 +934,7 @@ bangtoat(char *addr)
* throw in the non-domain elements separated by '!'s
*/
s_append(buf, field[d]);
- for(j=d+1; j<=i-1; j++) {
+ for(j = d + 1; j <= i - 1; j++){
s_append(buf, "!");
s_append(buf, field[j]);
}
@@ -865,6 +951,7 @@ bangtoat(char *addr)
String*
convertheader(String *from)
{
+ char *s, buf[64];
Field *f;
Node *p, *lastp;
String *a;
@@ -875,8 +962,10 @@ convertheader(String *from)
addhostdom(from, hostdomain);
} else
if(strchr(s_to_c(from), '@') == 0){
- a = username(from);
- if(a) {
+ if(s = username(s_to_c(from))){
+ /* this has always been here, but username() was broken */
+ snprint(buf, sizeof buf, "%U", s);
+ s_append(a = s_new(), buf);
s_append(a, " <");
s_append(a, s_to_c(from));
addhostdom(a, hostdomain);
@@ -929,11 +1018,10 @@ fixrouteaddr(String *raddr, Node *next, Node *last)
int
printheader(void)
{
+ char *cp, c[1];
int n, len;
Field *f;
Node *p;
- char *cp;
- char c[1];
n = 0;
for(f = firstfield; f; f = f->next){
@@ -966,10 +1054,10 @@ printheader(void)
char *
domainify(char *name, char *domain)
{
- static String *s;
char *p;
+ static String *s;
- if(domain==0 || strchr(name, '.')!=0)
+ if(domain == 0 || strchr(name, '.') != 0)
return name;
s = s_reset(s);
@@ -1008,8 +1096,7 @@ putcrnl(char *cp, int n)
char *
getcrnl(String *s)
{
- int c;
- int count;
+ int c, count;
count = 0;
for(;;){
@@ -1052,16 +1139,17 @@ getcrnl(String *s)
int
printdate(Node *p)
{
- int n, sep = 0;
+ int n, sep;
n = dBprint("Date: %s,", s_to_c(p->s));
+ sep = 0;
for(p = p->next; p; p = p->next){
if(p->s){
- if(sep == 0) {
+ if(sep == 0){
dBputc(' ');
n++;
}
- if (p->next)
+ if(p->next)
n += dBprint("%s", s_to_c(p->s));
else
n += dBprint("%s", rewritezone(s_to_c(p->s)));
@@ -1079,8 +1167,8 @@ printdate(Node *p)
char *
rewritezone(char *z)
{
- int mindiff;
char s;
+ int mindiff;
Tm *tm;
static char x[7];
@@ -1104,22 +1192,22 @@ rewritezone(char *z)
/*
* stolen from libc/port/print.c
*/
-#define SIZE 4096
+
int
dBprint(char *fmt, ...)
{
- char buf[SIZE], *out;
- va_list arg;
+ char buf[4096], *out;
int n;
+ va_list arg;
va_start(arg, fmt);
- out = vseprint(buf, buf+SIZE, fmt, arg);
+ out = vseprint(buf, buf + sizeof buf, fmt, arg);
va_end(arg);
if(debug){
- Bwrite(&berr, buf, (long)(out-buf));
+ Bwrite(&berr, buf, out - buf);
Bflush(&berr);
}
- n = Bwrite(&bout, buf, (long)(out-buf));
+ n = Bwrite(&bout, buf,out - buf);
Bflush(&bout);
return n;
}
diff --git a/sys/src/cmd/upas/smtp/smtp.h b/sys/src/cmd/upas/smtp/smtp.h
index bfac33ed9..d420d1dc3 100644
--- a/sys/src/cmd/upas/smtp/smtp.h
+++ b/sys/src/cmd/upas/smtp/smtp.h
@@ -24,8 +24,11 @@ struct Field {
};
typedef struct DS DS;
+typedef struct Mx Mx;
+typedef struct Mxtab Mxtab;
+
struct DS {
- /* dist string */
+ /* dial string */
char buf[128];
char expand[128];
char *netdir;
@@ -34,6 +37,25 @@ struct DS {
char *service;
};
+struct Mx
+{
+ char *netdir;
+ char host[256];
+ char ip[24];
+ int pref;
+ int valid;
+};
+
+struct Mxtab
+{
+ DS ds[2];
+ int nmx;
+ int amx;
+ int pmx;
+ int fd;
+ Mx *mx;
+};
+
extern Field *firstfield;
extern Field *lastfield;
extern Node *usender;
@@ -51,7 +73,6 @@ Node* address(Node*);
int badfieldname(Node*);
Node* bang(Node*, Node*);
Node* colon(Node*, Node*);
-int cistrcmp(char*, char*);
Node* link2(Node*, Node*);
Node* link3(Node*, Node*, Node*);
void freenode(Node*);
@@ -63,5 +84,9 @@ int yylex(void);
String* yywhite(void);
Node* whiten(Node*);
void yycleanup(void);
-int mxdial(char*, char*, char*);
-void dial_string_parse(char*, DS*);
+int mxdial0(char*, char*, char*, Mxtab*);
+int mxdial(char*, char*, char*, Mx*);
+void mxtabfree(Mxtab*);
+void dialstringparse(char*, DS*);
+
+#define dprint(...) do if(debug)print(__VA_ARGS__); while(0)
diff --git a/sys/src/cmd/upas/smtp/smtpd.c b/sys/src/cmd/upas/smtp/smtpd.c
index 85fbb098a..6c75e9399 100644
--- a/sys/src/cmd/upas/smtp/smtpd.c
+++ b/sys/src/cmd/upas/smtp/smtpd.c
@@ -22,14 +22,15 @@ int logged;
int rejectcount;
int hardreject;
-ulong starttime;
-
Biobuf bin;
int debug;
int Dflag;
+int Eflag;
+int eflag;
int fflag;
int gflag;
+int qflag;
int rflag;
int sflag;
int authenticate;
@@ -50,48 +51,35 @@ int pipemsg(int*);
int rejectcheck(void);
String* startcmd(void);
-static void logmsg(char *action);
-
static int
-catchalarm(void *a, char *msg)
+catchalarm(void*, char *msg)
{
- int rv;
-
- USED(a);
+ int ign;
+ static int chattycathy;
- /* log alarms but continue */
- if(strstr(msg, "alarm") != nil){
- if(senders.first && senders.first->p &&
- rcvers.first && rcvers.first->p)
+ ign = strstr(msg, "closed pipe") != nil;
+ if(ign)
+ return 0;
+ if(chattycathy++ < 5){
+ if(senders.first && rcvers.first)
syslog(0, "smtpd", "note: %s->%s: %s",
s_to_c(senders.first->p),
s_to_c(rcvers.first->p), msg);
else
syslog(0, "smtpd", "note: %s", msg);
- rv = Atnoterecog;
- } else
- rv = Atnoteunknown;
- if (debug) {
- seek(2, 0, 2);
- fprint(2, "caught note: %s\n", msg);
}
-
- /* kill the children if there are any */
- if(pp && pp->pid > 0) {
- syskillpg(pp->pid);
- /* none can't syskillpg, so try a variant */
- sleep(500);
+ if(pp){
syskill(pp->pid);
+ // pp = 0;
}
-
- return rv;
+ return strstr(msg, "alarm") != nil;
}
/* override string error functions to do something reasonable */
void
s_error(char *f, char *status)
{
- char errbuf[Errlen];
+ char errbuf[ERRMAX];
errbuf[0] = 0;
rerrstr(errbuf, sizeof(errbuf));
@@ -106,8 +94,7 @@ s_error(char *f, char *status)
static void
usage(void)
{
- fprint(2,
- "usage: smtpd [-adDfghprs] [-c cert] [-k ip] [-m mailer] [-n net]\n");
+ fprint(2, "usage: smtpd [-DEadefghpqrs] [-c cert] [-k ip] [-m mailer] [-n net]\n");
exits("usage");
}
@@ -121,7 +108,6 @@ main(int argc, char **argv)
quotefmtinstall();
fmtinstall('I', eipfmt);
fmtinstall('[', encodefmt);
- starttime = time(0);
ARGBEGIN{
case 'a':
authenticate = 1;
@@ -135,6 +121,12 @@ main(int argc, char **argv)
case 'd':
debug++;
break;
+ case 'E':
+ Eflag = 1;
+ break; /* if you fail extra helo checks, you must authenticate */
+ case 'e':
+ eflag = 1; /* disable extra helo checks */
+ break;
case 'f': /* disallow relaying */
fflag = 1;
break;
@@ -156,41 +148,39 @@ main(int argc, char **argv)
case 'p':
passwordinclear = 1;
break;
+ case 'q':
+ qflag = 1; /* don't log invalid hello */
+ break;
case 'r':
rflag = 1; /* verify sender's domain */
break;
case 's': /* save blocked messages */
sflag = 1;
break;
- case 't':
- fprint(2, "%s: the -t option is no longer supported, see -c\n",
- argv0);
- tlscert = "/sys/lib/ssl/smtpd-cert.pem";
- break;
default:
usage();
}ARGEND;
nci = getnetconninfo(netdir, 0);
if(nci == nil)
- sysfatal("can't get remote system's address: %r");
+ sysfatal("can't get remote system's address");
parseip(rsysip, nci->rsys);
if(mailer == nil)
mailer = mailerpath("send");
if(debug){
- snprint(buf, sizeof buf, "%s/smtpdb/%ld", UPASLOG, time(0));
close(2);
- if (create(buf, OWRITE | OEXCL, 0662) >= 0) {
+ snprint(buf, sizeof(buf), "%s/smtpd.db", UPASLOG);
+ if (open(buf, OWRITE) >= 0) {
seek(2, 0, 2);
fprint(2, "%d smtpd %s\n", getpid(), thedate());
} else
debug = 0;
}
getconf();
- if (isbadguy())
- exits("banned");
+ if(isbadguy())
+ exits("");
Binit(&bin, 0, OREAD);
if (chdir(UPASLOG) < 0)
@@ -239,32 +229,20 @@ listadd(List *l, String *path)
l->last = lp;
}
-void
-stamp(void)
-{
- if(debug) {
- seek(2, 0, 2);
- fprint(2, "%3lud ", time(0) - starttime);
- }
-}
-
-#define SIZE 4096
-
int
reply(char *fmt, ...)
{
- long n;
- char buf[SIZE], *out;
+ char buf[4096], *out;
+ int n;
va_list arg;
va_start(arg, fmt);
- out = vseprint(buf, buf+SIZE, fmt, arg);
+ out = vseprint(buf, buf + 4096, fmt, arg);
va_end(arg);
n = out - buf;
if(debug) {
seek(2, 0, 2);
- stamp();
write(2, buf, n);
}
write(1, buf, n);
@@ -291,6 +269,36 @@ sayhi(void)
reply("220 %s ESMTP\r\n", dom);
}
+Ndbtuple*
+rquery(char *d)
+{
+ Ndbtuple *t, *p;
+
+ t = dnsquery(nci->root, nci->rsys, "ptr");
+ for(p = t; p != nil; p = p->entry)
+ if(strcmp(p->attr, "dom") == 0
+ && strcmp(p->val, d) == 0){
+ syslog(0, "smtpd", "ptr only from %s as %s",
+ nci->rsys, d);
+ return t;
+ }
+ ndbfree(t);
+ return nil;
+}
+
+int
+dnsexists(char *d)
+{
+ int r;
+ Ndbtuple *t;
+
+ r = -1;
+ if((t = dnsquery(nci->root, d, "any")) != nil || (t = rquery(d)) != nil)
+ r = 0;
+ ndbfree(t);
+ return r;
+}
+
/*
* make callers from class A networks infested by spammers
* wait longer.
@@ -324,102 +332,153 @@ static char netaspam[256] = {
static int
delaysecs(void)
{
- if (trusted)
+ if (netaspam[rsysip[0]])
+ return 60;
+ return 15;
+}
+
+static char *badtld[] = {
+ "localdomain",
+ "localhost",
+ "local",
+ "example",
+ "invalid",
+ "lan",
+ "test",
+};
+
+static char *bad2ld[] = {
+ "example.com",
+ "example.net",
+ "example.org"
+};
+
+int
+badname(void)
+{
+ char *p;
+
+ /*
+ * similarly, if the claimed domain is not an address-literal,
+ * require at least one letter, which there will be in
+ * at least the last component (e.g., .com, .net) if it's real.
+ * this rejects non-address-literal IP addresses,
+ * among other bogosities.
+ */
+ for (p = him; *p; p++)
+ if(isascii(*p) && isalpha(*p))
+ return 0;
+ return -1;
+}
+
+int
+ckhello(void)
+{
+ char *ldot, *rdot;
+ int i;
+
+ /*
+ * it is unacceptable to claim any string that doesn't look like
+ * a domain name (e.g., has at least one dot in it), but
+ * Microsoft mail client software gets this wrong, so let trusted
+ * (local) clients omit the dot.
+ */
+ rdot = strrchr(him, '.');
+ if(rdot && rdot[1] == '\0') {
+ *rdot = '\0'; /* clobber trailing dot */
+ rdot = strrchr(him, '.'); /* try again */
+ }
+ if(rdot == nil)
+ return -1;
+ /*
+ * Reject obviously bogus domains and those reserved by RFC 2606.
+ */
+ if(rdot == nil)
+ rdot = him;
+ else
+ rdot++;
+ for(i = 0; i < nelem(badtld); i++)
+ if(!cistrcmp(rdot, badtld[i]))
+ return -1;
+ /* check second-level RFC 2606 domains: example\.(com|net|org) */
+ if(rdot != him)
+ *--rdot = '\0';
+ ldot = strrchr(him, '.');
+ if(rdot != him)
+ *rdot = '.';
+ if(ldot == nil)
+ ldot = him;
+ else
+ ldot++;
+ for(i = 0; i < nelem(bad2ld); i++)
+ if(!cistrcmp(ldot, bad2ld[i]))
+ return -1;
+ if(badname() == -1)
+ return -1;
+ if(dnsexists(him) == -1)
+ return -1;
+ return 0;
+}
+
+int
+heloclaims(void)
+{
+ char **s;
+
+ /*
+ * We don't care if he lies about who he is, but it is
+ * not okay to pretend to be us. Many viruses do this,
+ * just parroting back what we say in the greeting.
+ */
+ if(strcmp(nci->rsys, nci->lsys) == 0)
return 0;
- if (0 && netaspam[rsysip[0]])
- return 20;
- return 12;
+ if(strcmp(him, dom) == 0)
+ return -1;
+ for(s = sysnames_read(); s && *s; s++)
+ if(cistrcmp(*s, him) == 0)
+ return -1;
+ if(him[0] != '[' && badname() == -1)
+ return -1;
+
+ return 0;
}
void
hello(String *himp, int extended)
{
- char **mynames;
- char *ldot, *rdot;
- char *p;
+ int ck;
him = s_to_c(himp);
- syslog(0, "smtpd", "%s from %s as %s", extended? "ehlo": "helo",
- nci->rsys, him);
+ if(!qflag)
+ syslog(0, "smtpd", "%s from %s as %s", extended? "ehlo": "helo",
+ nci->rsys, him);
if(rejectcheck())
return;
- if (him[0] == '[') {
- /*
- * reject literal ip addresses when not trusted.
- */
- if (!trusted)
- goto Liarliar;
- him = nci->rsys;
- } else {
- if (!trusted && fflag && nci && strcmp(nci->rsys, nci->lsys) != 0){
- /*
- * We don't care if he lies about who he is, but it is
- * not okay to pretend to be us. Many viruses do this,
- * just parroting back what we say in the greeting.
- */
- if(cistrcmp(him, dom) == 0)
- goto Liarliar;
- for(mynames = sysnames_read(); mynames && *mynames; mynames++)
- if(cistrcmp(*mynames, him) == 0)
- goto Liarliar;
- }
-
- /*
- * require at least one letter, which there will be in
- * at least the last component (e.g., .com, .net) if it's real.
- * this rejects non-address-literal IP addresses,
- * among other bogosities.
- */
- for (p = him; *p != '\0'; p++)
- if (isascii(*p) && isalpha(*p))
- break;
- if (*p == '\0')
- goto Liarliar;
-
- /*
- * it is unacceptable to claim any string that doesn't look like
- * a domain name (e.g., has at least one dot in it), but
- * Microsoft mail client software gets this wrong, so let trusted
- * (local) clients omit the dot.
- */
- rdot = strrchr(him, '.');
- if (rdot && rdot[1] == '\0') {
- *rdot = '\0'; /* clobber trailing dot */
- rdot = strrchr(him, '.'); /* try again */
- }
- if (!trusted && rdot == nil)
- goto Liarliar;
-
- /*
- * Reject obviously bogus domains and those reserved by RFC 2606.
- */
- if (rdot == nil)
- rdot = him;
- else
- rdot++;
- if (cistrcmp(rdot, "localdomain") == 0 ||
- cistrcmp(rdot, "localhost") == 0 ||
- cistrcmp(rdot, "example") == 0 ||
- cistrcmp(rdot, "invalid") == 0 ||
- cistrcmp(rdot, "test") == 0)
- goto Liarliar; /* bad top-level domain */
- /* check second-level RFC 2606 domains: example\.(com|net|org) */
- if (rdot != him)
- *--rdot = '\0';
- ldot = strrchr(him, '.');
- if (rdot != him)
- *rdot = '.';
- if (ldot == nil)
- ldot = him;
- else
- ldot++;
- if (cistrcmp(ldot, "example.com") == 0 ||
- cistrcmp(ldot, "example.net") == 0 ||
- cistrcmp(ldot, "example.org") == 0)
- goto Liarliar;
+ ck = -1;
+ if(!trusted && nci)
+ if(heloclaims() || (!eflag && (ck = ckhello())))
+ if(ck && Eflag){
+ reply("250-you lie. authentication required.\r\n");
+ authenticate = 1;
+ }else{
+ if(Dflag)
+ sleep(delaysecs()*1000);
+ if(!qflag)
+ syslog(0, "smtpd", "Hung up on %s; claimed to be %s",
+ nci->rsys, him);
+ rejectcount++;
+ reply("554 5.7.0 Liar!\r\n");
+ exits("client pretended to be us");
+ return;
}
+ if(strchr(him, '.') == 0 && nci != nil && strchr(nci->rsys, '.') != nil)
+ him = nci->rsys;
+
+ if(qflag)
+ syslog(0, "smtpd", "%s from %s as %s", extended? "ehlo": "helo",
+ nci->rsys, him);
if(Dflag)
sleep(delaysecs()*1000);
reply("250%c%s you are %s\r\n", extended ? '-' : ' ', dom, him);
@@ -432,15 +491,6 @@ hello(String *himp, int extended)
else
reply("250 AUTH CRAM-MD5\r\n");
}
- return;
-
-Liarliar:
- syslog(0, "smtpd", "Hung up on %s; claimed to be %s",
- nci->rsys, him);
- if(Dflag)
- sleep(delaysecs()*1000);
- reply("554 5.7.0 Liar!\r\n");
- exits("client pretended to be us");
}
void
@@ -481,33 +531,9 @@ sender(String *path)
/*
* see if this ip address, domain name, user name or account is blocked
*/
- logged = 0;
filterstate = blocked(path);
- /*
- * permanently reject what we can before trying smtp ping, which
- * often leads to merely temporary rejections.
- */
- switch (filterstate){
- case DENIED:
- syslog(0, "smtpd", "Denied %s (%s/%s)",
- s_to_c(path), him, nci->rsys);
- rejectcount++;
- logged++;
- reply("554-5.7.1 We don't accept mail from %s.\r\n",
- s_to_c(path));
- reply("554 5.7.1 Contact postmaster@%s for more information.\r\n",
- dom);
- return;
- case REFUSED:
- syslog(0, "smtpd", "Refused %s (%s/%s)",
- s_to_c(path), him, nci->rsys);
- rejectcount++;
- logged++;
- reply("554 5.7.1 Sender domain must exist: %s\r\n",
- s_to_c(path));
- return;
- }
+ logged = 0;
listadd(&senders, path);
reply("250 2.0.0 sender is %s\r\n", s_to_c(path));
}
@@ -642,7 +668,7 @@ receiver(String *path)
if(!recipok(s_to_c(path))){
rejectcount++;
syslog(0, "smtpd",
- "Disallowed %s (%s/%s) to blocked, unknown or invalid name %s",
+ "Disallowed %s (%s/%s) to blocked name %s",
sender, him, nci->rsys, s_to_c(path));
reply("550 5.1.1 %s ... user unknown\r\n", s_to_c(path));
return;
@@ -660,11 +686,9 @@ receiver(String *path)
/* forwarding() can modify 'path' on loopback request */
if(filterstate == ACCEPT && fflag && !authenticated && forwarding(path)) {
- syslog(0, "smtpd", "Bad Forward %s (%s/%s) (%s)",
- senders.last && senders.last->p?
- s_to_c(senders.last->p): sender,
- him, nci->rsys, path? s_to_c(path): rcpt);
rejectcount++;
+ syslog(0, "smtpd", "Bad Forward %s (%s/%s) (%s)",
+ sender, him, nci->rsys, rcpt);
reply("550 5.7.1 we don't relay. send to your-path@[] for "
"loopback.\r\n");
return;
@@ -677,12 +701,6 @@ void
quit(void)
{
reply("221 2.0.0 Successful termination\r\n");
- if(debug){
- seek(2, 0, 2);
- stamp();
- fprint(2, "# %d sent 221 reply to QUIT %s\n",
- getpid(), thedate());
- }
close(0);
exits(0);
}
@@ -710,10 +728,14 @@ verify(String *path)
{
char *p, *q;
char *av[4];
+ static uint nverify;
if(rejectcheck())
return;
+ if(nverify++ >= 2)
+ sleep(1000 * (4 << nverify - 2));
if(shellchars(s_to_c(path))){
+ rejectcount++;
reply("503 5.1.3 Bad character in address %s.\r\n", s_to_c(path));
return;
}
@@ -722,7 +744,7 @@ verify(String *path)
av[2] = s_to_c(path);
av[3] = 0;
- pp = noshell_proc_start(av, (stream *)0, outstream(), (stream *)0, 1, 0);
+ pp = noshell_proc_start(av, 0, outstream(), 0, 1, 0);
if (pp == 0) {
reply("450 4.3.2 We're busy right now, try later\r\n");
return;
@@ -732,13 +754,13 @@ verify(String *path)
if(p == 0){
reply("550 5.1.0 String does not match anything.\r\n");
} else {
- p[Blinelen(pp->std[1]->fp)-1] = 0;
+ p[Blinelen(pp->std[1]->fp) - 1] = 0;
if(strchr(p, ':'))
reply("550 5.1.0 String does not match anything.\r\n");
else{
q = strrchr(p, '!');
if(q)
- p = q+1;
+ p = q + 1;
reply("250 2.0.0 %s <%s@%s>\r\n", s_to_c(path), p, dom);
}
}
@@ -765,6 +787,7 @@ getcrnl(String *s, Biobuf *fp)
}
switch(c){
case 0:
+ /* idiot html email! */
break;
case -1:
goto out;
@@ -774,7 +797,6 @@ getcrnl(String *s, Biobuf *fp)
if(debug) {
seek(2, 0, 2);
fprint(2, "%c", c);
- stamp();
}
s_putc(s, '\n');
goto out;
@@ -912,15 +934,14 @@ startcmd(void)
dom);
return 0;
case ACCEPT:
+ case TRUSTED:
/*
* now that all other filters have been passed,
* do grey-list processing.
*/
if(gflag)
vfysenderhostok();
- /* fall through */
- case TRUSTED:
/*
* set up mail command
*/
@@ -962,34 +983,24 @@ startcmd(void)
* address@him
*/
char*
-bprintnode(Biobuf *b, Node *p, int *cntp)
+bprintnode(Biobuf *b, Node *p, int *nbytes)
{
- int len;
+ int n, m;
- *cntp = 0;
if(p->s){
- if(p->addr && strchr(s_to_c(p->s), '@') == nil){
- if(Bprint(b, "%s@%s", s_to_c(p->s), him) < 0)
- return nil;
- *cntp += s_len(p->s) + 1 + strlen(him);
- } else {
- len = s_len(p->s);
- if(Bwrite(b, s_to_c(p->s), len) < 0)
- return nil;
- *cntp += len;
- }
- }else{
- if(Bputc(b, p->c) < 0)
- return nil;
- ++*cntp;
- }
- if(p->white) {
- len = s_len(p->white);
- if(Bwrite(b, s_to_c(p->white), len) < 0)
- return nil;
- *cntp += len;
- }
- return p->end+1;
+ if(p->addr && strchr(s_to_c(p->s), '@') == nil)
+ n = Bprint(b, "%s@%s", s_to_c(p->s), him);
+ else
+ n = Bwrite(b, s_to_c(p->s), s_len(p->s));
+ }else
+ n = Bputc(b, p->c) == -1? -1: 1;
+ m = 0;
+ if(n != -1 && p->white)
+ m = Bwrite(b, s_to_c(p->white), s_len(p->white));
+ if(n == -1 || m == -1)
+ return nil;
+ *nbytes += n + m;
+ return p->end + 1;
}
static String*
@@ -1017,8 +1028,7 @@ forgedheaderwarnings(void)
nbytes = 0;
/* warn about envelope sender */
- if(senders.last != nil && senders.last->p != nil &&
- strcmp(s_to_c(senders.last->p), "/dev/null") != 0 &&
+ if(strcmp(s_to_c(senders.last->p), "/dev/null") != 0 &&
masquerade(senders.last->p, nil))
nbytes += Bprint(pp->std[0]->fp,
"X-warning: suspect envelope domain\n");
@@ -1044,68 +1054,16 @@ forgedheaderwarnings(void)
return nbytes;
}
-/*
- * pipe message to mailer with the following transformations:
- * - change \r\n into \n.
- * - add sender's domain to any addrs with no domain
- * - add a From: if none of From:, Sender:, or Replyto: exists
- * - add a Received: line
- */
-int
-pipemsg(int *byteswritten)
+static int
+parseheader(String *hdr, int *nbytesp, int *status)
{
- int n, nbytes, sawdot, status, nonhdr, bpr;
char *cp;
+ int nbytes, n;
Field *f;
Link *l;
Node *p;
- String *hdr, *line;
- pipesig(&status); /* set status to 1 on write to closed pipe */
- sawdot = 0;
- status = 0;
-
- /*
- * add a 'From ' line as envelope
- */
- nbytes = 0;
- nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n",
- s_to_c(senders.first->p), thedate());
-
- /*
- * add our own Received: stamp
- */
- nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him);
- if(nci->rsys)
- nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys);
- nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate());
-
- /*
- * read first 16k obeying '.' escape. we're assuming
- * the header will all be there.
- */
- line = s_new();
- hdr = s_new();
- while(sawdot == 0 && s_len(hdr) < 16*1024){
- n = getcrnl(s_reset(line), &bin);
-
- /* eof or error ends the message */
- if(n <= 0)
- break;
-
- /* a line with only a '.' ends the message */
- cp = s_to_c(line);
- if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
- sawdot = 1;
- break;
- }
-
- s_append(hdr, *cp == '.' ? cp+1 : cp);
- }
-
- /*
- * parse header
- */
+ nbytes = *nbytesp;
yyinit(s_to_c(hdr), s_len(hdr));
yyparse();
@@ -1120,9 +1078,8 @@ pipemsg(int *byteswritten)
* add an orginator and/or destination if either is missing
*/
if(originator == 0){
- if(senders.last == nil || senders.last->p == nil)
- nbytes += Bprint(pp->std[0]->fp, "From: /dev/null@%s\n",
- him);
+ if(senders.last == nil)
+ nbytes += Bprint(pp->std[0]->fp, "From: /dev/null@%s\n", him);
else
nbytes += Bprint(pp->std[0]->fp, "From: %s\n",
s_to_c(senders.last->p));
@@ -1143,45 +1100,152 @@ pipemsg(int *byteswritten)
*/
cp = s_to_c(hdr);
for(f = firstfield; cp != nil && f; f = f->next){
- for(p = f->node; cp != 0 && p; p = p->next) {
- bpr = 0;
- cp = bprintnode(pp->std[0]->fp, p, &bpr);
- nbytes += bpr;
- }
- if(status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){
+ for(p = f->node; cp != 0 && p; p = p->next)
+ cp = bprintnode(pp->std[0]->fp, p, &nbytes);
+ if(*status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){
piperror = "write error";
- status = 1;
+ *status = 1;
}
- nbytes++; /* for newline */
+ nbytes++;
}
if(cp == nil){
piperror = "sender domain";
- status = 1;
+ *status = 1;
}
-
/* write anything we read following the header */
- nonhdr = s_to_c(hdr) + s_len(hdr) - cp;
- if(status == 0 && Bwrite(pp->std[0]->fp, cp, nonhdr) < 0){
- piperror = "write error 2";
- status = 1;
+ if(*status == 0){
+ n = Bwrite(pp->std[0]->fp, cp, s_to_c(hdr) + s_len(hdr) - cp);
+ if(n == -1){
+ piperror = "write error 2";
+ *status = 1;
+ }
+ nbytes += n;
+ }
+
+ *nbytesp = nbytes;
+ return *status;
+}
+
+static int
+chkhdr(char *s, int n)
+{
+ int i;
+ Rune r;
+
+ for(i = 0; i < n; ){
+ if(!fullrune(s + i, n - i))
+ return -1;
+ i += chartorune(&r, s + i);
+ if(r == Runeerror)
+ return -1;
+ }
+ return 0;
+}
+
+static void
+fancymsg(int status)
+{
+ static char msg[2*ERRMAX], *p, *e;
+
+ if(!status)
+ return;
+
+ p = seprint(msg, msg+ERRMAX, "%s: ", piperror);
+ rerrstr(p, ERRMAX);
+ piperror = msg;
+}
+
+/*
+ * pipe message to mailer with the following transformations:
+ * - change \r\n into \n.
+ * - add sender's domain to any addrs with no domain
+ * - add a From: if none of From:, Sender:, or Replyto: exists
+ * - add a Received: line
+ * - elide leading dot
+ */
+int
+pipemsg(int *byteswritten)
+{
+ char *cp;
+ int n, nbytes, sawdot, status;
+ String *hdr, *line;
+
+ pipesig(&status); /* set status to 1 on write to closed pipe */
+ sawdot = 0;
+ status = 0;
+ werrstr("");
+ piperror = nil;
+
+ /*
+ * add a 'From ' line as envelope and Received: stamp
+ */
+ nbytes = 0;
+ nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n",
+ s_to_c(senders.first->p), thedate());
+ nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him);
+ if(nci->rsys)
+ nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys);
+ nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate());
+
+ /*
+ * read first 16k obeying '.' escape. we're assuming
+ * the header will all be there.
+ */
+ line = s_new();
+ hdr = s_new();
+ while(s_len(hdr) < 16*1024){
+ n = getcrnl(s_reset(line), &bin);
+
+ /* eof or error ends the message */
+ if(n <= 0){
+ piperror = "header read error";
+ status = 1;
+ break;
+ }
+
+ cp = s_to_c(line);
+ if(chkhdr(cp, s_len(line)) == -1){
+ status = 1;
+ piperror = "mail refused: illegal header chars";
+ break;
+ }
+
+ /* a line with only a '.' ends the message */
+ if(cp[0] == '.' && cp[1] == '\n'){
+ sawdot = 1;
+ break;
+ }
+ if(cp[0] == '.'){
+ cp++;
+ n--;
+ }
+ s_append(hdr, cp);
+ nbytes += n;
+ if(*cp == '\n')
+ break;
}
- nbytes += nonhdr;
+ if(status == 0)
+ parseheader(hdr, &nbytes, &status);
s_free(hdr);
/*
* pass rest of message to mailer. take care of '.'
* escapes.
*/
- while(sawdot == 0){
+ for(;;){
n = getcrnl(s_reset(line), &bin);
/* eof or error ends the message */
+ if(n < 0){
+ piperror = "body read error";
+ status = 1;
+ }
if(n <= 0)
break;
/* a line with only a '.' ends the message */
cp = s_to_c(line);
- if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
+ if(cp[0] == '.' && cp[1] == '\n'){
sawdot = 1;
break;
}
@@ -1193,37 +1257,29 @@ pipemsg(int *byteswritten)
if(status == 0 && Bwrite(pp->std[0]->fp, cp, n) < 0){
piperror = "write error 3";
status = 1;
+ break;
}
}
s_free(line);
- if(sawdot == 0){
+ if(status == 0 && sawdot == 0){
/* message did not terminate normally */
- snprint(pipbuf, sizeof pipbuf, "network eof: %r");
+ snprint(pipbuf, sizeof pipbuf, "network eof no dot: %r");
piperror = pipbuf;
- if (pp->pid > 0) {
- syskillpg(pp->pid);
- /* none can't syskillpg, so try a variant */
- sleep(500);
- syskill(pp->pid);
- }
status = 1;
}
-
if(status == 0 && Bflush(pp->std[0]->fp) < 0){
piperror = "write error 4";
status = 1;
}
- if (debug) {
- stamp();
- fprint(2, "at end of message; %s .\n",
- (sawdot? "saw": "didn't see"));
- }
+ if(status != 0)
+ syskill(pp->pid);
stream_free(pp->std[0]);
pp->std[0] = 0;
*byteswritten = nbytes;
pipesigoff();
- if(status && !piperror)
+ if(status && piperror == nil)
piperror = "write on closed pipe";
+ fancymsg(status);
return status;
}
@@ -1233,8 +1289,8 @@ firstline(char *x)
char *p;
static char buf[128];
- strncpy(buf, x, sizeof(buf));
- buf[sizeof(buf)-1] = 0;
+ strncpy(buf, x, sizeof buf);
+ buf[sizeof buf - 1] = 0;
p = strchr(buf, '\n');
if(p)
*p = 0;
@@ -1248,6 +1304,7 @@ sendermxcheck(void)
char *cp, *senddom, *user, *who;
Waitmsg *w;
+ senddom = 0;
who = s_to_c(senders.first->p);
if(strcmp(who, "/dev/null") == 0){
/* /dev/null can only send to one rcpt at a time */
@@ -1256,13 +1313,14 @@ sendermxcheck(void)
"recipients");
return -1;
}
- return 0;
+ /* 4408 spf ยง2.2 notes that 2821 says /dev/null == postmaster@domain */
+ senddom = smprint("%s!postmaster", him);
}
if(access("/mail/lib/validatesender", AEXEC) < 0)
return 0;
-
- senddom = strdup(who);
+ if(!senddom)
+ senddom = strdup(who);
if((cp = strchr(senddom, '!')) == nil){
werrstr("rejected: domainless sender %s", who);
free(senddom);
@@ -1270,6 +1328,12 @@ sendermxcheck(void)
}
*cp++ = 0;
user = cp;
+ /* shellchars isn't restrictive. should probablly disallow specialchars */
+ if(shellchars(senddom) || shellchars(user) || shellchars(him)){
+ werrstr("rejected: evil sender/domain/helo");
+ free(senddom);
+ return -1;
+ }
switch(pid = fork()){
case -1:
@@ -1281,7 +1345,7 @@ sendermxcheck(void)
* to allow validatesender to implement SPF eventually.
*/
execl("/mail/lib/validatesender", "validatesender",
- "-n", nci->root, senddom, user, nil);
+ "-n", nci->root, senddom, user, nci->rsys, him, nil);
_exits("exec validatesender: %r");
default:
break;
@@ -1307,20 +1371,42 @@ sendermxcheck(void)
* skip over validatesender 143123132: prefix from rc.
*/
cp = strchr(w->msg, ':');
- if(cp && *(cp+1) == ' ')
- werrstr("%s", cp+2);
+ if(cp && cp[1] == ' ')
+ werrstr("%s", cp + 2);
else
werrstr("%s", w->msg);
free(w);
return -1;
}
+int
+refused(char *e)
+{
+ return e && strstr(e, "mail refused") != nil;
+}
+
+/*
+ * if a message appeared on stderr, despite good status,
+ * log it. this can happen if rewrite.in contains a bad
+ * r.e., for example.
+ */
+void
+logerrors(String *err)
+{
+ char *s;
+
+ s = s_to_c(err);
+ if(*s == 0)
+ return;
+ syslog(0, "smtpd", "%s returned good status, but said: %s",
+ s_to_c(mailer), s);
+}
+
void
data(void)
{
+ char *cp, *ep, *e, buf[ERRMAX];
int status, nbytes;
- char *cp, *ep;
- char errx[ERRMAX];
Link *l;
String *cmd, *err;
@@ -1337,122 +1423,82 @@ data(void)
return;
}
if(!trusted && sendermxcheck()){
- rerrstr(errx, sizeof errx);
- if(strncmp(errx, "rejected:", 9) == 0)
- reply("554 5.7.1 %s\r\n", errx);
+ rerrstr(buf, sizeof buf);
+ if(strncmp(buf, "rejected:", 9) == 0)
+ reply("554 5.7.1 %s\r\n", buf);
else
- reply("450 4.7.0 %s\r\n", errx);
+ reply("450 4.7.0 %s\r\n", buf);
for(l=rcvers.first; l; l=l->next)
syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s",
him, nci->rsys, s_to_c(senders.first->p),
- s_to_c(l->p), errx);
+ s_to_c(l->p), buf);
rejectcount++;
return;
}
+ /*
+ * allow 145 more minutes to move the data
+ */
cmd = startcmd();
if(cmd == 0)
return;
-
reply("354 Input message; end with <CRLF>.<CRLF>\r\n");
- if(debug){
- seek(2, 0, 2);
- stamp();
- fprint(2, "# sent 354; accepting DATA %s\n", thedate());
- }
-
-
- /*
- * allow 145 more minutes to move the data
- */
alarm(145*60*1000);
-
+ piperror = nil;
status = pipemsg(&nbytes);
-
- /*
- * read any error messages
- */
err = s_new();
- if (debug) {
- stamp();
- fprint(2, "waiting for upas/send to close stderr\n");
- }
while(s_read_line(pp->std[2]->fp, err))
;
-
alarm(0);
atnotify(catchalarm, 0);
- if (debug) {
- stamp();
- fprint(2, "waiting for upas/send to exit\n");
- }
status |= proc_wait(pp);
if(debug){
seek(2, 0, 2);
- stamp();
- fprint(2, "# %d upas/send status %#ux at %s\n",
- getpid(), status, thedate());
+ fprint(2, "%d status %ux\n", getpid(), status);
if(*s_to_c(err))
- fprint(2, "# %d error %s\n", getpid(), s_to_c(err));
+ fprint(2, "%d error %s\n", getpid(), s_to_c(err));
}
/*
* if process terminated abnormally, send back error message
*/
+ if(status && (refused(piperror) || refused(s_to_c(err)))){
+ filterstate = BLOCKED;
+ status = 0;
+ }
if(status){
- int code;
- char *ecode;
-
- if(strstr(s_to_c(err), "mail refused")){
- syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s",
- him, nci->rsys, s_to_c(senders.first->p),
- s_to_c(cmd), firstline(s_to_c(err)));
- code = 554;
- ecode = "5.0.0";
- } else {
- syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s",
- him, nci->rsys,
- s_to_c(senders.first->p), s_to_c(cmd),
- piperror? "error during pipemsg: ": "",
- piperror? piperror: "",
- piperror? "; ": "",
- pp->waitmsg->msg, firstline(s_to_c(err)));
- code = 450;
- ecode = "4.0.0";
- }
+ buf[0] = 0;
+ if(piperror != nil)
+ snprint(buf, sizeof buf, "pipemesg: %s; ", piperror);
+ syslog(0, "smtpd", "++[%s/%s] %s %s %sreturned %#q %s",
+ him, nci->rsys, s_to_c(senders.first->p),
+ s_to_c(cmd), buf,
+ pp->waitmsg->msg, firstline(s_to_c(err)));
for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){
*ep++ = 0;
- reply("%d-%s %s\r\n", code, ecode, cp);
+ reply("450-4.0.0 %s\r\n", cp);
}
- reply("%d %s mail process terminated abnormally\r\n",
- code, ecode);
+ reply("450 4.0.0 mail process terminated abnormally\r\n");
+ rejectcount++;
} else {
- /*
- * if a message appeared on stderr, despite good status,
- * log it. this can happen if rewrite.in contains a bad
- * r.e., for example.
- */
- if(*s_to_c(err))
- syslog(0, "smtpd",
- "%s returned good status, but said: %s",
- s_to_c(mailer), s_to_c(err));
-
- if(filterstate == BLOCKED)
- reply("554 5.7.1 we believe this is spam. "
- "we don't accept it.\r\n");
- else if(filterstate == DELAY)
+ if(filterstate == BLOCKED){
+ e = firstline(s_to_c(err));
+ if(e[0] == 0)
+ e = piperror;
+ if(e == nil)
+ e = "we believe this is spam.";
+ syslog(0, "smtpd", "++[%s/%s] blocked: %s", him, nci->rsys, e);
+ reply("554 5.7.1 %s\r\n", e);
+ rejectcount++;
+ }else if(filterstate == DELAY){
+ logerrors(err);
reply("450 4.3.0 There will be a delay in delivery "
"of this message.\r\n");
- else {
+ }else{
+ logerrors(err);
reply("250 2.5.0 sent\r\n");
logcall(nbytes);
- if(debug){
- seek(2, 0, 2);
- stamp();
- fprint(2, "# %d sent 250 reply %s\n",
- getpid(), thedate());
- }
}
}
proc_free(pp);
@@ -1475,6 +1521,8 @@ data(void)
int
rejectcheck(void)
{
+ if(rejectcount)
+ sleep(1000 * (4<<rejectcount));
if(rejectcount > MAXREJECTS){
syslog(0, "smtpd", "Rejected (%s/%s)", him, nci->rsys);
reply("554 5.5.0 too many errors. transaction failed.\r\n");
@@ -1518,9 +1566,9 @@ s_dec64(String *sin)
* if the string is coming from smtpd.y, it will have no nl.
* if it is coming from getcrnl below, it will have an nl.
*/
- if (*(s_to_c(sin)+lin-1) == '\n')
+ if (*(s_to_c(sin) + lin - 1) == '\n')
lin--;
- sout = s_newalloc(lin+1);
+ sout = s_newalloc(lin + 1);
lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin);
if (lout < 0) {
s_free(sout);
@@ -1567,6 +1615,32 @@ starttls(void)
syslog(0, "smtpd", "started TLS with %s", him);
}
+int
+passauth(char *u, char *secret)
+{
+ char response[2*MD5dlen + 1];
+ uchar digest[MD5dlen];
+ int i;
+ AuthInfo *ai;
+ Chalstate *cs;
+
+ if((cs = auth_challenge("proto=cram role=server")) == nil)
+ return -1;
+ hmac_md5((uchar*)cs->chal, strlen(cs->chal),
+ (uchar*)secret, strlen(secret), digest, nil);
+ for(i = 0; i < MD5dlen; i++)
+ snprint(response + 2*i, sizeof response - 2*i, "%2.2ux", digest[i]);
+ cs->user = u;
+ cs->resp = response;
+ cs->nresp = strlen(response);
+ ai = auth_response(cs);
+ if(ai == nil)
+ return -1;
+ auth_freechal(cs);
+ auth_freeAI(ai);
+ return 0;
+}
+
void
auth(String *mech, String *resp)
{
@@ -1576,19 +1650,19 @@ auth(String *mech, String *resp)
String *s_resp1_64 = nil, *s_resp2_64 = nil, *s_resp1 = nil;
String *s_resp2 = nil;
- if (rejectcheck())
+ if(rejectcheck())
goto bomb_out;
syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech),
"(protected)", him);
- if (authenticated) {
+ if(authenticated) {
bad_sequence:
rejectcount++;
reply("503 5.5.2 Bad sequence of commands\r\n");
goto bomb_out;
}
- if (cistrcmp(s_to_c(mech), "plain") == 0) {
+ if(cistrcmp(s_to_c(mech), "plain") == 0){
if (!passwordinclear) {
rejectcount++;
reply("538 5.7.1 Encryption required for requested "
@@ -1611,12 +1685,13 @@ auth(String *mech, String *resp)
memset(s_to_c(s_resp1_64), 'X', s_len(s_resp1_64));
user = s_to_c(s_resp1) + strlen(s_to_c(s_resp1)) + 1;
pass = user + strlen(user) + 1;
- ai = auth_userpasswd(user, pass);
- authenticated = ai != nil;
+// ai = auth_userpasswd(user, pass);
+// authenticated = ai != nil;
+authenticated = passauth(user, pass) != -1;
memset(pass, 'X', strlen(pass));
goto windup;
}
- else if (cistrcmp(s_to_c(mech), "login") == 0) {
+ else if(cistrcmp(s_to_c(mech), "login") == 0){
if (!passwordinclear) {
rejectcount++;
reply("538 5.7.1 Encryption required for requested "
@@ -1628,7 +1703,8 @@ auth(String *mech, String *resp)
s_resp1_64 = s_new();
if (getcrnl(s_resp1_64, &bin) <= 0)
goto bad_sequence;
- }
+ }else
+ s_resp1_64 = resp;
reply("334 UGFzc3dvcmQ6\r\n");
s_resp2_64 = s_new();
if (getcrnl(s_resp2_64, &bin) <= 0)
@@ -1656,7 +1732,7 @@ windup:
}
goto bomb_out;
}
- else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) {
+ else if(cistrcmp(s_to_c(mech), "cram-md5") == 0){
char *resp, *t;
chs = auth_challenge("proto=cram role=server");
diff --git a/sys/src/cmd/upas/smtp/spam.c b/sys/src/cmd/upas/smtp/spam.c
index 4b7fe4134..20ef9f944 100644
--- a/sys/src/cmd/upas/smtp/spam.c
+++ b/sys/src/cmd/upas/smtp/spam.c
@@ -65,10 +65,10 @@ actstr(int a)
static char buf[32];
Keyword *p;
- for(p=actions; p->name; p++)
+ for(p = actions; p->name; p++)
if(p->code == a)
return p->name;
- if(a==NONE)
+ if(a == NONE)
return "none";
sprint(buf, "%d", a);
return buf;
@@ -94,13 +94,13 @@ getaction(char *s, char *type)
int
istrusted(char *s)
{
- char buf[1024];
+ char buf[Pathlen];
if(s == nil || *s == 0)
return 0;
snprint(buf, sizeof buf, "/mail/ratify/trusted/%s", s);
- return access(buf,0) >= 0;
+ return access(buf, 0) >= 0;
}
void
@@ -130,7 +130,7 @@ getconf(void)
cp = getline(bp);
if(cp == 0)
break;
- p = cp+strlen(cp)+1;
+ p = cp + strlen(cp) + 1;
switch(findkey(cp, options)){
case NORELAY:
if(fflag == 0 && strcmp(p, "on") == 0)
@@ -157,7 +157,7 @@ getconf(void)
s = s_new();
s_append(s, p);
listadd(&ourdoms, s);
- p += strlen(p)+1;
+ p += strlen(p) + 1;
}
break;
default:
@@ -178,7 +178,7 @@ usermatch(char *pathuser, char *specuser)
{
int n;
- n = strlen(specuser)-1;
+ n = strlen(specuser) - 1;
if(specuser[n] == '*'){
if(n == 0) /* match everything */
return 0;
@@ -195,9 +195,9 @@ dommatch(char *pathdom, char *specdom)
if (*specdom == '*'){
if (specdom[1] == '.' && specdom[2]){
specdom += 2;
- n = strlen(pathdom)-strlen(specdom);
+ n = strlen(pathdom) - strlen(specdom);
if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
- return strcmp(pathdom+n, specdom);
+ return strcmp(pathdom + n, specdom);
return n;
}
}
@@ -261,10 +261,10 @@ getline(Biobuf *bp)
return 0;
n = Blinelen(bp);
cp[n-1] = 0;
- if(buf == 0 || bufsize < n+1){
+ if(buf == 0 || bufsize < n + 1){
bufsize += 512;
- if(bufsize < n+1)
- bufsize = n+1;
+ if(bufsize < n + 1)
+ bufsize = n + 1;
buf = realloc(buf, bufsize);
if(buf == 0)
break;
@@ -328,7 +328,7 @@ found:
s_append(path, "[");
s_append(path, nci->rsys);
s_append(path, "]!");
- s_append(path, cp+3);
+ s_append(path, cp + 3);
s_terminate(path);
s_free(lpath);
return 0;
@@ -348,7 +348,7 @@ found:
for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
*cp = tolower(*cp);
- for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
+ for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp + 1){
*cp = 0;
if(!isourdom(s)){
s_free(lpath);
@@ -367,13 +367,12 @@ masquerade(String *path, char *him)
int rv = 0;
if(debug)
- fprint(2, "masquerade(%s) ", s_to_c(path));
+ fprint(2, "masquerade(%s)\n", s_to_c(path));
- if(trusted || path == nil) {
- if(debug)
- fprint(2, "0\n");
+ if(trusted)
+ return 0;
+ if(path == nil)
return 0;
- }
lpath = s_copy(s_to_c(path));
@@ -388,7 +387,7 @@ masquerade(String *path, char *him)
if(isourdom(s))
rv = 1;
} else if((cp = strrchr(s, '@')) != nil){
- if(isourdom(cp+1))
+ if(isourdom(cp + 1))
rv = 1;
} else {
if(isourdom(him))
@@ -396,8 +395,6 @@ masquerade(String *path, char *him)
}
s_free(lpath);
- if (debug)
- fprint(2, "%d\n", rv);
return rv;
}
@@ -426,15 +423,15 @@ cidrcheck(char *cp)
if(strchr(cp, '/') == 0){
m = 0xff000000;
p = cp;
- for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
- m = (m>>8)|0xff000000;
+ for(p = strchr(p, '.'); p && p[1]; p = strchr(p + 1, '.'))
+ m = (m>>8)|0xff000000;
/* force at least a class B */
m |= 0xffff0000;
}
- if((v4peerip&m) == a)
+ if((v4peerip & m) == a)
return 1;
- cp += strlen(cp)+1;
+ cp += strlen(cp) + 1;
}
return 0;
}
@@ -470,10 +467,10 @@ dumpfile(char *sender)
cp = ctime(time(0));
cp[7] = 0;
if(cp[8] == ' ')
- sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
+ sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp + 4, cp[9]);
else
- sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
- cp = buf+strlen(buf);
+ sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp + 4, cp[8], cp[9]);
+ cp = buf + strlen(buf);
if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0)
return "/dev/null";
h = 0;
@@ -484,7 +481,7 @@ dumpfile(char *sender)
sprint(cp, "/%lud", h);
if(access(buf, 0) >= 0)
continue;
- fd = syscreate(buf, ORDWR, 0666);
+ fd = create(buf, ORDWR, 0666);
if(fd >= 0){
if(debug)
fprint(2, "saving in %s\n", buf);
@@ -586,7 +583,7 @@ optoutofspamfilter(char *addr)
rv = 0;
f = smprint("/mail/box/%s/nospamfiltering", p);
if(f != nil){
- rv = access(f, 0)==0;
+ rv = access(f, 0) == 0;
free(f);
}