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 */ 47db7ae703SDavid du Colombier 487dd7cddfSDavid du Colombier char *quitrv; /* deferred return value when in quit */ 493e12c5d1SDavid du Colombier char ddomain[1024]; /* domain name of destination machine */ 503e12c5d1SDavid du Colombier char *gdomain; /* domain name of gateway */ 51bd389b36SDavid du Colombier char *uneaten; /* first character after rfc822 headers */ 527dd7cddfSDavid du Colombier char *farend; /* system we are trying to send to */ 53d9306527SDavid du Colombier char *user; /* user we are authenticating as, if authenticating */ 54219b2ee8SDavid du Colombier char hostdomain[256]; 55db7ae703SDavid du Colombier 563e12c5d1SDavid du Colombier Biobuf bin; 573e12c5d1SDavid du Colombier Biobuf bout; 583e12c5d1SDavid du Colombier Biobuf berr; 597dd7cddfSDavid du Colombier Biobuf bfile; 603e12c5d1SDavid du Colombier 61617c0e1eSDavid du Colombier static int bustedmx; 62617c0e1eSDavid du Colombier 633e12c5d1SDavid du Colombier void 643e12c5d1SDavid du Colombier usage(void) 653e12c5d1SDavid du Colombier { 66617c0e1eSDavid du Colombier fprint(2, "usage: smtp [-aAdfips] [-b busted-mx] [-g gw] [-h host] " 67617c0e1eSDavid du Colombier "[-u user] [.domain] net!host[!service] sender rcpt-list\n"); 68219b2ee8SDavid du Colombier exits(Giveup); 69219b2ee8SDavid du Colombier } 70219b2ee8SDavid du Colombier 717dd7cddfSDavid du Colombier int 72219b2ee8SDavid du Colombier timeout(void *x, char *msg) 73219b2ee8SDavid du Colombier { 74219b2ee8SDavid du Colombier USED(x); 757dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg); 76219b2ee8SDavid du Colombier if(strstr(msg, "alarm")){ 777dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection to %s timed out\n", farend); 787dd7cddfSDavid du Colombier if(quitting) 797dd7cddfSDavid du Colombier exits(quitrv); 807dd7cddfSDavid du Colombier exits(Retry); 81219b2ee8SDavid du Colombier } 827dd7cddfSDavid du Colombier if(strstr(msg, "closed pipe")){ 837dd7cddfSDavid du Colombier /* call _exits() to prevent Bio from trying to flush closed pipe */ 847dd7cddfSDavid du Colombier fprint(2, "smtp timeout: connection closed to %s\n", farend); 857dd7cddfSDavid du Colombier if(quitting){ 867dd7cddfSDavid du Colombier syslog(0, "smtp.fail", "closed pipe to %s", farend); 877dd7cddfSDavid du Colombier _exits(quitrv); 887dd7cddfSDavid du Colombier } 897dd7cddfSDavid du Colombier _exits(Retry); 907dd7cddfSDavid du Colombier } 917dd7cddfSDavid du Colombier return 0; 923e12c5d1SDavid du Colombier } 933e12c5d1SDavid du Colombier 943e12c5d1SDavid du Colombier void 95a8453668SDavid du Colombier removenewline(char *p) 96a8453668SDavid du Colombier { 97a8453668SDavid du Colombier int n = strlen(p)-1; 98a8453668SDavid du Colombier 99a8453668SDavid du Colombier if(n < 0) 100a8453668SDavid du Colombier return; 101a8453668SDavid du Colombier if(p[n] == '\n') 102a8453668SDavid du Colombier p[n] = 0; 103a8453668SDavid du Colombier } 104a8453668SDavid du Colombier 105a8453668SDavid du Colombier void 1063e12c5d1SDavid du Colombier main(int argc, char **argv) 1073e12c5d1SDavid du Colombier { 1087dd7cddfSDavid du Colombier int i, ok, rcvrs; 109db7ae703SDavid du Colombier char *addr, *rv, *trv, *host, *domain; 1107dd7cddfSDavid du Colombier char **errs; 111db7ae703SDavid du Colombier char hellodomain[256]; 112db7ae703SDavid du Colombier String *from, *fromm, *sender; 1133e12c5d1SDavid du Colombier 114a8453668SDavid du Colombier alarmscale = 60*1000; /* minutes */ 115fe096d1aSDavid du Colombier quotefmtinstall(); 1167dd7cddfSDavid du Colombier errs = malloc(argc*sizeof(char*)); 1173e12c5d1SDavid du Colombier reply = s_new(); 118219b2ee8SDavid du Colombier host = 0; 1193e12c5d1SDavid du Colombier ARGBEGIN{ 120d9306527SDavid du Colombier case 'a': 121d9306527SDavid du Colombier tryauth = 1; 122d9306527SDavid du Colombier trysecure = 1; 123d9306527SDavid du Colombier break; 124db7ae703SDavid du Colombier case 'A': /* autistic: won't talk to us until we talk (Verizon) */ 125db7ae703SDavid du Colombier autistic = 1; 1263e12c5d1SDavid du Colombier break; 127617c0e1eSDavid du Colombier case 'b': 128617c0e1eSDavid du Colombier if (bustedmx >= Maxbustedmx) 129617c0e1eSDavid du Colombier sysfatal("more than %d busted mxs given", Maxbustedmx); 130617c0e1eSDavid du Colombier bustedmxs[bustedmx++] = EARGF(usage()); 131617c0e1eSDavid du Colombier break; 1323e12c5d1SDavid du Colombier case 'd': 1333e12c5d1SDavid du Colombier debug = 1; 1343e12c5d1SDavid du Colombier break; 135db7ae703SDavid du Colombier case 'f': 136db7ae703SDavid du Colombier filter = 1; 137db7ae703SDavid du Colombier break; 1383e12c5d1SDavid du Colombier case 'g': 139db7ae703SDavid du Colombier gdomain = EARGF(usage()); 1403e12c5d1SDavid du Colombier break; 1413e12c5d1SDavid du Colombier case 'h': 142db7ae703SDavid du Colombier host = EARGF(usage()); 1433e12c5d1SDavid du Colombier break; 144a8453668SDavid du Colombier case 'i': 145a8453668SDavid du Colombier insecure = 1; 146a8453668SDavid du Colombier break; 147a8453668SDavid du Colombier case 'p': 148a8453668SDavid du Colombier alarmscale = 10*1000; /* tens of seconds */ 149a8453668SDavid du Colombier ping = 1; 150a8453668SDavid du Colombier break; 151d9306527SDavid du Colombier case 's': 152d9306527SDavid du Colombier trysecure = 1; 153d9306527SDavid du Colombier break; 154d9306527SDavid du Colombier case 'u': 155db7ae703SDavid du Colombier user = EARGF(usage()); 156d9306527SDavid du Colombier break; 1573e12c5d1SDavid du Colombier default: 1583e12c5d1SDavid du Colombier usage(); 1593e12c5d1SDavid du Colombier break; 1603e12c5d1SDavid du Colombier }ARGEND; 1613e12c5d1SDavid du Colombier 1623e12c5d1SDavid du Colombier Binit(&berr, 2, OWRITE); 1637dd7cddfSDavid du Colombier Binit(&bfile, 0, OREAD); 1643e12c5d1SDavid du Colombier 1653e12c5d1SDavid du Colombier /* 1663e12c5d1SDavid du Colombier * get domain and add to host name 1673e12c5d1SDavid du Colombier */ 1687dd7cddfSDavid du Colombier if(*argv && **argv=='.') { 1697dd7cddfSDavid du Colombier domain = *argv; 1707dd7cddfSDavid du Colombier argv++; argc--; 1717dd7cddfSDavid du Colombier } else 1727dd7cddfSDavid du Colombier domain = domainname_read(); 1733e12c5d1SDavid du Colombier if(host == 0) 1743e12c5d1SDavid du Colombier host = sysname_read(); 1753e12c5d1SDavid du Colombier strcpy(hostdomain, domainify(host, domain)); 1767dd7cddfSDavid du Colombier strcpy(hellodomain, domainify(sysname_read(), domain)); 1773e12c5d1SDavid du Colombier 1783e12c5d1SDavid du Colombier /* 1793e12c5d1SDavid du Colombier * get destination address 1803e12c5d1SDavid du Colombier */ 1813e12c5d1SDavid du Colombier if(*argv == 0) 1823e12c5d1SDavid du Colombier usage(); 1837dd7cddfSDavid du Colombier addr = *argv++; argc--; 1847dd7cddfSDavid du Colombier farend = addr; 1853e12c5d1SDavid du Colombier 1863e12c5d1SDavid du Colombier /* 1873e12c5d1SDavid du Colombier * get sender's machine. 1883e12c5d1SDavid du Colombier * get sender in internet style. domainify if necessary. 1893e12c5d1SDavid du Colombier */ 190219b2ee8SDavid du Colombier if(*argv == 0) 191219b2ee8SDavid du Colombier usage(); 1927dd7cddfSDavid du Colombier sender = unescapespecial(s_copy(*argv++)); 1937dd7cddfSDavid du Colombier argc--; 1947dd7cddfSDavid du Colombier fromm = s_clone(sender); 1953e12c5d1SDavid du Colombier rv = strrchr(s_to_c(fromm), '!'); 1963e12c5d1SDavid du Colombier if(rv) 1973e12c5d1SDavid du Colombier *rv = 0; 1983e12c5d1SDavid du Colombier else 1993e12c5d1SDavid du Colombier *s_to_c(fromm) = 0; 2007dd7cddfSDavid du Colombier from = bangtoat(s_to_c(sender)); 2013e12c5d1SDavid du Colombier 2023e12c5d1SDavid du Colombier /* 2033e12c5d1SDavid du Colombier * send the mail 2043e12c5d1SDavid du Colombier */ 2053e12c5d1SDavid du Colombier if(filter){ 2063e12c5d1SDavid du Colombier Binit(&bout, 1, OWRITE); 2077dd7cddfSDavid du Colombier rv = data(from, &bfile); 2083e12c5d1SDavid du Colombier if(rv != 0) 2093e12c5d1SDavid du Colombier goto error; 2107dd7cddfSDavid du Colombier exits(0); 2117dd7cddfSDavid du Colombier } 2127dd7cddfSDavid du Colombier 213ab3dc52fSDavid du Colombier /* mxdial uses its own timeout handler */ 2147dd7cddfSDavid du Colombier if((rv = connect(addr)) != 0) 2157dd7cddfSDavid du Colombier exits(rv); 216ab3dc52fSDavid du Colombier 217ab3dc52fSDavid du Colombier /* 10 minutes to get through the initial handshake */ 218ab3dc52fSDavid du Colombier atnotify(timeout, 1); 219a8453668SDavid du Colombier alarm(10*alarmscale); 220d9306527SDavid du Colombier if((rv = hello(hellodomain, 0)) != 0) 2217dd7cddfSDavid du Colombier goto error; 222a8453668SDavid du Colombier alarm(10*alarmscale); 2237dd7cddfSDavid du Colombier if((rv = mailfrom(s_to_c(from))) != 0) 2247dd7cddfSDavid du Colombier goto error; 2257dd7cddfSDavid du Colombier 2267dd7cddfSDavid du Colombier ok = 0; 2277dd7cddfSDavid du Colombier rcvrs = 0; 2287dd7cddfSDavid du Colombier /* if any rcvrs are ok, we try to send the message */ 2297dd7cddfSDavid du Colombier for(i = 0; i < argc; i++){ 2307dd7cddfSDavid du Colombier if((trv = rcptto(argv[i])) != 0){ 2317dd7cddfSDavid du Colombier /* remember worst error */ 2327dd7cddfSDavid du Colombier if(rv != Giveup) 2337dd7cddfSDavid du Colombier rv = trv; 2347dd7cddfSDavid du Colombier errs[rcvrs] = strdup(s_to_c(reply)); 235a8453668SDavid du Colombier removenewline(errs[rcvrs]); 2367dd7cddfSDavid du Colombier } else { 2377dd7cddfSDavid du Colombier ok++; 2387dd7cddfSDavid du Colombier errs[rcvrs] = 0; 2397dd7cddfSDavid du Colombier } 2407dd7cddfSDavid du Colombier rcvrs++; 2417dd7cddfSDavid du Colombier } 2427dd7cddfSDavid du Colombier 2437dd7cddfSDavid du Colombier /* if no ok rcvrs or worst error is retry, give up */ 2447dd7cddfSDavid du Colombier if(ok == 0 || rv == Retry) 2457dd7cddfSDavid du Colombier goto error; 2467dd7cddfSDavid du Colombier 247a8453668SDavid du Colombier if(ping){ 248a8453668SDavid du Colombier quit(0); 249a8453668SDavid du Colombier exits(0); 250a8453668SDavid du Colombier } 251a8453668SDavid du Colombier 2527dd7cddfSDavid du Colombier rv = data(from, &bfile); 2537dd7cddfSDavid du Colombier if(rv != 0) 2547dd7cddfSDavid du Colombier goto error; 2557dd7cddfSDavid du Colombier quit(0); 2567dd7cddfSDavid du Colombier if(rcvrs == ok) 2577dd7cddfSDavid du Colombier exits(0); 2587dd7cddfSDavid du Colombier 2597dd7cddfSDavid du Colombier /* 2607dd7cddfSDavid du Colombier * here when some but not all rcvrs failed 2617dd7cddfSDavid du Colombier */ 2627dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n", thedate(), addr); 2637dd7cddfSDavid du Colombier for(i = 0; i < rcvrs; i++){ 2647dd7cddfSDavid du Colombier if(errs[i]){ 265a8453668SDavid du Colombier syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]); 2667dd7cddfSDavid du Colombier fprint(2, " mail to %s failed: %s", argv[i], errs[i]); 2677dd7cddfSDavid du Colombier } 2687dd7cddfSDavid du Colombier } 2697dd7cddfSDavid du Colombier exits(Giveup); 2707dd7cddfSDavid du Colombier 2717dd7cddfSDavid du Colombier /* 2727dd7cddfSDavid du Colombier * here when all rcvrs failed 2737dd7cddfSDavid du Colombier */ 2743e12c5d1SDavid du Colombier error: 275a8453668SDavid du Colombier removenewline(s_to_c(reply)); 276a8453668SDavid du Colombier syslog(0, "smtp.fail", "%s to %s failed: %s", 277a8453668SDavid du Colombier ping ? "ping" : "delivery", 278a8453668SDavid du Colombier addr, s_to_c(reply)); 2797dd7cddfSDavid du Colombier fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply)); 2807dd7cddfSDavid du Colombier if(!filter) 2817dd7cddfSDavid du Colombier quit(rv); 2823e12c5d1SDavid du Colombier exits(rv); 2833e12c5d1SDavid du Colombier } 2843e12c5d1SDavid du Colombier 2853e12c5d1SDavid du Colombier /* 2863e12c5d1SDavid du Colombier * connect to the remote host 2873e12c5d1SDavid du Colombier */ 2887dd7cddfSDavid du Colombier static char * 2893e12c5d1SDavid du Colombier connect(char* net) 2903e12c5d1SDavid du Colombier { 291*21887c0bSDavid du Colombier char buf[Errlen]; 2923e12c5d1SDavid du Colombier int fd; 2933e12c5d1SDavid du Colombier 2947dd7cddfSDavid du Colombier fd = mxdial(net, ddomain, gdomain); 2953e12c5d1SDavid du Colombier 2963e12c5d1SDavid du Colombier if(fd < 0){ 2979a747e4fSDavid du Colombier rerrstr(buf, sizeof(buf)); 2983ff48bf5SDavid du Colombier Bprint(&berr, "smtp: %s (%s)\n", buf, net); 2993ff48bf5SDavid du Colombier syslog(0, "smtp.fail", "%s (%s)", buf, net); 3007dd7cddfSDavid du Colombier if(strstr(buf, "illegal") 3017dd7cddfSDavid du Colombier || strstr(buf, "unknown") 302271b8d73SDavid du Colombier || strstr(buf, "can't translate")) 3033e12c5d1SDavid du Colombier return Giveup; 3043e12c5d1SDavid du Colombier else 3053e12c5d1SDavid du Colombier return Retry; 3063e12c5d1SDavid du Colombier } 3073e12c5d1SDavid du Colombier Binit(&bin, fd, OREAD); 3083e12c5d1SDavid du Colombier fd = dup(fd, -1); 3093e12c5d1SDavid du Colombier Binit(&bout, fd, OWRITE); 3103e12c5d1SDavid du Colombier return 0; 3113e12c5d1SDavid du Colombier } 3123e12c5d1SDavid du Colombier 313fe096d1aSDavid du Colombier static char smtpthumbs[] = "/sys/lib/tls/smtp"; 314fe096d1aSDavid du Colombier static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude"; 315fe096d1aSDavid du Colombier 3163e12c5d1SDavid du Colombier /* 317fe096d1aSDavid du Colombier * exchange names with remote host, attempt to 318fe096d1aSDavid du Colombier * enable encryption and optionally authenticate. 319fe096d1aSDavid du Colombier * not fatal if we can't. 3203e12c5d1SDavid du Colombier */ 321d9306527SDavid du Colombier static char * 322d9306527SDavid du Colombier dotls(char *me) 3233e12c5d1SDavid du Colombier { 324d9306527SDavid du Colombier TLSconn *c; 325d9306527SDavid du Colombier Thumbprint *goodcerts; 326d9306527SDavid du Colombier char *h; 327d9306527SDavid du Colombier int fd; 328d9306527SDavid du Colombier uchar hash[SHA1dlen]; 329d9306527SDavid du Colombier 330d9306527SDavid du Colombier c = mallocz(sizeof(*c), 1); /* Note: not freed on success */ 331d9306527SDavid du Colombier if (c == nil) 332d9306527SDavid du Colombier return Giveup; 333fe096d1aSDavid du Colombier 334d9306527SDavid du Colombier dBprint("STARTTLS\r\n"); 335fe096d1aSDavid du Colombier if (getreply() != 2) 336fe096d1aSDavid du Colombier return Giveup; 337fe096d1aSDavid du Colombier 338d9306527SDavid du Colombier fd = tlsClient(Bfildes(&bout), c); 339fe096d1aSDavid du Colombier if (fd < 0) { 340fe096d1aSDavid du Colombier syslog(0, "smtp", "tlsClient to %q: %r", ddomain); 341d9306527SDavid du Colombier return Giveup; 342dddc47c2SDavid du Colombier } 343fe096d1aSDavid du Colombier goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs); 344d9306527SDavid du Colombier if (goodcerts == nil) { 345d9306527SDavid du Colombier free(c); 346fe096d1aSDavid du Colombier close(fd); 347fe096d1aSDavid du Colombier syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs); 348fe096d1aSDavid du Colombier return Giveup; /* how to recover? TLS is started */ 349d9306527SDavid du Colombier } 350fe096d1aSDavid du Colombier 351fe096d1aSDavid du Colombier /* compute sha1 hash of remote's certificate, see if we know it */ 352d9306527SDavid du Colombier sha1(c->cert, c->certlen, hash, nil); 353d9306527SDavid du Colombier if (!okThumbprint(hash, goodcerts)) { 354fe096d1aSDavid du Colombier /* TODO? if not excluded, add hash to thumb list */ 355fe096d1aSDavid du Colombier free(c); 356fe096d1aSDavid du Colombier close(fd); 357d9306527SDavid du Colombier h = malloc(2*sizeof hash + 1); 358d9306527SDavid du Colombier if (h != nil) { 359d9306527SDavid du Colombier enc16(h, 2*sizeof hash + 1, hash, sizeof hash); 360c6569576SDavid du Colombier // fprint(2, "x509 sha1=%s", h); 361fe096d1aSDavid du Colombier syslog(0, "smtp", 362fe096d1aSDavid du Colombier "remote cert. has bad thumbprint: x509 sha1=%s server=%q", 363fe096d1aSDavid du Colombier h, ddomain); 364d9306527SDavid du Colombier free(h); 365d9306527SDavid du Colombier } 366fe096d1aSDavid du Colombier return Giveup; /* how to recover? TLS is started */ 367d9306527SDavid du Colombier } 368d9306527SDavid du Colombier freeThumbprints(goodcerts); 369d9306527SDavid du Colombier Bterm(&bin); 370d9306527SDavid du Colombier Bterm(&bout); 371fe096d1aSDavid du Colombier 372fe096d1aSDavid du Colombier /* 373fe096d1aSDavid du Colombier * set up bin & bout to use the TLS fd, i/o upon which generates 374fe096d1aSDavid du Colombier * i/o on the original, underlying fd. 375fe096d1aSDavid du Colombier */ 376d9306527SDavid du Colombier Binit(&bin, fd, OREAD); 377d9306527SDavid du Colombier fd = dup(fd, -1); 378d9306527SDavid du Colombier Binit(&bout, fd, OWRITE); 379fe096d1aSDavid du Colombier 380fe096d1aSDavid du Colombier syslog(0, "smtp", "started TLS to %q", ddomain); 381d9306527SDavid du Colombier return(hello(me, 1)); 382d9306527SDavid du Colombier } 383d9306527SDavid du Colombier 384d9306527SDavid du Colombier static char * 3853cf081f0SDavid du Colombier doauth(char *methods) 386d9306527SDavid du Colombier { 3879863c128SDavid du Colombier char *buf, *base64; 388d9306527SDavid du Colombier int n; 3899863c128SDavid du Colombier DS ds; 3909863c128SDavid du Colombier UserPasswd *p; 391d9306527SDavid du Colombier 3929863c128SDavid du Colombier dial_string_parse(ddomain, &ds); 3933cf081f0SDavid du Colombier 394d9306527SDavid du Colombier if(user != nil) 395d9306527SDavid du Colombier p = auth_getuserpasswd(nil, 3969863c128SDavid du Colombier "proto=pass service=smtp server=%q user=%q", ds.host, user); 397d9306527SDavid du Colombier else 398d9306527SDavid du Colombier p = auth_getuserpasswd(nil, 3999863c128SDavid du Colombier "proto=pass service=smtp server=%q", ds.host); 400d9306527SDavid du Colombier if (p == nil) 401d9306527SDavid du Colombier return Giveup; 4023cf081f0SDavid du Colombier 4033cf081f0SDavid du Colombier if (strstr(methods, "LOGIN")){ 4043cf081f0SDavid du Colombier dBprint("AUTH LOGIN\r\n"); 4053cf081f0SDavid du Colombier if (getreply() != 3) 4063cf081f0SDavid du Colombier return Retry; 4073cf081f0SDavid du Colombier 4083cf081f0SDavid du Colombier n = strlen(p->user); 4093cf081f0SDavid du Colombier base64 = malloc(2*n); 4103cf081f0SDavid du Colombier if (base64 == nil) 4113cf081f0SDavid du Colombier return Retry; /* Out of memory */ 4123cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->user, n); 4133cf081f0SDavid du Colombier dBprint("%s\r\n", base64); 4143cf081f0SDavid du Colombier if (getreply() != 3) 4153cf081f0SDavid du Colombier return Retry; 4163cf081f0SDavid du Colombier 4173cf081f0SDavid du Colombier n = strlen(p->passwd); 4183cf081f0SDavid du Colombier base64 = malloc(2*n); 4193cf081f0SDavid du Colombier if (base64 == nil) 4203cf081f0SDavid du Colombier return Retry; /* Out of memory */ 4213cf081f0SDavid du Colombier enc64(base64, 2*n, (uchar *)p->passwd, n); 4223cf081f0SDavid du Colombier dBprint("%s\r\n", base64); 4233cf081f0SDavid du Colombier if (getreply() != 2) 4243cf081f0SDavid du Colombier return Retry; 4253cf081f0SDavid du Colombier 4263cf081f0SDavid du Colombier free(base64); 4273cf081f0SDavid du Colombier } 4283cf081f0SDavid du Colombier else 4293cf081f0SDavid du Colombier if (strstr(methods, "PLAIN")){ 430d9306527SDavid du Colombier n = strlen(p->user) + strlen(p->passwd) + 3; 431d9306527SDavid du Colombier buf = malloc(n); 432d9306527SDavid du Colombier base64 = malloc(2 * n); 433fe096d1aSDavid du Colombier if (buf == nil || base64 == nil) { 434d9306527SDavid du Colombier free(buf); 435d9306527SDavid du Colombier return Retry; /* Out of memory */ 436d9306527SDavid du Colombier } 437d9306527SDavid du Colombier snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd); 438d9306527SDavid du Colombier enc64(base64, 2 * n, (uchar *)buf, n - 1); 439d9306527SDavid du Colombier free(buf); 440d9306527SDavid du Colombier dBprint("AUTH PLAIN %s\r\n", base64); 441d9306527SDavid du Colombier free(base64); 442d9306527SDavid du Colombier if (getreply() != 2) 443d9306527SDavid du Colombier return Retry; 4443cf081f0SDavid du Colombier } 4453cf081f0SDavid du Colombier else 4463cf081f0SDavid du Colombier return "No supported AUTH method"; 447d9306527SDavid du Colombier return(0); 448d9306527SDavid du Colombier } 449d9306527SDavid du Colombier 450d9306527SDavid du Colombier char * 451d9306527SDavid du Colombier hello(char *me, int encrypted) 452d9306527SDavid du Colombier { 453d9306527SDavid du Colombier int ehlo; 454d9306527SDavid du Colombier String *r; 4553cf081f0SDavid du Colombier char *ret, *s, *t; 456d9306527SDavid du Colombier 457db7ae703SDavid du Colombier if (!encrypted) { 458db7ae703SDavid du Colombier /* 459db7ae703SDavid du Colombier * Verizon fails to print the smtp greeting banner when it 460db7ae703SDavid du Colombier * answers a call. Send a no-op in the hope of making it 461db7ae703SDavid du Colombier * talk. 462db7ae703SDavid du Colombier */ 463ea813757SDavid du Colombier if (autistic) { 464db7ae703SDavid du Colombier dBprint("NOOP\r\n"); 465ea813757SDavid du Colombier getreply(); /* consume the smtp greeting */ 466ea813757SDavid du Colombier /* next reply will be response to noop */ 467ea813757SDavid du Colombier } 4683e12c5d1SDavid du Colombier switch(getreply()){ 4693e12c5d1SDavid du Colombier case 2: 4703e12c5d1SDavid du Colombier break; 4713e12c5d1SDavid du Colombier case 5: 4723e12c5d1SDavid du Colombier return Giveup; 4733e12c5d1SDavid du Colombier default: 4743e12c5d1SDavid du Colombier return Retry; 4753e12c5d1SDavid du Colombier } 476db7ae703SDavid du Colombier } 477d9306527SDavid du Colombier 478d9306527SDavid du Colombier ehlo = 1; 479d9306527SDavid du Colombier Again: 480d9306527SDavid du Colombier if(ehlo) 481d9306527SDavid du Colombier dBprint("EHLO %s\r\n", me); 482d9306527SDavid du Colombier else 4833e12c5d1SDavid du Colombier dBprint("HELO %s\r\n", me); 4843e12c5d1SDavid du Colombier switch (getreply()) { 4853e12c5d1SDavid du Colombier case 2: 4863e12c5d1SDavid du Colombier break; 4873e12c5d1SDavid du Colombier case 5: 488d9306527SDavid du Colombier if(ehlo){ 489d9306527SDavid du Colombier ehlo = 0; 490d9306527SDavid du Colombier goto Again; 491d9306527SDavid du Colombier } 4923e12c5d1SDavid du Colombier return Giveup; 4933e12c5d1SDavid du Colombier default: 4943e12c5d1SDavid du Colombier return Retry; 4953e12c5d1SDavid du Colombier } 496d9306527SDavid du Colombier r = s_clone(reply); 497d9306527SDavid du Colombier if(r == nil) 498d9306527SDavid du Colombier return Retry; /* Out of memory or couldn't get string */ 499d9306527SDavid du Colombier 500d9306527SDavid du Colombier /* Invariant: every line has a newline, a result of getcrlf() */ 501d9306527SDavid du Colombier for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){ 502d9306527SDavid du Colombier *t = '\0'; 503d9306527SDavid du Colombier for (t = s; *t != '\0'; t++) 504d9306527SDavid du Colombier *t = toupper(*t); 505d9306527SDavid du Colombier if(!encrypted && trysecure && 506d9306527SDavid du Colombier (strcmp(s, "250-STARTTLS") == 0 || 507d9306527SDavid du Colombier strcmp(s, "250 STARTTLS") == 0)){ 508d9306527SDavid du Colombier s_free(r); 509db7ae703SDavid du Colombier return dotls(me); 510d9306527SDavid du Colombier } 511a8453668SDavid du Colombier if(tryauth && (encrypted || insecure) && 512d9306527SDavid du Colombier (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 || 5133cf081f0SDavid du Colombier strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){ 5143cf081f0SDavid du Colombier ret = doauth(s + strlen("250 AUTH ")); 515d9306527SDavid du Colombier s_free(r); 5163cf081f0SDavid du Colombier return ret; 517d9306527SDavid du Colombier } 518d9306527SDavid du Colombier } 519d9306527SDavid du Colombier s_free(r); 5203e12c5d1SDavid du Colombier return 0; 5213e12c5d1SDavid du Colombier } 5223e12c5d1SDavid du Colombier 5233e12c5d1SDavid du Colombier /* 5243e12c5d1SDavid du Colombier * report sender to remote 5253e12c5d1SDavid du Colombier */ 5263e12c5d1SDavid du Colombier char * 5273e12c5d1SDavid du Colombier mailfrom(char *from) 5283e12c5d1SDavid du Colombier { 5297dd7cddfSDavid du Colombier if(!returnable(from)) 5307dd7cddfSDavid du Colombier dBprint("MAIL FROM:<>\r\n"); 5317dd7cddfSDavid du Colombier else 532219b2ee8SDavid du Colombier if(strchr(from, '@')) 5333e12c5d1SDavid du Colombier dBprint("MAIL FROM:<%s>\r\n", from); 534219b2ee8SDavid du Colombier else 535219b2ee8SDavid du Colombier dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain); 5363e12c5d1SDavid du Colombier switch(getreply()){ 5373e12c5d1SDavid du Colombier case 2: 5383e12c5d1SDavid du Colombier break; 5393e12c5d1SDavid du Colombier case 5: 5403e12c5d1SDavid du Colombier return Giveup; 5413e12c5d1SDavid du Colombier default: 5423e12c5d1SDavid du Colombier return Retry; 5433e12c5d1SDavid du Colombier } 5443e12c5d1SDavid du Colombier return 0; 5453e12c5d1SDavid du Colombier } 5463e12c5d1SDavid du Colombier 5473e12c5d1SDavid du Colombier /* 5483e12c5d1SDavid du Colombier * report a recipient to remote 5493e12c5d1SDavid du Colombier */ 5503e12c5d1SDavid du Colombier char * 5513e12c5d1SDavid du Colombier rcptto(char *to) 5523e12c5d1SDavid du Colombier { 553219b2ee8SDavid du Colombier String *s; 554219b2ee8SDavid du Colombier 5557dd7cddfSDavid du Colombier s = unescapespecial(bangtoat(to)); 5567dd7cddfSDavid du Colombier if(toline == 0) 557219b2ee8SDavid du Colombier toline = s_new(); 5587dd7cddfSDavid du Colombier else 559219b2ee8SDavid du Colombier s_append(toline, ", "); 560219b2ee8SDavid du Colombier s_append(toline, s_to_c(s)); 561219b2ee8SDavid du Colombier if(strchr(s_to_c(s), '@')) 562219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s>\r\n", s_to_c(s)); 563219b2ee8SDavid du Colombier else { 564219b2ee8SDavid du Colombier s_append(toline, "@"); 565219b2ee8SDavid du Colombier s_append(toline, ddomain); 566219b2ee8SDavid du Colombier dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain); 567219b2ee8SDavid du Colombier } 568a8453668SDavid du Colombier alarm(10*alarmscale); 5693e12c5d1SDavid du Colombier switch(getreply()){ 5703e12c5d1SDavid du Colombier case 2: 5713e12c5d1SDavid du Colombier break; 5723e12c5d1SDavid du Colombier case 5: 5733e12c5d1SDavid du Colombier return Giveup; 5743e12c5d1SDavid du Colombier default: 5753e12c5d1SDavid du Colombier return Retry; 5763e12c5d1SDavid du Colombier } 5773e12c5d1SDavid du Colombier return 0; 5783e12c5d1SDavid du Colombier } 5793e12c5d1SDavid du Colombier 5809a747e4fSDavid du Colombier static char hex[] = "0123456789abcdef"; 5819a747e4fSDavid du Colombier 5823e12c5d1SDavid du Colombier /* 5833e12c5d1SDavid du Colombier * send the damn thing 5843e12c5d1SDavid du Colombier */ 5853e12c5d1SDavid du Colombier char * 5867dd7cddfSDavid du Colombier data(String *from, Biobuf *b) 5873e12c5d1SDavid du Colombier { 5887dd7cddfSDavid du Colombier char *buf, *cp; 5899a747e4fSDavid du Colombier int i, n, nbytes, bufsize, eof, r; 5907dd7cddfSDavid du Colombier String *fromline; 5919a747e4fSDavid du Colombier char errmsg[Errlen]; 5929a747e4fSDavid du Colombier char id[40]; 5933e12c5d1SDavid du Colombier 5943e12c5d1SDavid du Colombier /* 5957dd7cddfSDavid du Colombier * input the header. 5963e12c5d1SDavid du Colombier */ 5977dd7cddfSDavid du Colombier 5987dd7cddfSDavid du Colombier buf = malloc(1); 5997dd7cddfSDavid du Colombier if(buf == 0){ 6007dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory"); 6017dd7cddfSDavid du Colombier return Retry; 6027dd7cddfSDavid du Colombier } 6037dd7cddfSDavid du Colombier n = 0; 6043e12c5d1SDavid du Colombier eof = 0; 6057dd7cddfSDavid du Colombier for(;;){ 6067dd7cddfSDavid du Colombier cp = Brdline(b, '\n'); 6077dd7cddfSDavid du Colombier if(cp == nil){ 6083e12c5d1SDavid du Colombier eof = 1; 6093e12c5d1SDavid du Colombier break; 6103e12c5d1SDavid du Colombier } 6117dd7cddfSDavid du Colombier nbytes = Blinelen(b); 6127dd7cddfSDavid du Colombier buf = realloc(buf, n+nbytes+1); 6137dd7cddfSDavid du Colombier if(buf == 0){ 6147dd7cddfSDavid du Colombier s_append(s_restart(reply), "out of memory"); 6157dd7cddfSDavid du Colombier return Retry; 6167dd7cddfSDavid du Colombier } 6177dd7cddfSDavid du Colombier strncpy(buf+n, cp, nbytes); 6187dd7cddfSDavid du Colombier n += nbytes; 6197dd7cddfSDavid du Colombier if(nbytes == 1) /* end of header */ 6207dd7cddfSDavid du Colombier break; 6213e12c5d1SDavid du Colombier } 6223e12c5d1SDavid du Colombier buf[n] = 0; 6237dd7cddfSDavid du Colombier bufsize = n; 6243e12c5d1SDavid du Colombier 6253e12c5d1SDavid du Colombier /* 6263e12c5d1SDavid du Colombier * parse the header, turn all addresses into @ format 6273e12c5d1SDavid du Colombier */ 6289a747e4fSDavid du Colombier yyinit(buf, n); 6293e12c5d1SDavid du Colombier yyparse(); 6303e12c5d1SDavid du Colombier 6313e12c5d1SDavid du Colombier /* 6323e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n 6333e12c5d1SDavid du Colombier */ 634a8453668SDavid du Colombier alarm(20*alarmscale); 6353e12c5d1SDavid du Colombier if(!filter){ 6363e12c5d1SDavid du Colombier dBprint("DATA\r\n"); 6373e12c5d1SDavid du Colombier switch(getreply()){ 6383e12c5d1SDavid du Colombier case 3: 6393e12c5d1SDavid du Colombier break; 6403e12c5d1SDavid du Colombier case 5: 6417dd7cddfSDavid du Colombier free(buf); 6423e12c5d1SDavid du Colombier return Giveup; 6433e12c5d1SDavid du Colombier default: 6447dd7cddfSDavid du Colombier free(buf); 6453e12c5d1SDavid du Colombier return Retry; 6463e12c5d1SDavid du Colombier } 6473e12c5d1SDavid du Colombier } 6483e12c5d1SDavid du Colombier /* 6499a747e4fSDavid du Colombier * send header. add a message-id, a sender, and a date if there 6503e12c5d1SDavid du Colombier * isn't one 6513e12c5d1SDavid du Colombier */ 6527dd7cddfSDavid du Colombier nbytes = 0; 6537dd7cddfSDavid du Colombier fromline = convertheader(from); 654bd389b36SDavid du Colombier uneaten = buf; 6559a747e4fSDavid du Colombier 656d9306527SDavid du Colombier srand(truerand()); 6579a747e4fSDavid du Colombier if(messageid == 0){ 6589a747e4fSDavid du Colombier for(i=0; i<16; i++){ 659d9306527SDavid du Colombier r = rand()&0xFF; 6609a747e4fSDavid du Colombier id[2*i] = hex[r&0xF]; 6619a747e4fSDavid du Colombier id[2*i+1] = hex[(r>>4)&0xF]; 6629a747e4fSDavid du Colombier } 6639a747e4fSDavid du Colombier id[2*i] = '\0'; 6649a747e4fSDavid du Colombier nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain); 6659a747e4fSDavid du Colombier if(debug) 6669a747e4fSDavid du Colombier Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain); 6679a747e4fSDavid du Colombier } 6689a747e4fSDavid du Colombier 6697dd7cddfSDavid du Colombier if(originator==0){ 6707dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline)); 6717dd7cddfSDavid du Colombier if(debug) 6727dd7cddfSDavid du Colombier Bprint(&berr, "From: %s\r\n", s_to_c(fromline)); 6737dd7cddfSDavid du Colombier } 6747dd7cddfSDavid du Colombier s_free(fromline); 6757dd7cddfSDavid du Colombier 676219b2ee8SDavid du Colombier if(destination == 0 && toline) 6777dd7cddfSDavid du Colombier if(*s_to_c(toline) == '@'){ /* route addr */ 6787dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline)); 6797dd7cddfSDavid du Colombier if(debug) 6807dd7cddfSDavid du Colombier Bprint(&berr, "To: <%s>\r\n", s_to_c(toline)); 6817dd7cddfSDavid du Colombier } else { 6827dd7cddfSDavid du Colombier nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline)); 6837dd7cddfSDavid du Colombier if(debug) 6847dd7cddfSDavid du Colombier Bprint(&berr, "To: %s\r\n", s_to_c(toline)); 6857dd7cddfSDavid du Colombier } 6867dd7cddfSDavid du Colombier 6873e12c5d1SDavid du Colombier if(date==0 && udate) 6887dd7cddfSDavid du Colombier nbytes += printdate(udate); 689bd389b36SDavid du Colombier if (usys) 690bd389b36SDavid du Colombier uneaten = usys->end + 1; 6917dd7cddfSDavid du Colombier nbytes += printheader(); 692bd389b36SDavid du Colombier if (*uneaten != '\n') 693bd389b36SDavid du Colombier putcrnl("\n", 1); 6943e12c5d1SDavid du Colombier 6953e12c5d1SDavid du Colombier /* 6963e12c5d1SDavid du Colombier * send body 6973e12c5d1SDavid du Colombier */ 6987dd7cddfSDavid du Colombier 699bd389b36SDavid du Colombier putcrnl(uneaten, buf+n - uneaten); 7007dd7cddfSDavid du Colombier nbytes += buf+n - uneaten; 7017dd7cddfSDavid du Colombier if(eof == 0){ 702219b2ee8SDavid du Colombier for(;;){ 7037dd7cddfSDavid du Colombier n = Bread(b, buf, bufsize); 704219b2ee8SDavid du Colombier if(n < 0){ 7059a747e4fSDavid du Colombier rerrstr(errmsg, sizeof(errmsg)); 7067dd7cddfSDavid du Colombier s_append(s_restart(reply), errmsg); 7077dd7cddfSDavid du Colombier free(buf); 7087dd7cddfSDavid du Colombier return Retry; 709219b2ee8SDavid du Colombier } 710219b2ee8SDavid du Colombier if(n == 0) 711219b2ee8SDavid du Colombier break; 712a8453668SDavid du Colombier alarm(10*alarmscale); 7133e12c5d1SDavid du Colombier putcrnl(buf, n); 7147dd7cddfSDavid du Colombier nbytes += n; 715219b2ee8SDavid du Colombier } 7167dd7cddfSDavid du Colombier } 7177dd7cddfSDavid du Colombier free(buf); 7183e12c5d1SDavid du Colombier if(!filter){ 7193e12c5d1SDavid du Colombier if(last != '\n') 7203e12c5d1SDavid du Colombier dBprint("\r\n.\r\n"); 7213e12c5d1SDavid du Colombier else 7223e12c5d1SDavid du Colombier dBprint(".\r\n"); 723a8453668SDavid du Colombier alarm(10*alarmscale); 7243e12c5d1SDavid du Colombier switch(getreply()){ 7253e12c5d1SDavid du Colombier case 2: 7263e12c5d1SDavid du Colombier break; 7273e12c5d1SDavid du Colombier case 5: 7283e12c5d1SDavid du Colombier return Giveup; 7297dd7cddfSDavid du Colombier default: 7307dd7cddfSDavid du Colombier return Retry; 7313e12c5d1SDavid du Colombier } 7327dd7cddfSDavid du Colombier syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from), 7337dd7cddfSDavid du Colombier nbytes, s_to_c(toline));/**/ 7343e12c5d1SDavid du Colombier } 7353e12c5d1SDavid du Colombier return 0; 7363e12c5d1SDavid du Colombier } 7373e12c5d1SDavid du Colombier 7383e12c5d1SDavid du Colombier /* 7393e12c5d1SDavid du Colombier * we're leaving 7403e12c5d1SDavid du Colombier */ 7413e12c5d1SDavid du Colombier void 7427dd7cddfSDavid du Colombier quit(char *rv) 7433e12c5d1SDavid du Colombier { 7447dd7cddfSDavid du Colombier /* 60 minutes to quit */ 7457dd7cddfSDavid du Colombier quitting = 1; 7467dd7cddfSDavid du Colombier quitrv = rv; 747a8453668SDavid du Colombier alarm(60*alarmscale); 7483e12c5d1SDavid du Colombier dBprint("QUIT\r\n"); 7493e12c5d1SDavid du Colombier getreply(); 7507dd7cddfSDavid du Colombier Bterm(&bout); 7517dd7cddfSDavid du Colombier Bterm(&bfile); 7523e12c5d1SDavid du Colombier } 7533e12c5d1SDavid du Colombier 7543e12c5d1SDavid du Colombier /* 7553e12c5d1SDavid du Colombier * read a reply into a string, return the reply code 7563e12c5d1SDavid du Colombier */ 7573e12c5d1SDavid du Colombier int 7583e12c5d1SDavid du Colombier getreply(void) 7593e12c5d1SDavid du Colombier { 7603e12c5d1SDavid du Colombier char *line; 7613e12c5d1SDavid du Colombier int rv; 7623e12c5d1SDavid du Colombier 7633e12c5d1SDavid du Colombier reply = s_reset(reply); 7643e12c5d1SDavid du Colombier for(;;){ 7653e12c5d1SDavid du Colombier line = getcrnl(reply); 766499069deSDavid du Colombier if(debug) 767499069deSDavid du Colombier Bflush(&berr); 7683e12c5d1SDavid du Colombier if(line == 0) 7693e12c5d1SDavid du Colombier return -1; 7703e12c5d1SDavid du Colombier if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2])) 7713e12c5d1SDavid du Colombier return -1; 7723e12c5d1SDavid du Colombier if(line[3] != '-') 7733e12c5d1SDavid du Colombier break; 7743e12c5d1SDavid du Colombier } 7757dd7cddfSDavid du Colombier if(debug) 7767dd7cddfSDavid du Colombier Bflush(&berr); 7773e12c5d1SDavid du Colombier rv = atoi(line)/100; 7783e12c5d1SDavid du Colombier return rv; 7793e12c5d1SDavid du Colombier } 7807dd7cddfSDavid du Colombier void 7817dd7cddfSDavid du Colombier addhostdom(String *buf, char *host) 7827dd7cddfSDavid du Colombier { 7837dd7cddfSDavid du Colombier s_append(buf, "@"); 7847dd7cddfSDavid du Colombier s_append(buf, host); 7857dd7cddfSDavid du Colombier } 7863e12c5d1SDavid du Colombier 7873e12c5d1SDavid du Colombier /* 7883e12c5d1SDavid du Colombier * Convert from `bang' to `source routing' format. 7893e12c5d1SDavid du Colombier * 7903e12c5d1SDavid du Colombier * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o 7913e12c5d1SDavid du Colombier */ 7923e12c5d1SDavid du Colombier String * 793219b2ee8SDavid du Colombier bangtoat(char *addr) 7943e12c5d1SDavid du Colombier { 7953e12c5d1SDavid du Colombier String *buf; 7963e12c5d1SDavid du Colombier register int i; 7973e12c5d1SDavid du Colombier int j, d; 7983e12c5d1SDavid du Colombier char *field[128]; 7993e12c5d1SDavid du Colombier 8003e12c5d1SDavid du Colombier /* parse the '!' format address */ 8013e12c5d1SDavid du Colombier buf = s_new(); 8023e12c5d1SDavid du Colombier for(i = 0; addr; i++){ 8033e12c5d1SDavid du Colombier field[i] = addr; 8043e12c5d1SDavid du Colombier addr = strchr(addr, '!'); 8053e12c5d1SDavid du Colombier if(addr) 8063e12c5d1SDavid du Colombier *addr++ = 0; 8073e12c5d1SDavid du Colombier } 8083e12c5d1SDavid du Colombier if (i==1) { 8093e12c5d1SDavid du Colombier s_append(buf, field[0]); 8103e12c5d1SDavid du Colombier return buf; 8113e12c5d1SDavid du Colombier } 8123e12c5d1SDavid du Colombier 8133e12c5d1SDavid du Colombier /* 8143e12c5d1SDavid du Colombier * count leading domain fields (non-domains don't count) 8153e12c5d1SDavid du Colombier */ 8167dd7cddfSDavid du Colombier for(d = 0; d<i-1; d++) 8173e12c5d1SDavid du Colombier if(strchr(field[d], '.')==0) 8183e12c5d1SDavid du Colombier break; 8193e12c5d1SDavid du Colombier /* 8203e12c5d1SDavid du Colombier * if there are more than 1 leading domain elements, 8213e12c5d1SDavid du Colombier * put them in as source routing 8223e12c5d1SDavid du Colombier */ 8233e12c5d1SDavid du Colombier if(d > 1){ 824219b2ee8SDavid du Colombier addhostdom(buf, field[0]); 8253e12c5d1SDavid du Colombier for(j=1; j<d-1; j++){ 8263e12c5d1SDavid du Colombier s_append(buf, ","); 8273e12c5d1SDavid du Colombier s_append(buf, "@"); 8283e12c5d1SDavid du Colombier s_append(buf, field[j]); 8293e12c5d1SDavid du Colombier } 8303e12c5d1SDavid du Colombier s_append(buf, ":"); 8313e12c5d1SDavid du Colombier } 8323e12c5d1SDavid du Colombier 8333e12c5d1SDavid du Colombier /* 8343e12c5d1SDavid du Colombier * throw in the non-domain elements separated by '!'s 8353e12c5d1SDavid du Colombier */ 8363e12c5d1SDavid du Colombier s_append(buf, field[d]); 8373e12c5d1SDavid du Colombier for(j=d+1; j<=i-1; j++) { 8383e12c5d1SDavid du Colombier s_append(buf, "!"); 8393e12c5d1SDavid du Colombier s_append(buf, field[j]); 8403e12c5d1SDavid du Colombier } 8413e12c5d1SDavid du Colombier if(d) 842219b2ee8SDavid du Colombier addhostdom(buf, field[d-1]); 8433e12c5d1SDavid du Colombier return buf; 8443e12c5d1SDavid du Colombier } 8453e12c5d1SDavid du Colombier 8463e12c5d1SDavid du Colombier /* 8473e12c5d1SDavid du Colombier * convert header addresses to @ format. 8483e12c5d1SDavid du Colombier * if the address is a source address, and a domain is specified, 8493e12c5d1SDavid du Colombier * make sure it falls in the domain. 8503e12c5d1SDavid du Colombier */ 8517dd7cddfSDavid du Colombier String* 852219b2ee8SDavid du Colombier convertheader(String *from) 8533e12c5d1SDavid du Colombier { 8543e12c5d1SDavid du Colombier Field *f; 8557dd7cddfSDavid du Colombier Node *p, *lastp; 8563e12c5d1SDavid du Colombier String *a; 8573e12c5d1SDavid du Colombier 8587dd7cddfSDavid du Colombier if(!returnable(s_to_c(from))){ 8597dd7cddfSDavid du Colombier from = s_new(); 8607dd7cddfSDavid du Colombier s_append(from, "Postmaster"); 8617dd7cddfSDavid du Colombier addhostdom(from, hostdomain); 8627dd7cddfSDavid du Colombier } else 8637dd7cddfSDavid du Colombier if(strchr(s_to_c(from), '@') == 0){ 8647dd7cddfSDavid du Colombier a = username(from); 8657dd7cddfSDavid du Colombier if(a) { 8667dd7cddfSDavid du Colombier s_append(a, " <"); 8677dd7cddfSDavid du Colombier s_append(a, s_to_c(from)); 8687dd7cddfSDavid du Colombier addhostdom(a, hostdomain); 8697dd7cddfSDavid du Colombier s_append(a, ">"); 8707dd7cddfSDavid du Colombier from = a; 8717dd7cddfSDavid du Colombier } else { 8727dd7cddfSDavid du Colombier from = s_copy(s_to_c(from)); 8737dd7cddfSDavid du Colombier addhostdom(from, hostdomain); 874219b2ee8SDavid du Colombier } 8757dd7cddfSDavid du Colombier } else 8767dd7cddfSDavid du Colombier from = s_copy(s_to_c(from)); 8773e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){ 8787dd7cddfSDavid du Colombier lastp = 0; 8797dd7cddfSDavid du Colombier for(p = f->node; p; lastp = p, p = p->next){ 8803e12c5d1SDavid du Colombier if(!p->addr) 8813e12c5d1SDavid du Colombier continue; 882219b2ee8SDavid du Colombier a = bangtoat(s_to_c(p->s)); 8833e12c5d1SDavid du Colombier s_free(p->s); 8847dd7cddfSDavid du Colombier if(strchr(s_to_c(a), '@') == 0) 8857dd7cddfSDavid du Colombier addhostdom(a, hostdomain); 8867dd7cddfSDavid du Colombier else if(*s_to_c(a) == '@') 8877dd7cddfSDavid du Colombier a = fixrouteaddr(a, p->next, lastp); 8883e12c5d1SDavid du Colombier p->s = a; 8893e12c5d1SDavid du Colombier } 8903e12c5d1SDavid du Colombier } 8917dd7cddfSDavid du Colombier return from; 8927dd7cddfSDavid du Colombier } 8937dd7cddfSDavid du Colombier /* 8947dd7cddfSDavid du Colombier * ensure route addr has brackets around it 8957dd7cddfSDavid du Colombier */ 8967dd7cddfSDavid du Colombier String* 8977dd7cddfSDavid du Colombier fixrouteaddr(String *raddr, Node *next, Node *last) 8987dd7cddfSDavid du Colombier { 8997dd7cddfSDavid du Colombier String *a; 9007dd7cddfSDavid du Colombier 9017dd7cddfSDavid du Colombier if(last && last->c == '<' && next && next->c == '>') 9027dd7cddfSDavid du Colombier return raddr; /* properly formed already */ 9037dd7cddfSDavid du Colombier 9047dd7cddfSDavid du Colombier a = s_new(); 9057dd7cddfSDavid du Colombier s_append(a, "<"); 9067dd7cddfSDavid du Colombier s_append(a, s_to_c(raddr)); 9077dd7cddfSDavid du Colombier s_append(a, ">"); 9087dd7cddfSDavid du Colombier s_free(raddr); 9097dd7cddfSDavid du Colombier return a; 9103e12c5d1SDavid du Colombier } 9113e12c5d1SDavid du Colombier 9123e12c5d1SDavid du Colombier /* 9133e12c5d1SDavid du Colombier * print out the parsed header 9143e12c5d1SDavid du Colombier */ 9157dd7cddfSDavid du Colombier int 9163e12c5d1SDavid du Colombier printheader(void) 9173e12c5d1SDavid du Colombier { 9187dd7cddfSDavid du Colombier int n, len; 9193e12c5d1SDavid du Colombier Field *f; 9203e12c5d1SDavid du Colombier Node *p; 9213e12c5d1SDavid du Colombier char *cp; 9223e12c5d1SDavid du Colombier char c[1]; 9233e12c5d1SDavid du Colombier 9247dd7cddfSDavid du Colombier n = 0; 9253e12c5d1SDavid du Colombier for(f = firstfield; f; f = f->next){ 9263e12c5d1SDavid du Colombier for(p = f->node; p; p = p->next){ 9273e12c5d1SDavid du Colombier if(p->s) 9287dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s)); 9293e12c5d1SDavid du Colombier else { 9303e12c5d1SDavid du Colombier c[0] = p->c; 9313e12c5d1SDavid du Colombier putcrnl(c, 1); 9327dd7cddfSDavid du Colombier n++; 9333e12c5d1SDavid du Colombier } 9343e12c5d1SDavid du Colombier if(p->white){ 9353e12c5d1SDavid du Colombier cp = s_to_c(p->white); 9367dd7cddfSDavid du Colombier len = strlen(cp); 9377dd7cddfSDavid du Colombier putcrnl(cp, len); 9387dd7cddfSDavid du Colombier n += len; 9393e12c5d1SDavid du Colombier } 940bd389b36SDavid du Colombier uneaten = p->end; 9413e12c5d1SDavid du Colombier } 9423e12c5d1SDavid du Colombier putcrnl("\n", 1); 9437dd7cddfSDavid du Colombier n++; 944bd389b36SDavid du Colombier uneaten++; /* skip newline */ 9453e12c5d1SDavid du Colombier } 9467dd7cddfSDavid du Colombier return n; 9473e12c5d1SDavid du Colombier } 9483e12c5d1SDavid du Colombier 9493e12c5d1SDavid du Colombier /* 9503e12c5d1SDavid du Colombier * add a domain onto an name, return the new name 9513e12c5d1SDavid du Colombier */ 9523e12c5d1SDavid du Colombier char * 9533e12c5d1SDavid du Colombier domainify(char *name, char *domain) 9543e12c5d1SDavid du Colombier { 9553e12c5d1SDavid du Colombier static String *s; 9567dd7cddfSDavid du Colombier char *p; 9573e12c5d1SDavid du Colombier 9583e12c5d1SDavid du Colombier if(domain==0 || strchr(name, '.')!=0) 9593e12c5d1SDavid du Colombier return name; 9603e12c5d1SDavid du Colombier 9613e12c5d1SDavid du Colombier s = s_reset(s); 9623e12c5d1SDavid du Colombier s_append(s, name); 9637dd7cddfSDavid du Colombier p = strchr(domain, '.'); 9647dd7cddfSDavid du Colombier if(p == 0){ 965219b2ee8SDavid du Colombier s_append(s, "."); 9667dd7cddfSDavid du Colombier p = domain; 9677dd7cddfSDavid du Colombier } 9687dd7cddfSDavid du Colombier s_append(s, p); 9693e12c5d1SDavid du Colombier return s_to_c(s); 9703e12c5d1SDavid du Colombier } 9713e12c5d1SDavid du Colombier 9723e12c5d1SDavid du Colombier /* 9733e12c5d1SDavid du Colombier * print message observing '.' escapes and using \r\n for \n 9743e12c5d1SDavid du Colombier */ 9753e12c5d1SDavid du Colombier void 9763e12c5d1SDavid du Colombier putcrnl(char *cp, int n) 9773e12c5d1SDavid du Colombier { 9783e12c5d1SDavid du Colombier int c; 9793e12c5d1SDavid du Colombier 9803e12c5d1SDavid du Colombier for(; n; n--, cp++){ 9813e12c5d1SDavid du Colombier c = *cp; 9823e12c5d1SDavid du Colombier if(c == '\n') 9837dd7cddfSDavid du Colombier dBputc('\r'); 9843e12c5d1SDavid du Colombier else if(c == '.' && last=='\n') 9857dd7cddfSDavid du Colombier dBputc('.'); 9867dd7cddfSDavid du Colombier dBputc(c); 9873e12c5d1SDavid du Colombier last = c; 9883e12c5d1SDavid du Colombier } 9893e12c5d1SDavid du Colombier } 9903e12c5d1SDavid du Colombier 9913e12c5d1SDavid du Colombier /* 9923e12c5d1SDavid du Colombier * Get a line including a crnl into a string. Convert crnl into nl. 9933e12c5d1SDavid du Colombier */ 9943e12c5d1SDavid du Colombier char * 9953e12c5d1SDavid du Colombier getcrnl(String *s) 9963e12c5d1SDavid du Colombier { 9973e12c5d1SDavid du Colombier int c; 9983e12c5d1SDavid du Colombier int count; 9993e12c5d1SDavid du Colombier 10003e12c5d1SDavid du Colombier count = 0; 10013e12c5d1SDavid du Colombier for(;;){ 10023e12c5d1SDavid du Colombier c = Bgetc(&bin); 10033e12c5d1SDavid du Colombier if(debug) 10043e12c5d1SDavid du Colombier Bputc(&berr, c); 10053e12c5d1SDavid du Colombier switch(c){ 10063e12c5d1SDavid du Colombier case -1: 10077dd7cddfSDavid du Colombier s_append(s, "connection closed unexpectedly by remote system"); 10083e12c5d1SDavid du Colombier s_terminate(s); 10093e12c5d1SDavid du Colombier return 0; 10103e12c5d1SDavid du Colombier case '\r': 10113e12c5d1SDavid du Colombier c = Bgetc(&bin); 10123e12c5d1SDavid du Colombier if(c == '\n'){ 1013499069deSDavid du Colombier case '\n': 10143e12c5d1SDavid du Colombier s_putc(s, c); 10153e12c5d1SDavid du Colombier if(debug) 10163e12c5d1SDavid du Colombier Bputc(&berr, c); 10173e12c5d1SDavid du Colombier count++; 10183e12c5d1SDavid du Colombier s_terminate(s); 10193e12c5d1SDavid du Colombier return s->ptr - count; 10203e12c5d1SDavid du Colombier } 10213e12c5d1SDavid du Colombier Bungetc(&bin); 10223e12c5d1SDavid du Colombier s_putc(s, '\r'); 10233e12c5d1SDavid du Colombier if(debug) 10243e12c5d1SDavid du Colombier Bputc(&berr, '\r'); 10253e12c5d1SDavid du Colombier count++; 10263e12c5d1SDavid du Colombier break; 10273e12c5d1SDavid du Colombier default: 10283e12c5d1SDavid du Colombier s_putc(s, c); 10293e12c5d1SDavid du Colombier count++; 10303e12c5d1SDavid du Colombier break; 10313e12c5d1SDavid du Colombier } 10323e12c5d1SDavid du Colombier } 10333e12c5d1SDavid du Colombier } 10343e12c5d1SDavid du Colombier 10353e12c5d1SDavid du Colombier /* 10363e12c5d1SDavid du Colombier * print out a parsed date 10373e12c5d1SDavid du Colombier */ 10387dd7cddfSDavid du Colombier int 10393e12c5d1SDavid du Colombier printdate(Node *p) 10403e12c5d1SDavid du Colombier { 10417dd7cddfSDavid du Colombier int n, sep = 0; 10423e12c5d1SDavid du Colombier 10437dd7cddfSDavid du Colombier n = dBprint("Date: %s,", s_to_c(p->s)); 10443e12c5d1SDavid du Colombier for(p = p->next; p; p = p->next){ 10453e12c5d1SDavid du Colombier if(p->s){ 10467dd7cddfSDavid du Colombier if(sep == 0) { 10477dd7cddfSDavid du Colombier dBputc(' '); 10487dd7cddfSDavid du Colombier n++; 10497dd7cddfSDavid du Colombier } 1050bd389b36SDavid du Colombier if (p->next) 10517dd7cddfSDavid du Colombier n += dBprint("%s", s_to_c(p->s)); 1052bd389b36SDavid du Colombier else 10537dd7cddfSDavid du Colombier n += dBprint("%s", rewritezone(s_to_c(p->s))); 10543e12c5d1SDavid du Colombier sep = 0; 10553e12c5d1SDavid du Colombier } else { 10567dd7cddfSDavid du Colombier dBputc(p->c); 10577dd7cddfSDavid du Colombier n++; 10583e12c5d1SDavid du Colombier sep = 1; 10593e12c5d1SDavid du Colombier } 10603e12c5d1SDavid du Colombier } 10617dd7cddfSDavid du Colombier n += dBprint("\r\n"); 10627dd7cddfSDavid du Colombier return n; 10633e12c5d1SDavid du Colombier } 10643e12c5d1SDavid du Colombier 1065bd389b36SDavid du Colombier char * 1066bd389b36SDavid du Colombier rewritezone(char *z) 1067bd389b36SDavid du Colombier { 10687dd7cddfSDavid du Colombier int mindiff; 10697dd7cddfSDavid du Colombier char s; 10707dd7cddfSDavid du Colombier Tm *tm; 10717dd7cddfSDavid du Colombier static char x[7]; 1072bd389b36SDavid du Colombier 10737dd7cddfSDavid du Colombier tm = localtime(time(0)); 10747dd7cddfSDavid du Colombier mindiff = tm->tzoff/60; 1075bd389b36SDavid du Colombier 10767dd7cddfSDavid du Colombier /* if not in my timezone, don't change anything */ 10777dd7cddfSDavid du Colombier if(strcmp(tm->zone, z) != 0) 1078bd389b36SDavid du Colombier return z; 10797dd7cddfSDavid du Colombier 10807dd7cddfSDavid du Colombier if(mindiff < 0){ 10817dd7cddfSDavid du Colombier s = '-'; 10827dd7cddfSDavid du Colombier mindiff = -mindiff; 10837dd7cddfSDavid du Colombier } else 10847dd7cddfSDavid du Colombier s = '+'; 10857dd7cddfSDavid du Colombier 10867dd7cddfSDavid du Colombier sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60); 10877dd7cddfSDavid du Colombier return x; 1088bd389b36SDavid du Colombier } 1089bd389b36SDavid du Colombier 10903e12c5d1SDavid du Colombier /* 10913e12c5d1SDavid du Colombier * stolen from libc/port/print.c 10923e12c5d1SDavid du Colombier */ 10933e12c5d1SDavid du Colombier #define SIZE 4096 10943e12c5d1SDavid du Colombier int 10953e12c5d1SDavid du Colombier dBprint(char *fmt, ...) 10963e12c5d1SDavid du Colombier { 10973e12c5d1SDavid du Colombier char buf[SIZE], *out; 10987dd7cddfSDavid du Colombier va_list arg; 10993e12c5d1SDavid du Colombier int n; 11003e12c5d1SDavid du Colombier 11017dd7cddfSDavid du Colombier va_start(arg, fmt); 11029a747e4fSDavid du Colombier out = vseprint(buf, buf+SIZE, fmt, arg); 11037dd7cddfSDavid du Colombier va_end(arg); 11043e12c5d1SDavid du Colombier if(debug){ 11053e12c5d1SDavid du Colombier Bwrite(&berr, buf, (long)(out-buf)); 11063e12c5d1SDavid du Colombier Bflush(&berr); 11073e12c5d1SDavid du Colombier } 11083e12c5d1SDavid du Colombier n = Bwrite(&bout, buf, (long)(out-buf)); 11093e12c5d1SDavid du Colombier Bflush(&bout); 11103e12c5d1SDavid du Colombier return n; 11113e12c5d1SDavid du Colombier } 11127dd7cddfSDavid du Colombier 11137dd7cddfSDavid du Colombier int 11147dd7cddfSDavid du Colombier dBputc(int x) 11157dd7cddfSDavid du Colombier { 11167dd7cddfSDavid du Colombier if(debug) 11177dd7cddfSDavid du Colombier Bputc(&berr, x); 11187dd7cddfSDavid du Colombier return Bputc(&bout, x); 11197dd7cddfSDavid du Colombier } 1120