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