|
|
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.