|
|
1.1 root 1: /*
2: * Copyright (c) 1983 Eric P. Allman
3: * Copyright (c) 1988 Regents of the University of California.
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms are permitted provided
7: * that: (1) source distributions retain this entire copyright notice and
8: * comment, and (2) distributions including binaries display the following
9: * acknowledgement: ``This product includes software developed by the
10: * University of California, Berkeley and its contributors'' in the
11: * documentation or other materials provided with the distribution and in
12: * all advertising materials mentioning features or use of this software.
13: * Neither the name of the University nor the names of its contributors may
14: * be used to endorse or promote products derived from this software without
15: * specific prior written permission.
16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
17: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
18: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19: */
20:
21: # include "sendmail.h"
22:
23: #ifndef lint
24: #ifdef SMTP
25: static char sccsid[] = "@(#)srvrsmtp.c 5.28 (Berkeley) 6/1/90 (with SMTP)";
26: #else
27: static char sccsid[] = "@(#)srvrsmtp.c 5.28 (Berkeley) 6/1/90 (without SMTP)";
28: #endif
29: #endif /* not lint */
30:
31: # include <errno.h>
32: # include <signal.h>
33:
34: # ifdef SMTP
35:
36: /*
37: ** SMTP -- run the SMTP protocol.
38: **
39: ** Parameters:
40: ** none.
41: **
42: ** Returns:
43: ** never.
44: **
45: ** Side Effects:
46: ** Reads commands from the input channel and processes
47: ** them.
48: */
49:
50: struct cmd
51: {
52: char *cmdname; /* command name */
53: int cmdcode; /* internal code, see below */
54: };
55:
56: /* values for cmdcode */
57: # define CMDERROR 0 /* bad command */
58: # define CMDMAIL 1 /* mail -- designate sender */
59: # define CMDRCPT 2 /* rcpt -- designate recipient */
60: # define CMDDATA 3 /* data -- send message text */
61: # define CMDRSET 4 /* rset -- reset state */
62: # define CMDVRFY 5 /* vrfy -- verify address */
63: # define CMDHELP 6 /* help -- give usage info */
64: # define CMDNOOP 7 /* noop -- do nothing */
65: # define CMDQUIT 8 /* quit -- close connection and die */
66: # define CMDHELO 9 /* helo -- be polite */
67: # define CMDONEX 10 /* onex -- sending one transaction only */
68: # define CMDVERB 11 /* verb -- go into verbose mode */
69: /* debugging-only commands, only enabled if SMTPDEBUG is defined */
70: # define CMDDBGQSHOW 12 /* showq -- show send queue */
71: # define CMDDBGDEBUG 13 /* debug -- set debug mode */
72:
73: static struct cmd CmdTab[] =
74: {
75: "mail", CMDMAIL,
76: "rcpt", CMDRCPT,
77: "data", CMDDATA,
78: "rset", CMDRSET,
79: "vrfy", CMDVRFY,
80: "expn", CMDVRFY,
81: "help", CMDHELP,
82: "noop", CMDNOOP,
83: "quit", CMDQUIT,
84: "helo", CMDHELO,
85: "verb", CMDVERB,
86: "onex", CMDONEX,
87: /*
88: * remaining commands are here only
89: * to trap and log attempts to use them
90: */
91: "showq", CMDDBGQSHOW,
92: "debug", CMDDBGDEBUG,
93: NULL, CMDERROR,
94: };
95:
96: bool InChild = FALSE; /* true if running in a subprocess */
97: bool OneXact = FALSE; /* one xaction only this run */
98:
99: #define EX_QUIT 22 /* special code for QUIT command */
100:
101: smtp()
102: {
103: register char *p;
104: register struct cmd *c;
105: char *cmd;
106: extern char *skipword();
107: bool hasmail; /* mail command received */
108: auto ADDRESS *vrfyqueue;
109: ADDRESS *a;
110: char *sendinghost;
111: char inp[MAXLINE];
112: char cmdbuf[100];
113: extern char Version[];
114: extern char *macvalue();
115: extern ADDRESS *recipient();
116: extern ENVELOPE BlankEnvelope;
117: extern ENVELOPE *newenvelope();
118:
119: hasmail = FALSE;
120: if (OutChannel != stdout)
121: {
122: /* arrange for debugging output to go to remote host */
123: (void) close(1);
124: (void) dup(fileno(OutChannel));
125: }
126: settime();
127: if (RealHostName != NULL)
128: {
129: CurHostName = RealHostName;
130: setproctitle("srvrsmtp %s", CurHostName);
131: }
132: else
133: {
134: /* this must be us!! */
135: CurHostName = MyHostName;
136: }
137: expand("\001e", inp, &inp[sizeof inp], CurEnv);
138: message("220", inp);
139: SmtpPhase = "startup";
140: sendinghost = NULL;
141: for (;;)
142: {
143: /* arrange for backout */
144: if (setjmp(TopFrame) > 0 && InChild)
145: finis();
146: QuickAbort = FALSE;
147: HoldErrs = FALSE;
148:
149: /* setup for the read */
150: CurEnv->e_to = NULL;
151: Errors = 0;
152: (void) fflush(stdout);
153:
154: /* read the input line */
155: p = sfgets(inp, sizeof inp, InChannel);
156:
157: /* handle errors */
158: if (p == NULL)
159: {
160: /* end of file, just die */
161: message("421", "%s Lost input channel from %s",
162: MyHostName, CurHostName);
163: finis();
164: }
165:
166: /* clean up end of line */
167: fixcrlf(inp, TRUE);
168:
169: /* echo command to transcript */
170: if (CurEnv->e_xfp != NULL)
171: fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
172:
173: /* break off command */
174: for (p = inp; isspace(*p); p++)
175: continue;
176: cmd = p;
177: for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
178: *cmd++ = *p++;
179: *cmd = '\0';
180:
181: /* throw away leading whitespace */
182: while (isspace(*p))
183: p++;
184:
185: /* decode command */
186: for (c = CmdTab; c->cmdname != NULL; c++)
187: {
188: if (!strcasecmp(c->cmdname, cmdbuf))
189: break;
190: }
191:
192: /* process command */
193: switch (c->cmdcode)
194: {
195: case CMDHELO: /* hello -- introduce yourself */
196: SmtpPhase = "HELO";
197: setproctitle("%s: %s", CurHostName, inp);
198: if (!strcasecmp(p, MyHostName))
199: {
200: /*
201: * didn't know about alias,
202: * or connected to an echo server
203: */
204: message("553", "Local configuration error, hostname not recognized as local");
205: break;
206: }
207: if (RealHostName != NULL && strcasecmp(p, RealHostName))
208: {
209: char hostbuf[MAXNAME];
210:
211: (void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
212: sendinghost = newstr(hostbuf);
213: }
214: else
215: sendinghost = newstr(p);
216: message("250", "%s Hello %s, pleased to meet you",
217: MyHostName, sendinghost);
218: break;
219:
220: case CMDMAIL: /* mail -- designate sender */
221: SmtpPhase = "MAIL";
222:
223: /* force a sending host even if no HELO given */
224: if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
225: sendinghost = RealHostName;
226:
227: /* check for validity of this command */
228: if (hasmail)
229: {
230: message("503", "Sender already specified");
231: break;
232: }
233: if (InChild)
234: {
235: errno = 0;
236: syserr("Nested MAIL command");
237: exit(0);
238: }
239:
240: /* fork a subprocess to process this command */
241: if (runinchild("SMTP-MAIL") > 0)
242: break;
243: define('s', sendinghost, CurEnv);
244: define('r', "SMTP", CurEnv);
245: initsys();
246: setproctitle("%s %s: %s", CurEnv->e_id,
247: CurHostName, inp);
248:
249: /* child -- go do the processing */
250: p = skipword(p, "from");
251: if (p == NULL)
252: break;
253: setsender(p);
254: if (Errors == 0)
255: {
256: message("250", "Sender ok");
257: hasmail = TRUE;
258: }
259: else if (InChild)
260: finis();
261: break;
262:
263: case CMDRCPT: /* rcpt -- designate recipient */
264: SmtpPhase = "RCPT";
265: setproctitle("%s %s: %s", CurEnv->e_id,
266: CurHostName, inp);
267: if (setjmp(TopFrame) > 0)
268: {
269: CurEnv->e_flags &= ~EF_FATALERRS;
270: break;
271: }
272: QuickAbort = TRUE;
273: p = skipword(p, "to");
274: if (p == NULL)
275: break;
276: a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
277: if (a == NULL)
278: break;
279: a->q_flags |= QPRIMARY;
280: a = recipient(a, &CurEnv->e_sendqueue);
281: if (Errors != 0)
282: break;
283:
284: /* no errors during parsing, but might be a duplicate */
285: CurEnv->e_to = p;
286: if (!bitset(QBADADDR, a->q_flags))
287: message("250", "Recipient ok");
288: else
289: {
290: /* punt -- should keep message in ADDRESS.... */
291: message("550", "Addressee unknown");
292: }
293: CurEnv->e_to = NULL;
294: break;
295:
296: case CMDDATA: /* data -- text of mail */
297: SmtpPhase = "DATA";
298: if (!hasmail)
299: {
300: message("503", "Need MAIL command");
301: break;
302: }
303: else if (CurEnv->e_nrcpts <= 0)
304: {
305: message("503", "Need RCPT (recipient)");
306: break;
307: }
308:
309: /* collect the text of the message */
310: SmtpPhase = "collect";
311: setproctitle("%s %s: %s", CurEnv->e_id,
312: CurHostName, inp);
313: collect(TRUE);
314: if (Errors != 0)
315: break;
316:
317: /*
318: ** Arrange to send to everyone.
319: ** If sending to multiple people, mail back
320: ** errors rather than reporting directly.
321: ** In any case, don't mail back errors for
322: ** anything that has happened up to
323: ** now (the other end will do this).
324: ** Truncate our transcript -- the mail has gotten
325: ** to us successfully, and if we have
326: ** to mail this back, it will be easier
327: ** on the reader.
328: ** Then send to everyone.
329: ** Finally give a reply code. If an error has
330: ** already been given, don't mail a
331: ** message back.
332: ** We goose error returns by clearing error bit.
333: */
334:
335: SmtpPhase = "delivery";
336: if (CurEnv->e_nrcpts != 1)
337: {
338: HoldErrs = TRUE;
339: ErrorMode = EM_MAIL;
340: }
341: CurEnv->e_flags &= ~EF_FATALERRS;
342: CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
343:
344: /* send to all recipients */
345: sendall(CurEnv, SM_DEFAULT);
346: CurEnv->e_to = NULL;
347:
348: /* save statistics */
349: markstats(CurEnv, (ADDRESS *) NULL);
350:
351: /* issue success if appropriate and reset */
352: if (Errors == 0 || HoldErrs)
353: message("250", "Ok");
354: else
355: CurEnv->e_flags &= ~EF_FATALERRS;
356:
357: /* if in a child, pop back to our parent */
358: if (InChild)
359: finis();
360:
361: /* clean up a bit */
362: hasmail = 0;
363: dropenvelope(CurEnv);
364: CurEnv = newenvelope(CurEnv);
365: CurEnv->e_flags = BlankEnvelope.e_flags;
366: break;
367:
368: case CMDRSET: /* rset -- reset state */
369: message("250", "Reset state");
370: if (InChild)
371: finis();
372: break;
373:
374: case CMDVRFY: /* vrfy -- verify address */
375: if (runinchild("SMTP-VRFY") > 0)
376: break;
377: setproctitle("%s: %s", CurHostName, inp);
378: vrfyqueue = NULL;
379: QuickAbort = TRUE;
380: sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
381: if (Errors != 0)
382: {
383: if (InChild)
384: finis();
385: break;
386: }
387: while (vrfyqueue != NULL)
388: {
389: register ADDRESS *a = vrfyqueue->q_next;
390: char *code;
391:
392: while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
393: a = a->q_next;
394:
395: if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
396: {
397: if (a != NULL)
398: code = "250-";
399: else
400: code = "250";
401: if (vrfyqueue->q_fullname == NULL)
402: message(code, "<%s>", vrfyqueue->q_paddr);
403: else
404: message(code, "%s <%s>",
405: vrfyqueue->q_fullname, vrfyqueue->q_paddr);
406: }
407: else if (a == NULL)
408: message("554", "Self destructive alias loop");
409: vrfyqueue = a;
410: }
411: if (InChild)
412: finis();
413: break;
414:
415: case CMDHELP: /* help -- give user info */
416: if (*p == '\0')
417: p = "SMTP";
418: help(p);
419: break;
420:
421: case CMDNOOP: /* noop -- do nothing */
422: message("200", "OK");
423: break;
424:
425: case CMDQUIT: /* quit -- leave mail */
426: message("221", "%s closing connection", MyHostName);
427: if (InChild)
428: ExitStat = EX_QUIT;
429: finis();
430:
431: case CMDVERB: /* set verbose mode */
432: Verbose = TRUE;
433: SendMode = SM_DELIVER;
434: message("200", "Verbose mode");
435: break;
436:
437: case CMDONEX: /* doing one transaction only */
438: OneXact = TRUE;
439: message("200", "Only one transaction");
440: break;
441:
442: # ifdef SMTPDEBUG
443: case CMDDBGQSHOW: /* show queues */
444: printf("Send Queue=");
445: printaddr(CurEnv->e_sendqueue, TRUE);
446: break;
447:
448: case CMDDBGDEBUG: /* set debug mode */
449: tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
450: tTflag(p);
451: message("200", "Debug set");
452: break;
453:
454: # else /* not SMTPDEBUG */
455:
456: case CMDDBGQSHOW: /* show queues */
457: case CMDDBGDEBUG: /* set debug mode */
458: # ifdef LOG
459: if (RealHostName != NULL && LogLevel > 0)
460: syslog(LOG_NOTICE,
461: "\"%s\" command from %s (%s)\n",
462: c->cmdname, RealHostName,
463: inet_ntoa(RealHostAddr.sin_addr));
464: # endif
465: /* FALL THROUGH */
466: # endif /* SMTPDEBUG */
467:
468: case CMDERROR: /* unknown command */
469: message("500", "Command unrecognized");
470: break;
471:
472: default:
473: errno = 0;
474: syserr("smtp: unknown code %d", c->cmdcode);
475: break;
476: }
477: }
478: }
479: /*
480: ** SKIPWORD -- skip a fixed word.
481: **
482: ** Parameters:
483: ** p -- place to start looking.
484: ** w -- word to skip.
485: **
486: ** Returns:
487: ** p following w.
488: ** NULL on error.
489: **
490: ** Side Effects:
491: ** clobbers the p data area.
492: */
493:
494: static char *
495: skipword(p, w)
496: register char *p;
497: char *w;
498: {
499: register char *q;
500:
501: /* find beginning of word */
502: while (isspace(*p))
503: p++;
504: q = p;
505:
506: /* find end of word */
507: while (*p != '\0' && *p != ':' && !isspace(*p))
508: p++;
509: while (isspace(*p))
510: *p++ = '\0';
511: if (*p != ':')
512: {
513: syntax:
514: message("501", "Syntax error");
515: Errors++;
516: return (NULL);
517: }
518: *p++ = '\0';
519: while (isspace(*p))
520: p++;
521:
522: /* see if the input word matches desired word */
523: if (strcasecmp(q, w))
524: goto syntax;
525:
526: return (p);
527: }
528: /*
529: ** HELP -- implement the HELP command.
530: **
531: ** Parameters:
532: ** topic -- the topic we want help for.
533: **
534: ** Returns:
535: ** none.
536: **
537: ** Side Effects:
538: ** outputs the help file to message output.
539: */
540:
541: help(topic)
542: char *topic;
543: {
544: register FILE *hf;
545: int len;
546: char buf[MAXLINE];
547: bool noinfo;
548:
549: if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
550: {
551: /* no help */
552: errno = 0;
553: message("502", "HELP not implemented");
554: return;
555: }
556:
557: len = strlen(topic);
558: makelower(topic);
559: noinfo = TRUE;
560:
561: while (fgets(buf, sizeof buf, hf) != NULL)
562: {
563: if (strncmp(buf, topic, len) == 0)
564: {
565: register char *p;
566:
567: p = index(buf, '\t');
568: if (p == NULL)
569: p = buf;
570: else
571: p++;
572: fixcrlf(p, TRUE);
573: message("214-", p);
574: noinfo = FALSE;
575: }
576: }
577:
578: if (noinfo)
579: message("504", "HELP topic unknown");
580: else
581: message("214", "End of HELP info");
582: (void) fclose(hf);
583: }
584: /*
585: ** RUNINCHILD -- return twice -- once in the child, then in the parent again
586: **
587: ** Parameters:
588: ** label -- a string used in error messages
589: **
590: ** Returns:
591: ** zero in the child
592: ** one in the parent
593: **
594: ** Side Effects:
595: ** none.
596: */
597:
598: runinchild(label)
599: char *label;
600: {
601: int childpid;
602:
603: if (!OneXact)
604: {
605: childpid = dofork();
606: if (childpid < 0)
607: {
608: syserr("%s: cannot fork", label);
609: return (1);
610: }
611: if (childpid > 0)
612: {
613: auto int st;
614:
615: /* parent -- wait for child to complete */
616: st = waitfor(childpid);
617: if (st == -1)
618: syserr("%s: lost child", label);
619:
620: /* if we exited on a QUIT command, complete the process */
621: if (st == (EX_QUIT << 8))
622: finis();
623:
624: return (1);
625: }
626: else
627: {
628: /* child */
629: InChild = TRUE;
630: QuickAbort = FALSE;
631: clearenvelope(CurEnv, FALSE);
632: }
633: }
634:
635: /* open alias database */
636: initaliases(AliasFile, FALSE);
637:
638: return (0);
639: }
640:
641: # endif SMTP
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.