1 /*
2 * Copyright (c) 1983, 1995 Eric P. Allman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * %sccs.include.redist.c%
7 */
8
9 # include "sendmail.h"
10
11 #ifndef lint
12 #ifdef SMTP
13 static char sccsid[] = "@(#)srvrsmtp.c 8.83 (Berkeley) 06/21/95 (with SMTP)";
14 #else
15 static char sccsid[] = "@(#)srvrsmtp.c 8.83 (Berkeley) 06/21/95 (without SMTP)";
16 #endif
17 #endif /* not lint */
18
19 # include <errno.h>
20
21 # ifdef SMTP
22
23 /*
24 ** SMTP -- run the SMTP protocol.
25 **
26 ** Parameters:
27 ** none.
28 **
29 ** Returns:
30 ** never.
31 **
32 ** Side Effects:
33 ** Reads commands from the input channel and processes
34 ** them.
35 */
36
37 struct cmd
38 {
39 char *cmdname; /* command name */
40 int cmdcode; /* internal code, see below */
41 };
42
43 /* values for cmdcode */
44 # define CMDERROR 0 /* bad command */
45 # define CMDMAIL 1 /* mail -- designate sender */
46 # define CMDRCPT 2 /* rcpt -- designate recipient */
47 # define CMDDATA 3 /* data -- send message text */
48 # define CMDRSET 4 /* rset -- reset state */
49 # define CMDVRFY 5 /* vrfy -- verify address */
50 # define CMDEXPN 6 /* expn -- expand address */
51 # define CMDNOOP 7 /* noop -- do nothing */
52 # define CMDQUIT 8 /* quit -- close connection and die */
53 # define CMDHELO 9 /* helo -- be polite */
54 # define CMDHELP 10 /* help -- give usage info */
55 # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
56 /* non-standard commands */
57 # define CMDONEX 16 /* onex -- sending one transaction only */
58 # define CMDVERB 17 /* verb -- go into verbose mode */
59 /* use this to catch and log "door handle" attempts on your system */
60 # define CMDLOGBOGUS 23 /* bogus command that should be logged */
61 /* debugging-only commands, only enabled if SMTPDEBUG is defined */
62 # define CMDDBGQSHOW 24 /* showq -- show send queue */
63 # define CMDDBGDEBUG 25 /* debug -- set debug mode */
64
65 static struct cmd CmdTab[] =
66 {
67 "mail", CMDMAIL,
68 "rcpt", CMDRCPT,
69 "data", CMDDATA,
70 "rset", CMDRSET,
71 "vrfy", CMDVRFY,
72 "expn", CMDEXPN,
73 "help", CMDHELP,
74 "noop", CMDNOOP,
75 "quit", CMDQUIT,
76 "helo", CMDHELO,
77 "ehlo", CMDEHLO,
78 "verb", CMDVERB,
79 "onex", CMDONEX,
80 /*
81 * remaining commands are here only
82 * to trap and log attempts to use them
83 */
84 "showq", CMDDBGQSHOW,
85 "debug", CMDDBGDEBUG,
86 "wiz", CMDLOGBOGUS,
87 NULL, CMDERROR,
88 };
89
90 bool OneXact = FALSE; /* one xaction only this run */
91 char *CurSmtpClient; /* who's at the other end of channel */
92
93 static char *skipword();
94
95
96 #define MAXBADCOMMANDS 25 /* maximum number of bad commands */
97
98 void
smtp(e)99 smtp(e)
100 register ENVELOPE *e;
101 {
102 register char *p;
103 register struct cmd *c;
104 char *cmd;
105 auto ADDRESS *vrfyqueue;
106 ADDRESS *a;
107 bool gotmail; /* mail command received */
108 bool gothello; /* helo command received */
109 bool vrfy; /* set if this is a vrfy command */
110 char *protocol; /* sending protocol */
111 char *sendinghost; /* sending hostname */
112 char *peerhostname; /* name of SMTP peer or "localhost" */
113 auto char *delimptr;
114 char *id;
115 int nrcpts = 0; /* number of RCPT commands */
116 bool doublequeue;
117 int badcommands = 0; /* count of bad commands */
118 char inp[MAXLINE];
119 char cmdbuf[MAXLINE];
120 extern ENVELOPE BlankEnvelope;
121 extern void help __P((char *));
122
123 if (fileno(OutChannel) != fileno(stdout))
124 {
125 /* arrange for debugging output to go to remote host */
126 (void) dup2(fileno(OutChannel), fileno(stdout));
127 }
128 settime(e);
129 peerhostname = RealHostName;
130 if (peerhostname == NULL)
131 peerhostname = "localhost";
132 CurHostName = peerhostname;
133 CurSmtpClient = macvalue('_', e);
134 if (CurSmtpClient == NULL)
135 CurSmtpClient = CurHostName;
136
137 setproctitle("server %s startup", CurSmtpClient);
138 #ifdef LOG
139 if (LogLevel > 11)
140 {
141 /* log connection information */
142 syslog(LOG_INFO, "SMTP connect from %s (%s)",
143 CurSmtpClient, anynet_ntoa(&RealHostAddr));
144 }
145 #endif
146
147 /* output the first line, inserting "ESMTP" as second word */
148 expand("\201e", inp, sizeof inp, e);
149 p = strchr(inp, '\n');
150 if (p != NULL)
151 *p++ = '\0';
152 id = strchr(inp, ' ');
153 if (id == NULL)
154 id = &inp[strlen(inp)];
155 cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
156 message(cmd, id - inp, inp, id);
157
158 /* output remaining lines */
159 while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
160 {
161 *p++ = '\0';
162 if (isascii(*id) && isspace(*id))
163 id++;
164 message("220-%s", id);
165 }
166 if (id != NULL)
167 {
168 if (isascii(*id) && isspace(*id))
169 id++;
170 message("220 %s", id);
171 }
172
173 protocol = NULL;
174 sendinghost = macvalue('s', e);
175 gothello = FALSE;
176 gotmail = FALSE;
177 for (;;)
178 {
179 /* arrange for backout */
180 if (setjmp(TopFrame) > 0)
181 {
182 /* if() nesting is necessary for Cray UNICOS */
183 if (InChild)
184 {
185 QuickAbort = FALSE;
186 SuprErrs = TRUE;
187 finis();
188 }
189 }
190 QuickAbort = FALSE;
191 HoldErrs = FALSE;
192 LogUsrErrs = FALSE;
193 e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
194
195 /* setup for the read */
196 e->e_to = NULL;
197 Errors = 0;
198 (void) fflush(stdout);
199
200 /* read the input line */
201 SmtpPhase = "server cmd read";
202 setproctitle("server %s cmd read", CurSmtpClient);
203 p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
204 SmtpPhase);
205
206 /* handle errors */
207 if (p == NULL)
208 {
209 /* end of file, just die */
210 disconnect(1, e);
211 message("421 %s Lost input channel from %s",
212 MyHostName, CurSmtpClient);
213 #ifdef LOG
214 if (LogLevel > (gotmail ? 1 : 19))
215 syslog(LOG_NOTICE, "lost input channel from %s",
216 CurSmtpClient);
217 #endif
218 if (InChild)
219 ExitStat = EX_QUIT;
220 finis();
221 }
222
223 /* clean up end of line */
224 fixcrlf(inp, TRUE);
225
226 /* echo command to transcript */
227 if (e->e_xfp != NULL)
228 fprintf(e->e_xfp, "<<< %s\n", inp);
229
230 if (e->e_id == NULL)
231 setproctitle("%s: %.80s", CurSmtpClient, inp);
232 else
233 setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
234
235 /* break off command */
236 for (p = inp; isascii(*p) && isspace(*p); p++)
237 continue;
238 cmd = cmdbuf;
239 while (*p != '\0' &&
240 !(isascii(*p) && isspace(*p)) &&
241 cmd < &cmdbuf[sizeof cmdbuf - 2])
242 *cmd++ = *p++;
243 *cmd = '\0';
244
245 /* throw away leading whitespace */
246 while (isascii(*p) && isspace(*p))
247 p++;
248
249 /* decode command */
250 for (c = CmdTab; c->cmdname != NULL; c++)
251 {
252 if (!strcasecmp(c->cmdname, cmdbuf))
253 break;
254 }
255
256 /* reset errors */
257 errno = 0;
258
259 /* process command */
260 switch (c->cmdcode)
261 {
262 case CMDHELO: /* hello -- introduce yourself */
263 case CMDEHLO: /* extended hello */
264 if (c->cmdcode == CMDEHLO)
265 {
266 protocol = "ESMTP";
267 SmtpPhase = "server EHLO";
268 }
269 else
270 {
271 protocol = "SMTP";
272 SmtpPhase = "server HELO";
273 }
274
275 /* check for valid domain name (re 1123 5.2.5) */
276 if (*p == '\0')
277 {
278 message("501 %s requires domain address",
279 cmdbuf);
280 break;
281 }
282 else
283 {
284 register char *q;
285
286 for (q = p; *q != '\0'; q++)
287 {
288 if (!isascii(*q))
289 break;
290 if (isalnum(*q))
291 continue;
292 if (strchr("[].-_#", *q) == NULL)
293 break;
294 }
295 if (*q != '\0')
296 {
297 message("501 Invalid domain name");
298 break;
299 }
300 }
301
302 sendinghost = newstr(p);
303 gothello = TRUE;
304 if (c->cmdcode != CMDEHLO)
305 {
306 /* print old message and be done with it */
307 message("250 %s Hello %s, pleased to meet you",
308 MyHostName, CurSmtpClient);
309 break;
310 }
311
312 /* print extended message and brag */
313 message("250-%s Hello %s, pleased to meet you",
314 MyHostName, CurSmtpClient);
315 if (!bitset(PRIV_NOEXPN, PrivacyFlags))
316 message("250-EXPN");
317 #if MIME8TO7
318 message("250-8BITMIME");
319 #endif
320 if (MaxMessageSize > 0)
321 message("250-SIZE %ld", MaxMessageSize);
322 else
323 message("250-SIZE");
324 #if DSN
325 message("250-X-DSN-04 (Draft of May 29, 1995)");
326 #endif
327 message("250 HELP");
328 break;
329
330 case CMDMAIL: /* mail -- designate sender */
331 SmtpPhase = "server MAIL";
332
333 /* check for validity of this command */
334 if (!gothello)
335 {
336 /* set sending host to our known value */
337 if (sendinghost == NULL)
338 sendinghost = peerhostname;
339
340 if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
341 {
342 message("503 Polite people say HELO first");
343 break;
344 }
345 }
346 if (gotmail)
347 {
348 message("503 Sender already specified");
349 if (InChild)
350 finis();
351 break;
352 }
353 if (InChild)
354 {
355 errno = 0;
356 syserr("503 Nested MAIL command: MAIL %s", p);
357 finis();
358 }
359
360 /* fork a subprocess to process this command */
361 if (runinchild("SMTP-MAIL", e) > 0)
362 break;
363 if (!gothello)
364 {
365 auth_warning(e,
366 "Host %s didn't use HELO protocol",
367 CurSmtpClient);
368 }
369 #ifdef PICKY_HELO_CHECK
370 if (strcasecmp(sendinghost, peerhostname) != 0 &&
371 (strcasecmp(peerhostname, "localhost") != 0 ||
372 strcasecmp(sendinghost, MyHostName) != 0))
373 {
374 auth_warning(e, "Host %s claimed to be %s",
375 CurSmtpClient, sendinghost);
376 }
377 #endif
378
379 if (protocol == NULL)
380 protocol = "SMTP";
381 define('r', protocol, e);
382 define('s', sendinghost, e);
383 initsys(e);
384 nrcpts = 0;
385 e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
386 setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
387
388 /* child -- go do the processing */
389 p = skipword(p, "from");
390 if (p == NULL)
391 break;
392 if (setjmp(TopFrame) > 0)
393 {
394 /* this failed -- undo work */
395 if (InChild)
396 {
397 QuickAbort = FALSE;
398 SuprErrs = TRUE;
399 e->e_flags &= ~EF_FATALERRS;
400 finis();
401 }
402 break;
403 }
404 QuickAbort = TRUE;
405
406 /* must parse sender first */
407 delimptr = NULL;
408 setsender(p, e, &delimptr, FALSE);
409 p = delimptr;
410 if (p != NULL && *p != '\0')
411 *p++ = '\0';
412
413 /* check for possible spoofing */
414 if (RealUid != 0 && OpMode == MD_SMTP &&
415 !wordinclass(RealUserName, 't') &&
416 !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
417 strcmp(e->e_from.q_user, RealUserName) != 0)
418 {
419 auth_warning(e, "%s owned process doing -bs",
420 RealUserName);
421 }
422
423 /* now parse ESMTP arguments */
424 e->e_msgsize = 0;
425 while (p != NULL && *p != '\0')
426 {
427 char *kp;
428 char *vp = NULL;
429 extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
430
431 /* locate the beginning of the keyword */
432 while (isascii(*p) && isspace(*p))
433 p++;
434 if (*p == '\0')
435 break;
436 kp = p;
437
438 /* skip to the value portion */
439 while (isascii(*p) && isalnum(*p) || *p == '-')
440 p++;
441 if (*p == '=')
442 {
443 *p++ = '\0';
444 vp = p;
445
446 /* skip to the end of the value */
447 while (*p != '\0' && *p != ' ' &&
448 !(isascii(*p) && iscntrl(*p)) &&
449 *p != '=')
450 p++;
451 }
452
453 if (*p != '\0')
454 *p++ = '\0';
455
456 if (tTd(19, 1))
457 printf("MAIL: got arg %s=\"%s\"\n", kp,
458 vp == NULL ? "<null>" : vp);
459
460 mail_esmtp_args(kp, vp, e);
461 }
462
463 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
464 {
465 usrerr("552 Message size exceeds fixed maximum message size (%ld)",
466 MaxMessageSize);
467 /* NOTREACHED */
468 }
469
470 if (!enoughdiskspace(e->e_msgsize))
471 {
472 message("452 Insufficient disk space; try again later");
473 break;
474 }
475 message("250 Sender ok");
476 gotmail = TRUE;
477 break;
478
479 case CMDRCPT: /* rcpt -- designate recipient */
480 if (!gotmail)
481 {
482 usrerr("503 Need MAIL before RCPT");
483 break;
484 }
485 SmtpPhase = "server RCPT";
486 if (setjmp(TopFrame) > 0)
487 {
488 e->e_flags &= ~EF_FATALERRS;
489 break;
490 }
491 QuickAbort = TRUE;
492 LogUsrErrs = TRUE;
493
494 if (e->e_sendmode != SM_DELIVER)
495 e->e_flags |= EF_VRFYONLY;
496
497 p = skipword(p, "to");
498 if (p == NULL)
499 break;
500 a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
501 if (a == NULL)
502 break;
503 p = delimptr;
504
505 /* now parse ESMTP arguments */
506 while (p != NULL && *p != '\0')
507 {
508 char *kp;
509 char *vp = NULL;
510 extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
511
512 /* locate the beginning of the keyword */
513 while (isascii(*p) && isspace(*p))
514 p++;
515 if (*p == '\0')
516 break;
517 kp = p;
518
519 /* skip to the value portion */
520 while (isascii(*p) && isalnum(*p) || *p == '-')
521 p++;
522 if (*p == '=')
523 {
524 *p++ = '\0';
525 vp = p;
526
527 /* skip to the end of the value */
528 while (*p != '\0' && *p != ' ' &&
529 !(isascii(*p) && iscntrl(*p)) &&
530 *p != '=')
531 p++;
532 }
533
534 if (*p != '\0')
535 *p++ = '\0';
536
537 if (tTd(19, 1))
538 printf("RCPT: got arg %s=\"%s\"\n", kp,
539 vp == NULL ? "<null>" : vp);
540
541 rcpt_esmtp_args(a, kp, vp, e);
542 }
543
544 /* save in recipient list after ESMTP mods */
545 a = recipient(a, &e->e_sendqueue, 0, e);
546
547 if (Errors != 0)
548 break;
549
550 /* no errors during parsing, but might be a duplicate */
551 e->e_to = p;
552 if (!bitset(QBADADDR, a->q_flags))
553 {
554 message("250 Recipient ok%s",
555 bitset(QQUEUEUP, a->q_flags) ?
556 " (will queue)" : "");
557 nrcpts++;
558 }
559 else
560 {
561 /* punt -- should keep message in ADDRESS.... */
562 message("550 Addressee unknown");
563 }
564 e->e_to = NULL;
565 break;
566
567 case CMDDATA: /* data -- text of mail */
568 SmtpPhase = "server DATA";
569 if (!gotmail)
570 {
571 message("503 Need MAIL command");
572 break;
573 }
574 else if (nrcpts <= 0)
575 {
576 message("503 Need RCPT (recipient)");
577 break;
578 }
579
580 /* check to see if we need to re-expand aliases */
581 /* also reset QBADADDR on already-diagnosted addrs */
582 doublequeue = FALSE;
583 for (a = e->e_sendqueue; a != NULL; a = a->q_next)
584 {
585 if (bitset(QVERIFIED, a->q_flags))
586 {
587 /* need to re-expand aliases */
588 doublequeue = TRUE;
589 }
590 if (bitset(QBADADDR, a->q_flags))
591 {
592 /* make this "go away" */
593 a->q_flags |= QDONTSEND;
594 a->q_flags &= ~QBADADDR;
595 }
596 }
597
598 /* collect the text of the message */
599 SmtpPhase = "collect";
600 buffer_errors();
601 collect(InChannel, TRUE, doublequeue, NULL, e);
602 flush_errors(TRUE);
603 if (Errors != 0)
604 goto abortmessage;
605
606 /* make sure we actually do delivery */
607 e->e_flags &= ~EF_CLRQUEUE;
608
609 /* from now on, we have to operate silently */
610 buffer_errors();
611 e->e_errormode = EM_MAIL;
612
613 /*
614 ** Arrange to send to everyone.
615 ** If sending to multiple people, mail back
616 ** errors rather than reporting directly.
617 ** In any case, don't mail back errors for
618 ** anything that has happened up to
619 ** now (the other end will do this).
620 ** Truncate our transcript -- the mail has gotten
621 ** to us successfully, and if we have
622 ** to mail this back, it will be easier
623 ** on the reader.
624 ** Then send to everyone.
625 ** Finally give a reply code. If an error has
626 ** already been given, don't mail a
627 ** message back.
628 ** We goose error returns by clearing error bit.
629 */
630
631 SmtpPhase = "delivery";
632 e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
633 id = e->e_id;
634
635 if (doublequeue)
636 {
637 /* make sure it is in the queue */
638 queueup(e, TRUE, FALSE);
639 if (e->e_sendmode == SM_QUEUE)
640 e->e_flags |= EF_KEEPQUEUE;
641 }
642 else
643 {
644 /* send to all recipients */
645 sendall(e, SM_DEFAULT);
646 }
647 e->e_to = NULL;
648
649 /* issue success message */
650 message("250 %s Message accepted for delivery", id);
651
652 /* if we just queued, poke it */
653 if (doublequeue && e->e_sendmode != SM_QUEUE)
654 {
655 extern pid_t dowork();
656
657 unlockqueue(e);
658 (void) dowork(id, TRUE, TRUE, e);
659 }
660
661 abortmessage:
662 /* if in a child, pop back to our parent */
663 if (InChild)
664 finis();
665
666 /* clean up a bit */
667 gotmail = FALSE;
668 dropenvelope(e);
669 CurEnv = e = newenvelope(e, CurEnv);
670 e->e_flags = BlankEnvelope.e_flags;
671 break;
672
673 case CMDRSET: /* rset -- reset state */
674 message("250 Reset state");
675
676 /* arrange to ignore any current send list */
677 e->e_sendqueue = NULL;
678 e->e_flags |= EF_CLRQUEUE;
679 if (InChild)
680 finis();
681
682 /* clean up a bit */
683 gotmail = FALSE;
684 dropenvelope(e);
685 CurEnv = e = newenvelope(e, CurEnv);
686 break;
687
688 case CMDVRFY: /* vrfy -- verify address */
689 case CMDEXPN: /* expn -- expand address */
690 vrfy = c->cmdcode == CMDVRFY;
691 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
692 PrivacyFlags))
693 {
694 if (vrfy)
695 message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
696 else
697 message("502 Sorry, we do not allow this operation");
698 #ifdef LOG
699 if (LogLevel > 5)
700 syslog(LOG_INFO, "%s: %s [rejected]",
701 CurSmtpClient, inp);
702 #endif
703 break;
704 }
705 else if (!gothello &&
706 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
707 PrivacyFlags))
708 {
709 message("503 I demand that you introduce yourself first");
710 break;
711 }
712 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
713 break;
714 #ifdef LOG
715 if (LogLevel > 5)
716 syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
717 #endif
718 vrfyqueue = NULL;
719 QuickAbort = TRUE;
720 if (vrfy)
721 e->e_flags |= EF_VRFYONLY;
722 while (*p != '\0' && isascii(*p) && isspace(*p))
723 p++;
724 if (*p == '\0')
725 {
726 message("501 Argument required");
727 Errors++;
728 }
729 else
730 {
731 (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
732 }
733 if (Errors != 0)
734 {
735 if (InChild)
736 finis();
737 break;
738 }
739 if (vrfyqueue == NULL)
740 {
741 message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
742 }
743 while (vrfyqueue != NULL)
744 {
745 extern void printvrfyaddr __P((ADDRESS *, bool));
746
747 a = vrfyqueue;
748 while ((a = a->q_next) != NULL &&
749 bitset(QDONTSEND|QBADADDR, a->q_flags))
750 continue;
751 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
752 printvrfyaddr(vrfyqueue, a == NULL);
753 vrfyqueue = vrfyqueue->q_next;
754 }
755 if (InChild)
756 finis();
757 break;
758
759 case CMDHELP: /* help -- give user info */
760 help(p);
761 break;
762
763 case CMDNOOP: /* noop -- do nothing */
764 message("250 OK");
765 break;
766
767 case CMDQUIT: /* quit -- leave mail */
768 message("221 %s closing connection", MyHostName);
769
770 doquit:
771 /* arrange to ignore any current send list */
772 e->e_sendqueue = NULL;
773
774 /* avoid future 050 messages */
775 disconnect(1, e);
776
777 if (InChild)
778 ExitStat = EX_QUIT;
779 finis();
780
781 case CMDVERB: /* set verbose mode */
782 if (bitset(PRIV_NOEXPN, PrivacyFlags))
783 {
784 /* this would give out the same info */
785 message("502 Verbose unavailable");
786 break;
787 }
788 Verbose = TRUE;
789 e->e_sendmode = SM_DELIVER;
790 message("250 Verbose mode");
791 break;
792
793 case CMDONEX: /* doing one transaction only */
794 OneXact = TRUE;
795 message("250 Only one transaction");
796 break;
797
798 # ifdef SMTPDEBUG
799 case CMDDBGQSHOW: /* show queues */
800 printf("Send Queue=");
801 printaddr(e->e_sendqueue, TRUE);
802 break;
803
804 case CMDDBGDEBUG: /* set debug mode */
805 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
806 tTflag(p);
807 message("200 Debug set");
808 break;
809
810 # else /* not SMTPDEBUG */
811 case CMDDBGQSHOW: /* show queues */
812 case CMDDBGDEBUG: /* set debug mode */
813 # endif /* SMTPDEBUG */
814 case CMDLOGBOGUS: /* bogus command */
815 # ifdef LOG
816 if (LogLevel > 0)
817 syslog(LOG_CRIT,
818 "\"%s\" command from %s (%s)",
819 c->cmdname, CurSmtpClient,
820 anynet_ntoa(&RealHostAddr));
821 # endif
822 /* FALL THROUGH */
823
824 case CMDERROR: /* unknown command */
825 if (++badcommands > MAXBADCOMMANDS)
826 {
827 message("421 %s Too many bad commands; closing connection",
828 MyHostName);
829 goto doquit;
830 }
831
832 message("500 Command unrecognized");
833 break;
834
835 default:
836 errno = 0;
837 syserr("500 smtp: unknown code %d", c->cmdcode);
838 break;
839 }
840 }
841 }
842 /*
843 ** SKIPWORD -- skip a fixed word.
844 **
845 ** Parameters:
846 ** p -- place to start looking.
847 ** w -- word to skip.
848 **
849 ** Returns:
850 ** p following w.
851 ** NULL on error.
852 **
853 ** Side Effects:
854 ** clobbers the p data area.
855 */
856
857 static char *
skipword(p,w)858 skipword(p, w)
859 register char *p;
860 char *w;
861 {
862 register char *q;
863 char *firstp = p;
864
865 /* find beginning of word */
866 while (isascii(*p) && isspace(*p))
867 p++;
868 q = p;
869
870 /* find end of word */
871 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
872 p++;
873 while (isascii(*p) && isspace(*p))
874 *p++ = '\0';
875 if (*p != ':')
876 {
877 syntax:
878 message("501 Syntax error in parameters scanning \"%s\"",
879 firstp);
880 Errors++;
881 return (NULL);
882 }
883 *p++ = '\0';
884 while (isascii(*p) && isspace(*p))
885 p++;
886
887 if (*p == '\0')
888 goto syntax;
889
890 /* see if the input word matches desired word */
891 if (strcasecmp(q, w))
892 goto syntax;
893
894 return (p);
895 }
896 /*
897 ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
898 **
899 ** Parameters:
900 ** kp -- the parameter key.
901 ** vp -- the value of that parameter.
902 ** e -- the envelope.
903 **
904 ** Returns:
905 ** none.
906 */
907
908 void
mail_esmtp_args(kp,vp,e)909 mail_esmtp_args(kp, vp, e)
910 char *kp;
911 char *vp;
912 ENVELOPE *e;
913 {
914 if (strcasecmp(kp, "size") == 0)
915 {
916 if (vp == NULL)
917 {
918 usrerr("501 SIZE requires a value");
919 /* NOTREACHED */
920 }
921 # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
922 e->e_msgsize = strtoul(vp, (char **) NULL, 10);
923 # else
924 e->e_msgsize = strtol(vp, (char **) NULL, 10);
925 # endif
926 }
927 else if (strcasecmp(kp, "body") == 0)
928 {
929 if (vp == NULL)
930 {
931 usrerr("501 BODY requires a value");
932 /* NOTREACHED */
933 }
934 else if (strcasecmp(vp, "8bitmime") == 0)
935 {
936 SevenBitInput = FALSE;
937 }
938 else if (strcasecmp(vp, "7bit") == 0)
939 {
940 SevenBitInput = TRUE;
941 }
942 else
943 {
944 usrerr("501 Unknown BODY type %s",
945 vp);
946 /* NOTREACHED */
947 }
948 e->e_bodytype = newstr(vp);
949 }
950 else if (strcasecmp(kp, "envid") == 0)
951 {
952 if (vp == NULL)
953 {
954 usrerr("501 ENVID requires a value");
955 /* NOTREACHED */
956 }
957 if (!xtextok(vp))
958 {
959 usrerr("501 Syntax error in ENVID parameter value");
960 /* NOTREACHED */
961 }
962 if (e->e_envid != NULL)
963 {
964 usrerr("501 Duplicate ENVID parameter");
965 /* NOTREACHED */
966 }
967 e->e_envid = newstr(vp);
968 }
969 else if (strcasecmp(kp, "ret") == 0)
970 {
971 if (vp == NULL)
972 {
973 usrerr("501 RET requires a value");
974 /* NOTREACHED */
975 }
976 if (bitset(EF_RET_PARAM, e->e_flags))
977 {
978 usrerr("501 Duplicate RET parameter");
979 /* NOTREACHED */
980 }
981 e->e_flags |= EF_RET_PARAM;
982 if (strcasecmp(vp, "hdrs") == 0)
983 e->e_flags |= EF_NO_BODY_RETN;
984 else if (strcasecmp(vp, "full") != 0)
985 {
986 usrerr("501 Bad argument \"%s\" to RET", vp);
987 /* NOTREACHED */
988 }
989 }
990 else
991 {
992 usrerr("501 %s parameter unrecognized", kp);
993 /* NOTREACHED */
994 }
995 }
996 /*
997 ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
998 **
999 ** Parameters:
1000 ** a -- the address corresponding to the To: parameter.
1001 ** kp -- the parameter key.
1002 ** vp -- the value of that parameter.
1003 ** e -- the envelope.
1004 **
1005 ** Returns:
1006 ** none.
1007 */
1008
1009 void
rcpt_esmtp_args(a,kp,vp,e)1010 rcpt_esmtp_args(a, kp, vp, e)
1011 ADDRESS *a;
1012 char *kp;
1013 char *vp;
1014 ENVELOPE *e;
1015 {
1016 if (strcasecmp(kp, "notify") == 0)
1017 {
1018 char *p;
1019
1020 if (vp == NULL)
1021 {
1022 usrerr("501 NOTIFY requires a value");
1023 /* NOTREACHED */
1024 }
1025 a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
1026 a->q_flags |= QHASNOTIFY;
1027 if (strcasecmp(vp, "never") == 0)
1028 return;
1029 for (p = vp; p != NULL; vp = p)
1030 {
1031 p = strchr(p, ',');
1032 if (p != NULL)
1033 *p++ = '\0';
1034 if (strcasecmp(vp, "success") == 0)
1035 a->q_flags |= QPINGONSUCCESS;
1036 else if (strcasecmp(vp, "failure") == 0)
1037 a->q_flags |= QPINGONFAILURE;
1038 else if (strcasecmp(vp, "delay") == 0)
1039 a->q_flags |= QPINGONDELAY;
1040 else
1041 {
1042 usrerr("501 Bad argument \"%s\" to NOTIFY",
1043 vp);
1044 /* NOTREACHED */
1045 }
1046 }
1047 }
1048 else if (strcasecmp(kp, "orcpt") == 0)
1049 {
1050 if (vp == NULL)
1051 {
1052 usrerr("501 ORCPT requires a value");
1053 /* NOTREACHED */
1054 }
1055 if (strchr(vp, ';') == NULL || !xtextok(vp))
1056 {
1057 usrerr("501 Syntax error in ORCPT parameter value");
1058 /* NOTREACHED */
1059 }
1060 if (a->q_orcpt != NULL)
1061 {
1062 usrerr("501 Duplicate ORCPT parameter");
1063 /* NOTREACHED */
1064 }
1065 a->q_orcpt = newstr(vp);
1066 }
1067 else
1068 {
1069 usrerr("501 %s parameter unrecognized", kp);
1070 /* NOTREACHED */
1071 }
1072 }
1073 /*
1074 ** PRINTVRFYADDR -- print an entry in the verify queue
1075 **
1076 ** Parameters:
1077 ** a -- the address to print
1078 ** last -- set if this is the last one.
1079 **
1080 ** Returns:
1081 ** none.
1082 **
1083 ** Side Effects:
1084 ** Prints the appropriate 250 codes.
1085 */
1086
1087 void
printvrfyaddr(a,last)1088 printvrfyaddr(a, last)
1089 register ADDRESS *a;
1090 bool last;
1091 {
1092 char fmtbuf[20];
1093
1094 strcpy(fmtbuf, "250");
1095 fmtbuf[3] = last ? ' ' : '-';
1096
1097 if (a->q_fullname == NULL)
1098 {
1099 if (strchr(a->q_user, '@') == NULL)
1100 strcpy(&fmtbuf[4], "<%s@%s>");
1101 else
1102 strcpy(&fmtbuf[4], "<%s>");
1103 message(fmtbuf, a->q_user, MyHostName);
1104 }
1105 else
1106 {
1107 if (strchr(a->q_user, '@') == NULL)
1108 strcpy(&fmtbuf[4], "%s <%s@%s>");
1109 else
1110 strcpy(&fmtbuf[4], "%s <%s>");
1111 message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
1112 }
1113 }
1114 /*
1115 ** HELP -- implement the HELP command.
1116 **
1117 ** Parameters:
1118 ** topic -- the topic we want help for.
1119 **
1120 ** Returns:
1121 ** none.
1122 **
1123 ** Side Effects:
1124 ** outputs the help file to message output.
1125 */
1126
1127 void
help(topic)1128 help(topic)
1129 char *topic;
1130 {
1131 register FILE *hf;
1132 int len;
1133 bool noinfo;
1134 char buf[MAXLINE];
1135 extern char Version[];
1136
1137
1138 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
1139 {
1140 /* no help */
1141 errno = 0;
1142 message("502 Sendmail %s -- HELP not implemented", Version);
1143 return;
1144 }
1145
1146 if (topic == NULL || *topic == '\0')
1147 {
1148 topic = "smtp";
1149 message("214-This is Sendmail version %s", Version);
1150 noinfo = FALSE;
1151 }
1152 else
1153 {
1154 makelower(topic);
1155 noinfo = TRUE;
1156 }
1157
1158 len = strlen(topic);
1159
1160 while (fgets(buf, sizeof buf, hf) != NULL)
1161 {
1162 if (strncmp(buf, topic, len) == 0)
1163 {
1164 register char *p;
1165
1166 p = strchr(buf, '\t');
1167 if (p == NULL)
1168 p = buf;
1169 else
1170 p++;
1171 fixcrlf(p, TRUE);
1172 message("214-%s", p);
1173 noinfo = FALSE;
1174 }
1175 }
1176
1177 if (noinfo)
1178 message("504 HELP topic unknown");
1179 else
1180 message("214 End of HELP info");
1181 (void) fclose(hf);
1182 }
1183 /*
1184 ** RUNINCHILD -- return twice -- once in the child, then in the parent again
1185 **
1186 ** Parameters:
1187 ** label -- a string used in error messages
1188 **
1189 ** Returns:
1190 ** zero in the child
1191 ** one in the parent
1192 **
1193 ** Side Effects:
1194 ** none.
1195 */
1196
1197 int
runinchild(label,e)1198 runinchild(label, e)
1199 char *label;
1200 register ENVELOPE *e;
1201 {
1202 int childpid;
1203
1204 if (!OneXact)
1205 {
1206 childpid = dofork();
1207 if (childpid < 0)
1208 {
1209 syserr("451 %s: cannot fork", label);
1210 return (1);
1211 }
1212 if (childpid > 0)
1213 {
1214 auto int st;
1215
1216 /* parent -- wait for child to complete */
1217 setproctitle("server %s child wait", CurSmtpClient);
1218 st = waitfor(childpid);
1219 if (st == -1)
1220 syserr("451 %s: lost child", label);
1221 else if (!WIFEXITED(st))
1222 syserr("451 %s: died on signal %d",
1223 label, st & 0177);
1224
1225 /* if we exited on a QUIT command, complete the process */
1226 if (WEXITSTATUS(st) == EX_QUIT)
1227 {
1228 disconnect(1, e);
1229 finis();
1230 }
1231
1232 return (1);
1233 }
1234 else
1235 {
1236 /* child */
1237 InChild = TRUE;
1238 QuickAbort = FALSE;
1239 clearenvelope(e, FALSE);
1240 }
1241 }
1242
1243 /* open alias database */
1244 initmaps(FALSE, e);
1245
1246 return (0);
1247 }
1248
1249 # endif /* SMTP */
1250