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*); 11d9306527SDavid du Colombier char* hello(char*, int); 123e12c5d1SDavid du Colombier char* mailfrom(char*); 133e12c5d1SDavid du Colombier char* rcptto(char*); 147dd7cddfSDavid du Colombier char* data(String*, Biobuf*); 157dd7cddfSDavid du Colombier void quit(char*); 163e12c5d1SDavid du Colombier int getreply(void); 17219b2ee8SDavid du Colombier void addhostdom(String*, char*); 18219b2ee8SDavid du Colombier String* bangtoat(char*); 197dd7cddfSDavid du Colombier String* convertheader(String*); 207dd7cddfSDavid du Colombier int printheader(void); 213e12c5d1SDavid du Colombier char* domainify(char*, char*); 223e12c5d1SDavid du Colombier void putcrnl(char*, int); 233e12c5d1SDavid du Colombier char* getcrnl(String*); 247dd7cddfSDavid du Colombier int printdate(Node*); 25bd389b36SDavid du Colombier char *rewritezone(char *); 263e12c5d1SDavid du Colombier int dBprint(char*, ...); 277dd7cddfSDavid du Colombier int dBputc(int); 287dd7cddfSDavid du Colombier String* fixrouteaddr(String*, Node*, Node*); 29a8453668SDavid du Colombier int ping; 30a8453668SDavid du Colombier int insecure; 313e12c5d1SDavid du Colombier 327dd7cddfSDavid du Colombier #define Retry "Retry, Temporary Failure" 333e12c5d1SDavid du Colombier #define Giveup "Permanent Failure" 343e12c5d1SDavid du Colombier 353e12c5d1SDavid du Colombier int debug; /* true if we're debugging */ 363e12c5d1SDavid du Colombier String *reply; /* last reply */ 37219b2ee8SDavid du Colombier String *toline; 38a8453668SDavid du Colombier int alarmscale; 393e12c5d1SDavid du Colombier int last = 'n'; /* last character sent by putcrnl() */ 403e12c5d1SDavid du Colombier int filter; 41d9306527SDavid du Colombier int trysecure; /* Try to use TLS if the other side supports it */ 42d9306527SDavid du Colombier int tryauth; /* Try to authenticate, if supported */ 437dd7cddfSDavid du Colombier int quitting; /* when error occurs in quit */ 447dd7cddfSDavid du Colombier char *quitrv; /* deferred return value when in quit */ 453e12c5d1SDavid du Colombier char ddomain[1024]; /* domain name of destination machine */ 463e12c5d1SDavid du Colombier char *gdomain; /* domain name of gateway */ 47bd389b36SDavid du Colombier char *uneaten; /* first character after rfc822 headers */ 487dd7cddfSDavid du Colombier char *farend; /* system we are trying to send to */ 49d9306527SDavid du Colombier char *user; /* user we are authenticating as, if authenticating */ 50219b2ee8SDavid du Colombier char hostdomain[256]; 513e12c5d1SDavid du Colombier Biobuf bin; 523e12c5d1SDavid du Colombier Biobuf bout; 533e12c5d1SDavid du Colombier Biobuf berr; 547dd7cddfSDavid du Colombier Biobuf bfile; 553e12c5d1SDavid du Colombier 563e12c5d1SDavid du Colombier void 573e12c5d1SDavid du Colombier usage(void) 583e12c5d1SDavid du Colombier { 59a8453668SDavid du Colombier fprint(2, "usage: smtp [-adips] [-uuser] [-hhost] [.domain] net!host[!service] sender rcpt-list\n"); 60219b2ee8SDavid du Colombier exits(Giveup); 61219b2ee8SDavid du Colombier } 62219b2ee8SDavid du Colombier 637dd7cddfSDavid du Colombier int 64219b2ee8SDavid du Colombier timeout(void *x, char *msg) 65219b2ee8SDavid du Colombier { 66219b2ee8SDavid du Colombier USED(x); 677dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg); 68219b2ee8SDavid du Colombier if(strstr(msg, "alarm")){ 697dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection to %s timed out\n", farend); 707dd7cddfSDavid du Colombier if(quitting) 717dd7cddfSDavid du Colombier exits(quitrv); 727dd7cddfSDavid du Colombier exits(Retry); 73219b2ee8SDavid du Colombier } 747dd7cddfSDavid du Colombier if(strstr(msg, "closed pipe")){ 757dd7cddfSDavid du Colombier /* call _exits() to prevent Bio from trying to flush closed pipe */ 767dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection closed to %s\n", farend); 777dd7cddfSDavid du Colombier if(quitting){ 787dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "closed pipe to %s", farend); 797dd7cddfSDavid du Colombier _exits(quitrv); 807dd7cddfSDavid du Colombier } 817dd7cddfSDavid du Colombier _exits(Retry); 827dd7cddfSDavid du Colombier } 837dd7cddfSDavid du Colombier return 0; 843e12c5d1SDavid du Colombier } 853e12c5d1SDavid du Colombier 863e12c5d1SDavid du Colombier void 87a8453668SDavid du Colombier removenewline(char *p) 88a8453668SDavid du Colombier { 89a8453668SDavid du Colombier int n = strlen(p)-1; 90a8453668SDavid du Colombier 91a8453668SDavid du Colombier if(n < 0) 92a8453668SDavid du Colombier return; 93a8453668SDavid du Colombier if(p[n] == '\n') 94a8453668SDavid du Colombier p[n] = 0; 95a8453668SDavid du Colombier } 96a8453668SDavid du Colombier 97a8453668SDavid du Colombier void 983e12c5d1SDavid du Colombier main(int argc, char **argv) 993e12c5d1SDavid du Colombier { 1007dd7cddfSDavid du Colombier char hellodomain[256]; 1017dd7cddfSDavid du Colombier char *host, *domain; 1023e12c5d1SDavid du Colombier String *from; 1033e12c5d1SDavid du Colombier String *fromm; 1047dd7cddfSDavid du Colombier String *sender; 1053e12c5d1SDavid du Colombier char *addr; 1067dd7cddfSDavid du Colombier char *rv, *trv; 1077dd7cddfSDavid du Colombier int i, ok, rcvrs; 1087dd7cddfSDavid du Colombier char **errs; 1093e12c5d1SDavid du Colombier 110a8453668SDavid du Colombier alarmscale = 60*1000; /* minutes */ 111fe096d1aSDavid du Colombier quotefmtinstall(); 1127dd7cddfSDavid du Colombier errs = malloc(argc*sizeof(char*)); 1133e12c5d1SDavid du Colombier reply = s_new(); 114219b2ee8SDavid du Colombier host = 0; 1153e12c5d1SDavid du Colombier ARGBEGIN{ 116d9306527SDavid du Colombier case 'a': 117d9306527SDavid du Colombier tryauth = 1; 118d9306527SDavid du Colombier trysecure = 1; 119d9306527SDavid du Colombier break; 1203e12c5d1SDavid du Colombier case 'f': 1213e12c5d1SDavid du Colombier filter = 1; 1223e12c5d1SDavid du Colombier break; 1233e12c5d1SDavid du Colombier case 'd': 1243e12c5d1SDavid du Colombier debug = 1; 1253e12c5d1SDavid du Colombier break; 1263e12c5d1SDavid du Colombier case 'g': 1273e12c5d1SDavid du Colombier gdomain = ARGF(); 1283e12c5d1SDavid du Colombier break; 1293e12c5d1SDavid du Colombier case 'h': 1303e12c5d1SDavid du Colombier host = ARGF(); 1313e12c5d1SDavid du Colombier break; 132a8453668SDavid du Colombier case 'i': 133a8453668SDavid du Colombier insecure = 1; 134a8453668SDavid du Colombier break; 135a8453668SDavid du Colombier case 'p': 136a8453668SDavid du Colombier alarmscale = 10*1000; /* tens of seconds */ 137a8453668SDavid du Colombier ping = 1; 138a8453668SDavid du Colombier break; 139d9306527SDavid du Colombier case 's': 140d9306527SDavid du Colombier trysecure = 1; 141d9306527SDavid du Colombier break; 142d9306527SDavid du Colombier case 'u': 143d9306527SDavid du Colombier user = ARGF(); 144d9306527SDavid du Colombier break; 1453e12c5d1SDavid du Colombier default: 1463e12c5d1SDavid du Colombier usage(); 1473e12c5d1SDavid du Colombier break; 1483e12c5d1SDavid du Colombier }ARGEND; 1493e12c5d1SDavid du Colombier 1503e12c5d1SDavid du Colombier Binit(&berr, 2, OWRITE); 1517dd7cddfSDavid du Colombier Binit(&bfile, 0, OREAD); 1523e12c5d1SDavid du Colombier 1533e12c5d1SDavid du Colombier /* 1543e12c5d1SDavid du Colombier * get domain and add to host name 1553e12c5d1SDavid du Colombier */ 1567dd7cddfSDavid du Colombier if(*argv && **argv=='.') { 1577dd7cddfSDavid du Colombier domain = *argv; 1587dd7cddfSDavid du Colombier argv++; argc--; 1597dd7cddfSDavid du Colombier } else 1607dd7cddfSDavid du Colombier domain = domainname_read(); 1613e12c5d1SDavid du Colombier if(host == 0) 1623e12c5d1SDavid du Colombier host = sysname_read(); 1633e12c5d1SDavid du Colombier strcpy(hostdomain, domainify(host, domain)); 1647dd7cddfSDavid du Colombier strcpy(hellodomain, domainify(sysname_read(), domain)); 1653e12c5d1SDavid du Colombier 1663e12c5d1SDavid du Colombier /* 1673e12c5d1SDavid du Colombier * get destination address 1683e12c5d1SDavid du Colombier */ 1693e12c5d1SDavid du Colombier if(*argv == 0) 1703e12c5d1SDavid du Colombier usage(); 1717dd7cddfSDavid du Colombier addr = *argv++; argc--; 1727dd7cddfSDavid du Colombier farend = addr; 1733e12c5d1SDavid du Colombier 1743e12c5d1SDavid du Colombier /* 1753e12c5d1SDavid du Colombier * get sender's machine. 1763e12c5d1SDavid du Colombier * get sender in internet style. domainify if necessary. 1773e12c5d1SDavid du Colombier */ 178219b2ee8SDavid du Colombier if(*argv == 0) 179219b2ee8SDavid du Colombier usage(); 1807dd7cddfSDavid du Colombier sender = unescapespecial(s_copy(*argv++)); 1817dd7cddfSDavid du Colombier argc--; 1827dd7cddfSDavid du Colombier fromm = s_clone(sender); 1833e12c5d1SDavid du Colombier rv = strrchr(s_to_c(fromm), '!'); 1843e12c5d1SDavid du Colombier if(rv) 1853e12c5d1SDavid du Colombier *rv = 0; 1863e12c5d1SDavid du Colombier else 1873e12c5d1SDavid du Colombier *s_to_c(fromm) = 0; 1887dd7cddfSDavid du Colombier from = bangtoat(s_to_c(sender)); 1893e12c5d1SDavid du Colombier 1903e12c5d1SDavid du Colombier /* 1913e12c5d1SDavid du Colombier * send the mail 1923e12c5d1SDavid du Colombier */ 1933e12c5d1SDavid du Colombier if(filter){ 1943e12c5d1SDavid du Colombier Binit(&bout, 1, OWRITE); 1957dd7cddfSDavid du Colombier rv = data(from, &bfile); 1963e12c5d1SDavid du Colombier if(rv != 0) 1973e12c5d1SDavid du Colombier goto error; 1987dd7cddfSDavid du Colombier exits(0); 1997dd7cddfSDavid du Colombier } 2007dd7cddfSDavid du Colombier 201*ab3dc52fSDavid du Colombier /* mxdial uses its own timeout handler */ 2027dd7cddfSDavid du Colombier if((rv = connect(addr)) != 0) 2037dd7cddfSDavid du Colombier exits(rv); 204*ab3dc52fSDavid du Colombier 205*ab3dc52fSDavid du Colombier /* 10 minutes to get through the initial handshake */ 206*ab3dc52fSDavid du Colombier atnotify(timeout, 1); 207a8453668SDavid du Colombier alarm(10*alarmscale); 208d9306527SDavid du Colombier if((rv = hello(hellodomain, 0)) != 0) 2097dd7cddfSDavid du Colombier goto error; 210a8453668SDavid du Colombier alarm(10*alarmscale); 2117dd7cddfSDavid du Colombier if((rv = mailfrom(s_to_c(from))) != 0) 2127dd7cddfSDavid du Colombier goto error; 2137dd7cddfSDavid du Colombier 2147dd7cddfSDavid du Colombier ok = 0; 2157dd7cddfSDavid du Colombier rcvrs = 0; 2167dd7cddfSDavid du Colombier /* if any rcvrs are ok, we try to send the message */ 2177dd7cddfSDavid du Colombier for(i = 0; i < argc; i++){ 2187dd7cddfSDavid du Colombier if((trv = rcptto(argv[i])) != 0){ 2197dd7cddfSDavid du Colombier /* remember worst error */ 2207dd7cddfSDavid du Colombier if(rv != Giveup) 2217dd7cddfSDavid du Colombier rv = trv; 2227dd7cddfSDavid du Colombier errs[rcvrs] = strdup(s_to_c(reply)); 223a8453668SDavid du Colombier removenewline(errs[rcvrs]); 2247dd7cddfSDavid du Colombier } else { 2257dd7cddfSDavid du Colombier ok++; 2267dd7cddfSDavid du Colombier errs[rcvrs] = 0; 2277dd7cddfSDavid du Colombier } 2287dd7cddfSDavid du Colombier rcvrs++; 2297dd7cddfSDavid du Colombier } 2307dd7cddfSDavid du Colombier 2317dd7cddfSDavid du Colombier /* if no ok rcvrs or worst error is retry, give up */ 2327dd7cddfSDavid du Colombier if(ok == 0 || rv == Retry) 2337dd7cddfSDavid du Colombier goto error; 2347dd7cddfSDavid du Colombier 235a8453668SDavid du Colombier if(ping){ 236a8453668SDavid du Colombier quit(0); 237a8453668SDavid du Colombier exits(0); 238a8453668SDavid du Colombier } 239a8453668SDavid du Colombier 2407dd7cddfSDavid du Colombier rv = data(from, &bfile); 2417dd7cddfSDavid du Colombier if(rv != 0) 2427dd7cddfSDavid du Colombier goto error; 2437dd7cddfSDavid du Colombier quit(0); 2447dd7cddfSDavid du Colombier if(rcvrs == ok) 2457dd7cddfSDavid du Colombier exits(0); 2467dd7cddfSDavid du Colombier 2477dd7cddfSDavid du Colombier /* 2487dd7cddfSDavid du Colombier * here when some but not all rcvrs failed 2497dd7cddfSDavid du Colombier */ 2507dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n", thedate(), addr); 2517dd7cddfSDavid du Colombier for(i = 0; i < rcvrs; i++){ 2527dd7cddfSDavid du Colombier if(errs[i]){ 253a8453668SDavid du Colombier syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]); 2547dd7cddfSDavid du Colombier fprint(2, " mail to %s failed: %s", argv[i], errs[i]); 2557dd7cddfSDavid du Colombier } 2567dd7cddfSDavid du Colombier } 2577dd7cddfSDavid du Colombier exits(Giveup); 2587dd7cddfSDavid du Colombier 2597dd7cddfSDavid du Colombier /* 2607dd7cddfSDavid du Colombier * here when all rcvrs failed 2617dd7cddfSDavid du Colombier */ 2623e12c5d1SDavid du Colombier error: 263a8453668SDavid du Colombier removenewline(s_to_c(reply)); 264a8453668SDavid du Colombier syslog(0, "smtp.fail", "%s to %s failed: %s", 265a8453668SDavid du Colombier ping ? "ping" : "delivery", 266a8453668SDavid du Colombier addr, s_to_c(reply)); 2677dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply)); 2687dd7cddfSDavid du Colombier if(!filter) 2697dd7cddfSDavid du Colombier quit(rv); 2703e12c5d1SDavid du Colombier exits(rv); 2713e12c5d1SDavid du Colombier } 2723e12c5d1SDavid du Colombier 2733e12c5d1SDavid du Colombier /* 2743e12c5d1SDavid du Colombier * connect to the remote host 2753e12c5d1SDavid du Colombier */ 2767dd7cddfSDavid du Colombier static char * 2773e12c5d1SDavid du Colombier connect(char* net) 2783e12c5d1SDavid du Colombier { 2793e12c5d1SDavid du Colombier char buf[256]; 2803e12c5d1SDavid du Colombier int fd; 2813e12c5d1SDavid du Colombier 2827dd7cddfSDavid du Colombier fd = mxdial(net, ddomain, gdomain); 2833e12c5d1SDavid du Colombier 2843e12c5d1SDavid du Colombier if(fd < 0){ 2859a747e4fSDavid du Colombier rerrstr(buf, sizeof(buf)); 2863ff48bf5SDavid du Colombier Bprint(&berr, "smtp: %s (%s)\n", buf, net); 2873ff48bf5SDavid du Colombier syslog(0, "smtp.fail", "%s (%s)", buf, net); 2887dd7cddfSDavid du Colombier if(strstr(buf, "illegal") 2897dd7cddfSDavid du Colombier || strstr(buf, "unknown") 290271b8d73SDavid du Colombier || strstr(buf, "can't translate")) 2913e12c5d1SDavid du Colombier return Giveup; 2923e12c5d1SDavid du Colombier else 2933e12c5d1SDavid du Colombier return Retry; 2943e12c5d1SDavid du Colombier } 2953e12c5d1SDavid du Colombier Binit(&bin, fd, OREAD); 2963e12c5d1SDavid du Colombier fd = dup(fd, -1); 2973e12c5d1SDavid du Colombier Binit(&bout, fd, OWRITE); 2983e12c5d1SDavid du Colombier return 0; 2993e12c5d1SDavid du Colombier } 3003e12c5d1SDavid du Colombier 301fe096d1aSDavid du Colombier static char smtpthumbs[] = "/sys/lib/tls/smtp"; 302fe096d1aSDavid du Colombier static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude"; 303fe096d1aSDavid du Colombier 3043e12c5d1SDavid du Colombier /* 305fe096d1aSDavid du Colombier * exchange names with remote host, attempt to 306fe096d1aSDavid du Colombier * enable encryption and optionally authenticate. 307fe096d1aSDavid du Colombier * not fatal if we can't. 3083e12c5d1SDavid du Colombier */ 309d9306527SDavid du Colombier static char * 310d9306527SDavid du Colombier dotls(char *me) 3113e12c5d1SDavid du Colombier { 312d9306527SDavid du Colombier TLSconn *c; 313d9306527SDavid du Colombier Thumbprint *goodcerts; 314d9306527SDavid du Colombier char *h; 315d9306527SDavid du Colombier int fd; 316d9306527SDavid du Colombier uchar hash[SHA1dlen]; 317d9306527SDavid du Colombier 318d9306527SDavid du Colombier c = mallocz(sizeof(*c), 1); /* Note: not freed on success */ 319d9306527SDavid du Colombier if (c == nil) 320d9306527SDavid du Colombier return Giveup; 321fe096d1aSDavid du Colombier 322d9306527SDavid du Colombier dBprint("STARTTLS\r\n"); 323fe096d1aSDavid du Colombier if (getreply() != 2) 324fe096d1aSDavid du Colombier return Giveup; 325fe096d1aSDavid du Colombier 326d9306527SDavid du Colombier fd = tlsClient(Bfildes(&bout), c); 327fe096d1aSDavid du Colombier if (fd < 0) { 328fe096d1aSDavid du Colombier syslog(0, "smtp", "tlsClient to %q: %r", ddomain); 329d9306527SDavid du Colombier return Giveup; 330dddc47c2SDavid du Colombier } 331fe096d1aSDavid du Colombier goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs); 332d9306527SDavid du Colombier if (goodcerts == nil) { 333d9306527SDavid du Colombier free(c); 334fe096d1aSDavid du Colombier close(fd); 335fe096d1aSDavid du Colombier syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs); 336fe096d1aSDavid du Colombier return Giveup; /* how to recover? TLS is started */ 337d9306527SDavid du Colombier } 338fe096d1aSDavid du Colombier 339fe096d1aSDavid du Colombier /* compute sha1 hash of remote's certificate, see if we know it */ 340d9306527SDavid du Colombier sha1(c->cert, c->certlen, hash, nil); 341d9306527SDavid du Colombier if (!okThumbprint(hash, goodcerts)) { 342fe096d1aSDavid du Colombier /* TODO? if not excluded, add hash to thumb list */ 343fe096d1aSDavid du Colombier free(c); 344fe096d1aSDavid du Colombier close(fd); 345d9306527SDavid du Colombier h = malloc(2*sizeof hash + 1); 346d9306527SDavid du Colombier if (h != nil) { 347d9306527SDavid du Colombier enc16(h, 2*sizeof hash + 1, hash, sizeof hash); 348fe096d1aSDavid du Colombier // print("x509 sha1=%s", h); 349fe096d1aSDavid du Colombier syslog(0, "smtp", 350fe096d1aSDavid du Colombier "remote cert. has bad thumbprint: x509 sha1=%s server=%q", 351fe096d1aSDavid du Colombier h, ddomain); 352d9306527SDavid du Colombier free(h); 353d9306527SDavid du Colombier } 354fe096d1aSDavid du Colombier return Giveup; /* how to recover? TLS is started */ 355d9306527SDavid du Colombier } 356d9306527SDavid du Colombier freeThumbprints(goodcerts); 357d9306527SDavid du Colombier Bterm(&bin); 358d9306527SDavid du Colombier Bterm(&bout); 359fe096d1aSDavid du Colombier 360fe096d1aSDavid du Colombier /* 361fe096d1aSDavid du Colombier * set up bin & bout to use the TLS fd, i/o upon which generates 362fe096d1aSDavid du Colombier * i/o on the original, underlying fd. 363fe096d1aSDavid du Colombier */ 364d9306527SDavid du Colombier Binit(&bin, fd, OREAD); 365d9306527SDavid du Colombier fd = dup(fd, -1); 366d9306527SDavid du Colombier Binit(&bout, fd, OWRITE); 367fe096d1aSDavid du Colombier 368fe096d1aSDavid du Colombier syslog(0, "smtp", "started TLS to %q", ddomain); 369d9306527SDavid du Colombier return(hello(me, 1)); 370d9306527SDavid du Colombier } 371d9306527SDavid du Colombier 372d9306527SDavid du Colombier static char * 3733cf081f0SDavid du Colombier doauth(char *methods) 374d9306527SDavid du Colombier { 3759863c128SDavid du Colombier char *buf, *base64; 376d9306527SDavid du Colombier int n; 3779863c128SDavid du Colombier DS ds; 3789863c128SDavid du Colombier UserPasswd *p; 379d9306527SDavid du Colombier 3809863c128SDavid du Colombier dial_string_parse(ddomain, &ds); 3813cf081f0SDavid du Colombier 382d9306527SDavid du Colombier if(user != nil) 383d9306527SDavid du Colombier p = auth_getuserpasswd(nil, 3849863c128SDavid du Colombier "proto=pass service=smtp server=%q user=%q", ds.host, user); 385d9306527SDavid du Colombier else 386d9306527SDavid du Colombier p = auth_getuserpasswd(nil, 3879863c128SDavid du Colombier "proto=pass service=smtp server=%q", ds.host); 388d9306527SDavid du Colombier if (p == nil) 389d9306527SDavid du Colombier return Giveup; 3903cf081f0SDavid du Colombier 3913cf081f0SDavid du Colombier if (strstr(methods, "LOGIN")){ 3923cf081f0SDavid du Colombier dBprint("AUTH LOGIN\r\n"); 3933cf081f0SDavid du Colombier if (getreply() != 3) 3943cf081f0SDavid du Colombier return Retry; 3953cf081f0SDavid du Colombier 3963cf081f0SDavid du Colombier n = strlen(p->user); 3973cf081f0SDavid du Colombier base64 = malloc(2*n); 3983cf081f0SDavid du Colombier if (base64 == nil) 3993cf081f0SDavid du Colombier return Retry; /* Out of memory */ 4003cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->user, n); 4013cf081f0SDavid du Colombier dBprint("%s\r\n", base64); 4023cf081f0SDavid du Colombier if (getreply() != 3) 4033cf081f0SDavid du Colombier return Retry; 4043cf081f0SDavid du Colombier 4053cf081f0SDavid du Colombier n = strlen(p->passwd); 4063cf081f0SDavid du Colombier base64 = malloc(2*n); 4073cf081f0SDavid du Colombier if (base64 == nil) 4083cf081f0SDavid du Colombier return Retry; /* Out of memory */ 4093cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->passwd, n); 4103cf081f0SDavid du Colombier dBprint("%s\r\n", base64); 4113cf081f0SDavid du Colombier if (getreply() != 2) 4123cf081f0SDavid du Colombier return Retry; 4133cf081f0SDavid du Colombier 4143cf081f0SDavid du Colombier free(base64); 4153cf081f0SDavid du Colombier } 4163cf081f0SDavid du Colombier else 4173cf081f0SDavid du Colombier if (strstr(methods, "PLAIN")){ 418d9306527SDavid du Colombier n = strlen(p->user) + strlen(p->passwd) + 3; 419d9306527SDavid du Colombier buf = malloc(n); 420d9306527SDavid du Colombier base64 = malloc(2 * n); 421fe096d1aSDavid du Colombier if (buf == nil || base64 == nil) { 422d9306527SDavid du Colombier free(buf); 423d9306527SDavid du Colombier return Retry; /* Out of memory */ 424d9306527SDavid du Colombier } 425d9306527SDavid du Colombier snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd); 426d9306527SDavid du Colombier enc64(base64, 2 * n, (uchar *)buf, n - 1); 427d9306527SDavid du Colombier free(buf); 428d9306527SDavid du Colombier dBprint("AUTH PLAIN %s\r\n", base64); 429d9306527SDavid du Colombier free(base64); 430d9306527SDavid du Colombier if (getreply() != 2) 431d9306527SDavid du Colombier return Retry; 4323cf081f0SDavid du Colombier } 4333cf081f0SDavid du Colombier else 4343cf081f0SDavid du Colombier return "No supported AUTH method"; 435d9306527SDavid du Colombier return(0); 436d9306527SDavid du Colombier } 437d9306527SDavid du Colombier 438d9306527SDavid du Colombier char * 439d9306527SDavid du Colombier hello(char *me, int encrypted) 440d9306527SDavid du Colombier { 441d9306527SDavid du Colombier int ehlo; 442d9306527SDavid du Colombier String *r; 4433cf081f0SDavid du Colombier char *ret, *s, *t; 444d9306527SDavid du Colombier 445d9306527SDavid du Colombier if (!encrypted) 4463e12c5d1SDavid du Colombier switch(getreply()){ 4473e12c5d1SDavid du Colombier case 2: 4483e12c5d1SDavid du Colombier break; 4493e12c5d1SDavid du Colombier case 5: 4503e12c5d1SDavid du Colombier return Giveup; 4513e12c5d1SDavid du Colombier default: 4523e12c5d1SDavid du Colombier return Retry; 4533e12c5d1SDavid du Colombier } 454d9306527SDavid du Colombier 455d9306527SDavid du Colombier ehlo = 1; 456d9306527SDavid du Colombier Again: 457d9306527SDavid du Colombier if(ehlo) 458d9306527SDavid du Colombier dBprint("EHLO %s\r\n", me); 459d9306527SDavid du Colombier else 4603e12c5d1SDavid du Colombier dBprint("HELO %s\r\n", me); 4613e12c5d1SDavid du Colombier switch (getreply()) { 4623e12c5d1SDavid du Colombier case 2: 4633e12c5d1SDavid du Colombier break; 4643e12c5d1SDavid du Colombier case 5: 465d9306527SDavid du Colombier if(ehlo){ 466d9306527SDavid du Colombier ehlo = 0; 467d9306527SDavid du Colombier goto Again; 468d9306527SDavid du Colombier } 4693e12c5d1SDavid du Colombier return Giveup; 4703e12c5d1SDavid du Colombier default: 4713e12c5d1SDavid du Colombier return Retry; 4723e12c5d1SDavid du Colombier } 473d9306527SDavid du Colombier r = s_clone(reply); 474d9306527SDavid du Colombier if(r == nil) 475d9306527SDavid du Colombier return Retry; /* Out of memory or couldn't get string */ 476d9306527SDavid du Colombier 477d9306527SDavid du Colombier /* Invariant: every line has a newline, a result of getcrlf() */ 478d9306527SDavid du Colombier for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){ 479d9306527SDavid du Colombier *t = '\0'; 480d9306527SDavid du Colombier for (t = s; *t != '\0'; t++) 481d9306527SDavid du Colombier *t = toupper(*t); 482d9306527SDavid du Colombier if(!encrypted && trysecure && 483d9306527SDavid du Colombier (strcmp(s, "250-STARTTLS") == 0 || 484d9306527SDavid du Colombier strcmp(s, "250 STARTTLS") == 0)){ 485d9306527SDavid du Colombier s_free(r); 486d9306527SDavid du Colombier return(dotls(me)); 487d9306527SDavid du Colombier } 488a8453668SDavid du Colombier if(tryauth && (encrypted || insecure) && 489d9306527SDavid du Colombier (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 || 4903cf081f0SDavid du Colombier strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){ 4913cf081f0SDavid du Colombier ret = doauth(s + strlen("250 AUTH ")); 492d9306527SDavid du Colombier s_free(r); 4933cf081f0SDavid du Colombier return ret; 494d9306527SDavid du Colombier } 495d9306527SDavid du Colombier } 496d9306527SDavid du Colombier s_free(r); 4973e12c5d1SDavid du Colombier return 0; 4983e12c5d1SDavid du Colombier } 4993e12c5d1SDavid du Colombier 5003e12c5d1SDavid du Colombier /* 5013e12c5d1SDavid du Colombier * report sender to remote 5023e12c5d1SDavid du Colombier */ 5033e12c5d1SDavid du Colombier char * 5043e12c5d1SDavid du Colombier mailfrom(char *from) 5053e12c5d1SDavid du Colombier { 5067dd7cddfSDavid du Colombier if(!returnable(from)) 5077dd7cddfSDavid du Colombier dBprint("MAIL FROM:<>\r\n"); 5087dd7cddfSDavid du Colombier else 509219b2ee8SDavid du Colombier if(strchr(from, '@')) 5103e12c5d1SDavid du Colombier dBprint("MAIL FROM:<%s>\r\n", from); 511219b2ee8SDavid du Colombier else 512219b2ee8SDavid du Colombier dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain); 5133e12c5d1SDavid du Colombier switch(getreply()){ 5143e12c5d1SDavid du Colombier case 2: 5153e12c5d1SDavid du Colombier break; 5163e12c5d1SDavid du Colombier case 5: 5173e12c5d1SDavid du Colombier return Giveup; 5183e12c5d1SDavid du Colombier default: 5193e12c5d1SDavid du Colombier return Retry; 5203e12c5d1SDavid du Colombier } 5213e12c5d1SDavid du Colombier return 0; 5223e12c5d1SDavid du Colombier } 5233e12c5d1SDavid du Colombier 5243e12c5d1SDavid du Colombier /* 5253e12c5d1SDavid du Colombier * report a recipient to remote 5263e12c5d1SDavid du Colombier */ 5273e12c5d1SDavid du Colombier char * 5283e12c5d1SDavid du Colombier rcptto(char *to) 5293e12c5d1SDavid du Colombier { 530219b2ee8SDavid du Colombier String *s; 531219b2ee8SDavid du Colombier 5327dd7cddfSDavid du Colombier s = unescapespecial(bangtoat(to)); 5337dd7cddfSDavid du Colombier if(toline == 0) 534219b2ee8SDavid du Colombier toline = s_new(); 5357dd7cddfSDavid du Colombier else 536219b2ee8SDavid du Colombier s_append(toline, ", "); 537219b2ee8SDavid du Colombier s_append(toline, s_to_c(s)); 538219b2ee8SDavid du Colombier if(strchr(s_to_c(s), '@')) 539219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s>\r\n", s_to_c(s)); 540219b2ee8SDavid du Colombier else { 541219b2ee8SDavid du Colombier s_append(toline, "@"); 542219b2ee8SDavid du Colombier s_append(toline, ddomain); 543219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain); 544219b2ee8SDavid du Colombier } 545a8453668SDavid du Colombier alarm(10*alarmscale); 5463e12c5d1SDavid du Colombier switch(getreply()){ 5473e12c5d1SDavid du Colombier case 2: 5483e12c5d1SDavid du Colombier break; 5493e12c5d1SDavid du Colombier case 5: 5503e12c5d1SDavid du Colombier return Giveup; 5513e12c5d1SDavid du Colombier default: 5523e12c5d1SDavid du Colombier return Retry; 5533e12c5d1SDavid du Colombier } 5543e12c5d1SDavid du Colombier return 0; 5553e12c5d1SDavid du Colombier } 5563e12c5d1SDavid du Colombier 5579a747e4fSDavid du Colombier static char hex[] = "0123456789abcdef"; 5589a747e4fSDavid du Colombier 5593e12c5d1SDavid du Colombier /* 5603e12c5d1SDavid du Colombier * send the damn thing 5613e12c5d1SDavid du Colombier */ 5623e12c5d1SDavid du Colombier char * 5637dd7cddfSDavid du Colombier data(String *from, Biobuf *b) 5643e12c5d1SDavid du Colombier { 5657dd7cddfSDavid du Colombier char *buf, *cp; 5669a747e4fSDavid du Colombier int i, n, nbytes, bufsize, eof, r; 5677dd7cddfSDavid du Colombier String *fromline; 5689a747e4fSDavid du Colombier char errmsg[Errlen]; 5699a747e4fSDavid du Colombier char id[40]; 5703e12c5d1SDavid du Colombier 5713e12c5d1SDavid du Colombier /* 5727dd7cddfSDavid du Colombier * input the header. 5733e12c5d1SDavid du Colombier */ 5747dd7cddfSDavid du Colombier 5757dd7cddfSDavid du Colombier buf = malloc(1); 5767dd7cddfSDavid du Colombier if(buf == 0){ 5777dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory"); 5787dd7cddfSDavid du Colombier return Retry; 5797dd7cddfSDavid du Colombier } 5807dd7cddfSDavid du Colombier n = 0; 5813e12c5d1SDavid du Colombier eof = 0; 5827dd7cddfSDavid du Colombier for(;;){ 5837dd7cddfSDavid du Colombier cp = Brdline(b, '\n'); 5847dd7cddfSDavid du Colombier if(cp == nil){ 5853e12c5d1SDavid du Colombier eof = 1; 5863e12c5d1SDavid du Colombier break; 5873e12c5d1SDavid du Colombier } 5887dd7cddfSDavid du Colombier nbytes = Blinelen(b); 5897dd7cddfSDavid du Colombier buf = realloc(buf, n+nbytes+1); 5907dd7cddfSDavid du Colombier if(buf == 0){ 5917dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory"); 5927dd7cddfSDavid du Colombier return Retry; 5937dd7cddfSDavid du Colombier } 5947dd7cddfSDavid du Colombier strncpy(buf+n, cp, nbytes); 5957dd7cddfSDavid du Colombier n += nbytes; 5967dd7cddfSDavid du Colombier if(nbytes == 1) /* end of header */ 5977dd7cddfSDavid du Colombier break; 5983e12c5d1SDavid du Colombier } 5993e12c5d1SDavid du Colombier buf[n] = 0; 6007dd7cddfSDavid du Colombier bufsize = n; 6013e12c5d1SDavid du Colombier 6023e12c5d1SDavid du Colombier /* 6033e12c5d1SDavid du Colombier * parse the header, turn all addresses into @ format 6043e12c5d1SDavid du Colombier */ 6059a747e4fSDavid du Colombier yyinit(buf, n); 6063e12c5d1SDavid du Colombier yyparse(); 6073e12c5d1SDavid du Colombier 6083e12c5d1SDavid du Colombier /* 6093e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n 6103e12c5d1SDavid du Colombier */ 611a8453668SDavid du Colombier alarm(20*alarmscale); 6123e12c5d1SDavid du Colombier if(!filter){ 6133e12c5d1SDavid du Colombier dBprint("DATA\r\n"); 6143e12c5d1SDavid du Colombier switch(getreply()){ 6153e12c5d1SDavid du Colombier case 3: 6163e12c5d1SDavid du Colombier break; 6173e12c5d1SDavid du Colombier case 5: 6187dd7cddfSDavid du Colombier free(buf); 6193e12c5d1SDavid du Colombier return Giveup; 6203e12c5d1SDavid du Colombier default: 6217dd7cddfSDavid du Colombier free(buf); 6223e12c5d1SDavid du Colombier return Retry; 6233e12c5d1SDavid du Colombier } 6243e12c5d1SDavid du Colombier } 6253e12c5d1SDavid du Colombier /* 6269a747e4fSDavid du Colombier * send header. add a message-id, a sender, and a date if there 6273e12c5d1SDavid du Colombier * isn't one 6283e12c5d1SDavid du Colombier */ 6297dd7cddfSDavid du Colombier nbytes = 0; 6307dd7cddfSDavid du Colombier fromline = convertheader(from); 631bd389b36SDavid du Colombier uneaten = buf; 6329a747e4fSDavid du Colombier 633d9306527SDavid du Colombier srand(truerand()); 6349a747e4fSDavid du Colombier if(messageid == 0){ 6359a747e4fSDavid du Colombier for(i=0; i<16; i++){ 636d9306527SDavid du Colombier r = rand()&0xFF; 6379a747e4fSDavid du Colombier id[2*i] = hex[r&0xF]; 6389a747e4fSDavid du Colombier id[2*i+1] = hex[(r>>4)&0xF]; 6399a747e4fSDavid du Colombier } 6409a747e4fSDavid du Colombier id[2*i] = '\0'; 6419a747e4fSDavid du Colombier nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain); 6429a747e4fSDavid du Colombier if(debug) 6439a747e4fSDavid du Colombier Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain); 6449a747e4fSDavid du Colombier } 6459a747e4fSDavid du Colombier 6467dd7cddfSDavid du Colombier if(originator==0){ 6477dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline)); 6487dd7cddfSDavid du Colombier if(debug) 6497dd7cddfSDavid du Colombier Bprint(&berr, "From: %s\r\n", s_to_c(fromline)); 6507dd7cddfSDavid du Colombier } 6517dd7cddfSDavid du Colombier s_free(fromline); 6527dd7cddfSDavid du Colombier 653219b2ee8SDavid du Colombier if(destination == 0 && toline) 6547dd7cddfSDavid du Colombier if(*s_to_c(toline) == '@'){ /* route addr */ 6557dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline)); 6567dd7cddfSDavid du Colombier if(debug) 6577dd7cddfSDavid du Colombier Bprint(&berr, "To: <%s>\r\n", s_to_c(toline)); 6587dd7cddfSDavid du Colombier } else { 6597dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline)); 6607dd7cddfSDavid du Colombier if(debug) 6617dd7cddfSDavid du Colombier Bprint(&berr, "To: %s\r\n", s_to_c(toline)); 6627dd7cddfSDavid du Colombier } 6637dd7cddfSDavid du Colombier 6643e12c5d1SDavid du Colombier if(date==0 && udate) 6657dd7cddfSDavid du Colombier nbytes += printdate(udate); 666bd389b36SDavid du Colombier if (usys) 667bd389b36SDavid du Colombier uneaten = usys->end + 1; 6687dd7cddfSDavid du Colombier nbytes += printheader(); 669bd389b36SDavid du Colombier if (*uneaten != '\n') 670bd389b36SDavid du Colombier putcrnl("\n", 1); 6713e12c5d1SDavid du Colombier 6723e12c5d1SDavid du Colombier /* 6733e12c5d1SDavid du Colombier * send body 6743e12c5d1SDavid du Colombier */ 6757dd7cddfSDavid du Colombier 676bd389b36SDavid du Colombier putcrnl(uneaten, buf+n - uneaten); 6777dd7cddfSDavid du Colombier nbytes += buf+n - uneaten; 6787dd7cddfSDavid du Colombier if(eof == 0){ 679219b2ee8SDavid du Colombier for(;;){ 6807dd7cddfSDavid du Colombier n = Bread(b, buf, bufsize); 681219b2ee8SDavid du Colombier if(n < 0){ 6829a747e4fSDavid du Colombier rerrstr(errmsg, sizeof(errmsg)); 6837dd7cddfSDavid du Colombier s_append(s_restart(reply), errmsg); 6847dd7cddfSDavid du Colombier free(buf); 6857dd7cddfSDavid du Colombier return Retry; 686219b2ee8SDavid du Colombier } 687219b2ee8SDavid du Colombier if(n == 0) 688219b2ee8SDavid du Colombier break; 689a8453668SDavid du Colombier alarm(10*alarmscale); 6903e12c5d1SDavid du Colombier putcrnl(buf, n); 6917dd7cddfSDavid du Colombier nbytes += n; 692219b2ee8SDavid du Colombier } 6937dd7cddfSDavid du Colombier } 6947dd7cddfSDavid du Colombier free(buf); 6953e12c5d1SDavid du Colombier if(!filter){ 6963e12c5d1SDavid du Colombier if(last != '\n') 6973e12c5d1SDavid du Colombier dBprint("\r\n.\r\n"); 6983e12c5d1SDavid du Colombier else 6993e12c5d1SDavid du Colombier dBprint(".\r\n"); 700a8453668SDavid du Colombier alarm(10*alarmscale); 7013e12c5d1SDavid du Colombier switch(getreply()){ 7023e12c5d1SDavid du Colombier case 2: 7033e12c5d1SDavid du Colombier break; 7043e12c5d1SDavid du Colombier case 5: 7053e12c5d1SDavid du Colombier return Giveup; 7067dd7cddfSDavid du Colombier default: 7077dd7cddfSDavid du Colombier return Retry; 7083e12c5d1SDavid du Colombier } 7097dd7cddfSDavid du Colombier syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from), 7107dd7cddfSDavid du Colombier nbytes, s_to_c(toline));/**/ 7113e12c5d1SDavid du Colombier } 7123e12c5d1SDavid du Colombier return 0; 7133e12c5d1SDavid du Colombier } 7143e12c5d1SDavid du Colombier 7153e12c5d1SDavid du Colombier /* 7163e12c5d1SDavid du Colombier * we're leaving 7173e12c5d1SDavid du Colombier */ 7183e12c5d1SDavid du Colombier void 7197dd7cddfSDavid du Colombier quit(char *rv) 7203e12c5d1SDavid du Colombier { 7217dd7cddfSDavid du Colombier /* 60 minutes to quit */ 7227dd7cddfSDavid du Colombier quitting = 1; 7237dd7cddfSDavid du Colombier quitrv = rv; 724a8453668SDavid du Colombier alarm(60*alarmscale); 7253e12c5d1SDavid du Colombier dBprint("QUIT\r\n"); 7263e12c5d1SDavid du Colombier getreply(); 7277dd7cddfSDavid du Colombier Bterm(&bout); 7287dd7cddfSDavid du Colombier Bterm(&bfile); 7293e12c5d1SDavid du Colombier } 7303e12c5d1SDavid du Colombier 7313e12c5d1SDavid du Colombier /* 7323e12c5d1SDavid du Colombier * read a reply into a string, return the reply code 7333e12c5d1SDavid du Colombier */ 7343e12c5d1SDavid du Colombier int 7353e12c5d1SDavid du Colombier getreply(void) 7363e12c5d1SDavid du Colombier { 7373e12c5d1SDavid du Colombier char *line; 7383e12c5d1SDavid du Colombier int rv; 7393e12c5d1SDavid du Colombier 7403e12c5d1SDavid du Colombier reply = s_reset(reply); 7413e12c5d1SDavid du Colombier for(;;){ 7423e12c5d1SDavid du Colombier line = getcrnl(reply); 7433e12c5d1SDavid du Colombier if(line == 0) 7443e12c5d1SDavid du Colombier return -1; 7453e12c5d1SDavid du Colombier if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2])) 7463e12c5d1SDavid du Colombier return -1; 7473e12c5d1SDavid du Colombier if(line[3] != '-') 7483e12c5d1SDavid du Colombier break; 7493e12c5d1SDavid du Colombier } 7507dd7cddfSDavid du Colombier if(debug) 7517dd7cddfSDavid du Colombier Bflush(&berr); 7523e12c5d1SDavid du Colombier rv = atoi(line)/100; 7533e12c5d1SDavid du Colombier return rv; 7543e12c5d1SDavid du Colombier } 7557dd7cddfSDavid du Colombier void 7567dd7cddfSDavid du Colombier addhostdom(String *buf, char *host) 7577dd7cddfSDavid du Colombier { 7587dd7cddfSDavid du Colombier s_append(buf, "@"); 7597dd7cddfSDavid du Colombier s_append(buf, host); 7607dd7cddfSDavid du Colombier } 7613e12c5d1SDavid du Colombier 7623e12c5d1SDavid du Colombier /* 7633e12c5d1SDavid du Colombier * Convert from `bang' to `source routing' format. 7643e12c5d1SDavid du Colombier * 7653e12c5d1SDavid du Colombier * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o 7663e12c5d1SDavid du Colombier */ 7673e12c5d1SDavid du Colombier String * 768219b2ee8SDavid du Colombier bangtoat(char *addr) 7693e12c5d1SDavid du Colombier { 7703e12c5d1SDavid du Colombier String *buf; 7713e12c5d1SDavid du Colombier register int i; 7723e12c5d1SDavid du Colombier int j, d; 7733e12c5d1SDavid du Colombier char *field[128]; 7743e12c5d1SDavid du Colombier 7753e12c5d1SDavid du Colombier /* parse the '!' format address */ 7763e12c5d1SDavid du Colombier buf = s_new(); 7773e12c5d1SDavid du Colombier for(i = 0; addr; i++){ 7783e12c5d1SDavid du Colombier field[i] = addr; 7793e12c5d1SDavid du Colombier addr = strchr(addr, '!'); 7803e12c5d1SDavid du Colombier if(addr) 7813e12c5d1SDavid du Colombier *addr++ = 0; 7823e12c5d1SDavid du Colombier } 7833e12c5d1SDavid du Colombier if (i==1) { 7843e12c5d1SDavid du Colombier s_append(buf, field[0]); 7853e12c5d1SDavid du Colombier return buf; 7863e12c5d1SDavid du Colombier } 7873e12c5d1SDavid du Colombier 7883e12c5d1SDavid du Colombier /* 7893e12c5d1SDavid du Colombier * count leading domain fields (non-domains don't count) 7903e12c5d1SDavid du Colombier */ 7917dd7cddfSDavid du Colombier for(d = 0; d<i-1; d++) 7923e12c5d1SDavid du Colombier if(strchr(field[d], '.')==0) 7933e12c5d1SDavid du Colombier break; 7943e12c5d1SDavid du Colombier /* 7953e12c5d1SDavid du Colombier * if there are more than 1 leading domain elements, 7963e12c5d1SDavid du Colombier * put them in as source routing 7973e12c5d1SDavid du Colombier */ 7983e12c5d1SDavid du Colombier if(d > 1){ 799219b2ee8SDavid du Colombier addhostdom(buf, field[0]); 8003e12c5d1SDavid du Colombier for(j=1; j<d-1; j++){ 8013e12c5d1SDavid du Colombier s_append(buf, ","); 8023e12c5d1SDavid du Colombier s_append(buf, "@"); 8033e12c5d1SDavid du Colombier s_append(buf, field[j]); 8043e12c5d1SDavid du Colombier } 8053e12c5d1SDavid du Colombier s_append(buf, ":"); 8063e12c5d1SDavid du Colombier } 8073e12c5d1SDavid du Colombier 8083e12c5d1SDavid du Colombier /* 8093e12c5d1SDavid du Colombier * throw in the non-domain elements separated by '!'s 8103e12c5d1SDavid du Colombier */ 8113e12c5d1SDavid du Colombier s_append(buf, field[d]); 8123e12c5d1SDavid du Colombier for(j=d+1; j<=i-1; j++) { 8133e12c5d1SDavid du Colombier s_append(buf, "!"); 8143e12c5d1SDavid du Colombier s_append(buf, field[j]); 8153e12c5d1SDavid du Colombier } 8163e12c5d1SDavid du Colombier if(d) 817219b2ee8SDavid du Colombier addhostdom(buf, field[d-1]); 8183e12c5d1SDavid du Colombier return buf; 8193e12c5d1SDavid du Colombier } 8203e12c5d1SDavid du Colombier 8213e12c5d1SDavid du Colombier /* 8223e12c5d1SDavid du Colombier * convert header addresses to @ format. 8233e12c5d1SDavid du Colombier * if the address is a source address, and a domain is specified, 8243e12c5d1SDavid du Colombier * make sure it falls in the domain. 8253e12c5d1SDavid du Colombier */ 8267dd7cddfSDavid du Colombier String* 827219b2ee8SDavid du Colombier convertheader(String *from) 8283e12c5d1SDavid du Colombier { 8293e12c5d1SDavid du Colombier Field *f; 8307dd7cddfSDavid du Colombier Node *p, *lastp; 8313e12c5d1SDavid du Colombier String *a; 8323e12c5d1SDavid du Colombier 8337dd7cddfSDavid du Colombier if(!returnable(s_to_c(from))){ 8347dd7cddfSDavid du Colombier from = s_new(); 8357dd7cddfSDavid du Colombier s_append(from, "Postmaster"); 8367dd7cddfSDavid du Colombier addhostdom(from, hostdomain); 8377dd7cddfSDavid du Colombier } else 8387dd7cddfSDavid du Colombier if(strchr(s_to_c(from), '@') == 0){ 8397dd7cddfSDavid du Colombier a = username(from); 8407dd7cddfSDavid du Colombier if(a) { 8417dd7cddfSDavid du Colombier s_append(a, " <"); 8427dd7cddfSDavid du Colombier s_append(a, s_to_c(from)); 8437dd7cddfSDavid du Colombier addhostdom(a, hostdomain); 8447dd7cddfSDavid du Colombier s_append(a, ">"); 8457dd7cddfSDavid du Colombier from = a; 8467dd7cddfSDavid du Colombier } else { 8477dd7cddfSDavid du Colombier from = s_copy(s_to_c(from)); 8487dd7cddfSDavid du Colombier addhostdom(from, hostdomain); 849219b2ee8SDavid du Colombier } 8507dd7cddfSDavid du Colombier } else 8517dd7cddfSDavid du Colombier from = s_copy(s_to_c(from)); 8523e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){ 8537dd7cddfSDavid du Colombier lastp = 0; 8547dd7cddfSDavid du Colombier for(p = f->node; p; lastp = p, p = p->next){ 8553e12c5d1SDavid du Colombier if(!p->addr) 8563e12c5d1SDavid du Colombier continue; 857219b2ee8SDavid du Colombier a = bangtoat(s_to_c(p->s)); 8583e12c5d1SDavid du Colombier s_free(p->s); 8597dd7cddfSDavid du Colombier if(strchr(s_to_c(a), '@') == 0) 8607dd7cddfSDavid du Colombier addhostdom(a, hostdomain); 8617dd7cddfSDavid du Colombier else if(*s_to_c(a) == '@') 8627dd7cddfSDavid du Colombier a = fixrouteaddr(a, p->next, lastp); 8633e12c5d1SDavid du Colombier p->s = a; 8643e12c5d1SDavid du Colombier } 8653e12c5d1SDavid du Colombier } 8667dd7cddfSDavid du Colombier return from; 8677dd7cddfSDavid du Colombier } 8687dd7cddfSDavid du Colombier /* 8697dd7cddfSDavid du Colombier * ensure route addr has brackets around it 8707dd7cddfSDavid du Colombier */ 8717dd7cddfSDavid du Colombier String* 8727dd7cddfSDavid du Colombier fixrouteaddr(String *raddr, Node *next, Node *last) 8737dd7cddfSDavid du Colombier { 8747dd7cddfSDavid du Colombier String *a; 8757dd7cddfSDavid du Colombier 8767dd7cddfSDavid du Colombier if(last && last->c == '<' && next && next->c == '>') 8777dd7cddfSDavid du Colombier return raddr; /* properly formed already */ 8787dd7cddfSDavid du Colombier 8797dd7cddfSDavid du Colombier a = s_new(); 8807dd7cddfSDavid du Colombier s_append(a, "<"); 8817dd7cddfSDavid du Colombier s_append(a, s_to_c(raddr)); 8827dd7cddfSDavid du Colombier s_append(a, ">"); 8837dd7cddfSDavid du Colombier s_free(raddr); 8847dd7cddfSDavid du Colombier return a; 8853e12c5d1SDavid du Colombier } 8863e12c5d1SDavid du Colombier 8873e12c5d1SDavid du Colombier /* 8883e12c5d1SDavid du Colombier * print out the parsed header 8893e12c5d1SDavid du Colombier */ 8907dd7cddfSDavid du Colombier int 8913e12c5d1SDavid du Colombier printheader(void) 8923e12c5d1SDavid du Colombier { 8937dd7cddfSDavid du Colombier int n, len; 8943e12c5d1SDavid du Colombier Field *f; 8953e12c5d1SDavid du Colombier Node *p; 8963e12c5d1SDavid du Colombier char *cp; 8973e12c5d1SDavid du Colombier char c[1]; 8983e12c5d1SDavid du Colombier 8997dd7cddfSDavid du Colombier n = 0; 9003e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){ 9013e12c5d1SDavid du Colombier for(p = f->node; p; p = p->next){ 9023e12c5d1SDavid du Colombier if(p->s) 9037dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s)); 9043e12c5d1SDavid du Colombier else { 9053e12c5d1SDavid du Colombier c[0] = p->c; 9063e12c5d1SDavid du Colombier putcrnl(c, 1); 9077dd7cddfSDavid du Colombier n++; 9083e12c5d1SDavid du Colombier } 9093e12c5d1SDavid du Colombier if(p->white){ 9103e12c5d1SDavid du Colombier cp = s_to_c(p->white); 9117dd7cddfSDavid du Colombier len = strlen(cp); 9127dd7cddfSDavid du Colombier putcrnl(cp, len); 9137dd7cddfSDavid du Colombier n += len; 9143e12c5d1SDavid du Colombier } 915bd389b36SDavid du Colombier uneaten = p->end; 9163e12c5d1SDavid du Colombier } 9173e12c5d1SDavid du Colombier putcrnl("\n", 1); 9187dd7cddfSDavid du Colombier n++; 919bd389b36SDavid du Colombier uneaten++; /* skip newline */ 9203e12c5d1SDavid du Colombier } 9217dd7cddfSDavid du Colombier return n; 9223e12c5d1SDavid du Colombier } 9233e12c5d1SDavid du Colombier 9243e12c5d1SDavid du Colombier /* 9253e12c5d1SDavid du Colombier * add a domain onto an name, return the new name 9263e12c5d1SDavid du Colombier */ 9273e12c5d1SDavid du Colombier char * 9283e12c5d1SDavid du Colombier domainify(char *name, char *domain) 9293e12c5d1SDavid du Colombier { 9303e12c5d1SDavid du Colombier static String *s; 9317dd7cddfSDavid du Colombier char *p; 9323e12c5d1SDavid du Colombier 9333e12c5d1SDavid du Colombier if(domain==0 || strchr(name, '.')!=0) 9343e12c5d1SDavid du Colombier return name; 9353e12c5d1SDavid du Colombier 9363e12c5d1SDavid du Colombier s = s_reset(s); 9373e12c5d1SDavid du Colombier s_append(s, name); 9387dd7cddfSDavid du Colombier p = strchr(domain, '.'); 9397dd7cddfSDavid du Colombier if(p == 0){ 940219b2ee8SDavid du Colombier s_append(s, "."); 9417dd7cddfSDavid du Colombier p = domain; 9427dd7cddfSDavid du Colombier } 9437dd7cddfSDavid du Colombier s_append(s, p); 9443e12c5d1SDavid du Colombier return s_to_c(s); 9453e12c5d1SDavid du Colombier } 9463e12c5d1SDavid du Colombier 9473e12c5d1SDavid du Colombier /* 9483e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n 9493e12c5d1SDavid du Colombier */ 9503e12c5d1SDavid du Colombier void 9513e12c5d1SDavid du Colombier putcrnl(char *cp, int n) 9523e12c5d1SDavid du Colombier { 9533e12c5d1SDavid du Colombier int c; 9543e12c5d1SDavid du Colombier 9553e12c5d1SDavid du Colombier for(; n; n--, cp++){ 9563e12c5d1SDavid du Colombier c = *cp; 9573e12c5d1SDavid du Colombier if(c == '\n') 9587dd7cddfSDavid du Colombier dBputc('\r'); 9593e12c5d1SDavid du Colombier else if(c == '.' && last=='\n') 9607dd7cddfSDavid du Colombier dBputc('.'); 9617dd7cddfSDavid du Colombier dBputc(c); 9623e12c5d1SDavid du Colombier last = c; 9633e12c5d1SDavid du Colombier } 9643e12c5d1SDavid du Colombier } 9653e12c5d1SDavid du Colombier 9663e12c5d1SDavid du Colombier /* 9673e12c5d1SDavid du Colombier * Get a line including a crnl into a string. Convert crnl into nl. 9683e12c5d1SDavid du Colombier */ 9693e12c5d1SDavid du Colombier char * 9703e12c5d1SDavid du Colombier getcrnl(String *s) 9713e12c5d1SDavid du Colombier { 9723e12c5d1SDavid du Colombier int c; 9733e12c5d1SDavid du Colombier int count; 9743e12c5d1SDavid du Colombier 9753e12c5d1SDavid du Colombier count = 0; 9763e12c5d1SDavid du Colombier for(;;){ 9773e12c5d1SDavid du Colombier c = Bgetc(&bin); 9783e12c5d1SDavid du Colombier if(debug) 9793e12c5d1SDavid du Colombier Bputc(&berr, c); 9803e12c5d1SDavid du Colombier switch(c){ 9813e12c5d1SDavid du Colombier case -1: 9827dd7cddfSDavid du Colombier s_append(s, "connection closed unexpectedly by remote system"); 9833e12c5d1SDavid du Colombier s_terminate(s); 9843e12c5d1SDavid du Colombier return 0; 9853e12c5d1SDavid du Colombier case '\r': 9863e12c5d1SDavid du Colombier c = Bgetc(&bin); 9873e12c5d1SDavid du Colombier if(c == '\n'){ 9883e12c5d1SDavid du Colombier s_putc(s, c); 9893e12c5d1SDavid du Colombier if(debug) 9903e12c5d1SDavid du Colombier Bputc(&berr, c); 9913e12c5d1SDavid du Colombier count++; 9923e12c5d1SDavid du Colombier s_terminate(s); 9933e12c5d1SDavid du Colombier return s->ptr - count; 9943e12c5d1SDavid du Colombier } 9953e12c5d1SDavid du Colombier Bungetc(&bin); 9963e12c5d1SDavid du Colombier s_putc(s, '\r'); 9973e12c5d1SDavid du Colombier if(debug) 9983e12c5d1SDavid du Colombier Bputc(&berr, '\r'); 9993e12c5d1SDavid du Colombier count++; 10003e12c5d1SDavid du Colombier break; 10013e12c5d1SDavid du Colombier default: 10023e12c5d1SDavid du Colombier s_putc(s, c); 10033e12c5d1SDavid du Colombier count++; 10043e12c5d1SDavid du Colombier break; 10053e12c5d1SDavid du Colombier } 10063e12c5d1SDavid du Colombier } 10073e12c5d1SDavid du Colombier return 0; 10083e12c5d1SDavid du Colombier } 10093e12c5d1SDavid du Colombier 10103e12c5d1SDavid du Colombier /* 10113e12c5d1SDavid du Colombier * print out a parsed date 10123e12c5d1SDavid du Colombier */ 10137dd7cddfSDavid du Colombier int 10143e12c5d1SDavid du Colombier printdate(Node *p) 10153e12c5d1SDavid du Colombier { 10167dd7cddfSDavid du Colombier int n, sep = 0; 10173e12c5d1SDavid du Colombier 10187dd7cddfSDavid du Colombier n = dBprint("Date: %s,", s_to_c(p->s)); 10193e12c5d1SDavid du Colombier for(p = p->next; p; p = p->next){ 10203e12c5d1SDavid du Colombier if(p->s){ 10217dd7cddfSDavid du Colombier if(sep == 0) { 10227dd7cddfSDavid du Colombier dBputc(' '); 10237dd7cddfSDavid du Colombier n++; 10247dd7cddfSDavid du Colombier } 1025bd389b36SDavid du Colombier if (p->next) 10267dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s)); 1027bd389b36SDavid du Colombier else 10287dd7cddfSDavid du Colombier n += dBprint("%s", rewritezone(s_to_c(p->s))); 10293e12c5d1SDavid du Colombier sep = 0; 10303e12c5d1SDavid du Colombier } else { 10317dd7cddfSDavid du Colombier dBputc(p->c); 10327dd7cddfSDavid du Colombier n++; 10333e12c5d1SDavid du Colombier sep = 1; 10343e12c5d1SDavid du Colombier } 10353e12c5d1SDavid du Colombier } 10367dd7cddfSDavid du Colombier n += dBprint("\r\n"); 10377dd7cddfSDavid du Colombier return n; 10383e12c5d1SDavid du Colombier } 10393e12c5d1SDavid du Colombier 1040bd389b36SDavid du Colombier char * 1041bd389b36SDavid du Colombier rewritezone(char *z) 1042bd389b36SDavid du Colombier { 10437dd7cddfSDavid du Colombier int mindiff; 10447dd7cddfSDavid du Colombier char s; 10457dd7cddfSDavid du Colombier Tm *tm; 10467dd7cddfSDavid du Colombier static char x[7]; 1047bd389b36SDavid du Colombier 10487dd7cddfSDavid du Colombier tm = localtime(time(0)); 10497dd7cddfSDavid du Colombier mindiff = tm->tzoff/60; 1050bd389b36SDavid du Colombier 10517dd7cddfSDavid du Colombier /* if not in my timezone, don't change anything */ 10527dd7cddfSDavid du Colombier if(strcmp(tm->zone, z) != 0) 1053bd389b36SDavid du Colombier return z; 10547dd7cddfSDavid du Colombier 10557dd7cddfSDavid du Colombier if(mindiff < 0){ 10567dd7cddfSDavid du Colombier s = '-'; 10577dd7cddfSDavid du Colombier mindiff = -mindiff; 10587dd7cddfSDavid du Colombier } else 10597dd7cddfSDavid du Colombier s = '+'; 10607dd7cddfSDavid du Colombier 10617dd7cddfSDavid du Colombier sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60); 10627dd7cddfSDavid du Colombier return x; 1063bd389b36SDavid du Colombier } 1064bd389b36SDavid du Colombier 10653e12c5d1SDavid du Colombier /* 10663e12c5d1SDavid du Colombier * stolen from libc/port/print.c 10673e12c5d1SDavid du Colombier */ 10683e12c5d1SDavid du Colombier #define SIZE 4096 10693e12c5d1SDavid du Colombier int 10703e12c5d1SDavid du Colombier dBprint(char *fmt, ...) 10713e12c5d1SDavid du Colombier { 10723e12c5d1SDavid du Colombier char buf[SIZE], *out; 10737dd7cddfSDavid du Colombier va_list arg; 10743e12c5d1SDavid du Colombier int n; 10753e12c5d1SDavid du Colombier 10767dd7cddfSDavid du Colombier va_start(arg, fmt); 10779a747e4fSDavid du Colombier out = vseprint(buf, buf+SIZE, fmt, arg); 10787dd7cddfSDavid du Colombier va_end(arg); 10793e12c5d1SDavid du Colombier if(debug){ 10803e12c5d1SDavid du Colombier Bwrite(&berr, buf, (long)(out-buf)); 10813e12c5d1SDavid du Colombier Bflush(&berr); 10823e12c5d1SDavid du Colombier } 10833e12c5d1SDavid du Colombier n = Bwrite(&bout, buf, (long)(out-buf)); 10843e12c5d1SDavid du Colombier Bflush(&bout); 10853e12c5d1SDavid du Colombier return n; 10863e12c5d1SDavid du Colombier } 10877dd7cddfSDavid du Colombier 10887dd7cddfSDavid du Colombier int 10897dd7cddfSDavid du Colombier dBputc(int x) 10907dd7cddfSDavid du Colombier { 10917dd7cddfSDavid du Colombier if(debug) 10927dd7cddfSDavid du Colombier Bputc(&berr, x); 10937dd7cddfSDavid du Colombier return Bputc(&bout, x); 10947dd7cddfSDavid du Colombier } 1095