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