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