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