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