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.47 (Berkeley) 11/21/94 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)srvrsmtp.c 8.47 (Berkeley) 11/21/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 a->q_flags |= QPRIMARY; 545 a = recipient(a, &e->e_sendqueue, e); 546 547 /* now parse ESMTP arguments */ 548 while (p != NULL && *p != '\0') 549 { 550 char *kp; 551 char *vp = NULL; 552 553 /* locate the beginning of the keyword */ 554 while (isascii(*p) && isspace(*p)) 555 p++; 556 if (*p == '\0') 557 break; 558 kp = p; 559 560 /* skip to the value portion */ 561 while (isascii(*p) && isalnum(*p) || *p == '-') 562 p++; 563 if (*p == '=') 564 { 565 *p++ = '\0'; 566 vp = p; 567 568 /* skip to the end of the value */ 569 while (*p != '\0' && *p != ' ' && 570 !(isascii(*p) && iscntrl(*p)) && 571 *p != '=') 572 p++; 573 } 574 575 if (*p != '\0') 576 *p++ = '\0'; 577 578 if (tTd(19, 1)) 579 printf("RCPT: got arg %s=\"%s\"\n", kp, 580 vp == NULL ? "<null>" : vp); 581 582 rcpt_esmtp_args(a, kp, vp, e); 583 584 } 585 if (Errors != 0) 586 break; 587 588 /* no errors during parsing, but might be a duplicate */ 589 e->e_to = p; 590 if (!bitset(QBADADDR, a->q_flags)) 591 { 592 message("250 Recipient ok%s", 593 bitset(QQUEUEUP, a->q_flags) ? 594 " (will queue)" : ""); 595 nrcpts++; 596 } 597 else 598 { 599 /* punt -- should keep message in ADDRESS.... */ 600 message("550 Addressee unknown"); 601 } 602 e->e_to = NULL; 603 break; 604 605 case CMDDATA: /* data -- text of mail */ 606 SmtpPhase = "server DATA"; 607 if (!gotmail) 608 { 609 message("503 Need MAIL command"); 610 break; 611 } 612 else if (nrcpts <= 0) 613 { 614 message("503 Need RCPT (recipient)"); 615 break; 616 } 617 618 /* check to see if we need to re-expand aliases */ 619 /* also reset QBADADDR on already-diagnosted addrs */ 620 doublequeue = FALSE; 621 for (a = e->e_sendqueue; a != NULL; a = a->q_next) 622 { 623 if (bitset(QVERIFIED, a->q_flags)) 624 { 625 /* need to re-expand aliases */ 626 doublequeue = TRUE; 627 } 628 if (bitset(QBADADDR, a->q_flags)) 629 { 630 /* make this "go away" */ 631 a->q_flags |= QDONTSEND; 632 a->q_flags &= ~QBADADDR; 633 } 634 } 635 636 /* collect the text of the message */ 637 SmtpPhase = "collect"; 638 collect(InChannel, TRUE, doublequeue, NULL, e); 639 if (Errors != 0) 640 goto abortmessage; 641 642 /* from now on, we have to operate silently */ 643 HoldErrs = TRUE; 644 e->e_errormode = EM_MAIL; 645 646 /* 647 ** Arrange to send to everyone. 648 ** If sending to multiple people, mail back 649 ** errors rather than reporting directly. 650 ** In any case, don't mail back errors for 651 ** anything that has happened up to 652 ** now (the other end will do this). 653 ** Truncate our transcript -- the mail has gotten 654 ** to us successfully, and if we have 655 ** to mail this back, it will be easier 656 ** on the reader. 657 ** Then send to everyone. 658 ** Finally give a reply code. If an error has 659 ** already been given, don't mail a 660 ** message back. 661 ** We goose error returns by clearing error bit. 662 */ 663 664 SmtpPhase = "delivery"; 665 e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 666 id = e->e_id; 667 668 if (doublequeue) 669 { 670 /* make sure it is in the queue */ 671 queueup(e, TRUE, FALSE); 672 } 673 else 674 { 675 /* send to all recipients */ 676 sendall(e, SM_DEFAULT); 677 } 678 e->e_to = NULL; 679 680 /* issue success message */ 681 message("250 %s Message accepted for delivery", id); 682 683 /* if we just queued, poke it */ 684 if (doublequeue && e->e_sendmode != SM_QUEUE) 685 { 686 extern pid_t dowork(); 687 688 unlockqueue(e); 689 (void) dowork(id, TRUE, TRUE, e); 690 } 691 692 abortmessage: 693 /* if in a child, pop back to our parent */ 694 if (InChild) 695 finis(); 696 697 /* clean up a bit */ 698 gotmail = FALSE; 699 dropenvelope(e); 700 CurEnv = e = newenvelope(e, CurEnv); 701 e->e_flags = BlankEnvelope.e_flags; 702 break; 703 704 case CMDRSET: /* rset -- reset state */ 705 message("250 Reset state"); 706 e->e_flags |= EF_CLRQUEUE; 707 if (InChild) 708 finis(); 709 710 /* clean up a bit */ 711 gotmail = FALSE; 712 dropenvelope(e); 713 CurEnv = e = newenvelope(e, CurEnv); 714 break; 715 716 case CMDVRFY: /* vrfy -- verify address */ 717 case CMDEXPN: /* expn -- expand address */ 718 vrfy = c->cmdcode == CMDVRFY; 719 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 720 PrivacyFlags)) 721 { 722 if (vrfy) 723 message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 724 else 725 message("502 Sorry, we do not allow this operation"); 726 #ifdef LOG 727 if (LogLevel > 5) 728 syslog(LOG_INFO, "%s: %s [rejected]", 729 CurSmtpClient, inp); 730 #endif 731 break; 732 } 733 else if (!gothello && 734 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 735 PrivacyFlags)) 736 { 737 message("503 I demand that you introduce yourself first"); 738 break; 739 } 740 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 741 break; 742 #ifdef LOG 743 if (LogLevel > 5) 744 syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 745 #endif 746 vrfyqueue = NULL; 747 QuickAbort = TRUE; 748 if (vrfy) 749 e->e_flags |= EF_VRFYONLY; 750 while (*p != '\0' && isascii(*p) && isspace(*p)) 751 p++; 752 if (*p == '\0') 753 { 754 message("501 Argument required"); 755 Errors++; 756 } 757 else 758 { 759 (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 760 } 761 if (Errors != 0) 762 { 763 if (InChild) 764 finis(); 765 break; 766 } 767 if (vrfyqueue == NULL) 768 { 769 message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 770 } 771 while (vrfyqueue != NULL) 772 { 773 a = vrfyqueue; 774 while ((a = a->q_next) != NULL && 775 bitset(QDONTSEND|QBADADDR, a->q_flags)) 776 continue; 777 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 778 printvrfyaddr(vrfyqueue, a == NULL); 779 vrfyqueue = vrfyqueue->q_next; 780 } 781 if (InChild) 782 finis(); 783 break; 784 785 case CMDHELP: /* help -- give user info */ 786 help(p); 787 break; 788 789 case CMDNOOP: /* noop -- do nothing */ 790 message("250 OK"); 791 break; 792 793 case CMDQUIT: /* quit -- leave mail */ 794 message("221 %s closing connection", MyHostName); 795 796 doquit: 797 /* avoid future 050 messages */ 798 disconnect(1, e); 799 800 if (InChild) 801 ExitStat = EX_QUIT; 802 finis(); 803 804 case CMDVERB: /* set verbose mode */ 805 if (bitset(PRIV_NOEXPN, PrivacyFlags)) 806 { 807 /* this would give out the same info */ 808 message("502 Verbose unavailable"); 809 break; 810 } 811 Verbose = TRUE; 812 e->e_sendmode = SM_DELIVER; 813 message("250 Verbose mode"); 814 break; 815 816 case CMDONEX: /* doing one transaction only */ 817 OneXact = TRUE; 818 message("250 Only one transaction"); 819 break; 820 821 # ifdef SMTPDEBUG 822 case CMDDBGQSHOW: /* show queues */ 823 printf("Send Queue="); 824 printaddr(e->e_sendqueue, TRUE); 825 break; 826 827 case CMDDBGDEBUG: /* set debug mode */ 828 tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 829 tTflag(p); 830 message("200 Debug set"); 831 break; 832 833 # else /* not SMTPDEBUG */ 834 case CMDDBGQSHOW: /* show queues */ 835 case CMDDBGDEBUG: /* set debug mode */ 836 # endif /* SMTPDEBUG */ 837 case CMDLOGBOGUS: /* bogus command */ 838 # ifdef LOG 839 if (LogLevel > 0) 840 syslog(LOG_CRIT, 841 "\"%s\" command from %s (%s)", 842 c->cmdname, peerhostname, 843 anynet_ntoa(&RealHostAddr)); 844 # endif 845 /* FALL THROUGH */ 846 847 case CMDERROR: /* unknown command */ 848 if (++badcommands > MAXBADCOMMANDS) 849 { 850 message("421 %s Too many bad commands; closing connection", 851 MyHostName); 852 goto doquit; 853 } 854 855 message("500 Command unrecognized"); 856 break; 857 858 default: 859 errno = 0; 860 syserr("500 smtp: unknown code %d", c->cmdcode); 861 break; 862 } 863 } 864 } 865 /* 866 ** SKIPWORD -- skip a fixed word. 867 ** 868 ** Parameters: 869 ** p -- place to start looking. 870 ** w -- word to skip. 871 ** 872 ** Returns: 873 ** p following w. 874 ** NULL on error. 875 ** 876 ** Side Effects: 877 ** clobbers the p data area. 878 */ 879 880 static char * 881 skipword(p, w) 882 register char *p; 883 char *w; 884 { 885 register char *q; 886 char *firstp = p; 887 888 /* find beginning of word */ 889 while (isascii(*p) && isspace(*p)) 890 p++; 891 q = p; 892 893 /* find end of word */ 894 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 895 p++; 896 while (isascii(*p) && isspace(*p)) 897 *p++ = '\0'; 898 if (*p != ':') 899 { 900 syntax: 901 message("501 Syntax error in parameters scanning \"%s\"", 902 firstp); 903 Errors++; 904 return (NULL); 905 } 906 *p++ = '\0'; 907 while (isascii(*p) && isspace(*p)) 908 p++; 909 910 if (*p == '\0') 911 goto syntax; 912 913 /* see if the input word matches desired word */ 914 if (strcasecmp(q, w)) 915 goto syntax; 916 917 return (p); 918 } 919 /* 920 ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 921 ** 922 ** Parameters: 923 ** a -- the address corresponding to the To: parameter. 924 ** kp -- the parameter key. 925 ** vp -- the value of that parameter. 926 ** e -- the envelope. 927 ** 928 ** Returns: 929 ** none. 930 */ 931 932 rcpt_esmtp_args(a, kp, vp, e) 933 ADDRESS *a; 934 char *kp; 935 char *vp; 936 ENVELOPE *e; 937 { 938 if (strcasecmp(kp, "notify") == 0) 939 { 940 char *p; 941 942 if (vp == NULL) 943 { 944 usrerr("501 NOTIFY requires a value"); 945 /* NOTREACHED */ 946 } 947 a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 948 if (strcasecmp(vp, "never") == 0) 949 return; 950 for (p = vp; p != NULL; vp = p) 951 { 952 p = strchr(p, ','); 953 if (p != NULL) 954 *p++ = '\0'; 955 if (strcasecmp(vp, "success") == 0) 956 a->q_flags |= QPINGONSUCCESS; 957 else if (strcasecmp(vp, "failure") == 0) 958 a->q_flags |= QPINGONFAILURE; 959 else if (strcasecmp(vp, "delay") == 0) 960 a->q_flags |= QPINGONDELAY; 961 else 962 { 963 usrerr("501 Bad argument \"%s\" to NOTIFY", 964 vp); 965 /* NOTREACHED */ 966 } 967 } 968 } 969 else if (strcasecmp(kp, "ret") == 0) 970 { 971 if (vp == NULL) 972 { 973 usrerr("501 RET requires a value"); 974 /* NOTREACHED */ 975 } 976 a->q_flags |= QHAS_RET_PARAM; 977 if (strcasecmp(vp, "hdrs") == 0) 978 a->q_flags |= QRET_HDRS; 979 else if (strcasecmp(vp, "full") != 0) 980 { 981 usrerr("501 Bad argument \"%s\" to RET", vp); 982 /* NOTREACHED */ 983 } 984 } 985 else if (strcasecmp(kp, "orcpt") == 0) 986 { 987 if (vp == NULL) 988 { 989 usrerr("501 ORCPT requires a value"); 990 /* NOTREACHED */ 991 } 992 a->q_orcpt = newstr(vp); 993 } 994 else 995 { 996 usrerr("501 %s parameter unrecognized", kp); 997 /* NOTREACHED */ 998 } 999 } 1000 /* 1001 ** PRINTVRFYADDR -- print an entry in the verify queue 1002 ** 1003 ** Parameters: 1004 ** a -- the address to print 1005 ** last -- set if this is the last one. 1006 ** 1007 ** Returns: 1008 ** none. 1009 ** 1010 ** Side Effects: 1011 ** Prints the appropriate 250 codes. 1012 */ 1013 1014 printvrfyaddr(a, last) 1015 register ADDRESS *a; 1016 bool last; 1017 { 1018 char fmtbuf[20]; 1019 1020 strcpy(fmtbuf, "250"); 1021 fmtbuf[3] = last ? ' ' : '-'; 1022 1023 if (a->q_fullname == NULL) 1024 { 1025 if (strchr(a->q_user, '@') == NULL) 1026 strcpy(&fmtbuf[4], "<%s@%s>"); 1027 else 1028 strcpy(&fmtbuf[4], "<%s>"); 1029 message(fmtbuf, a->q_user, MyHostName); 1030 } 1031 else 1032 { 1033 if (strchr(a->q_user, '@') == NULL) 1034 strcpy(&fmtbuf[4], "%s <%s@%s>"); 1035 else 1036 strcpy(&fmtbuf[4], "%s <%s>"); 1037 message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 1038 } 1039 } 1040 /* 1041 ** HELP -- implement the HELP command. 1042 ** 1043 ** Parameters: 1044 ** topic -- the topic we want help for. 1045 ** 1046 ** Returns: 1047 ** none. 1048 ** 1049 ** Side Effects: 1050 ** outputs the help file to message output. 1051 */ 1052 1053 help(topic) 1054 char *topic; 1055 { 1056 register FILE *hf; 1057 int len; 1058 char buf[MAXLINE]; 1059 bool noinfo; 1060 1061 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 1062 { 1063 /* no help */ 1064 errno = 0; 1065 message("502 HELP not implemented"); 1066 return; 1067 } 1068 1069 if (topic == NULL || *topic == '\0') 1070 topic = "smtp"; 1071 else 1072 makelower(topic); 1073 1074 len = strlen(topic); 1075 noinfo = TRUE; 1076 1077 while (fgets(buf, sizeof buf, hf) != NULL) 1078 { 1079 if (strncmp(buf, topic, len) == 0) 1080 { 1081 register char *p; 1082 1083 p = strchr(buf, '\t'); 1084 if (p == NULL) 1085 p = buf; 1086 else 1087 p++; 1088 fixcrlf(p, TRUE); 1089 message("214-%s", p); 1090 noinfo = FALSE; 1091 } 1092 } 1093 1094 if (noinfo) 1095 message("504 HELP topic unknown"); 1096 else 1097 message("214 End of HELP info"); 1098 (void) fclose(hf); 1099 } 1100 /* 1101 ** RUNINCHILD -- return twice -- once in the child, then in the parent again 1102 ** 1103 ** Parameters: 1104 ** label -- a string used in error messages 1105 ** 1106 ** Returns: 1107 ** zero in the child 1108 ** one in the parent 1109 ** 1110 ** Side Effects: 1111 ** none. 1112 */ 1113 1114 runinchild(label, e) 1115 char *label; 1116 register ENVELOPE *e; 1117 { 1118 int childpid; 1119 1120 if (!OneXact) 1121 { 1122 childpid = dofork(); 1123 if (childpid < 0) 1124 { 1125 syserr("%s: cannot fork", label); 1126 return (1); 1127 } 1128 if (childpid > 0) 1129 { 1130 auto int st; 1131 1132 /* parent -- wait for child to complete */ 1133 setproctitle("server %s child wait", CurHostName); 1134 st = waitfor(childpid); 1135 if (st == -1) 1136 syserr("%s: lost child", label); 1137 else if (!WIFEXITED(st)) 1138 syserr("%s: died on signal %d", 1139 label, st & 0177); 1140 1141 /* if we exited on a QUIT command, complete the process */ 1142 if (WEXITSTATUS(st) == EX_QUIT) 1143 { 1144 disconnect(1, e); 1145 finis(); 1146 } 1147 1148 return (1); 1149 } 1150 else 1151 { 1152 /* child */ 1153 InChild = TRUE; 1154 QuickAbort = FALSE; 1155 clearenvelope(e, FALSE); 1156 } 1157 } 1158 1159 /* open alias database */ 1160 initmaps(FALSE, e); 1161 1162 return (0); 1163 } 1164 1165 # endif /* SMTP */ 1166