smtp.c 20 KB

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