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