|
|
1.1 ! root 1: #ifndef lint ! 2: static char *sccsid = "@(#)converse.c 1.9 87/07/31"; ! 3: #endif lint ! 4: /* Copyright 1984 Massachusetts Institute of Technology ! 5: ! 6: Permission to use, copy, modify, and distribute this program ! 7: for any purpose and without fee is hereby granted, provided ! 8: that this copyright and permission notice appear on all copies ! 9: and supporting documentation, the name of M.I.T. not be used ! 10: in advertising or publicity pertaining to distribution of the ! 11: program without specific prior permission, and notice be given ! 12: in supporting documentation that copying and distribution is ! 13: by permission of M.I.T. M.I.T. makes no representations about ! 14: the suitability of this software for any purpose. It is pro- ! 15: vided "as is" without express or implied warranty. */ ! 16: ! 17: /* ! 18: * I've raped this code severely. Bugs could be ! 19: * MIT's or mine. -- presotto ! 20: * ! 21: * Me too. -- ches ! 22: */ ! 23: ! 24: /* ! 25: * smtpd - World's most trivial SMTP server. Only accepts the MAIL, FROM, ! 26: * RCPT, and DATA commands. Generates a data file for the mail ! 27: * daemon and kicks the mail daemon off. ! 28: */ ! 29: ! 30: #include <stdio.h> ! 31: #include <signal.h> ! 32: #include <ctype.h> ! 33: ! 34: #include <fcntl.h> ! 35: #include <string.h> ! 36: #include <sysexits.h> ! 37: ! 38: #include "mail.h" ! 39: #include "smtp.h" ! 40: #include "cmds.h" ! 41: #include "string.h" ! 42: ! 43: /* fundamental constants */ ! 44: #define TRUE 1 ! 45: #define FALSE 0 ! 46: #define SECONDS 1 ! 47: #define MINUTES 60 ! 48: #define HOURS (60 * MINUTES) ! 49: ! 50: /* tunable constants */ ! 51: #define SHORTTIME (5 * MINUTES) /* enough time for easy stuff */ ! 52: #define LONGTIME (2 * HOURS) /* max time, DATA to `.' */ ! 53: ! 54: ! 55: static string *rcvrs; ! 56: ! 57: FILE *datafd; /* data file descriptor */ ! 58: FILE *fi; /* fd from remote host */ ! 59: FILE *fo; /* fd to remote host */ ! 60: ! 61: char dataname[NAMSIZ], rcptto[BUFSIZ]; /* data file name */ ! 62: ! 63: typedef int event; ! 64: ! 65: static void terminate(); ! 66: ! 67: extern char *convertaddr(); ! 68: extern char *UPASROOT; ! 69: extern int death(); ! 70: extern int alarmsend(); ! 71: extern char *helohost; ! 72: extern char *thishost; ! 73: extern int queuing; ! 74: extern int norun; ! 75: extern char *spoolsubdir[]; ! 76: ! 77: int n_rcpt = 0; ! 78: long nbytes = 0; ! 79: int virus = 0; ! 80: ! 81: static char mailfrom[BUFSIZ]; ! 82: static char *fromaddr; ! 83: ! 84: ! 85: #ifndef NOFILE ! 86: #define NOFILE 32 ! 87: #endif /*NOFILE*/ ! 88: ! 89: SIGRETURN ! 90: alarmtr(s) ! 91: int s; ! 92: { ! 93: Syslog(LOG_INFO, "timed out"); ! 94: death(EX_TEMPFAIL); ! 95: } ! 96: ! 97: /* ! 98: * This is the routine which processes incoming smtp commands from the ! 99: * user. It goes to sleep awaiting network input. When a complete ! 100: * command is received, the tcp receiver task awakens us to process it. ! 101: * Currently only the commands listed in the command table are accepted. ! 102: * This routine never returns. ! 103: */ ! 104: converse(rfi, rfo) ! 105: FILE *rfi, *rfo; ! 106: { ! 107: char greeting[MAXSTR]; ! 108: ! 109: fo = rfo; ! 110: fi = rfi; ! 111: ! 112: (void) signal(SIGALRM, alarmtr); ! 113: (void) alarm(SHORTTIME); /* make sure we eventually go away */ ! 114: (void) sprintf(greeting, "220 %s SMTP\n", helohost); ! 115: csend(LOG_DEBUG, greeting); ! 116: ! 117: do_helo(fi, fo); /* wait for the hello */ ! 118: ! 119: /* ! 120: * avoid annoying interuptions ! 121: */ ! 122: signal(SIGHUP, SIG_IGN); ! 123: signal(SIGPIPE, SIG_IGN); ! 124: ! 125: for (;;) { /* until QUIT */ ! 126: n_rcpt = 0; ! 127: rcvrs = s_reset(rcvrs); ! 128: *dataname = *rcptto = 0; ! 129: fromaddr = 0; ! 130: if (!do_mail(fi, fo)) ! 131: continue; /* wait for the mail command */ ! 132: while (do_rcpt(fi, fo)) /* do all the recipients */ ! 133: n_rcpt++; ! 134: (void) alarm(LONGTIME); ! 135: do_data(fi, fo); /* do the data */ ! 136: } ! 137: } ! 138: ! 139: /* ! 140: * Wait for the user to send the HELO command. Punt out if he sends ! 141: * QUIT or RSET. ! 142: * ! 143: * The spooling directory depends on the calling host. The host name ! 144: * is used to connect to the appropriate spool directory. ! 145: */ ! 146: do_helo(fi, fo) ! 147: FILE *fi, *fo; ! 148: { ! 149: char cmdbuf[MAXSTR]; ! 150: char greeting[MAXSTR], *nlptr; ! 151: int buflen; ! 152: char *hp; ! 153: char *parse_hello(); ! 154: ! 155: for (;;) { /* until HELO, or QUIT */ ! 156: buflen = crecv(cmdbuf, sizeof cmdbuf, fi); /* wait for command */ ! 157: switch (cmdparse(cmdbuf, buflen)) { ! 158: case QUIT: ! 159: quit(fi, fo); ! 160: case RSET: ! 161: case NOOP: ! 162: csend(LOG_DEBUG, "250 OK\n"); ! 163: continue; ! 164: case HELO: ! 165: hp = parse_hello(cmdbuf, sizeof(cmdbuf)); ! 166: Syslog(LOG_DEBUG, "HELO from %s", hp); ! 167: if(gotodir(hp)<0){ ! 168: csend(LOG_ALERT, "451 Transaction failed -- Could not access spool directory.\n"); ! 169: death(EX_OSERR); ! 170: } ! 171: (void) sprintf(greeting, "250 %s\n", helohost); ! 172: csend(LOG_DEBUG, greeting); ! 173: return; ! 174: case DEBG: ! 175: Syslog(LOG_ALERT, "DEBUG attempt"); ! 176: csend(LOG_DEBUG, "200 OK\n"); ! 177: virus = 1; ! 178: continue; ! 179: case NONE: ! 180: bitch(cmdbuf, fo); ! 181: continue; ! 182: default: ! 183: csend(LOG_DEBUG, "503 Expecting HELO\n"); ! 184: continue; ! 185: } ! 186: } ! 187: } ! 188: ! 189: /* ! 190: * Wait for the user to send the MAIL command. Punt out if he sends ! 191: * QUIT. Return false if he said RSET, so we can start over. ! 192: */ ! 193: do_mail(fi, fo) ! 194: FILE *fi, *fo; ! 195: { ! 196: char cmdbuf[MAXSTR]; ! 197: char gripe[MAXSTR], *nlptr; ! 198: int buflen; ! 199: ! 200: for (;;) { /* until MAIL, QUIT, or RSET */ ! 201: buflen = crecv(cmdbuf, sizeof cmdbuf, fi); /* wait for command */ ! 202: switch (cmdparse(cmdbuf, buflen)) { ! 203: case QUIT: ! 204: quit(fi, fo); ! 205: case NOOP: ! 206: csend(LOG_DEBUG, "250 OK\n"); ! 207: continue; ! 208: case MAIL: ! 209: strcpy(mailfrom, cmdbuf); ! 210: csend(LOG_DEBUG, "250 OK\n"); ! 211: return(TRUE); ! 212: case DEBG: ! 213: Syslog(LOG_ALERT, "DEBUG attempt"); ! 214: csend(LOG_WARNING, "200 OK\n"); ! 215: virus = 1; ! 216: continue; ! 217: case VRFY: ! 218: csend(LOG_INFO, "252 Cannot VRFY user\n"); ! 219: continue; ! 220: case NONE: ! 221: bitch(cmdbuf, fo); ! 222: continue; ! 223: case RSET: ! 224: csend(LOG_DEBUG, "250 OK\n"); ! 225: return(FALSE); ! 226: default: ! 227: csend(LOG_DEBUG, "503 Expecting MAIL\n"); ! 228: continue; ! 229: } ! 230: } ! 231: } ! 232: ! 233: /* ! 234: * Wait for the user to send the RCPT command. Punt out if he sends ! 235: * QUIT or RSET. Returns TRUE if a RCPT command was received, FALSE ! 236: * if a DATA command was received. ! 237: */ ! 238: do_rcpt(fi, fo) ! 239: FILE *fi, *fo; ! 240: { ! 241: char cmdbuf[MAXSTR]; ! 242: char gripe[MAXSTR], *nlptr; ! 243: int buflen; ! 244: int i; ! 245: ! 246: for (;;) { /* until RCPT, DATA, QUIT, or RSET */ ! 247: buflen = crecv(cmdbuf, sizeof cmdbuf, fi); /* wait for command */ ! 248: switch (cmdparse(cmdbuf, buflen)) { ! 249: case QUIT: ! 250: quit(fi, fo); ! 251: case NOOP: ! 252: csend(LOG_DEBUG, "250 OK\n"); ! 253: continue; ! 254: case RCPT: ! 255: if (!parse_rcpt(cmdbuf, buflen)) { ! 256: csend(LOG_DEBUG, "501 Syntax error in recipient name\n"); ! 257: continue; ! 258: } ! 259: csend(LOG_DEBUG, "250 OK\n"); ! 260: return(TRUE); ! 261: case DATA: ! 262: if (*s_to_c(rcvrs) == 0) { ! 263: csend(LOG_DEBUG, "503 Expecting RCPT\n"); ! 264: continue; ! 265: } ! 266: if ((i=init_xfr()) < 0) { /* set up data file */ ! 267: char buf[100]; ! 268: sprintf(buf, "451 Can't initialize files in spool directory %s(%d)\n", ! 269: dataname, i); ! 270: csend(LOG_ALERT, buf); ! 271: death(EX_CANTCREAT); ! 272: } ! 273: csend(LOG_DEBUG, "354 Start mail input; end with <CRLF>.<CRLF>\n"); ! 274: return(FALSE); ! 275: case VRFY: ! 276: csend(LOG_INFO, "252 Cannot VRFY user\n"); ! 277: continue; ! 278: case DEBG: ! 279: Syslog(LOG_ALERT, "DEBUG attempt"); ! 280: csend(LOG_WARNING, "200 OK\n"); ! 281: virus = 1; ! 282: continue; ! 283: case RSET: /* this code doesn't handle this here. Feign ignorance. */ ! 284: case NONE: ! 285: bitch(cmdbuf, fo); ! 286: continue; ! 287: default: ! 288: csend(LOG_DEBUG, "503 Expecting RCPT or DATA\n"); ! 289: continue; ! 290: } ! 291: } ! 292: } ! 293: ! 294: /* ! 295: * input a line at a time till a <cr>.<cr>. return the count of the characters ! 296: * input. if EOF is reached, return -1. if <cr>.<cr> is reached, return 0. ! 297: */ ! 298: static int atend; /* true when <cr>.<cr> is reached */ ! 299: ! 300: char * ! 301: smfgets(buf, len, fi) ! 302: char *buf; ! 303: int len; ! 304: FILE *fi; ! 305: { ! 306: int n; ! 307: int i; ! 308: ! 309: if(atend) ! 310: return NULL; ! 311: n = tgets(buf, len, fi); ! 312: if (n < 0) ! 313: return NULL; ! 314: if (buf[0] == '.') { ! 315: if(buf[1] == '\n'){ ! 316: atend = 1; ! 317: return NULL; ! 318: } else if(buf[1] == '.'){ ! 319: for(i=1; i<=n; i++) ! 320: buf[i-1] = buf[i]; ! 321: } ! 322: } ! 323: nbytes += n; ! 324: return buf; ! 325: } ! 326: ! 327: do_data(fi, fo) ! 328: FILE *fi, *fo; ! 329: { ! 330: string *cc; ! 331: int pid, wpid; ! 332: char gripe[MAXSTR]; ! 333: char cmd[MAXSTR]; ! 334: char ctlfile[MAXSTR]; ! 335: int ac, i; ! 336: char *cp; ! 337: ! 338: clearerr(fi); ! 339: clearerr(datafd); ! 340: ! 341: /* ! 342: * read data message ! 343: */ ! 344: atend = nbytes = 0; ! 345: from822(thishost, smfgets, fi, datafd, fromaddr, helohost); ! 346: fflush(datafd); ! 347: if(ferror(datafd) || ferror(fi)){ ! 348: fclose(datafd); ! 349: unlink(dataname); ! 350: csend(LOG_ALERT, "451 Transaction failed -- error writing data file\n"); ! 351: death(EX_IOERR); ! 352: } ! 353: fclose(datafd); ! 354: ! 355: /* ! 356: * create a control file. the two lines are ! 357: * <reply-address> <recipients> ! 358: * <recipients> ! 359: */ ! 360: cc = s_new(); ! 361: if (fromaddr == (char *)0 || *fromaddr == '\0') ! 362: fromaddr = "postmaster"; ! 363: s_append(cc, fromaddr); ! 364: s_append(cc, " "); ! 365: s_append(cc, s_to_c(rcvrs)); ! 366: s_append(cc, "\n"); ! 367: s_append(cc, s_to_c(rcvrs)); ! 368: s_append(cc, "\n"); ! 369: if(mkctlfile('X', dataname, s_to_c(cc))<0){ ! 370: unlink(dataname); ! 371: csend(LOG_ALERT, "451 Transaction failed -- can't make control file\n"); ! 372: death(EX_CANTCREAT); ! 373: } ! 374: s_free(cc); ! 375: csend(LOG_DEBUG, "250 OK\n"); ! 376: Syslog(LOG_INFO, "%s sent %d bytes to %s\n", ! 377: fromaddr ? fromaddr : "postmaster", nbytes, ! 378: s_to_c(rcvrs)); ! 379: ! 380: ! 381: /* ! 382: * reinitialize all the data pointers ! 383: */ ! 384: rcvrs = s_reset(rcvrs); ! 385: nbytes = 0; ! 386: *dataname = *rcptto = 0; ! 387: fromaddr = 0; ! 388: ! 389: } ! 390: ! 391: /* ! 392: * Create the data file for the transfer. Get unique ! 393: * names and create the files. ! 394: */ ! 395: init_xfr() ! 396: { ! 397: int dfd; /* file desc. for data file */ ! 398: char *cp; ! 399: ! 400: strcpy(dataname, "D.xxxxxxxxxxxx"); ! 401: if((dfd = mkdatafile(dataname)) < 0) ! 402: return -1; ! 403: datafd = fdopen(dfd, "w"); /* make stdio descriptor */ ! 404: if (datafd == NULL) ! 405: return -2; ! 406: ! 407: /* ! 408: * find the sender name if any ! 409: */ ! 410: if(*mailfrom){ ! 411: ! 412: /* skip noise */ ! 413: for(cp=mailfrom+sizeof("MAIL FROM:")-1; *cp; cp++) ! 414: if(strchr(";<>{}()\n| \t", *cp)==NULL) ! 415: break; ! 416: fromaddr = cp; ! 417: ! 418: /* find address */ ! 419: for(; *cp; cp++) ! 420: if(strchr(";<>{}()\n| \t", *cp)!=NULL){ ! 421: *cp = '\0'; ! 422: break; ! 423: } ! 424: } ! 425: if(fromaddr) ! 426: fromaddr = convertaddr(fromaddr); ! 427: ! 428: return 1; ! 429: } ! 430: ! 431: /* ! 432: * Give up on the transfer. Unlink the data file (if any), ! 433: * close the tcp connection, and exit. ! 434: */ ! 435: quit(fi, fo) ! 436: FILE *fi, *fo; ! 437: { ! 438: int i; ! 439: char greeting[MAXSTR]; ! 440: ! 441: (void) sprintf(greeting, "221 %s Terminating\n", helohost); ! 442: csend(LOG_DEBUG, greeting); ! 443: (void) fclose(fi); ! 444: (void) fclose(fo); ! 445: Syslog(LOG_DEBUG, "finished.\n"); ! 446: ! 447: /* ! 448: * run the queue from this caller ! 449: */ ! 450: for(i=0; i<NOFILE; i++) ! 451: close(i); ! 452: open("/dev/null", 0); ! 453: open("/dev/null", 1); ! 454: open("/dev/null", 1); ! 455: if(!norun) ! 456: smtpsched("Dsmtpsched", spoolsubdir); ! 457: ! 458: exit(0); ! 459: } ! 460: ! 461: /* ! 462: * Parse the command part off the specified buffer. Return the strchr ! 463: * of the command in the command table(or 0 if the command is not ! 464: * recognized). ! 465: * The commands and indices accepted are listed in the include file ! 466: * "cmds.h". ! 467: * If the len parameter is -1 (as returned by tgets), issue the QUIT command. ! 468: * This non-protocol extension was added to cool the jets of sail.stanford.edu. ! 469: */ ! 470: cmdparse(buf, len) ! 471: char *buf; ! 472: int len; ! 473: { ! 474: register char *cmdp, *bufp; /* command, buffer ptrs. */ ! 475: register struct cmdtab *ct; /* cmd table ptr */ ! 476: register int i; /* strchr in cmd table */ ! 477: int clen; /* length of this command */ ! 478: ! 479: if (len == -1) { /* EOF */ ! 480: buf = "QUIT"; ! 481: len = strlen(buf); ! 482: } ! 483: for (ct = &cmdtab[1], i = 1; ct->c_name != NULL; ct++, i++) { ! 484: clen = ct->c_len; ! 485: if (len < clen) /* buffer shorter than command? */ ! 486: continue; ! 487: /* case-insensitive matching of command names */ ! 488: for (cmdp = ct->c_name, bufp = buf; ! 489: clen > 0 && *cmdp == (ISLOWER(*bufp) ? TOUPPER(*bufp) : *bufp); ! 490: cmdp++, bufp++, clen--) ! 491: ; ! 492: if (clen == 0) { /* success */ ! 493: /* sendmail compatibility */ ! 494: if (i == ONEX || i == VERB) ! 495: i = NOOP; ! 496: return i; ! 497: } ! 498: } ! 499: return 0; ! 500: } ! 501: ! 502: /* ! 503: * Parse a hello and return a pointer to name of the last two elements ! 504: * of the calling machine's domain name (or last 14 chars). ! 505: */ ! 506: char * ! 507: parse_hello(buf, len) ! 508: char *buf; ! 509: int len; ! 510: { ! 511: char *bp = buf; ! 512: char *lp; ! 513: int dots; ! 514: ! 515: /* skip command */ ! 516: bp[len-1] = 0; ! 517: for(; *bp && !isspace(*bp); bp++) ! 518: ; ! 519: /* skip white */ ! 520: for(; isspace(*bp); bp++) ! 521: ; ! 522: /* skip arg */ ! 523: lp = bp; ! 524: for(; *bp && !isspace(*bp); bp++) ! 525: ; ! 526: /* null terminate */ ! 527: *bp = 0; ! 528: ! 529: return lp; ! 530: } ! 531: ! 532: static char *to; /* ptr. into request buffer */ ! 533: ! 534: /* ! 535: * Parse the recipient spec in the buffer. Start by stripping the ! 536: * command off the front of the buffer. Then call canon() to convert ! 537: * the recpient name into a format acceptable to the mailer daemon ! 538: * (ie. !-format). ! 539: * Returns TRUE if parsed successfully, FALSE otherwise. ! 540: */ ! 541: /* ARGSUSED len */ ! 542: parse_rcpt(buf, len) ! 543: char *buf; /* command buffer */ ! 544: int len; /* size of buffer string */ ! 545: { ! 546: register char *from; /* ptr to recipient name */ ! 547: char *end; ! 548: char *rv; ! 549: char *sysname_read(); ! 550: char *thissys; ! 551: ! 552: from = &buf[cmdtab[RCPT].c_len]; ! 553: while (*from == ' ' || *from == '\t') ! 554: from++; ! 555: if (*from == '<') { ! 556: end = strchr(from++, '>'); ! 557: if (end == 0) { ! 558: return FALSE; ! 559: } ! 560: *end = 0; ! 561: } ! 562: ! 563: /* ! 564: * convert to lower case (this is wrong but rfc822 is case ! 565: * insensitive) ! 566: */ ! 567: for(rv = from; *rv; rv++) ! 568: if(isupper(*rv)) ! 569: *rv = tolower(*rv); ! 570: ! 571: /* ! 572: * convert address to bang format. Assume the first site ! 573: * in the list is us and take it out. ! 574: */ ! 575: rv=convertaddr(from); ! 576: if(end=strchr(rv, '!')){ ! 577: thissys = sysname_read(); ! 578: *end = '\0'; ! 579: if(strcmp(rv, thissys)==0) ! 580: rv = end+1; ! 581: else ! 582: *end = '!'; ! 583: } ! 584: ! 585: /* ! 586: * check for address syntax ! 587: */ ! 588: if(shellchars(rv)){ ! 589: Syslog(LOG_ALERT, "shell characters in address: %s", rv); ! 590: if(virus) ! 591: rv = "upas.security"; ! 592: else ! 593: return FALSE; ! 594: } ! 595: ! 596: /* ! 597: * add to list of recipients ! 598: */ ! 599: if(*s_to_c(rcvrs)) ! 600: s_append(rcvrs, " "); ! 601: s_append(rcvrs, rv); ! 602: return TRUE; ! 603: } ! 604: ! 605: ! 606: /* Time to live elapsed or io error. */ ! 607: death(weapon) ! 608: { ! 609: (void) unlink(dataname); ! 610: exit(1); ! 611: } ! 612: ! 613: alarmsend() ! 614: { ! 615: csend(LOG_WARNING, "451 Our mailer appears to be hung.\n"); ! 616: death(EX_TEMPFAIL); ! 617: } ! 618: ! 619: funnychars(str) ! 620: register char *str; ! 621: { ! 622: for (;;) ! 623: switch(*str++) { ! 624: case '^': ! 625: case '&': ! 626: case '>': ! 627: case '<': ! 628: case '`': ! 629: case '|': ! 630: case ';': ! 631: case '\'': ! 632: return TRUE; ! 633: ! 634: case 0: ! 635: return FALSE; ! 636: } ! 637: } ! 638: ! 639: bitch(buf, fo) ! 640: char *buf; ! 641: FILE *fo; ! 642: { ! 643: char gripe[BUFSIZ], *nlptr; ! 644: ! 645: if ((nlptr = strchr(buf, '\n')) != 0) ! 646: *nlptr = 0; ! 647: (void) sprintf(gripe, "502 %s ... Not recognized\n", buf); ! 648: csend(LOG_DEBUG, gripe); ! 649: } ! 650: ! 651: bomb(err) ! 652: int err; ! 653: { ! 654: death(err); ! 655: } ! 656: ! 657: csend(loglevel, message) ! 658: int loglevel; ! 659: char *message; ! 660: { ! 661: Syslog(loglevel, "<--- %s", message); ! 662: (void) tputs(message, fo); ! 663: } ! 664: ! 665: int ! 666: crecv(buf, len, fd) ! 667: char *buf; ! 668: int len, fd; ! 669: { ! 670: int n = tgets(buf, len, fi); ! 671: Syslog(LOG_DEBUG, "-------> %s", buf); ! 672: return n; ! 673: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.