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