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