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