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