smtp.c 20 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096
  1. #include "common.h"
  2. #include "smtp.h"
  3. #include <ctype.h>
  4. #include <mp.h>
  5. #include <libsec.h>
  6. #include <auth.h>
  7. static char* connect(char*);
  8. static char* dotls(char*);
  9. static char* doauth(char*);
  10. char* hello(char*, int);
  11. char* mailfrom(char*);
  12. char* rcptto(char*);
  13. char* data(String*, Biobuf*);
  14. void quit(char*);
  15. int getreply(void);
  16. void addhostdom(String*, char*);
  17. String* bangtoat(char*);
  18. String* convertheader(String*);
  19. int printheader(void);
  20. char* domainify(char*, char*);
  21. void putcrnl(char*, int);
  22. char* getcrnl(String*);
  23. int printdate(Node*);
  24. char *rewritezone(char *);
  25. int dBprint(char*, ...);
  26. int dBputc(int);
  27. String* fixrouteaddr(String*, Node*, Node*);
  28. int ping;
  29. int insecure;
  30. #define Retry "Retry, Temporary Failure"
  31. #define Giveup "Permanent Failure"
  32. int debug; /* true if we're debugging */
  33. String *reply; /* last reply */
  34. String *toline;
  35. int alarmscale;
  36. int last = 'n'; /* last character sent by putcrnl() */
  37. int filter;
  38. int trysecure; /* Try to use TLS if the other side supports it */
  39. int tryauth; /* Try to authenticate, if supported */
  40. int quitting; /* when error occurs in quit */
  41. char *quitrv; /* deferred return value when in quit */
  42. char ddomain[1024]; /* domain name of destination machine */
  43. char *gdomain; /* domain name of gateway */
  44. char *uneaten; /* first character after rfc822 headers */
  45. char *farend; /* system we are trying to send to */
  46. char *user; /* user we are authenticating as, if authenticating */
  47. char hostdomain[256];
  48. Biobuf bin;
  49. Biobuf bout;
  50. Biobuf berr;
  51. Biobuf bfile;
  52. void
  53. usage(void)
  54. {
  55. fprint(2, "usage: smtp [-adips] [-uuser] [-hhost] [.domain] net!host[!service] sender rcpt-list\n");
  56. exits(Giveup);
  57. }
  58. int
  59. timeout(void *x, char *msg)
  60. {
  61. USED(x);
  62. syslog(0, "smtp.fail", "interrupt: %s: %s", farend, msg);
  63. if(strstr(msg, "alarm")){
  64. fprint(2, "smtp timeout: connection to %s timed out\n", farend);
  65. if(quitting)
  66. exits(quitrv);
  67. exits(Retry);
  68. }
  69. if(strstr(msg, "closed pipe")){
  70. /* call _exits() to prevent Bio from trying to flush closed pipe */
  71. fprint(2, "smtp timeout: connection closed to %s\n", farend);
  72. if(quitting){
  73. syslog(0, "smtp.fail", "closed pipe to %s", farend);
  74. _exits(quitrv);
  75. }
  76. _exits(Retry);
  77. }
  78. return 0;
  79. }
  80. void
  81. removenewline(char *p)
  82. {
  83. int n = strlen(p)-1;
  84. if(n < 0)
  85. return;
  86. if(p[n] == '\n')
  87. p[n] = 0;
  88. }
  89. void
  90. main(int argc, char **argv)
  91. {
  92. char hellodomain[256];
  93. char *host, *domain;
  94. String *from;
  95. String *fromm;
  96. String *sender;
  97. char *addr;
  98. char *rv, *trv;
  99. int i, ok, rcvrs;
  100. char **errs;
  101. alarmscale = 60*1000; /* minutes */
  102. quotefmtinstall();
  103. errs = malloc(argc*sizeof(char*));
  104. reply = s_new();
  105. host = 0;
  106. ARGBEGIN{
  107. case 'a':
  108. tryauth = 1;
  109. trysecure = 1;
  110. break;
  111. case 'f':
  112. filter = 1;
  113. break;
  114. case 'd':
  115. debug = 1;
  116. break;
  117. case 'g':
  118. gdomain = ARGF();
  119. break;
  120. case 'h':
  121. host = ARGF();
  122. break;
  123. case 'i':
  124. insecure = 1;
  125. break;
  126. case 'p':
  127. alarmscale = 10*1000; /* tens of seconds */
  128. ping = 1;
  129. break;
  130. case 's':
  131. trysecure = 1;
  132. break;
  133. case 'u':
  134. user = ARGF();
  135. break;
  136. default:
  137. usage();
  138. break;
  139. }ARGEND;
  140. Binit(&berr, 2, OWRITE);
  141. Binit(&bfile, 0, OREAD);
  142. /*
  143. * get domain and add to host name
  144. */
  145. if(*argv && **argv=='.') {
  146. domain = *argv;
  147. argv++; argc--;
  148. } else
  149. domain = domainname_read();
  150. if(host == 0)
  151. host = sysname_read();
  152. strcpy(hostdomain, domainify(host, domain));
  153. strcpy(hellodomain, domainify(sysname_read(), domain));
  154. /*
  155. * get destination address
  156. */
  157. if(*argv == 0)
  158. usage();
  159. addr = *argv++; argc--;
  160. farend = addr;
  161. /*
  162. * get sender's machine.
  163. * get sender in internet style. domainify if necessary.
  164. */
  165. if(*argv == 0)
  166. usage();
  167. sender = unescapespecial(s_copy(*argv++));
  168. argc--;
  169. fromm = s_clone(sender);
  170. rv = strrchr(s_to_c(fromm), '!');
  171. if(rv)
  172. *rv = 0;
  173. else
  174. *s_to_c(fromm) = 0;
  175. from = bangtoat(s_to_c(sender));
  176. /*
  177. * send the mail
  178. */
  179. if(filter){
  180. Binit(&bout, 1, OWRITE);
  181. rv = data(from, &bfile);
  182. if(rv != 0)
  183. goto error;
  184. exits(0);
  185. }
  186. /* mxdial uses its own timeout handler */
  187. if((rv = connect(addr)) != 0)
  188. exits(rv);
  189. /* 10 minutes to get through the initial handshake */
  190. atnotify(timeout, 1);
  191. alarm(10*alarmscale);
  192. if((rv = hello(hellodomain, 0)) != 0)
  193. goto error;
  194. alarm(10*alarmscale);
  195. if((rv = mailfrom(s_to_c(from))) != 0)
  196. goto error;
  197. ok = 0;
  198. rcvrs = 0;
  199. /* if any rcvrs are ok, we try to send the message */
  200. for(i = 0; i < argc; i++){
  201. if((trv = rcptto(argv[i])) != 0){
  202. /* remember worst error */
  203. if(rv != Giveup)
  204. rv = trv;
  205. errs[rcvrs] = strdup(s_to_c(reply));
  206. removenewline(errs[rcvrs]);
  207. } else {
  208. ok++;
  209. errs[rcvrs] = 0;
  210. }
  211. rcvrs++;
  212. }
  213. /* if no ok rcvrs or worst error is retry, give up */
  214. if(ok == 0 || rv == Retry)
  215. goto error;
  216. if(ping){
  217. quit(0);
  218. exits(0);
  219. }
  220. rv = data(from, &bfile);
  221. if(rv != 0)
  222. goto error;
  223. quit(0);
  224. if(rcvrs == ok)
  225. exits(0);
  226. /*
  227. * here when some but not all rcvrs failed
  228. */
  229. fprint(2, "%s connect to %s:\n", thedate(), addr);
  230. for(i = 0; i < rcvrs; i++){
  231. if(errs[i]){
  232. syslog(0, "smtp.fail", "delivery to %s at %s failed: %s", argv[i], addr, errs[i]);
  233. fprint(2, " mail to %s failed: %s", argv[i], errs[i]);
  234. }
  235. }
  236. exits(Giveup);
  237. /*
  238. * here when all rcvrs failed
  239. */
  240. error:
  241. removenewline(s_to_c(reply));
  242. syslog(0, "smtp.fail", "%s to %s failed: %s",
  243. ping ? "ping" : "delivery",
  244. addr, s_to_c(reply));
  245. fprint(2, "%s connect to %s:\n%s\n", thedate(), addr, s_to_c(reply));
  246. if(!filter)
  247. quit(rv);
  248. exits(rv);
  249. }
  250. /*
  251. * connect to the remote host
  252. */
  253. static char *
  254. connect(char* net)
  255. {
  256. char buf[256];
  257. int fd;
  258. fd = mxdial(net, ddomain, gdomain);
  259. if(fd < 0){
  260. rerrstr(buf, sizeof(buf));
  261. Bprint(&berr, "smtp: %s (%s)\n", buf, net);
  262. syslog(0, "smtp.fail", "%s (%s)", buf, net);
  263. if(strstr(buf, "illegal")
  264. || strstr(buf, "unknown")
  265. || strstr(buf, "can't translate"))
  266. return Giveup;
  267. else
  268. return Retry;
  269. }
  270. Binit(&bin, fd, OREAD);
  271. fd = dup(fd, -1);
  272. Binit(&bout, fd, OWRITE);
  273. return 0;
  274. }
  275. static char smtpthumbs[] = "/sys/lib/tls/smtp";
  276. static char smtpexclthumbs[] = "/sys/lib/tls/smtp.exclude";
  277. /*
  278. * exchange names with remote host, attempt to
  279. * enable encryption and optionally authenticate.
  280. * not fatal if we can't.
  281. */
  282. static char *
  283. dotls(char *me)
  284. {
  285. TLSconn *c;
  286. Thumbprint *goodcerts;
  287. char *h;
  288. int fd;
  289. uchar hash[SHA1dlen];
  290. c = mallocz(sizeof(*c), 1); /* Note: not freed on success */
  291. if (c == nil)
  292. return Giveup;
  293. dBprint("STARTTLS\r\n");
  294. if (getreply() != 2)
  295. return Giveup;
  296. fd = tlsClient(Bfildes(&bout), c);
  297. if (fd < 0) {
  298. syslog(0, "smtp", "tlsClient to %q: %r", ddomain);
  299. return Giveup;
  300. }
  301. goodcerts = initThumbprints(smtpthumbs, smtpexclthumbs);
  302. if (goodcerts == nil) {
  303. free(c);
  304. close(fd);
  305. syslog(0, "smtp", "bad thumbprints in %s", smtpthumbs);
  306. return Giveup; /* how to recover? TLS is started */
  307. }
  308. /* compute sha1 hash of remote's certificate, see if we know it */
  309. sha1(c->cert, c->certlen, hash, nil);
  310. if (!okThumbprint(hash, goodcerts)) {
  311. /* TODO? if not excluded, add hash to thumb list */
  312. free(c);
  313. close(fd);
  314. h = malloc(2*sizeof hash + 1);
  315. if (h != nil) {
  316. enc16(h, 2*sizeof hash + 1, hash, sizeof hash);
  317. // print("x509 sha1=%s", h);
  318. syslog(0, "smtp",
  319. "remote cert. has bad thumbprint: x509 sha1=%s server=%q",
  320. h, ddomain);
  321. free(h);
  322. }
  323. return Giveup; /* how to recover? TLS is started */
  324. }
  325. freeThumbprints(goodcerts);
  326. Bterm(&bin);
  327. Bterm(&bout);
  328. /*
  329. * set up bin & bout to use the TLS fd, i/o upon which generates
  330. * i/o on the original, underlying fd.
  331. */
  332. Binit(&bin, fd, OREAD);
  333. fd = dup(fd, -1);
  334. Binit(&bout, fd, OWRITE);
  335. syslog(0, "smtp", "started TLS to %q", ddomain);
  336. return(hello(me, 1));
  337. }
  338. static char *
  339. doauth(char *methods)
  340. {
  341. char *buf, *base64;
  342. int n;
  343. DS ds;
  344. UserPasswd *p;
  345. dial_string_parse(ddomain, &ds);
  346. if(user != nil)
  347. p = auth_getuserpasswd(nil,
  348. "proto=pass service=smtp server=%q user=%q", ds.host, user);
  349. else
  350. p = auth_getuserpasswd(nil,
  351. "proto=pass service=smtp server=%q", ds.host);
  352. if (p == nil)
  353. return Giveup;
  354. if (strstr(methods, "LOGIN")){
  355. dBprint("AUTH LOGIN\r\n");
  356. if (getreply() != 3)
  357. return Retry;
  358. n = strlen(p->user);
  359. base64 = malloc(2*n);
  360. if (base64 == nil)
  361. return Retry; /* Out of memory */
  362. enc64(base64, 2*n, (uchar *)p->user, n);
  363. dBprint("%s\r\n", base64);
  364. if (getreply() != 3)
  365. return Retry;
  366. n = strlen(p->passwd);
  367. base64 = malloc(2*n);
  368. if (base64 == nil)
  369. return Retry; /* Out of memory */
  370. enc64(base64, 2*n, (uchar *)p->passwd, n);
  371. dBprint("%s\r\n", base64);
  372. if (getreply() != 2)
  373. return Retry;
  374. free(base64);
  375. }
  376. else
  377. if (strstr(methods, "PLAIN")){
  378. n = strlen(p->user) + strlen(p->passwd) + 3;
  379. buf = malloc(n);
  380. base64 = malloc(2 * n);
  381. if (buf == nil || base64 == nil) {
  382. free(buf);
  383. return Retry; /* Out of memory */
  384. }
  385. snprint(buf, n, "%c%s%c%s", 0, p->user, 0, p->passwd);
  386. enc64(base64, 2 * n, (uchar *)buf, n - 1);
  387. free(buf);
  388. dBprint("AUTH PLAIN %s\r\n", base64);
  389. free(base64);
  390. if (getreply() != 2)
  391. return Retry;
  392. }
  393. else
  394. return "No supported AUTH method";
  395. return(0);
  396. }
  397. char *
  398. hello(char *me, int encrypted)
  399. {
  400. int ehlo;
  401. String *r;
  402. char *ret, *s, *t;
  403. if (!encrypted)
  404. switch(getreply()){
  405. case 2:
  406. break;
  407. case 5:
  408. return Giveup;
  409. default:
  410. return Retry;
  411. }
  412. ehlo = 1;
  413. Again:
  414. if(ehlo)
  415. dBprint("EHLO %s\r\n", me);
  416. else
  417. dBprint("HELO %s\r\n", me);
  418. switch (getreply()) {
  419. case 2:
  420. break;
  421. case 5:
  422. if(ehlo){
  423. ehlo = 0;
  424. goto Again;
  425. }
  426. return Giveup;
  427. default:
  428. return Retry;
  429. }
  430. r = s_clone(reply);
  431. if(r == nil)
  432. return Retry; /* Out of memory or couldn't get string */
  433. /* Invariant: every line has a newline, a result of getcrlf() */
  434. for(s = s_to_c(r); (t = strchr(s, '\n')) != nil; s = t + 1){
  435. *t = '\0';
  436. for (t = s; *t != '\0'; t++)
  437. *t = toupper(*t);
  438. if(!encrypted && trysecure &&
  439. (strcmp(s, "250-STARTTLS") == 0 ||
  440. strcmp(s, "250 STARTTLS") == 0)){
  441. s_free(r);
  442. return(dotls(me));
  443. }
  444. if(tryauth && (encrypted || insecure) &&
  445. (strncmp(s, "250 AUTH", strlen("250 AUTH")) == 0 ||
  446. strncmp(s, "250-AUTH", strlen("250 AUTH")) == 0)){
  447. ret = doauth(s + strlen("250 AUTH "));
  448. s_free(r);
  449. return ret;
  450. }
  451. }
  452. s_free(r);
  453. return 0;
  454. }
  455. /*
  456. * report sender to remote
  457. */
  458. char *
  459. mailfrom(char *from)
  460. {
  461. if(!returnable(from))
  462. dBprint("MAIL FROM:<>\r\n");
  463. else
  464. if(strchr(from, '@'))
  465. dBprint("MAIL FROM:<%s>\r\n", from);
  466. else
  467. dBprint("MAIL FROM:<%s@%s>\r\n", from, hostdomain);
  468. switch(getreply()){
  469. case 2:
  470. break;
  471. case 5:
  472. return Giveup;
  473. default:
  474. return Retry;
  475. }
  476. return 0;
  477. }
  478. /*
  479. * report a recipient to remote
  480. */
  481. char *
  482. rcptto(char *to)
  483. {
  484. String *s;
  485. s = unescapespecial(bangtoat(to));
  486. if(toline == 0)
  487. toline = s_new();
  488. else
  489. s_append(toline, ", ");
  490. s_append(toline, s_to_c(s));
  491. if(strchr(s_to_c(s), '@'))
  492. dBprint("RCPT TO:<%s>\r\n", s_to_c(s));
  493. else {
  494. s_append(toline, "@");
  495. s_append(toline, ddomain);
  496. dBprint("RCPT TO:<%s@%s>\r\n", s_to_c(s), ddomain);
  497. }
  498. alarm(10*alarmscale);
  499. switch(getreply()){
  500. case 2:
  501. break;
  502. case 5:
  503. return Giveup;
  504. default:
  505. return Retry;
  506. }
  507. return 0;
  508. }
  509. static char hex[] = "0123456789abcdef";
  510. /*
  511. * send the damn thing
  512. */
  513. char *
  514. data(String *from, Biobuf *b)
  515. {
  516. char *buf, *cp;
  517. int i, n, nbytes, bufsize, eof, r;
  518. String *fromline;
  519. char errmsg[Errlen];
  520. char id[40];
  521. /*
  522. * input the header.
  523. */
  524. buf = malloc(1);
  525. if(buf == 0){
  526. s_append(s_restart(reply), "out of memory");
  527. return Retry;
  528. }
  529. n = 0;
  530. eof = 0;
  531. for(;;){
  532. cp = Brdline(b, '\n');
  533. if(cp == nil){
  534. eof = 1;
  535. break;
  536. }
  537. nbytes = Blinelen(b);
  538. buf = realloc(buf, n+nbytes+1);
  539. if(buf == 0){
  540. s_append(s_restart(reply), "out of memory");
  541. return Retry;
  542. }
  543. strncpy(buf+n, cp, nbytes);
  544. n += nbytes;
  545. if(nbytes == 1) /* end of header */
  546. break;
  547. }
  548. buf[n] = 0;
  549. bufsize = n;
  550. /*
  551. * parse the header, turn all addresses into @ format
  552. */
  553. yyinit(buf, n);
  554. yyparse();
  555. /*
  556. * print message observing '.' escapes and using \r\n for \n
  557. */
  558. alarm(20*alarmscale);
  559. if(!filter){
  560. dBprint("DATA\r\n");
  561. switch(getreply()){
  562. case 3:
  563. break;
  564. case 5:
  565. free(buf);
  566. return Giveup;
  567. default:
  568. free(buf);
  569. return Retry;
  570. }
  571. }
  572. /*
  573. * send header. add a message-id, a sender, and a date if there
  574. * isn't one
  575. */
  576. nbytes = 0;
  577. fromline = convertheader(from);
  578. uneaten = buf;
  579. srand(truerand());
  580. if(messageid == 0){
  581. for(i=0; i<16; i++){
  582. r = rand()&0xFF;
  583. id[2*i] = hex[r&0xF];
  584. id[2*i+1] = hex[(r>>4)&0xF];
  585. }
  586. id[2*i] = '\0';
  587. nbytes += Bprint(&bout, "Message-ID: <%s@%s>\r\n", id, hostdomain);
  588. if(debug)
  589. Bprint(&berr, "Message-ID: <%s@%s>\r\n", id, hostdomain);
  590. }
  591. if(originator==0){
  592. nbytes += Bprint(&bout, "From: %s\r\n", s_to_c(fromline));
  593. if(debug)
  594. Bprint(&berr, "From: %s\r\n", s_to_c(fromline));
  595. }
  596. s_free(fromline);
  597. if(destination == 0 && toline)
  598. if(*s_to_c(toline) == '@'){ /* route addr */
  599. nbytes += Bprint(&bout, "To: <%s>\r\n", s_to_c(toline));
  600. if(debug)
  601. Bprint(&berr, "To: <%s>\r\n", s_to_c(toline));
  602. } else {
  603. nbytes += Bprint(&bout, "To: %s\r\n", s_to_c(toline));
  604. if(debug)
  605. Bprint(&berr, "To: %s\r\n", s_to_c(toline));
  606. }
  607. if(date==0 && udate)
  608. nbytes += printdate(udate);
  609. if (usys)
  610. uneaten = usys->end + 1;
  611. nbytes += printheader();
  612. if (*uneaten != '\n')
  613. putcrnl("\n", 1);
  614. /*
  615. * send body
  616. */
  617. putcrnl(uneaten, buf+n - uneaten);
  618. nbytes += buf+n - uneaten;
  619. if(eof == 0){
  620. for(;;){
  621. n = Bread(b, buf, bufsize);
  622. if(n < 0){
  623. rerrstr(errmsg, sizeof(errmsg));
  624. s_append(s_restart(reply), errmsg);
  625. free(buf);
  626. return Retry;
  627. }
  628. if(n == 0)
  629. break;
  630. alarm(10*alarmscale);
  631. putcrnl(buf, n);
  632. nbytes += n;
  633. }
  634. }
  635. free(buf);
  636. if(!filter){
  637. if(last != '\n')
  638. dBprint("\r\n.\r\n");
  639. else
  640. dBprint(".\r\n");
  641. alarm(10*alarmscale);
  642. switch(getreply()){
  643. case 2:
  644. break;
  645. case 5:
  646. return Giveup;
  647. default:
  648. return Retry;
  649. }
  650. syslog(0, "smtp", "%s sent %d bytes to %s", s_to_c(from),
  651. nbytes, s_to_c(toline));/**/
  652. }
  653. return 0;
  654. }
  655. /*
  656. * we're leaving
  657. */
  658. void
  659. quit(char *rv)
  660. {
  661. /* 60 minutes to quit */
  662. quitting = 1;
  663. quitrv = rv;
  664. alarm(60*alarmscale);
  665. dBprint("QUIT\r\n");
  666. getreply();
  667. Bterm(&bout);
  668. Bterm(&bfile);
  669. }
  670. /*
  671. * read a reply into a string, return the reply code
  672. */
  673. int
  674. getreply(void)
  675. {
  676. char *line;
  677. int rv;
  678. reply = s_reset(reply);
  679. for(;;){
  680. line = getcrnl(reply);
  681. if(debug)
  682. Bflush(&berr);
  683. if(line == 0)
  684. return -1;
  685. if(!isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]))
  686. return -1;
  687. if(line[3] != '-')
  688. break;
  689. }
  690. if(debug)
  691. Bflush(&berr);
  692. rv = atoi(line)/100;
  693. return rv;
  694. }
  695. void
  696. addhostdom(String *buf, char *host)
  697. {
  698. s_append(buf, "@");
  699. s_append(buf, host);
  700. }
  701. /*
  702. * Convert from `bang' to `source routing' format.
  703. *
  704. * a.x.y!b.p.o!c!d -> @a.x.y:c!d@b.p.o
  705. */
  706. String *
  707. bangtoat(char *addr)
  708. {
  709. String *buf;
  710. register int i;
  711. int j, d;
  712. char *field[128];
  713. /* parse the '!' format address */
  714. buf = s_new();
  715. for(i = 0; addr; i++){
  716. field[i] = addr;
  717. addr = strchr(addr, '!');
  718. if(addr)
  719. *addr++ = 0;
  720. }
  721. if (i==1) {
  722. s_append(buf, field[0]);
  723. return buf;
  724. }
  725. /*
  726. * count leading domain fields (non-domains don't count)
  727. */
  728. for(d = 0; d<i-1; d++)
  729. if(strchr(field[d], '.')==0)
  730. break;
  731. /*
  732. * if there are more than 1 leading domain elements,
  733. * put them in as source routing
  734. */
  735. if(d > 1){
  736. addhostdom(buf, field[0]);
  737. for(j=1; j<d-1; j++){
  738. s_append(buf, ",");
  739. s_append(buf, "@");
  740. s_append(buf, field[j]);
  741. }
  742. s_append(buf, ":");
  743. }
  744. /*
  745. * throw in the non-domain elements separated by '!'s
  746. */
  747. s_append(buf, field[d]);
  748. for(j=d+1; j<=i-1; j++) {
  749. s_append(buf, "!");
  750. s_append(buf, field[j]);
  751. }
  752. if(d)
  753. addhostdom(buf, field[d-1]);
  754. return buf;
  755. }
  756. /*
  757. * convert header addresses to @ format.
  758. * if the address is a source address, and a domain is specified,
  759. * make sure it falls in the domain.
  760. */
  761. String*
  762. convertheader(String *from)
  763. {
  764. Field *f;
  765. Node *p, *lastp;
  766. String *a;
  767. if(!returnable(s_to_c(from))){
  768. from = s_new();
  769. s_append(from, "Postmaster");
  770. addhostdom(from, hostdomain);
  771. } else
  772. if(strchr(s_to_c(from), '@') == 0){
  773. a = username(from);
  774. if(a) {
  775. s_append(a, " <");
  776. s_append(a, s_to_c(from));
  777. addhostdom(a, hostdomain);
  778. s_append(a, ">");
  779. from = a;
  780. } else {
  781. from = s_copy(s_to_c(from));
  782. addhostdom(from, hostdomain);
  783. }
  784. } else
  785. from = s_copy(s_to_c(from));
  786. for(f = firstfield; f; f = f->next){
  787. lastp = 0;
  788. for(p = f->node; p; lastp = p, p = p->next){
  789. if(!p->addr)
  790. continue;
  791. a = bangtoat(s_to_c(p->s));
  792. s_free(p->s);
  793. if(strchr(s_to_c(a), '@') == 0)
  794. addhostdom(a, hostdomain);
  795. else if(*s_to_c(a) == '@')
  796. a = fixrouteaddr(a, p->next, lastp);
  797. p->s = a;
  798. }
  799. }
  800. return from;
  801. }
  802. /*
  803. * ensure route addr has brackets around it
  804. */
  805. String*
  806. fixrouteaddr(String *raddr, Node *next, Node *last)
  807. {
  808. String *a;
  809. if(last && last->c == '<' && next && next->c == '>')
  810. return raddr; /* properly formed already */
  811. a = s_new();
  812. s_append(a, "<");
  813. s_append(a, s_to_c(raddr));
  814. s_append(a, ">");
  815. s_free(raddr);
  816. return a;
  817. }
  818. /*
  819. * print out the parsed header
  820. */
  821. int
  822. printheader(void)
  823. {
  824. int n, len;
  825. Field *f;
  826. Node *p;
  827. char *cp;
  828. char c[1];
  829. n = 0;
  830. for(f = firstfield; f; f = f->next){
  831. for(p = f->node; p; p = p->next){
  832. if(p->s)
  833. n += dBprint("%s", s_to_c(p->s));
  834. else {
  835. c[0] = p->c;
  836. putcrnl(c, 1);
  837. n++;
  838. }
  839. if(p->white){
  840. cp = s_to_c(p->white);
  841. len = strlen(cp);
  842. putcrnl(cp, len);
  843. n += len;
  844. }
  845. uneaten = p->end;
  846. }
  847. putcrnl("\n", 1);
  848. n++;
  849. uneaten++; /* skip newline */
  850. }
  851. return n;
  852. }
  853. /*
  854. * add a domain onto an name, return the new name
  855. */
  856. char *
  857. domainify(char *name, char *domain)
  858. {
  859. static String *s;
  860. char *p;
  861. if(domain==0 || strchr(name, '.')!=0)
  862. return name;
  863. s = s_reset(s);
  864. s_append(s, name);
  865. p = strchr(domain, '.');
  866. if(p == 0){
  867. s_append(s, ".");
  868. p = domain;
  869. }
  870. s_append(s, p);
  871. return s_to_c(s);
  872. }
  873. /*
  874. * print message observing '.' escapes and using \r\n for \n
  875. */
  876. void
  877. putcrnl(char *cp, int n)
  878. {
  879. int c;
  880. for(; n; n--, cp++){
  881. c = *cp;
  882. if(c == '\n')
  883. dBputc('\r');
  884. else if(c == '.' && last=='\n')
  885. dBputc('.');
  886. dBputc(c);
  887. last = c;
  888. }
  889. }
  890. /*
  891. * Get a line including a crnl into a string. Convert crnl into nl.
  892. */
  893. char *
  894. getcrnl(String *s)
  895. {
  896. int c;
  897. int count;
  898. count = 0;
  899. for(;;){
  900. c = Bgetc(&bin);
  901. if(debug)
  902. Bputc(&berr, c);
  903. switch(c){
  904. case -1:
  905. s_append(s, "connection closed unexpectedly by remote system");
  906. s_terminate(s);
  907. return 0;
  908. case '\r':
  909. c = Bgetc(&bin);
  910. if(c == '\n'){
  911. case '\n':
  912. s_putc(s, c);
  913. if(debug)
  914. Bputc(&berr, c);
  915. count++;
  916. s_terminate(s);
  917. return s->ptr - count;
  918. }
  919. Bungetc(&bin);
  920. s_putc(s, '\r');
  921. if(debug)
  922. Bputc(&berr, '\r');
  923. count++;
  924. break;
  925. default:
  926. s_putc(s, c);
  927. count++;
  928. break;
  929. }
  930. }
  931. }
  932. /*
  933. * print out a parsed date
  934. */
  935. int
  936. printdate(Node *p)
  937. {
  938. int n, sep = 0;
  939. n = dBprint("Date: %s,", s_to_c(p->s));
  940. for(p = p->next; p; p = p->next){
  941. if(p->s){
  942. if(sep == 0) {
  943. dBputc(' ');
  944. n++;
  945. }
  946. if (p->next)
  947. n += dBprint("%s", s_to_c(p->s));
  948. else
  949. n += dBprint("%s", rewritezone(s_to_c(p->s)));
  950. sep = 0;
  951. } else {
  952. dBputc(p->c);
  953. n++;
  954. sep = 1;
  955. }
  956. }
  957. n += dBprint("\r\n");
  958. return n;
  959. }
  960. char *
  961. rewritezone(char *z)
  962. {
  963. int mindiff;
  964. char s;
  965. Tm *tm;
  966. static char x[7];
  967. tm = localtime(time(0));
  968. mindiff = tm->tzoff/60;
  969. /* if not in my timezone, don't change anything */
  970. if(strcmp(tm->zone, z) != 0)
  971. return z;
  972. if(mindiff < 0){
  973. s = '-';
  974. mindiff = -mindiff;
  975. } else
  976. s = '+';
  977. sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
  978. return x;
  979. }
  980. /*
  981. * stolen from libc/port/print.c
  982. */
  983. #define SIZE 4096
  984. int
  985. dBprint(char *fmt, ...)
  986. {
  987. char buf[SIZE], *out;
  988. va_list arg;
  989. int n;
  990. va_start(arg, fmt);
  991. out = vseprint(buf, buf+SIZE, fmt, arg);
  992. va_end(arg);
  993. if(debug){
  994. Bwrite(&berr, buf, (long)(out-buf));
  995. Bflush(&berr);
  996. }
  997. n = Bwrite(&bout, buf, (long)(out-buf));
  998. Bflush(&bout);
  999. return n;
  1000. }
  1001. int
  1002. dBputc(int x)
  1003. {
  1004. if(debug)
  1005. Bputc(&berr, x);
  1006. return Bputc(&bout, x);
  1007. }