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.82 (Berkeley) 06/14/95 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)srvrsmtp.c 8.82 (Berkeley) 06/14/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 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 * 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 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 if (strcasecmp(vp, "8bitmime") == 0) 935 { 936 SevenBitInput = FALSE; 937 e->e_flags |= EF_NL_NOT_EOL; 938 } 939 else if (strcasecmp(vp, "7bit") == 0) 940 { 941 SevenBitInput = TRUE; 942 } 943 else 944 { 945 usrerr("501 Unknown BODY type %s", 946 vp); 947 /* NOTREACHED */ 948 } 949 e->e_bodytype = newstr(vp); 950 } 951 else if (strcasecmp(kp, "envid") == 0) 952 { 953 if (vp == NULL) 954 { 955 usrerr("501 ENVID requires a value"); 956 /* NOTREACHED */ 957 } 958 if (!xtextok(vp)) 959 { 960 usrerr("501 Syntax error in ENVID parameter value"); 961 /* NOTREACHED */ 962 } 963 if (e->e_envid != NULL) 964 { 965 usrerr("501 Duplicate ENVID parameter"); 966 /* NOTREACHED */ 967 } 968 e->e_envid = newstr(vp); 969 } 970 else if (strcasecmp(kp, "ret") == 0) 971 { 972 if (vp == NULL) 973 { 974 usrerr("501 RET requires a value"); 975 /* NOTREACHED */ 976 } 977 if (bitset(EF_RET_PARAM, e->e_flags)) 978 { 979 usrerr("501 Duplicate RET parameter"); 980 /* NOTREACHED */ 981 } 982 e->e_flags |= EF_RET_PARAM; 983 if (strcasecmp(vp, "hdrs") == 0) 984 e->e_flags |= EF_NO_BODY_RETN; 985 else if (strcasecmp(vp, "full") != 0) 986 { 987 usrerr("501 Bad argument \"%s\" to RET", vp); 988 /* NOTREACHED */ 989 } 990 } 991 else 992 { 993 usrerr("501 %s parameter unrecognized", kp); 994 /* NOTREACHED */ 995 } 996 } 997 /* 998 ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 999 ** 1000 ** Parameters: 1001 ** a -- the address corresponding to the To: parameter. 1002 ** kp -- the parameter key. 1003 ** vp -- the value of that parameter. 1004 ** e -- the envelope. 1005 ** 1006 ** Returns: 1007 ** none. 1008 */ 1009 1010 void 1011 rcpt_esmtp_args(a, kp, vp, e) 1012 ADDRESS *a; 1013 char *kp; 1014 char *vp; 1015 ENVELOPE *e; 1016 { 1017 if (strcasecmp(kp, "notify") == 0) 1018 { 1019 char *p; 1020 1021 if (vp == NULL) 1022 { 1023 usrerr("501 NOTIFY requires a value"); 1024 /* NOTREACHED */ 1025 } 1026 a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 1027 a->q_flags |= QHASNOTIFY; 1028 if (strcasecmp(vp, "never") == 0) 1029 return; 1030 for (p = vp; p != NULL; vp = p) 1031 { 1032 p = strchr(p, ','); 1033 if (p != NULL) 1034 *p++ = '\0'; 1035 if (strcasecmp(vp, "success") == 0) 1036 a->q_flags |= QPINGONSUCCESS; 1037 else if (strcasecmp(vp, "failure") == 0) 1038 a->q_flags |= QPINGONFAILURE; 1039 else if (strcasecmp(vp, "delay") == 0) 1040 a->q_flags |= QPINGONDELAY; 1041 else 1042 { 1043 usrerr("501 Bad argument \"%s\" to NOTIFY", 1044 vp); 1045 /* NOTREACHED */ 1046 } 1047 } 1048 } 1049 else if (strcasecmp(kp, "orcpt") == 0) 1050 { 1051 if (vp == NULL) 1052 { 1053 usrerr("501 ORCPT requires a value"); 1054 /* NOTREACHED */ 1055 } 1056 if (strchr(vp, ';') == NULL || !xtextok(vp)) 1057 { 1058 usrerr("501 Syntax error in ORCPT parameter value"); 1059 /* NOTREACHED */ 1060 } 1061 if (a->q_orcpt != NULL) 1062 { 1063 usrerr("501 Duplicate ORCPT parameter"); 1064 /* NOTREACHED */ 1065 } 1066 a->q_orcpt = newstr(vp); 1067 } 1068 else 1069 { 1070 usrerr("501 %s parameter unrecognized", kp); 1071 /* NOTREACHED */ 1072 } 1073 } 1074 /* 1075 ** PRINTVRFYADDR -- print an entry in the verify queue 1076 ** 1077 ** Parameters: 1078 ** a -- the address to print 1079 ** last -- set if this is the last one. 1080 ** 1081 ** Returns: 1082 ** none. 1083 ** 1084 ** Side Effects: 1085 ** Prints the appropriate 250 codes. 1086 */ 1087 1088 void 1089 printvrfyaddr(a, last) 1090 register ADDRESS *a; 1091 bool last; 1092 { 1093 char fmtbuf[20]; 1094 1095 strcpy(fmtbuf, "250"); 1096 fmtbuf[3] = last ? ' ' : '-'; 1097 1098 if (a->q_fullname == NULL) 1099 { 1100 if (strchr(a->q_user, '@') == NULL) 1101 strcpy(&fmtbuf[4], "<%s@%s>"); 1102 else 1103 strcpy(&fmtbuf[4], "<%s>"); 1104 message(fmtbuf, a->q_user, MyHostName); 1105 } 1106 else 1107 { 1108 if (strchr(a->q_user, '@') == NULL) 1109 strcpy(&fmtbuf[4], "%s <%s@%s>"); 1110 else 1111 strcpy(&fmtbuf[4], "%s <%s>"); 1112 message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 1113 } 1114 } 1115 /* 1116 ** HELP -- implement the HELP command. 1117 ** 1118 ** Parameters: 1119 ** topic -- the topic we want help for. 1120 ** 1121 ** Returns: 1122 ** none. 1123 ** 1124 ** Side Effects: 1125 ** outputs the help file to message output. 1126 */ 1127 1128 void 1129 help(topic) 1130 char *topic; 1131 { 1132 register FILE *hf; 1133 int len; 1134 bool noinfo; 1135 char buf[MAXLINE]; 1136 extern char Version[]; 1137 1138 1139 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 1140 { 1141 /* no help */ 1142 errno = 0; 1143 message("502 Sendmail %s -- HELP not implemented", Version); 1144 return; 1145 } 1146 1147 if (topic == NULL || *topic == '\0') 1148 { 1149 topic = "smtp"; 1150 message("214-This is Sendmail version %s", Version); 1151 noinfo = FALSE; 1152 } 1153 else 1154 { 1155 makelower(topic); 1156 noinfo = TRUE; 1157 } 1158 1159 len = strlen(topic); 1160 1161 while (fgets(buf, sizeof buf, hf) != NULL) 1162 { 1163 if (strncmp(buf, topic, len) == 0) 1164 { 1165 register char *p; 1166 1167 p = strchr(buf, '\t'); 1168 if (p == NULL) 1169 p = buf; 1170 else 1171 p++; 1172 fixcrlf(p, TRUE); 1173 message("214-%s", p); 1174 noinfo = FALSE; 1175 } 1176 } 1177 1178 if (noinfo) 1179 message("504 HELP topic unknown"); 1180 else 1181 message("214 End of HELP info"); 1182 (void) fclose(hf); 1183 } 1184 /* 1185 ** RUNINCHILD -- return twice -- once in the child, then in the parent again 1186 ** 1187 ** Parameters: 1188 ** label -- a string used in error messages 1189 ** 1190 ** Returns: 1191 ** zero in the child 1192 ** one in the parent 1193 ** 1194 ** Side Effects: 1195 ** none. 1196 */ 1197 1198 int 1199 runinchild(label, e) 1200 char *label; 1201 register ENVELOPE *e; 1202 { 1203 int childpid; 1204 1205 if (!OneXact) 1206 { 1207 childpid = dofork(); 1208 if (childpid < 0) 1209 { 1210 syserr("451 %s: cannot fork", label); 1211 return (1); 1212 } 1213 if (childpid > 0) 1214 { 1215 auto int st; 1216 1217 /* parent -- wait for child to complete */ 1218 setproctitle("server %s child wait", CurSmtpClient); 1219 st = waitfor(childpid); 1220 if (st == -1) 1221 syserr("451 %s: lost child", label); 1222 else if (!WIFEXITED(st)) 1223 syserr("451 %s: died on signal %d", 1224 label, st & 0177); 1225 1226 /* if we exited on a QUIT command, complete the process */ 1227 if (WEXITSTATUS(st) == EX_QUIT) 1228 { 1229 disconnect(1, e); 1230 finis(); 1231 } 1232 1233 return (1); 1234 } 1235 else 1236 { 1237 /* child */ 1238 InChild = TRUE; 1239 QuickAbort = FALSE; 1240 clearenvelope(e, FALSE); 1241 } 1242 } 1243 1244 /* open alias database */ 1245 initmaps(FALSE, e); 1246 1247 return (0); 1248 } 1249 1250 # endif /* SMTP */ 1251