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