13e12c5d1SDavid du Colombier #include "common.h"
23e12c5d1SDavid du Colombier #include "smtp.h"
33e12c5d1SDavid du Colombier #include <ctype.h>
4d9306527SDavid du Colombier #include <mp.h>
5d9306527SDavid du Colombier #include <libsec.h>
6d9306527SDavid du Colombier #include <auth.h>
73e12c5d1SDavid du Colombier
87dd7cddfSDavid du Colombier static char* connect(char*);
9d9306527SDavid du Colombier static char* dotls(char*);
103cf081f0SDavid du Colombier static char* doauth(char*);
11db7ae703SDavid du Colombier
12219b2ee8SDavid du Colombier void addhostdom(String*, char*);
13219b2ee8SDavid du Colombier String* bangtoat(char*);
147dd7cddfSDavid du Colombier String* convertheader(String*);
153e12c5d1SDavid du Colombier int dBprint(char*, ...);
167dd7cddfSDavid du Colombier int dBputc(int);
17db7ae703SDavid du Colombier char* data(String*, Biobuf*);
18db7ae703SDavid du Colombier char* domainify(char*, char*);
197dd7cddfSDavid du Colombier String* fixrouteaddr(String*, Node*, Node*);
20db7ae703SDavid du Colombier char* getcrnl(String*);
21db7ae703SDavid du Colombier int getreply(void);
22db7ae703SDavid du Colombier char* hello(char*, int);
23db7ae703SDavid du Colombier char* mailfrom(char*);
24db7ae703SDavid du Colombier int printdate(Node*);
25db7ae703SDavid du Colombier int printheader(void);
26db7ae703SDavid du Colombier void putcrnl(char*, int);
27db7ae703SDavid du Colombier void quit(char*);
28db7ae703SDavid du Colombier char* rcptto(char*);
29db7ae703SDavid du Colombier char *rewritezone(char *);
303e12c5d1SDavid du Colombier
317dd7cddfSDavid du Colombier #define Retry "Retry, Temporary Failure"
323e12c5d1SDavid du Colombier #define Giveup "Permanent Failure"
333e12c5d1SDavid du Colombier
343e12c5d1SDavid du Colombier String *reply; /* last reply */
35219b2ee8SDavid du Colombier String *toline;
36db7ae703SDavid du Colombier
37a8453668SDavid du Colombier int alarmscale;
38db7ae703SDavid du Colombier int autistic;
39db7ae703SDavid du Colombier int debug; /* true if we're debugging */
403e12c5d1SDavid du Colombier int filter;
41db7ae703SDavid du Colombier int insecure;
42db7ae703SDavid du Colombier int last = 'n'; /* last character sent by putcrnl() */
43db7ae703SDavid du Colombier int ping;
447dd7cddfSDavid du Colombier int quitting; /* when error occurs in quit */
45db7ae703SDavid du Colombier int tryauth; /* Try to authenticate, if supported */
46db7ae703SDavid du Colombier int trysecure; /* Try to use TLS if the other side supports it */
47ab6a5be1SDavid du Colombier int okunksecure; /* okay to use TLS to unknown servers */
48db7ae703SDavid du Colombier
497dd7cddfSDavid du Colombier char *quitrv; /* deferred return value when in quit */
50ab6a5be1SDavid du Colombier char ddomain[Maxdomain]; /* domain name of destination machine */
513e12c5d1SDavid du Colombier char *gdomain; /* domain name of gateway */
52bd389b36SDavid du Colombier char *uneaten; /* first character after rfc822 headers */
537dd7cddfSDavid du Colombier char *farend; /* system we are trying to send to */
54d9306527SDavid du Colombier char *user; /* user we are authenticating as, if authenticating */
55219b2ee8SDavid du Colombier char hostdomain[256];
56db7ae703SDavid du Colombier
573e12c5d1SDavid du Colombier Biobuf bin;
583e12c5d1SDavid du Colombier Biobuf bout;
593e12c5d1SDavid du Colombier Biobuf berr;
607dd7cddfSDavid du Colombier Biobuf bfile;
613e12c5d1SDavid du Colombier
62617c0e1eSDavid du Colombier static int bustedmx;
63617c0e1eSDavid du Colombier
643e12c5d1SDavid du Colombier void
usage(void)653e12c5d1SDavid du Colombier usage(void)
663e12c5d1SDavid du Colombier {
67617c0e1eSDavid du Colombier fprint(2, "usage: smtp [-aAdfips] [-b busted-mx] [-g gw] [-h host] "
68617c0e1eSDavid du Colombier "[-u user] [.domain] net!host[!service] sender rcpt-list\n");
69219b2ee8SDavid du Colombier exits(Giveup);
70219b2ee8SDavid du Colombier }
71219b2ee8SDavid du Colombier
727dd7cddfSDavid du Colombier int
timeout(void * x,char * msg)73219b2ee8SDavid du Colombier timeout(void *x, char *msg)
74219b2ee8SDavid du Colombier {
75219b2ee8SDavid du Colombier USED(x);
767dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg);
77219b2ee8SDavid du Colombier if(strstr(msg, "alarm")){
787dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection to %s timed out\n", farend);
797dd7cddfSDavid du Colombier if(quitting)
807dd7cddfSDavid du Colombier exits(quitrv);
817dd7cddfSDavid du Colombier exits(Retry);
82219b2ee8SDavid du Colombier }
837dd7cddfSDavid du Colombier if(strstr(msg, "closed pipe")){
847dd7cddfSDavid du Colombier /* call _exits() to prevent Bio from trying to flush closed pipe */
857dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection closed to %s\n", farend);
867dd7cddfSDavid du Colombier if(quitting){
877dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "closed pipe to %s", farend);
887dd7cddfSDavid du Colombier _exits(quitrv);
897dd7cddfSDavid du Colombier }
907dd7cddfSDavid du Colombier _exits(Retry);
917dd7cddfSDavid du Colombier }
927dd7cddfSDavid du Colombier return 0;
933e12c5d1SDavid du Colombier }
943e12c5d1SDavid du Colombier
953e12c5d1SDavid du Colombier void
removenewline(char * p)96a8453668SDavid du Colombier removenewline(char *p)
97a8453668SDavid du Colombier {
98a8453668SDavid du Colombier int n = strlen(p)-1;
99a8453668SDavid du Colombier
100a8453668SDavid du Colombier if(n < 0)
101a8453668SDavid du Colombier return;
102a8453668SDavid du Colombier if(p[n] == '\n')
103a8453668SDavid du Colombier p[n] = 0;
104a8453668SDavid du Colombier }
105a8453668SDavid du Colombier
106a8453668SDavid du Colombier void
main(int argc,char ** argv)1073e12c5d1SDavid du Colombier main(int argc, char **argv)
1083e12c5d1SDavid du Colombier {
1097dd7cddfSDavid du Colombier int i, ok, rcvrs;
110db7ae703SDavid du Colombier char *addr, *rv, *trv, *host, *domain;
1117dd7cddfSDavid du Colombier char **errs;
112db7ae703SDavid du Colombier char hellodomain[256];
113db7ae703SDavid du Colombier String *from, *fromm, *sender;
1143e12c5d1SDavid du Colombier
115a8453668SDavid du Colombier alarmscale = 60*1000; /* minutes */
116fe096d1aSDavid du Colombier quotefmtinstall();
1177dd7cddfSDavid du Colombier errs = malloc(argc*sizeof(char*));
1183e12c5d1SDavid du Colombier reply = s_new();
119219b2ee8SDavid du Colombier host = 0;
1203e12c5d1SDavid du Colombier ARGBEGIN{
121d9306527SDavid du Colombier case 'a':
122d9306527SDavid du Colombier tryauth = 1;
123d9306527SDavid du Colombier trysecure = 1;
124d9306527SDavid du Colombier break;
125db7ae703SDavid du Colombier case 'A': /* autistic: won't talk to us until we talk (Verizon) */
126db7ae703SDavid du Colombier autistic = 1;
1273e12c5d1SDavid du Colombier break;
128617c0e1eSDavid du Colombier case 'b':
129617c0e1eSDavid du Colombier if (bustedmx >= Maxbustedmx)
130617c0e1eSDavid du Colombier sysfatal("more than %d busted mxs given", Maxbustedmx);
131617c0e1eSDavid du Colombier bustedmxs[bustedmx++] = EARGF(usage());
132617c0e1eSDavid du Colombier break;
1333e12c5d1SDavid du Colombier case 'd':
1343e12c5d1SDavid du Colombier debug = 1;
1353e12c5d1SDavid du Colombier break;
136db7ae703SDavid du Colombier case 'f':
137db7ae703SDavid du Colombier filter = 1;
138db7ae703SDavid du Colombier break;
1393e12c5d1SDavid du Colombier case 'g':
140db7ae703SDavid du Colombier gdomain = EARGF(usage());
1413e12c5d1SDavid du Colombier break;
1423e12c5d1SDavid du Colombier case 'h':
143db7ae703SDavid du Colombier host = EARGF(usage());
1443e12c5d1SDavid du Colombier break;
145a8453668SDavid du Colombier case 'i':
146a8453668SDavid du Colombier insecure = 1;
147a8453668SDavid du Colombier break;
148ab6a5be1SDavid du Colombier case 'o':
149ab6a5be1SDavid du Colombier okunksecure = 1;
150ab6a5be1SDavid du Colombier break;
151a8453668SDavid du Colombier case 'p':
152a8453668SDavid du Colombier alarmscale = 10*1000; /* tens of seconds */
153a8453668SDavid du Colombier ping = 1;
154a8453668SDavid du Colombier break;
155d9306527SDavid du Colombier case 's':
156d9306527SDavid du Colombier trysecure = 1;
157d9306527SDavid du Colombier break;
158d9306527SDavid du Colombier case 'u':
159db7ae703SDavid du Colombier user = EARGF(usage());
160d9306527SDavid du Colombier break;
1613e12c5d1SDavid du Colombier default:
1623e12c5d1SDavid du Colombier usage();
1633e12c5d1SDavid du Colombier break;
1643e12c5d1SDavid du Colombier }ARGEND;
1653e12c5d1SDavid du Colombier
1663e12c5d1SDavid du Colombier Binit(&berr, 2, OWRITE);
1677dd7cddfSDavid du Colombier Binit(&bfile, 0, OREAD);
1683e12c5d1SDavid du Colombier
1693e12c5d1SDavid du Colombier /*
1703e12c5d1SDavid du Colombier * get domain and add to host name
1713e12c5d1SDavid du Colombier */
1727dd7cddfSDavid du Colombier if(*argv && **argv=='.') {
1737dd7cddfSDavid du Colombier domain = *argv;
1747dd7cddfSDavid du Colombier argv++; argc--;
1757dd7cddfSDavid du Colombier } else
1767dd7cddfSDavid du Colombier domain = domainname_read();
177*0519a00aSDavid du Colombier if(domain == nil)
178*0519a00aSDavid du Colombier fprint(2, "%s: nil domainname_read()\n", argv0);
1793e12c5d1SDavid du Colombier if(host == 0)
1803e12c5d1SDavid du Colombier host = sysname_read();
1813e12c5d1SDavid du Colombier strcpy(hostdomain, domainify(host, domain));
1827dd7cddfSDavid du Colombier strcpy(hellodomain, domainify(sysname_read(), domain));
1833e12c5d1SDavid du Colombier
1843e12c5d1SDavid du Colombier /*
1853e12c5d1SDavid du Colombier * get destination address
1863e12c5d1SDavid du Colombier */
1873e12c5d1SDavid du Colombier if(*argv == 0)
1883e12c5d1SDavid du Colombier usage();
1897dd7cddfSDavid du Colombier addr = *argv++; argc--;
1907dd7cddfSDavid du Colombier farend = addr;
1913e12c5d1SDavid du Colombier
1923e12c5d1SDavid du Colombier /*
1933e12c5d1SDavid du Colombier * get sender's machine.
1943e12c5d1SDavid du Colombier * get sender in internet style. domainify if necessary.
1953e12c5d1SDavid du Colombier */
196219b2ee8SDavid du Colombier if(*argv == 0)
197219b2ee8SDavid du Colombier usage();
1987dd7cddfSDavid du Colombier sender = unescapespecial(s_copy(*argv++));
1997dd7cddfSDavid du Colombier argc--;
2007dd7cddfSDavid du Colombier fromm = s_clone(sender);
2013e12c5d1SDavid du Colombier rv = strrchr(s_to_c(fromm), '!');
2023e12c5d1SDavid du Colombier if(rv)
2033e12c5d1SDavid du Colombier *rv = 0;
2043e12c5d1SDavid du Colombier else
2053e12c5d1SDavid du Colombier *s_to_c(fromm) = 0;
2067dd7cddfSDavid du Colombier from = bangtoat(s_to_c(sender));
2073e12c5d1SDavid du Colombier
2083e12c5d1SDavid du Colombier /*
2093e12c5d1SDavid du Colombier * send the mail
2103e12c5d1SDavid du Colombier */
2113e12c5d1SDavid du Colombier if(filter){
2123e12c5d1SDavid du Colombier Binit(&bout, 1, OWRITE);
2137dd7cddfSDavid du Colombier rv = data(from, &bfile);
2143e12c5d1SDavid du Colombier if(rv != 0)
2153e12c5d1SDavid du Colombier goto error;
2167dd7cddfSDavid du Colombier exits(0);
2177dd7cddfSDavid du Colombier }
2187dd7cddfSDavid du Colombier
219ab3dc52fSDavid du Colombier /* mxdial uses its own timeout handler */
2207dd7cddfSDavid du Colombier if((rv = connect(addr)) != 0)
2217dd7cddfSDavid du Colombier exits(rv);
222ab3dc52fSDavid du Colombier
223ab3dc52fSDavid du Colombier /* 10 minutes to get through the initial handshake */
224ab3dc52fSDavid du Colombier atnotify(timeout, 1);
225a8453668SDavid du Colombier alarm(10*alarmscale);
226d9306527SDavid du Colombier if((rv = hello(hellodomain, 0)) != 0)
2277dd7cddfSDavid du Colombier goto error;
228a8453668SDavid du Colombier alarm(10*alarmscale);
2297dd7cddfSDavid du Colombier if((rv = mailfrom(s_to_c(from))) != 0)
2307dd7cddfSDavid du Colombier goto error;
2317dd7cddfSDavid du Colombier
2327dd7cddfSDavid du Colombier ok = 0;
2337dd7cddfSDavid du Colombier rcvrs = 0;
2347dd7cddfSDavid du Colombier /* if any rcvrs are ok, we try to send the message */
2357dd7cddfSDavid du Colombier for(i = 0; i < argc; i++){
2367dd7cddfSDavid du Colombier if((trv = rcptto(argv[i])) != 0){
2377dd7cddfSDavid du Colombier /* remember worst error */
2387dd7cddfSDavid du Colombier if(rv != Giveup)
2397dd7cddfSDavid du Colombier rv = trv;
2407dd7cddfSDavid du Colombier errs[rcvrs] = strdup(s_to_c(reply));
241a8453668SDavid du Colombier removenewline(errs[rcvrs]);
2427dd7cddfSDavid du Colombier } else {
2437dd7cddfSDavid du Colombier ok++;
2447dd7cddfSDavid du Colombier errs[rcvrs] = 0;
2457dd7cddfSDavid du Colombier }
2467dd7cddfSDavid du Colombier rcvrs++;
2477dd7cddfSDavid du Colombier }
2487dd7cddfSDavid du Colombier
2497dd7cddfSDavid du Colombier /* if no ok rcvrs or worst error is retry, give up */
2507dd7cddfSDavid du Colombier if(ok == 0 || rv == Retry)
2517dd7cddfSDavid du Colombier goto error;
2527dd7cddfSDavid du Colombier
253a8453668SDavid du Colombier if(ping){
254a8453668SDavid du Colombier quit(0);
255a8453668SDavid du Colombier exits(0);
256a8453668SDavid du Colombier }
257a8453668SDavid du Colombier
2587dd7cddfSDavid du Colombier rv = data(from, &bfile);
2597dd7cddfSDavid du Colombier if(rv != 0)
2607dd7cddfSDavid du Colombier goto error;
2617dd7cddfSDavid du Colombier quit(0);
2627dd7cddfSDavid du Colombier if(rcvrs == ok)
2637dd7cddfSDavid du Colombier exits(0);
2647dd7cddfSDavid du Colombier
2657dd7cddfSDavid du Colombier /*
2667dd7cddfSDavid du Colombier * here when some but not all rcvrs failed
2677dd7cddfSDavid du Colombier */
2687dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n", thedate(), addr);
2697dd7cddfSDavid du Colombier for(i = 0; i < rcvrs; i++){
2707dd7cddfSDavid du Colombier if(errs[i]){
271a8453668SDavid du Colombier syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);
2727dd7cddfSDavid du Colombier fprint(2, " mail to %s failed: %s", argv[i], errs[i]);
2737dd7cddfSDavid du Colombier }
2747dd7cddfSDavid du Colombier }
2757dd7cddfSDavid du Colombier exits(Giveup);
2767dd7cddfSDavid du Colombier
2777dd7cddfSDavid du Colombier /*
2787dd7cddfSDavid du Colombier * here when all rcvrs failed
2797dd7cddfSDavid du Colombier */
2803e12c5d1SDavid du Colombier error:
281a8453668SDavid du Colombier removenewline(s_to_c(reply));
282a8453668SDavid du Colombier syslog(0, "smtp.fail", "%s to %s failed: %s",
283a8453668SDavid du Colombier ping ? "ping" : "delivery",
284a8453668SDavid du Colombier addr, s_to_c(reply));
2857dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
2867dd7cddfSDavid du Colombier if(!filter)
2877dd7cddfSDavid du Colombier quit(rv);
2883e12c5d1SDavid du Colombier exits(rv);
2893e12c5d1SDavid du Colombier }
2903e12c5d1SDavid du Colombier
2913e12c5d1SDavid du Colombier /*
2923e12c5d1SDavid du Colombier * connect to the remote host
2933e12c5d1SDavid du Colombier */
2947dd7cddfSDavid du Colombier static char *
connect(char * net)2953e12c5d1SDavid du Colombier connect(char* net)
2963e12c5d1SDavid du Colombier {
29721887c0bSDavid du Colombier char buf[Errlen];
2983e12c5d1SDavid du Colombier int fd;
2993e12c5d1SDavid du Colombier
3007dd7cddfSDavid du Colombier fd = mxdial(net, ddomain, gdomain);
3013e12c5d1SDavid du Colombier
3023e12c5d1SDavid du Colombier if(fd < 0){
3039a747e4fSDavid du Colombier rerrstr(buf, sizeof(buf));
3043ff48bf5SDavid du Colombier Bprint(&berr, "smtp: %s (%s)\n", buf, net);
3053ff48bf5SDavid du Colombier syslog(0, "smtp.fail", "%s (%s)", buf, net);
3067dd7cddfSDavid du Colombier if(strstr(buf, "illegal")
3077dd7cddfSDavid du Colombier || strstr(buf, "unknown")
308271b8d73SDavid du Colombier || strstr(buf, "can't translate"))
3093e12c5d1SDavid du Colombier return Giveup;
3103e12c5d1SDavid du Colombier else
3113e12c5d1SDavid du Colombier return Retry;
3123e12c5d1SDavid du Colombier }
3133e12c5d1SDavid du Colombier Binit(&bin, fd, OREAD);
3143e12c5d1SDavid du Colombier fd = dup(fd, -1);
3153e12c5d1SDavid du Colombier Binit(&bout, fd, OWRITE);
3163e12c5d1SDavid du Colombier return 0;
3173e12c5d1SDavid du Colombier }
3183e12c5d1SDavid du Colombier
319fe096d1aSDavid du Colombier static char smtpthumbs[] = "/sys/lib/tls/smtp";
320fe096d1aSDavid du Colombier static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude";
321fe096d1aSDavid du Colombier
322ab6a5be1SDavid du Colombier static char *
ckthumbs(TLSconn * c)323ab6a5be1SDavid du Colombier ckthumbs(TLSconn *c)
324ab6a5be1SDavid du Colombier {
325ab6a5be1SDavid du Colombier Thumbprint *goodcerts;
326ab6a5be1SDavid du Colombier char *h, *err;
327ab6a5be1SDavid du Colombier uchar hash[SHA1dlen];
328ab6a5be1SDavid du Colombier
329ab6a5be1SDavid du Colombier err = nil;
330ab6a5be1SDavid du Colombier goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
331ab6a5be1SDavid du Colombier if (goodcerts == nil) {
332ab6a5be1SDavid du Colombier if (!okunksecure)
333ab6a5be1SDavid du Colombier syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
334ab6a5be1SDavid du Colombier return Giveup; /* how to recover? TLS is started */
335ab6a5be1SDavid du Colombier }
336ab6a5be1SDavid du Colombier
337ab6a5be1SDavid du Colombier /* compute sha1 hash of remote's certificate, see if we know it */
338ab6a5be1SDavid du Colombier sha1(c->cert, c->certlen, hash, nil);
339ab6a5be1SDavid du Colombier if (!okThumbprint(hash, goodcerts) && !okunksecure) {
340ab6a5be1SDavid du Colombier h = malloc(2*sizeof hash + 1);
341ab6a5be1SDavid du Colombier if (h != nil) {
342ab6a5be1SDavid du Colombier enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
343ab6a5be1SDavid du Colombier syslog(0, "smtp", "remote cert. has bad thumbprint: "
344ab6a5be1SDavid du Colombier "x509 sha1=%s server=%q", h, ddomain);
345ab6a5be1SDavid du Colombier free(h);
346ab6a5be1SDavid du Colombier }
347ab6a5be1SDavid du Colombier err = Giveup; /* how to recover? TLS is started */
348ab6a5be1SDavid du Colombier }
349ab6a5be1SDavid du Colombier freeThumbprints(goodcerts);
350ab6a5be1SDavid du Colombier return err;
351ab6a5be1SDavid du Colombier }
352ab6a5be1SDavid du Colombier
3533e12c5d1SDavid du Colombier /*
354fe096d1aSDavid du Colombier * exchange names with remote host, attempt to
355fe096d1aSDavid du Colombier * enable encryption and optionally authenticate.
356fe096d1aSDavid du Colombier * not fatal if we can't.
3573e12c5d1SDavid du Colombier */
358d9306527SDavid du Colombier static char *
dotls(char * me)359d9306527SDavid du Colombier dotls(char *me)
3603e12c5d1SDavid du Colombier {
361d9306527SDavid du Colombier TLSconn *c;
362ab6a5be1SDavid du Colombier char *err;
363d9306527SDavid du Colombier int fd;
364d9306527SDavid du Colombier
365d9306527SDavid du Colombier c = mallocz(sizeof(*c), 1); /* Note: not freed on success */
366d9306527SDavid du Colombier if (c == nil)
367d9306527SDavid du Colombier return Giveup;
368fe096d1aSDavid du Colombier
369d9306527SDavid du Colombier dBprint("STARTTLS\r\n");
370fe096d1aSDavid du Colombier if (getreply() != 2)
371fe096d1aSDavid du Colombier return Giveup;
372fe096d1aSDavid du Colombier
373d9306527SDavid du Colombier fd = tlsClient(Bfildes(&bout), c);
374fe096d1aSDavid du Colombier if (fd < 0) {
375fe096d1aSDavid du Colombier syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
376d9306527SDavid du Colombier return Giveup;
377dddc47c2SDavid du Colombier }
378ab6a5be1SDavid du Colombier
379ab6a5be1SDavid du Colombier err = ckthumbs(c);
380ab6a5be1SDavid du Colombier if (err && !okunksecure) {
381d9306527SDavid du Colombier free(c);
382fe096d1aSDavid du Colombier close(fd);
383ab6a5be1SDavid du Colombier return err; /* how to recover? TLS is started */
384d9306527SDavid du Colombier }
385fe096d1aSDavid du Colombier
386d9306527SDavid du Colombier Bterm(&bin);
387d9306527SDavid du Colombier Bterm(&bout);
388fe096d1aSDavid du Colombier
389fe096d1aSDavid du Colombier /*
390fe096d1aSDavid du Colombier * set up bin & bout to use the TLS fd, i/o upon which generates
391fe096d1aSDavid du Colombier * i/o on the original, underlying fd.
392fe096d1aSDavid du Colombier */
393d9306527SDavid du Colombier Binit(&bin, fd, OREAD);
394d9306527SDavid du Colombier fd = dup(fd, -1);
395d9306527SDavid du Colombier Binit(&bout, fd, OWRITE);
396fe096d1aSDavid du Colombier
397fe096d1aSDavid du Colombier syslog(0, "smtp", "started TLS to %q", ddomain);
398d9306527SDavid du Colombier return(hello(me, 1));
399d9306527SDavid du Colombier }
400d9306527SDavid du Colombier
401d9306527SDavid du Colombier static char *
doauth(char * methods)4023cf081f0SDavid du Colombier doauth(char *methods)
403d9306527SDavid du Colombier {
4049863c128SDavid du Colombier char *buf, *base64;
405d9306527SDavid du Colombier int n;
4069863c128SDavid du Colombier DS ds;
4079863c128SDavid du Colombier UserPasswd *p;
408d9306527SDavid du Colombier
4099863c128SDavid du Colombier dial_string_parse(ddomain, &ds);
4103cf081f0SDavid du Colombier
411d9306527SDavid du Colombier if(user != nil)
412d9306527SDavid du Colombier p = auth_getuserpasswd(nil,
4139863c128SDavid du Colombier "proto=pass service=smtp server=%q user=%q", ds.host, user);
414d9306527SDavid du Colombier else
415d9306527SDavid du Colombier p = auth_getuserpasswd(nil,
4169863c128SDavid du Colombier "proto=pass service=smtp server=%q", ds.host);
417d9306527SDavid du Colombier if (p == nil)
418d9306527SDavid du Colombier return Giveup;
4193cf081f0SDavid du Colombier
4203cf081f0SDavid du Colombier if (strstr(methods, "LOGIN")){
4213cf081f0SDavid du Colombier dBprint("AUTH LOGIN\r\n");
4223cf081f0SDavid du Colombier if (getreply() != 3)
4233cf081f0SDavid du Colombier return Retry;
4243cf081f0SDavid du Colombier
4253cf081f0SDavid du Colombier n = strlen(p->user);
4263cf081f0SDavid du Colombier base64 = malloc(2*n);
4273cf081f0SDavid du Colombier if (base64 == nil)
4283cf081f0SDavid du Colombier return Retry; /* Out of memory */
4293cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->user, n);
4303cf081f0SDavid du Colombier dBprint("%s\r\n", base64);
4313cf081f0SDavid du Colombier if (getreply() != 3)
4323cf081f0SDavid du Colombier return Retry;
4333cf081f0SDavid du Colombier
4343cf081f0SDavid du Colombier n = strlen(p->passwd);
4353cf081f0SDavid du Colombier base64 = malloc(2*n);
4363cf081f0SDavid du Colombier if (base64 == nil)
4373cf081f0SDavid du Colombier return Retry; /* Out of memory */
4383cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->passwd, n);
4393cf081f0SDavid du Colombier dBprint("%s\r\n", base64);
4403cf081f0SDavid du Colombier if (getreply() != 2)
4413cf081f0SDavid du Colombier return Retry;
4423cf081f0SDavid du Colombier
4433cf081f0SDavid du Colombier free(base64);
4443cf081f0SDavid du Colombier }
4453cf081f0SDavid du Colombier else
4463cf081f0SDavid du Colombier if (strstr(methods, "PLAIN")){
447d9306527SDavid du Colombier n = strlen(p->user) + strlen(p->passwd) + 3;
448d9306527SDavid du Colombier buf = malloc(n);
449d9306527SDavid du Colombier base64 = malloc(2 * n);
450fe096d1aSDavid du Colombier if (buf == nil || base64 == nil) {
451d9306527SDavid du Colombier free(buf);
452d9306527SDavid du Colombier return Retry; /* Out of memory */
453d9306527SDavid du Colombier }
454d9306527SDavid du Colombier snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);
455d9306527SDavid du Colombier enc64(base64, 2 * n, (uchar *)buf, n - 1);
456d9306527SDavid du Colombier free(buf);
457d9306527SDavid du Colombier dBprint("AUTH PLAIN %s\r\n", base64);
458d9306527SDavid du Colombier free(base64);
459d9306527SDavid du Colombier if (getreply() != 2)
460d9306527SDavid du Colombier return Retry;
4613cf081f0SDavid du Colombier }
4623cf081f0SDavid du Colombier else
4633cf081f0SDavid du Colombier return "No supported AUTH method";
464d9306527SDavid du Colombier return(0);
465d9306527SDavid du Colombier }
466d9306527SDavid du Colombier
467d9306527SDavid du Colombier char *
hello(char * me,int encrypted)468d9306527SDavid du Colombier hello(char *me, int encrypted)
469d9306527SDavid du Colombier {
470d9306527SDavid du Colombier int ehlo;
471d9306527SDavid du Colombier String *r;
4723cf081f0SDavid du Colombier char *ret, *s, *t;
473d9306527SDavid du Colombier
474db7ae703SDavid du Colombier if (!encrypted) {
475db7ae703SDavid du Colombier /*
476db7ae703SDavid du Colombier * Verizon fails to print the smtp greeting banner when it
477db7ae703SDavid du Colombier * answers a call. Send a no-op in the hope of making it
478db7ae703SDavid du Colombier * talk.
479db7ae703SDavid du Colombier */
480ea813757SDavid du Colombier if (autistic) {
481db7ae703SDavid du Colombier dBprint("NOOP\r\n");
482ea813757SDavid du Colombier getreply(); /* consume the smtp greeting */
483ea813757SDavid du Colombier /* next reply will be response to noop */
484ea813757SDavid du Colombier }
4853e12c5d1SDavid du Colombier switch(getreply()){
4863e12c5d1SDavid du Colombier case 2:
4873e12c5d1SDavid du Colombier break;
4883e12c5d1SDavid du Colombier case 5:
4893e12c5d1SDavid du Colombier return Giveup;
4903e12c5d1SDavid du Colombier default:
4913e12c5d1SDavid du Colombier return Retry;
4923e12c5d1SDavid du Colombier }
493db7ae703SDavid du Colombier }
494d9306527SDavid du Colombier
495d9306527SDavid du Colombier ehlo = 1;
496d9306527SDavid du Colombier Again:
497d9306527SDavid du Colombier if(ehlo)
498d9306527SDavid du Colombier dBprint("EHLO %s\r\n", me);
499d9306527SDavid du Colombier else
5003e12c5d1SDavid du Colombier dBprint("HELO %s\r\n", me);
5013e12c5d1SDavid du Colombier switch (getreply()) {
5023e12c5d1SDavid du Colombier case 2:
5033e12c5d1SDavid du Colombier break;
5043e12c5d1SDavid du Colombier case 5:
505d9306527SDavid du Colombier if(ehlo){
506d9306527SDavid du Colombier ehlo = 0;
507d9306527SDavid du Colombier goto Again;
508d9306527SDavid du Colombier }
5093e12c5d1SDavid du Colombier return Giveup;
5103e12c5d1SDavid du Colombier default:
5113e12c5d1SDavid du Colombier return Retry;
5123e12c5d1SDavid du Colombier }
513d9306527SDavid du Colombier r = s_clone(reply);
514d9306527SDavid du Colombier if(r == nil)
515d9306527SDavid du Colombier return Retry; /* Out of memory or couldn't get string */
516d9306527SDavid du Colombier
517d9306527SDavid du Colombier /* Invariant: every line has a newline, a result of getcrlf() */
518d9306527SDavid du Colombier for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
519d9306527SDavid du Colombier *t = '\0';
520d9306527SDavid du Colombier for (t = s; *t != '\0'; t++)
521d9306527SDavid du Colombier *t = toupper(*t);
522d9306527SDavid du Colombier if(!encrypted && trysecure &&
523d9306527SDavid du Colombier (strcmp(s, "250-STARTTLS") == 0 ||
524d9306527SDavid du Colombier strcmp(s, "250 STARTTLS") == 0)){
525d9306527SDavid du Colombier s_free(r);
526db7ae703SDavid du Colombier return dotls(me);
527d9306527SDavid du Colombier }
528a8453668SDavid du Colombier if(tryauth && (encrypted || insecure) &&
529d9306527SDavid du Colombier (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
5303cf081f0SDavid du Colombier strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
5313cf081f0SDavid du Colombier ret = doauth(s + strlen("250 AUTH "));
532d9306527SDavid du Colombier s_free(r);
5333cf081f0SDavid du Colombier return ret;
534d9306527SDavid du Colombier }
535d9306527SDavid du Colombier }
536d9306527SDavid du Colombier s_free(r);
5373e12c5d1SDavid du Colombier return 0;
5383e12c5d1SDavid du Colombier }
5393e12c5d1SDavid du Colombier
5403e12c5d1SDavid du Colombier /*
5413e12c5d1SDavid du Colombier * report sender to remote
5423e12c5d1SDavid du Colombier */
5433e12c5d1SDavid du Colombier char *
mailfrom(char * from)5443e12c5d1SDavid du Colombier mailfrom(char *from)
5453e12c5d1SDavid du Colombier {
5467dd7cddfSDavid du Colombier if(!returnable(from))
5477dd7cddfSDavid du Colombier dBprint("MAIL FROM:<>\r\n");
5487dd7cddfSDavid du Colombier else
549219b2ee8SDavid du Colombier if(strchr(from, '@'))
5503e12c5d1SDavid du Colombier dBprint("MAIL FROM:<%s>\r\n", from);
551219b2ee8SDavid du Colombier else
552219b2ee8SDavid du Colombier dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
5533e12c5d1SDavid du Colombier switch(getreply()){
5543e12c5d1SDavid du Colombier case 2:
5553e12c5d1SDavid du Colombier break;
5563e12c5d1SDavid du Colombier case 5:
5573e12c5d1SDavid du Colombier return Giveup;
5583e12c5d1SDavid du Colombier default:
5593e12c5d1SDavid du Colombier return Retry;
5603e12c5d1SDavid du Colombier }
5613e12c5d1SDavid du Colombier return 0;
5623e12c5d1SDavid du Colombier }
5633e12c5d1SDavid du Colombier
5643e12c5d1SDavid du Colombier /*
5653e12c5d1SDavid du Colombier * report a recipient to remote
5663e12c5d1SDavid du Colombier */
5673e12c5d1SDavid du Colombier char *
rcptto(char * to)5683e12c5d1SDavid du Colombier rcptto(char *to)
5693e12c5d1SDavid du Colombier {
570219b2ee8SDavid du Colombier String *s;
571219b2ee8SDavid du Colombier
5727dd7cddfSDavid du Colombier s = unescapespecial(bangtoat(to));
5737dd7cddfSDavid du Colombier if(toline == 0)
574219b2ee8SDavid du Colombier toline = s_new();
5757dd7cddfSDavid du Colombier else
576219b2ee8SDavid du Colombier s_append(toline, ", ");
577219b2ee8SDavid du Colombier s_append(toline, s_to_c(s));
578219b2ee8SDavid du Colombier if(strchr(s_to_c(s), '@'))
579219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
580219b2ee8SDavid du Colombier else {
581219b2ee8SDavid du Colombier s_append(toline, "@");
582219b2ee8SDavid du Colombier s_append(toline, ddomain);
583219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
584219b2ee8SDavid du Colombier }
585a8453668SDavid du Colombier alarm(10*alarmscale);
5863e12c5d1SDavid du Colombier switch(getreply()){
5873e12c5d1SDavid du Colombier case 2:
5883e12c5d1SDavid du Colombier break;
5893e12c5d1SDavid du Colombier case 5:
5903e12c5d1SDavid du Colombier return Giveup;
5913e12c5d1SDavid du Colombier default:
5923e12c5d1SDavid du Colombier return Retry;
5933e12c5d1SDavid du Colombier }
5943e12c5d1SDavid du Colombier return 0;
5953e12c5d1SDavid du Colombier }
5963e12c5d1SDavid du Colombier
5979a747e4fSDavid du Colombier static char hex[] = "0123456789abcdef";
5989a747e4fSDavid du Colombier
5993e12c5d1SDavid du Colombier /*
6003e12c5d1SDavid du Colombier * send the damn thing
6013e12c5d1SDavid du Colombier */
6023e12c5d1SDavid du Colombier char *
data(String * from,Biobuf * b)6037dd7cddfSDavid du Colombier data(String *from, Biobuf *b)
6043e12c5d1SDavid du Colombier {
6057dd7cddfSDavid du Colombier char *buf, *cp;
6069a747e4fSDavid du Colombier int i, n, nbytes, bufsize, eof, r;
6077dd7cddfSDavid du Colombier String *fromline;
6089a747e4fSDavid du Colombier char errmsg[Errlen];
6099a747e4fSDavid du Colombier char id[40];
6103e12c5d1SDavid du Colombier
6113e12c5d1SDavid du Colombier /*
6127dd7cddfSDavid du Colombier * input the header.
6133e12c5d1SDavid du Colombier */
6147dd7cddfSDavid du Colombier
6157dd7cddfSDavid du Colombier buf = malloc(1);
6167dd7cddfSDavid du Colombier if(buf == 0){
6177dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory");
6187dd7cddfSDavid du Colombier return Retry;
6197dd7cddfSDavid du Colombier }
6207dd7cddfSDavid du Colombier n = 0;
6213e12c5d1SDavid du Colombier eof = 0;
6227dd7cddfSDavid du Colombier for(;;){
6237dd7cddfSDavid du Colombier cp = Brdline(b, '\n');
6247dd7cddfSDavid du Colombier if(cp == nil){
6253e12c5d1SDavid du Colombier eof = 1;
6263e12c5d1SDavid du Colombier break;
6273e12c5d1SDavid du Colombier }
6287dd7cddfSDavid du Colombier nbytes = Blinelen(b);
6297dd7cddfSDavid du Colombier buf = realloc(buf, n+nbytes+1);
6307dd7cddfSDavid du Colombier if(buf == 0){
6317dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory");
6327dd7cddfSDavid du Colombier return Retry;
6337dd7cddfSDavid du Colombier }
6347dd7cddfSDavid du Colombier strncpy(buf+n, cp, nbytes);
6357dd7cddfSDavid du Colombier n += nbytes;
6367dd7cddfSDavid du Colombier if(nbytes == 1) /* end of header */
6377dd7cddfSDavid du Colombier break;
6383e12c5d1SDavid du Colombier }
6393e12c5d1SDavid du Colombier buf[n] = 0;
6407dd7cddfSDavid du Colombier bufsize = n;
6413e12c5d1SDavid du Colombier
6423e12c5d1SDavid du Colombier /*
6433e12c5d1SDavid du Colombier * parse the header, turn all addresses into @ format
6443e12c5d1SDavid du Colombier */
6459a747e4fSDavid du Colombier yyinit(buf, n);
6463e12c5d1SDavid du Colombier yyparse();
6473e12c5d1SDavid du Colombier
6483e12c5d1SDavid du Colombier /*
6493e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n
6503e12c5d1SDavid du Colombier */
651a8453668SDavid du Colombier alarm(20*alarmscale);
6523e12c5d1SDavid du Colombier if(!filter){
6533e12c5d1SDavid du Colombier dBprint("DATA\r\n");
6543e12c5d1SDavid du Colombier switch(getreply()){
6553e12c5d1SDavid du Colombier case 3:
6563e12c5d1SDavid du Colombier break;
6573e12c5d1SDavid du Colombier case 5:
6587dd7cddfSDavid du Colombier free(buf);
6593e12c5d1SDavid du Colombier return Giveup;
6603e12c5d1SDavid du Colombier default:
6617dd7cddfSDavid du Colombier free(buf);
6623e12c5d1SDavid du Colombier return Retry;
6633e12c5d1SDavid du Colombier }
6643e12c5d1SDavid du Colombier }
6653e12c5d1SDavid du Colombier /*
6669a747e4fSDavid du Colombier * send header. add a message-id, a sender, and a date if there
6673e12c5d1SDavid du Colombier * isn't one
6683e12c5d1SDavid du Colombier */
6697dd7cddfSDavid du Colombier nbytes = 0;
6707dd7cddfSDavid du Colombier fromline = convertheader(from);
671bd389b36SDavid du Colombier uneaten = buf;
6729a747e4fSDavid du Colombier
673d9306527SDavid du Colombier srand(truerand());
6749a747e4fSDavid du Colombier if(messageid == 0){
6759a747e4fSDavid du Colombier for(i=0; i<16; i++){
676d9306527SDavid du Colombier r = rand()&0xFF;
6779a747e4fSDavid du Colombier id[2*i] = hex[r&0xF];
6789a747e4fSDavid du Colombier id[2*i+1] = hex[(r>>4)&0xF];
6799a747e4fSDavid du Colombier }
6809a747e4fSDavid du Colombier id[2*i] = '\0';
6819a747e4fSDavid du Colombier nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
6829a747e4fSDavid du Colombier if(debug)
6839a747e4fSDavid du Colombier Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
6849a747e4fSDavid du Colombier }
6859a747e4fSDavid du Colombier
6867dd7cddfSDavid du Colombier if(originator==0){
6877dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
6887dd7cddfSDavid du Colombier if(debug)
6897dd7cddfSDavid du Colombier Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
6907dd7cddfSDavid du Colombier }
6917dd7cddfSDavid du Colombier s_free(fromline);
6927dd7cddfSDavid du Colombier
693219b2ee8SDavid du Colombier if(destination == 0 && toline)
6947dd7cddfSDavid du Colombier if(*s_to_c(toline) == '@'){ /* route addr */
6957dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline));
6967dd7cddfSDavid du Colombier if(debug)
6977dd7cddfSDavid du Colombier Bprint(&berr, "To: <%s>\r\n", s_to_c(toline));
6987dd7cddfSDavid du Colombier } else {
6997dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline));
7007dd7cddfSDavid du Colombier if(debug)
7017dd7cddfSDavid du Colombier Bprint(&berr, "To: %s\r\n", s_to_c(toline));
7027dd7cddfSDavid du Colombier }
7037dd7cddfSDavid du Colombier
7043e12c5d1SDavid du Colombier if(date==0 && udate)
7057dd7cddfSDavid du Colombier nbytes += printdate(udate);
706bd389b36SDavid du Colombier if (usys)
707bd389b36SDavid du Colombier uneaten = usys->end + 1;
7087dd7cddfSDavid du Colombier nbytes += printheader();
709bd389b36SDavid du Colombier if (*uneaten != '\n')
710bd389b36SDavid du Colombier putcrnl("\n", 1);
7113e12c5d1SDavid du Colombier
7123e12c5d1SDavid du Colombier /*
7133e12c5d1SDavid du Colombier * send body
7143e12c5d1SDavid du Colombier */
7157dd7cddfSDavid du Colombier
716bd389b36SDavid du Colombier putcrnl(uneaten, buf+n - uneaten);
7177dd7cddfSDavid du Colombier nbytes += buf+n - uneaten;
7187dd7cddfSDavid du Colombier if(eof == 0){
719219b2ee8SDavid du Colombier for(;;){
7207dd7cddfSDavid du Colombier n = Bread(b, buf, bufsize);
721219b2ee8SDavid du Colombier if(n < 0){
7229a747e4fSDavid du Colombier rerrstr(errmsg, sizeof(errmsg));
7237dd7cddfSDavid du Colombier s_append(s_restart(reply), errmsg);
7247dd7cddfSDavid du Colombier free(buf);
7257dd7cddfSDavid du Colombier return Retry;
726219b2ee8SDavid du Colombier }
727219b2ee8SDavid du Colombier if(n == 0)
728219b2ee8SDavid du Colombier break;
729a8453668SDavid du Colombier alarm(10*alarmscale);
7303e12c5d1SDavid du Colombier putcrnl(buf, n);
7317dd7cddfSDavid du Colombier nbytes += n;
732219b2ee8SDavid du Colombier }
7337dd7cddfSDavid du Colombier }
7347dd7cddfSDavid du Colombier free(buf);
7353e12c5d1SDavid du Colombier if(!filter){
7363e12c5d1SDavid du Colombier if(last != '\n')
7373e12c5d1SDavid du Colombier dBprint("\r\n.\r\n");
7383e12c5d1SDavid du Colombier else
7393e12c5d1SDavid du Colombier dBprint(".\r\n");
740a8453668SDavid du Colombier alarm(10*alarmscale);
7413e12c5d1SDavid du Colombier switch(getreply()){
7423e12c5d1SDavid du Colombier case 2:
7433e12c5d1SDavid du Colombier break;
7443e12c5d1SDavid du Colombier case 5:
7453e12c5d1SDavid du Colombier return Giveup;
7467dd7cddfSDavid du Colombier default:
7477dd7cddfSDavid du Colombier return Retry;
7483e12c5d1SDavid du Colombier }
7497dd7cddfSDavid du Colombier syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
7507dd7cddfSDavid du Colombier nbytes, s_to_c(toline));/**/
7513e12c5d1SDavid du Colombier }
7523e12c5d1SDavid du Colombier return 0;
7533e12c5d1SDavid du Colombier }
7543e12c5d1SDavid du Colombier
7553e12c5d1SDavid du Colombier /*
7563e12c5d1SDavid du Colombier * we're leaving
7573e12c5d1SDavid du Colombier */
7583e12c5d1SDavid du Colombier void
quit(char * rv)7597dd7cddfSDavid du Colombier quit(char *rv)
7603e12c5d1SDavid du Colombier {
7617dd7cddfSDavid du Colombier /* 60 minutes to quit */
7627dd7cddfSDavid du Colombier quitting = 1;
7637dd7cddfSDavid du Colombier quitrv = rv;
764a8453668SDavid du Colombier alarm(60*alarmscale);
7653e12c5d1SDavid du Colombier dBprint("QUIT\r\n");
7663e12c5d1SDavid du Colombier getreply();
7677dd7cddfSDavid du Colombier Bterm(&bout);
7687dd7cddfSDavid du Colombier Bterm(&bfile);
7693e12c5d1SDavid du Colombier }
7703e12c5d1SDavid du Colombier
7713e12c5d1SDavid du Colombier /*
7723e12c5d1SDavid du Colombier * read a reply into a string, return the reply code
7733e12c5d1SDavid du Colombier */
7743e12c5d1SDavid du Colombier int
getreply(void)7753e12c5d1SDavid du Colombier getreply(void)
7763e12c5d1SDavid du Colombier {
7773e12c5d1SDavid du Colombier char *line;
7783e12c5d1SDavid du Colombier int rv;
7793e12c5d1SDavid du Colombier
7803e12c5d1SDavid du Colombier reply = s_reset(reply);
7813e12c5d1SDavid du Colombier for(;;){
7823e12c5d1SDavid du Colombier line = getcrnl(reply);
783499069deSDavid du Colombier if(debug)
784499069deSDavid du Colombier Bflush(&berr);
7853e12c5d1SDavid du Colombier if(line == 0)
7863e12c5d1SDavid du Colombier return -1;
7873e12c5d1SDavid du Colombier if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
7883e12c5d1SDavid du Colombier return -1;
7893e12c5d1SDavid du Colombier if(line[3] != '-')
7903e12c5d1SDavid du Colombier break;
7913e12c5d1SDavid du Colombier }
7927dd7cddfSDavid du Colombier if(debug)
7937dd7cddfSDavid du Colombier Bflush(&berr);
7943e12c5d1SDavid du Colombier rv = atoi(line)/100;
7953e12c5d1SDavid du Colombier return rv;
7963e12c5d1SDavid du Colombier }
7977dd7cddfSDavid du Colombier void
addhostdom(String * buf,char * host)7987dd7cddfSDavid du Colombier addhostdom(String *buf, char *host)
7997dd7cddfSDavid du Colombier {
8007dd7cddfSDavid du Colombier s_append(buf, "@");
8017dd7cddfSDavid du Colombier s_append(buf, host);
8027dd7cddfSDavid du Colombier }
8033e12c5d1SDavid du Colombier
8043e12c5d1SDavid du Colombier /*
8053e12c5d1SDavid du Colombier * Convert from `bang' to `source routing' format.
8063e12c5d1SDavid du Colombier *
8073e12c5d1SDavid du Colombier * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o
8083e12c5d1SDavid du Colombier */
8093e12c5d1SDavid du Colombier String *
bangtoat(char * addr)810219b2ee8SDavid du Colombier bangtoat(char *addr)
8113e12c5d1SDavid du Colombier {
8123e12c5d1SDavid du Colombier String *buf;
8133e12c5d1SDavid du Colombier register int i;
8143e12c5d1SDavid du Colombier int j, d;
8153e12c5d1SDavid du Colombier char *field[128];
8163e12c5d1SDavid du Colombier
8173e12c5d1SDavid du Colombier /* parse the '!' format address */
8183e12c5d1SDavid du Colombier buf = s_new();
8193e12c5d1SDavid du Colombier for(i = 0; addr; i++){
8203e12c5d1SDavid du Colombier field[i] = addr;
8213e12c5d1SDavid du Colombier addr = strchr(addr, '!');
8223e12c5d1SDavid du Colombier if(addr)
8233e12c5d1SDavid du Colombier *addr++ = 0;
8243e12c5d1SDavid du Colombier }
8253e12c5d1SDavid du Colombier if (i==1) {
8263e12c5d1SDavid du Colombier s_append(buf, field[0]);
8273e12c5d1SDavid du Colombier return buf;
8283e12c5d1SDavid du Colombier }
8293e12c5d1SDavid du Colombier
8303e12c5d1SDavid du Colombier /*
8313e12c5d1SDavid du Colombier * count leading domain fields (non-domains don't count)
8323e12c5d1SDavid du Colombier */
8337dd7cddfSDavid du Colombier for(d = 0; d<i-1; d++)
8343e12c5d1SDavid du Colombier if(strchr(field[d], '.')==0)
8353e12c5d1SDavid du Colombier break;
8363e12c5d1SDavid du Colombier /*
8373e12c5d1SDavid du Colombier * if there are more than 1 leading domain elements,
8383e12c5d1SDavid du Colombier * put them in as source routing
8393e12c5d1SDavid du Colombier */
8403e12c5d1SDavid du Colombier if(d > 1){
841219b2ee8SDavid du Colombier addhostdom(buf, field[0]);
8423e12c5d1SDavid du Colombier for(j=1; j<d-1; j++){
8433e12c5d1SDavid du Colombier s_append(buf, ",");
8443e12c5d1SDavid du Colombier s_append(buf, "@");
8453e12c5d1SDavid du Colombier s_append(buf, field[j]);
8463e12c5d1SDavid du Colombier }
8473e12c5d1SDavid du Colombier s_append(buf, ":");
8483e12c5d1SDavid du Colombier }
8493e12c5d1SDavid du Colombier
8503e12c5d1SDavid du Colombier /*
8513e12c5d1SDavid du Colombier * throw in the non-domain elements separated by '!'s
8523e12c5d1SDavid du Colombier */
8533e12c5d1SDavid du Colombier s_append(buf, field[d]);
8543e12c5d1SDavid du Colombier for(j=d+1; j<=i-1; j++) {
8553e12c5d1SDavid du Colombier s_append(buf, "!");
8563e12c5d1SDavid du Colombier s_append(buf, field[j]);
8573e12c5d1SDavid du Colombier }
8583e12c5d1SDavid du Colombier if(d)
859219b2ee8SDavid du Colombier addhostdom(buf, field[d-1]);
8603e12c5d1SDavid du Colombier return buf;
8613e12c5d1SDavid du Colombier }
8623e12c5d1SDavid du Colombier
8633e12c5d1SDavid du Colombier /*
8643e12c5d1SDavid du Colombier * convert header addresses to @ format.
8653e12c5d1SDavid du Colombier * if the address is a source address, and a domain is specified,
8663e12c5d1SDavid du Colombier * make sure it falls in the domain.
8673e12c5d1SDavid du Colombier */
8687dd7cddfSDavid du Colombier String*
convertheader(String * from)869219b2ee8SDavid du Colombier convertheader(String *from)
8703e12c5d1SDavid du Colombier {
8713e12c5d1SDavid du Colombier Field *f;
8727dd7cddfSDavid du Colombier Node *p, *lastp;
8733e12c5d1SDavid du Colombier String *a;
8743e12c5d1SDavid du Colombier
8757dd7cddfSDavid du Colombier if(!returnable(s_to_c(from))){
8767dd7cddfSDavid du Colombier from = s_new();
8777dd7cddfSDavid du Colombier s_append(from, "Postmaster");
8787dd7cddfSDavid du Colombier addhostdom(from, hostdomain);
8797dd7cddfSDavid du Colombier } else
8807dd7cddfSDavid du Colombier if(strchr(s_to_c(from), '@') == 0){
8817dd7cddfSDavid du Colombier a = username(from);
8827dd7cddfSDavid du Colombier if(a) {
8837dd7cddfSDavid du Colombier s_append(a, " <");
8847dd7cddfSDavid du Colombier s_append(a, s_to_c(from));
8857dd7cddfSDavid du Colombier addhostdom(a, hostdomain);
8867dd7cddfSDavid du Colombier s_append(a, ">");
8877dd7cddfSDavid du Colombier from = a;
8887dd7cddfSDavid du Colombier } else {
8897dd7cddfSDavid du Colombier from = s_copy(s_to_c(from));
8907dd7cddfSDavid du Colombier addhostdom(from, hostdomain);
891219b2ee8SDavid du Colombier }
8927dd7cddfSDavid du Colombier } else
8937dd7cddfSDavid du Colombier from = s_copy(s_to_c(from));
8943e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){
8957dd7cddfSDavid du Colombier lastp = 0;
8967dd7cddfSDavid du Colombier for(p = f->node; p; lastp = p, p = p->next){
8973e12c5d1SDavid du Colombier if(!p->addr)
8983e12c5d1SDavid du Colombier continue;
899219b2ee8SDavid du Colombier a = bangtoat(s_to_c(p->s));
9003e12c5d1SDavid du Colombier s_free(p->s);
9017dd7cddfSDavid du Colombier if(strchr(s_to_c(a), '@') == 0)
9027dd7cddfSDavid du Colombier addhostdom(a, hostdomain);
9037dd7cddfSDavid du Colombier else if(*s_to_c(a) == '@')
9047dd7cddfSDavid du Colombier a = fixrouteaddr(a, p->next, lastp);
9053e12c5d1SDavid du Colombier p->s = a;
9063e12c5d1SDavid du Colombier }
9073e12c5d1SDavid du Colombier }
9087dd7cddfSDavid du Colombier return from;
9097dd7cddfSDavid du Colombier }
9107dd7cddfSDavid du Colombier /*
9117dd7cddfSDavid du Colombier * ensure route addr has brackets around it
9127dd7cddfSDavid du Colombier */
9137dd7cddfSDavid du Colombier String*
fixrouteaddr(String * raddr,Node * next,Node * last)9147dd7cddfSDavid du Colombier fixrouteaddr(String *raddr, Node *next, Node *last)
9157dd7cddfSDavid du Colombier {
9167dd7cddfSDavid du Colombier String *a;
9177dd7cddfSDavid du Colombier
9187dd7cddfSDavid du Colombier if(last && last->c == '<' && next && next->c == '>')
9197dd7cddfSDavid du Colombier return raddr; /* properly formed already */
9207dd7cddfSDavid du Colombier
9217dd7cddfSDavid du Colombier a = s_new();
9227dd7cddfSDavid du Colombier s_append(a, "<");
9237dd7cddfSDavid du Colombier s_append(a, s_to_c(raddr));
9247dd7cddfSDavid du Colombier s_append(a, ">");
9257dd7cddfSDavid du Colombier s_free(raddr);
9267dd7cddfSDavid du Colombier return a;
9273e12c5d1SDavid du Colombier }
9283e12c5d1SDavid du Colombier
9293e12c5d1SDavid du Colombier /*
9303e12c5d1SDavid du Colombier * print out the parsed header
9313e12c5d1SDavid du Colombier */
9327dd7cddfSDavid du Colombier int
printheader(void)9333e12c5d1SDavid du Colombier printheader(void)
9343e12c5d1SDavid du Colombier {
9357dd7cddfSDavid du Colombier int n, len;
9363e12c5d1SDavid du Colombier Field *f;
9373e12c5d1SDavid du Colombier Node *p;
9383e12c5d1SDavid du Colombier char *cp;
9393e12c5d1SDavid du Colombier char c[1];
9403e12c5d1SDavid du Colombier
9417dd7cddfSDavid du Colombier n = 0;
9423e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){
9433e12c5d1SDavid du Colombier for(p = f->node; p; p = p->next){
9443e12c5d1SDavid du Colombier if(p->s)
9457dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s));
9463e12c5d1SDavid du Colombier else {
9473e12c5d1SDavid du Colombier c[0] = p->c;
9483e12c5d1SDavid du Colombier putcrnl(c, 1);
9497dd7cddfSDavid du Colombier n++;
9503e12c5d1SDavid du Colombier }
9513e12c5d1SDavid du Colombier if(p->white){
9523e12c5d1SDavid du Colombier cp = s_to_c(p->white);
9537dd7cddfSDavid du Colombier len = strlen(cp);
9547dd7cddfSDavid du Colombier putcrnl(cp, len);
9557dd7cddfSDavid du Colombier n += len;
9563e12c5d1SDavid du Colombier }
957bd389b36SDavid du Colombier uneaten = p->end;
9583e12c5d1SDavid du Colombier }
9593e12c5d1SDavid du Colombier putcrnl("\n", 1);
9607dd7cddfSDavid du Colombier n++;
961bd389b36SDavid du Colombier uneaten++; /* skip newline */
9623e12c5d1SDavid du Colombier }
9637dd7cddfSDavid du Colombier return n;
9643e12c5d1SDavid du Colombier }
9653e12c5d1SDavid du Colombier
9663e12c5d1SDavid du Colombier /*
9673e12c5d1SDavid du Colombier * add a domain onto an name, return the new name
9683e12c5d1SDavid du Colombier */
9693e12c5d1SDavid du Colombier char *
domainify(char * name,char * domain)9703e12c5d1SDavid du Colombier domainify(char *name, char *domain)
9713e12c5d1SDavid du Colombier {
9723e12c5d1SDavid du Colombier static String *s;
9737dd7cddfSDavid du Colombier char *p;
9743e12c5d1SDavid du Colombier
9753e12c5d1SDavid du Colombier if(domain==0 || strchr(name, '.')!=0)
9763e12c5d1SDavid du Colombier return name;
9773e12c5d1SDavid du Colombier
9783e12c5d1SDavid du Colombier s = s_reset(s);
9793e12c5d1SDavid du Colombier s_append(s, name);
9807dd7cddfSDavid du Colombier p = strchr(domain, '.');
9817dd7cddfSDavid du Colombier if(p == 0){
982219b2ee8SDavid du Colombier s_append(s, ".");
9837dd7cddfSDavid du Colombier p = domain;
9847dd7cddfSDavid du Colombier }
9857dd7cddfSDavid du Colombier s_append(s, p);
9863e12c5d1SDavid du Colombier return s_to_c(s);
9873e12c5d1SDavid du Colombier }
9883e12c5d1SDavid du Colombier
9893e12c5d1SDavid du Colombier /*
9903e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n
9913e12c5d1SDavid du Colombier */
9923e12c5d1SDavid du Colombier void
putcrnl(char * cp,int n)9933e12c5d1SDavid du Colombier putcrnl(char *cp, int n)
9943e12c5d1SDavid du Colombier {
9953e12c5d1SDavid du Colombier int c;
9963e12c5d1SDavid du Colombier
9973e12c5d1SDavid du Colombier for(; n; n--, cp++){
9983e12c5d1SDavid du Colombier c = *cp;
9993e12c5d1SDavid du Colombier if(c == '\n')
10007dd7cddfSDavid du Colombier dBputc('\r');
10013e12c5d1SDavid du Colombier else if(c == '.' && last=='\n')
10027dd7cddfSDavid du Colombier dBputc('.');
10037dd7cddfSDavid du Colombier dBputc(c);
10043e12c5d1SDavid du Colombier last = c;
10053e12c5d1SDavid du Colombier }
10063e12c5d1SDavid du Colombier }
10073e12c5d1SDavid du Colombier
10083e12c5d1SDavid du Colombier /*
10093e12c5d1SDavid du Colombier * Get a line including a crnl into a string. Convert crnl into nl.
10103e12c5d1SDavid du Colombier */
10113e12c5d1SDavid du Colombier char *
getcrnl(String * s)10123e12c5d1SDavid du Colombier getcrnl(String *s)
10133e12c5d1SDavid du Colombier {
10143e12c5d1SDavid du Colombier int c;
10153e12c5d1SDavid du Colombier int count;
10163e12c5d1SDavid du Colombier
10173e12c5d1SDavid du Colombier count = 0;
10183e12c5d1SDavid du Colombier for(;;){
10193e12c5d1SDavid du Colombier c = Bgetc(&bin);
10203e12c5d1SDavid du Colombier if(debug)
10213e12c5d1SDavid du Colombier Bputc(&berr, c);
10223e12c5d1SDavid du Colombier switch(c){
10233e12c5d1SDavid du Colombier case -1:
10247dd7cddfSDavid du Colombier s_append(s, "connection closed unexpectedly by remote system");
10253e12c5d1SDavid du Colombier s_terminate(s);
10263e12c5d1SDavid du Colombier return 0;
10273e12c5d1SDavid du Colombier case '\r':
10283e12c5d1SDavid du Colombier c = Bgetc(&bin);
10293e12c5d1SDavid du Colombier if(c == '\n'){
1030499069deSDavid du Colombier case '\n':
10313e12c5d1SDavid du Colombier s_putc(s, c);
10323e12c5d1SDavid du Colombier if(debug)
10333e12c5d1SDavid du Colombier Bputc(&berr, c);
10343e12c5d1SDavid du Colombier count++;
10353e12c5d1SDavid du Colombier s_terminate(s);
10363e12c5d1SDavid du Colombier return s->ptr - count;
10373e12c5d1SDavid du Colombier }
10383e12c5d1SDavid du Colombier Bungetc(&bin);
10393e12c5d1SDavid du Colombier s_putc(s, '\r');
10403e12c5d1SDavid du Colombier if(debug)
10413e12c5d1SDavid du Colombier Bputc(&berr, '\r');
10423e12c5d1SDavid du Colombier count++;
10433e12c5d1SDavid du Colombier break;
10443e12c5d1SDavid du Colombier default:
10453e12c5d1SDavid du Colombier s_putc(s, c);
10463e12c5d1SDavid du Colombier count++;
10473e12c5d1SDavid du Colombier break;
10483e12c5d1SDavid du Colombier }
10493e12c5d1SDavid du Colombier }
10503e12c5d1SDavid du Colombier }
10513e12c5d1SDavid du Colombier
10523e12c5d1SDavid du Colombier /*
10533e12c5d1SDavid du Colombier * print out a parsed date
10543e12c5d1SDavid du Colombier */
10557dd7cddfSDavid du Colombier int
printdate(Node * p)10563e12c5d1SDavid du Colombier printdate(Node *p)
10573e12c5d1SDavid du Colombier {
10587dd7cddfSDavid du Colombier int n, sep = 0;
10593e12c5d1SDavid du Colombier
10607dd7cddfSDavid du Colombier n = dBprint("Date: %s,", s_to_c(p->s));
10613e12c5d1SDavid du Colombier for(p = p->next; p; p = p->next){
10623e12c5d1SDavid du Colombier if(p->s){
10637dd7cddfSDavid du Colombier if(sep == 0) {
10647dd7cddfSDavid du Colombier dBputc(' ');
10657dd7cddfSDavid du Colombier n++;
10667dd7cddfSDavid du Colombier }
1067bd389b36SDavid du Colombier if (p->next)
10687dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s));
1069bd389b36SDavid du Colombier else
10707dd7cddfSDavid du Colombier n += dBprint("%s", rewritezone(s_to_c(p->s)));
10713e12c5d1SDavid du Colombier sep = 0;
10723e12c5d1SDavid du Colombier } else {
10737dd7cddfSDavid du Colombier dBputc(p->c);
10747dd7cddfSDavid du Colombier n++;
10753e12c5d1SDavid du Colombier sep = 1;
10763e12c5d1SDavid du Colombier }
10773e12c5d1SDavid du Colombier }
10787dd7cddfSDavid du Colombier n += dBprint("\r\n");
10797dd7cddfSDavid du Colombier return n;
10803e12c5d1SDavid du Colombier }
10813e12c5d1SDavid du Colombier
1082bd389b36SDavid du Colombier char *
rewritezone(char * z)1083bd389b36SDavid du Colombier rewritezone(char *z)
1084bd389b36SDavid du Colombier {
10857dd7cddfSDavid du Colombier int mindiff;
10867dd7cddfSDavid du Colombier char s;
10877dd7cddfSDavid du Colombier Tm *tm;
10887dd7cddfSDavid du Colombier static char x[7];
1089bd389b36SDavid du Colombier
10907dd7cddfSDavid du Colombier tm = localtime(time(0));
10917dd7cddfSDavid du Colombier mindiff = tm->tzoff/60;
1092bd389b36SDavid du Colombier
10937dd7cddfSDavid du Colombier /* if not in my timezone, don't change anything */
10947dd7cddfSDavid du Colombier if(strcmp(tm->zone, z) != 0)
1095bd389b36SDavid du Colombier return z;
10967dd7cddfSDavid du Colombier
10977dd7cddfSDavid du Colombier if(mindiff < 0){
10987dd7cddfSDavid du Colombier s = '-';
10997dd7cddfSDavid du Colombier mindiff = -mindiff;
11007dd7cddfSDavid du Colombier } else
11017dd7cddfSDavid du Colombier s = '+';
11027dd7cddfSDavid du Colombier
11037dd7cddfSDavid du Colombier sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
11047dd7cddfSDavid du Colombier return x;
1105bd389b36SDavid du Colombier }
1106bd389b36SDavid du Colombier
11073e12c5d1SDavid du Colombier /*
11083e12c5d1SDavid du Colombier * stolen from libc/port/print.c
11093e12c5d1SDavid du Colombier */
11103e12c5d1SDavid du Colombier #define SIZE 4096
11113e12c5d1SDavid du Colombier int
dBprint(char * fmt,...)11123e12c5d1SDavid du Colombier dBprint(char *fmt, ...)
11133e12c5d1SDavid du Colombier {
11143e12c5d1SDavid du Colombier char buf[SIZE], *out;
11157dd7cddfSDavid du Colombier va_list arg;
11163e12c5d1SDavid du Colombier int n;
11173e12c5d1SDavid du Colombier
11187dd7cddfSDavid du Colombier va_start(arg, fmt);
11199a747e4fSDavid du Colombier out = vseprint(buf, buf+SIZE, fmt, arg);
11207dd7cddfSDavid du Colombier va_end(arg);
11213e12c5d1SDavid du Colombier if(debug){
11223e12c5d1SDavid du Colombier Bwrite(&berr, buf, (long)(out-buf));
11233e12c5d1SDavid du Colombier Bflush(&berr);
11243e12c5d1SDavid du Colombier }
11253e12c5d1SDavid du Colombier n = Bwrite(&bout, buf, (long)(out-buf));
11263e12c5d1SDavid du Colombier Bflush(&bout);
11273e12c5d1SDavid du Colombier return n;
11283e12c5d1SDavid du Colombier }
11297dd7cddfSDavid du Colombier
11307dd7cddfSDavid du Colombier int
dBputc(int x)11317dd7cddfSDavid du Colombier dBputc(int x)
11327dd7cddfSDavid du Colombier {
11337dd7cddfSDavid du Colombier if(debug)
11347dd7cddfSDavid du Colombier Bputc(&berr, x);
11357dd7cddfSDavid du Colombier return Bputc(&bout, x);
11367dd7cddfSDavid du Colombier }
1137