1*9339Seric # include <errno.h> 24549Seric # include "sendmail.h" 34549Seric 45181Seric # ifndef SMTP 5*9339Seric SCCSID(@(#)srvrsmtp.c 3.37 11/24/82 (no SMTP)); 65181Seric # else SMTP 74556Seric 8*9339Seric SCCSID(@(#)srvrsmtp.c 3.37 11/24/82); 95181Seric 104549Seric /* 114549Seric ** SMTP -- run the SMTP protocol. 124549Seric ** 134549Seric ** Parameters: 144549Seric ** none. 154549Seric ** 164549Seric ** Returns: 174549Seric ** never. 184549Seric ** 194549Seric ** Side Effects: 204549Seric ** Reads commands from the input channel and processes 214549Seric ** them. 224549Seric */ 234549Seric 244549Seric struct cmd 254549Seric { 264549Seric char *cmdname; /* command name */ 274549Seric int cmdcode; /* internal code, see below */ 284549Seric }; 294549Seric 304549Seric /* values for cmdcode */ 314549Seric # define CMDERROR 0 /* bad command */ 324549Seric # define CMDMAIL 1 /* mail -- designate sender */ 334976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 344549Seric # define CMDDATA 3 /* data -- send message text */ 35*9339Seric # define CMDRSET 4 /* rset -- reset state */ 36*9339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 37*9339Seric # define CMDHELP 6 /* help -- give usage info */ 38*9339Seric # define CMDNOOP 7 /* noop -- do nothing */ 39*9339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 40*9339Seric # define CMDHELO 9 /* helo -- be polite */ 41*9339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 42*9339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 43*9339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 44*9339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 45*9339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 46*9339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 47*9339Seric # define CMDDBGSHELL 16 /* shell -- give us a shell */ 484549Seric 494549Seric static struct cmd CmdTab[] = 504549Seric { 514549Seric "mail", CMDMAIL, 524976Seric "rcpt", CMDRCPT, 534549Seric "data", CMDDATA, 544549Seric "rset", CMDRSET, 554549Seric "vrfy", CMDVRFY, 567762Seric "expn", CMDVRFY, 574549Seric "help", CMDHELP, 584549Seric "noop", CMDNOOP, 594549Seric "quit", CMDQUIT, 604976Seric "helo", CMDHELO, 618544Seric "verb", CMDVERB, 629314Seric "onex", CMDONEX, 635003Seric # ifdef DEBUG 64*9339Seric "showq", CMDDBGQSHOW, 658544Seric "debug", CMDDBGDEBUG, 668544Seric "kill", CMDDBGKILL, 678544Seric "wiz", CMDDBGWIZ, 68*9339Seric "shell", CMDDBGSHELL, 695003Seric # endif DEBUG 704549Seric NULL, CMDERROR, 714549Seric }; 724549Seric 738544Seric # ifdef DEBUG 748544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 758544Seric char *WizWord = NULL; /* the wizard word to compare against */ 768544Seric # endif DEBUG 77*9339Seric bool InChild = FALSE; /* true if running in a subprocess */ 78*9339Seric #define EX_QUIT 22 /* special code for QUIT command */ 798544Seric 804549Seric smtp() 814549Seric { 824549Seric register char *p; 838544Seric register struct cmd *c; 844549Seric char *cmd; 854549Seric extern char *skipword(); 864549Seric extern bool sameword(); 874549Seric bool hasmail; /* mail command received */ 884713Seric int rcps; /* number of recipients */ 895003Seric auto ADDRESS *vrfyqueue; 909314Seric bool onexact = FALSE; /* one transaction this connection */ 919314Seric bool firsttime = TRUE; /* this is the first transaction */ 928544Seric char inp[MAXLINE]; 937124Seric extern char Version[]; 947356Seric extern tick(); 958544Seric extern bool iswiz(); 964549Seric 975003Seric hasmail = FALSE; 984713Seric rcps = 0; 997363Seric if (OutChannel != stdout) 1007363Seric { 1017363Seric /* arrange for debugging output to go to remote host */ 1027363Seric (void) close(1); 1037363Seric (void) dup(fileno(OutChannel)); 1047363Seric } 1057851Seric message("220", "%s Sendmail %s ready at %s", HostName, 1067797Seric Version, arpadate(NULL)); 1077762Seric (void) setjmp(TopFrame); 1087762Seric QuickAbort = FALSE; 1094549Seric for (;;) 1104549Seric { 1117356Seric /* setup for the read */ 1126907Seric CurEnv->e_to = NULL; 1134577Seric Errors = 0; 1147275Seric (void) fflush(stdout); 1157356Seric 1167356Seric /* read the input line */ 1177685Seric p = sfgets(inp, sizeof inp, InChannel); 1187356Seric 1197685Seric /* handle errors */ 1207356Seric if (p == NULL) 1217356Seric { 1224549Seric /* end of file, just die */ 1234558Seric message("421", "%s Lost input channel", HostName); 1244549Seric finis(); 1254549Seric } 1264549Seric 1274549Seric /* clean up end of line */ 1284558Seric fixcrlf(inp, TRUE); 1294549Seric 1304713Seric /* echo command to transcript */ 131*9339Seric if (Xscript != NULL) 132*9339Seric fprintf(Xscript, "<<< %s\n", inp); 1334713Seric 1344549Seric /* break off command */ 1354549Seric for (p = inp; isspace(*p); p++) 1364549Seric continue; 1374549Seric cmd = p; 1384549Seric while (*++p != '\0' && !isspace(*p)) 1394549Seric continue; 1404549Seric if (*p != '\0') 1414549Seric *p++ = '\0'; 1424549Seric 1434549Seric /* decode command */ 1444549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1454549Seric { 1464549Seric if (sameword(c->cmdname, cmd)) 1474549Seric break; 1484549Seric } 1494549Seric 1504549Seric /* process command */ 1514549Seric switch (c->cmdcode) 1524549Seric { 1534976Seric case CMDHELO: /* hello -- introduce yourself */ 1545187Seric define('s', newstr(p)); 1554997Seric message("250", "%s Hello %s, pleased to meet you", 1564997Seric HostName, p); 1574976Seric break; 1584976Seric 1594549Seric case CMDMAIL: /* mail -- designate sender */ 1609314Seric firsttime = FALSE; 1619314Seric 1629314Seric /* check for validity of this command */ 1634558Seric if (hasmail) 1644558Seric { 1654558Seric message("503", "Sender already specified"); 1664558Seric break; 1674558Seric } 168*9339Seric if (InChild) 169*9339Seric { 170*9339Seric syserr("Nested MAIL command"); 171*9339Seric exit(0); 172*9339Seric } 173*9339Seric 174*9339Seric /* fork a subprocess to process this command */ 175*9339Seric if (runinchild("SMTP-MAIL") > 0) 176*9339Seric break; 177*9339Seric initsys(); 178*9339Seric 179*9339Seric /* child -- go do the processing */ 1804549Seric p = skipword(p, "from"); 1814549Seric if (p == NULL) 1824549Seric break; 1834549Seric setsender(p); 1844577Seric if (Errors == 0) 1854549Seric { 1864549Seric message("250", "Sender ok"); 1874549Seric hasmail = TRUE; 1884549Seric } 189*9339Seric else if (InChild) 190*9339Seric finis(); 1914549Seric break; 1924549Seric 1934976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1944549Seric p = skipword(p, "to"); 1954549Seric if (p == NULL) 1964549Seric break; 1978081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1984577Seric if (Errors == 0) 1994549Seric { 2006057Seric message("250", "%s... Recipient ok", p); 2014713Seric rcps++; 2024549Seric } 2034549Seric break; 2044549Seric 2054549Seric case CMDDATA: /* data -- text of mail */ 2064976Seric if (!hasmail) 2074549Seric { 2084976Seric message("503", "Need MAIL command"); 2094976Seric break; 2104549Seric } 2114713Seric else if (rcps <= 0) 2124549Seric { 2134976Seric message("503", "Need RCPT (recipient)"); 2144976Seric break; 2154549Seric } 2164976Seric 2174976Seric /* collect the text of the message */ 2184976Seric collect(TRUE); 2194976Seric if (Errors != 0) 2204976Seric break; 2214976Seric 2228238Seric /* 2238238Seric ** Arrange to send to everyone. 2248238Seric ** If sending to multiple people, mail back 2258238Seric ** errors rather than reporting directly. 2268238Seric ** In any case, don't mail back errors for 2278238Seric ** anything that has happened up to 2288238Seric ** now (the other end will do this). 2298238Seric ** Then send to everyone. 2308238Seric ** Finally give a reply code. If an error has 2318238Seric ** already been given, don't mail a 2328238Seric ** message back. 233*9339Seric ** We goose error returns by clearing error bit. 2348238Seric */ 2358238Seric 2364976Seric if (rcps != 1) 2374976Seric HoldErrs = MailBack = TRUE; 238*9339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2394976Seric 2404976Seric /* send to all recipients */ 2419283Seric sendall(CurEnv, SendMode); 2426907Seric CurEnv->e_to = NULL; 2434976Seric 2448238Seric /* issue success if appropriate and reset */ 2458238Seric if (Errors == 0 || HoldErrs) 2468238Seric { 2478238Seric HoldErrs = FALSE; 2489283Seric message("250", "Ok"); 2498238Seric } 2508238Seric else 251*9339Seric CurEnv->e_flags &= ~EF_FATALERRS; 252*9339Seric 253*9339Seric /* if in a child, pop back to our parent */ 254*9339Seric if (InChild) 255*9339Seric finis(); 2564549Seric break; 2574549Seric 2584549Seric case CMDRSET: /* rset -- reset state */ 2594549Seric message("250", "Reset state"); 260*9339Seric if (InChild) 261*9339Seric finis(); 262*9339Seric break; 2634549Seric 2644549Seric case CMDVRFY: /* vrfy -- verify address */ 265*9339Seric if (runinchild("SMTP-VRFY") > 0) 266*9339Seric break; 2675003Seric vrfyqueue = NULL; 2687762Seric QuickAbort = TRUE; 2698081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2707762Seric if (Errors != 0) 271*9339Seric { 272*9339Seric if (InChild) 273*9339Seric finis(); 2747762Seric break; 275*9339Seric } 2765003Seric while (vrfyqueue != NULL) 2775003Seric { 2785003Seric register ADDRESS *a = vrfyqueue->q_next; 2795003Seric char *code; 2805003Seric 2817685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2825003Seric a = a->q_next; 2835003Seric 2847685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2855003Seric { 2865003Seric if (a != NULL) 2875003Seric code = "250-"; 2885003Seric else 2895003Seric code = "250"; 2905003Seric if (vrfyqueue->q_fullname == NULL) 2915003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2925003Seric else 2935003Seric message(code, "%s <%s>", 2945003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2955003Seric } 2965003Seric else if (a == NULL) 2975003Seric message("554", "Self destructive alias loop"); 2985003Seric vrfyqueue = a; 2995003Seric } 300*9339Seric if (InChild) 301*9339Seric finis(); 3024549Seric break; 3034549Seric 3044549Seric case CMDHELP: /* help -- give user info */ 3054577Seric if (*p == '\0') 3064577Seric p = "SMTP"; 3074577Seric help(p); 3084549Seric break; 3094549Seric 3104549Seric case CMDNOOP: /* noop -- do nothing */ 3114549Seric message("200", "OK"); 3124549Seric break; 3134549Seric 3144549Seric case CMDQUIT: /* quit -- leave mail */ 3154549Seric message("221", "%s closing connection", HostName); 316*9339Seric if (InChild) 317*9339Seric ExitStat = EX_QUIT; 3184549Seric finis(); 3194549Seric 3208544Seric case CMDVERB: /* set verbose mode */ 3218544Seric Verbose = TRUE; 3228544Seric message("200", "Verbose mode"); 3238544Seric break; 3248544Seric 3259314Seric case CMDONEX: /* doing one transaction only */ 3269314Seric onexact = TRUE; 3279314Seric message("200", "Only one transaction"); 3289314Seric break; 3299314Seric 3305003Seric # ifdef DEBUG 331*9339Seric case CMDDBGQSHOW: /* show queues */ 3326907Seric printf("Send Queue="); 3336907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3345003Seric break; 3357275Seric 3367275Seric case CMDDBGDEBUG: /* set debug mode */ 3377676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3387676Seric tTflag(p); 3397676Seric message("200", "Debug set"); 3407275Seric break; 3417275Seric 3427282Seric case CMDDBGKILL: /* kill the parent */ 3438544Seric if (!iswiz()) 3448544Seric break; 3457282Seric if (kill(MotherPid, SIGTERM) >= 0) 3467282Seric message("200", "Mother is dead"); 3477282Seric else 3487282Seric message("500", "Can't kill Mom"); 3497282Seric break; 3508544Seric 351*9339Seric case CMDDBGSHELL: /* give us an interactive shell */ 352*9339Seric if (!iswiz()) 353*9339Seric break; 354*9339Seric execl("/bin/csh", "sendmail", 0); 355*9339Seric execl("/bin/sh", "sendmail", 0); 356*9339Seric message("500", "Can't"); 357*9339Seric break; 358*9339Seric 3598544Seric case CMDDBGWIZ: /* become a wizard */ 3608544Seric if (WizWord != NULL) 3618544Seric { 3628544Seric char seed[3]; 3638544Seric extern char *crypt(); 3648544Seric 3658544Seric strncpy(seed, WizWord, 2); 3668544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3678544Seric { 3688544Seric message("500", "You are no wizard!"); 3698544Seric break; 3708544Seric } 3718544Seric } 3728544Seric IsWiz = TRUE; 3738544Seric message("200", "Please pass, oh mighty wizard"); 3748544Seric break; 3755003Seric # endif DEBUG 3765003Seric 3774549Seric case CMDERROR: /* unknown command */ 3784549Seric message("500", "Command unrecognized"); 3794549Seric break; 3804549Seric 3814549Seric default: 3824549Seric syserr("smtp: unknown code %d", c->cmdcode); 3834549Seric break; 3844549Seric } 3854549Seric } 3864549Seric } 3874549Seric /* 3884549Seric ** SKIPWORD -- skip a fixed word. 3894549Seric ** 3904549Seric ** Parameters: 3914549Seric ** p -- place to start looking. 3924549Seric ** w -- word to skip. 3934549Seric ** 3944549Seric ** Returns: 3954549Seric ** p following w. 3964549Seric ** NULL on error. 3974549Seric ** 3984549Seric ** Side Effects: 3994549Seric ** clobbers the p data area. 4004549Seric */ 4014549Seric 4024549Seric static char * 4034549Seric skipword(p, w) 4044549Seric register char *p; 4054549Seric char *w; 4064549Seric { 4074549Seric register char *q; 4084549Seric extern bool sameword(); 4094549Seric 4104549Seric /* find beginning of word */ 4114549Seric while (isspace(*p)) 4124549Seric p++; 4134549Seric q = p; 4144549Seric 4154549Seric /* find end of word */ 4164549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4174549Seric p++; 4184549Seric while (isspace(*p)) 4194549Seric *p++ = '\0'; 4204549Seric if (*p != ':') 4214549Seric { 4224549Seric syntax: 4234549Seric message("501", "Syntax error"); 4244549Seric Errors++; 4254549Seric return (NULL); 4264549Seric } 4274549Seric *p++ = '\0'; 4284549Seric while (isspace(*p)) 4294549Seric p++; 4304549Seric 4314549Seric /* see if the input word matches desired word */ 4324549Seric if (!sameword(q, w)) 4334549Seric goto syntax; 4344549Seric 4354549Seric return (p); 4364549Seric } 4374577Seric /* 4384577Seric ** HELP -- implement the HELP command. 4394577Seric ** 4404577Seric ** Parameters: 4414577Seric ** topic -- the topic we want help for. 4424577Seric ** 4434577Seric ** Returns: 4444577Seric ** none. 4454577Seric ** 4464577Seric ** Side Effects: 4474577Seric ** outputs the help file to message output. 4484577Seric */ 4494577Seric 4504577Seric help(topic) 4514577Seric char *topic; 4524577Seric { 4534577Seric register FILE *hf; 4544577Seric int len; 4554577Seric char buf[MAXLINE]; 4564577Seric bool noinfo; 4574577Seric 4588269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4594577Seric { 4604577Seric /* no help */ 4614577Seric message("502", "HELP not implemented"); 4624577Seric return; 4634577Seric } 4644577Seric 4654577Seric len = strlen(topic); 4664577Seric makelower(topic); 4674577Seric noinfo = TRUE; 4684577Seric 4694577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4704577Seric { 4714577Seric if (strncmp(buf, topic, len) == 0) 4724577Seric { 4734577Seric register char *p; 4744577Seric 4754577Seric p = index(buf, '\t'); 4764577Seric if (p == NULL) 4774577Seric p = buf; 4784577Seric else 4794577Seric p++; 4804577Seric fixcrlf(p, TRUE); 4814577Seric message("214-", p); 4824577Seric noinfo = FALSE; 4834577Seric } 4844577Seric } 4854577Seric 4864577Seric if (noinfo) 4874577Seric message("504", "HELP topic unknown"); 4884577Seric else 4894577Seric message("214", "End of HELP info"); 4904628Seric (void) fclose(hf); 4914577Seric } 4928544Seric /* 4938544Seric ** ISWIZ -- tell us if we are a wizard 4948544Seric ** 4958544Seric ** If not, print a nasty message. 4968544Seric ** 4978544Seric ** Parameters: 4988544Seric ** none. 4998544Seric ** 5008544Seric ** Returns: 5018544Seric ** TRUE if we are a wizard. 5028544Seric ** FALSE if we are not a wizard. 5038544Seric ** 5048544Seric ** Side Effects: 5058544Seric ** Prints a 500 exit stat if we are not a wizard. 5068544Seric */ 5075181Seric 5088544Seric bool 5098544Seric iswiz() 5108544Seric { 5118544Seric if (!IsWiz) 5128544Seric message("500", "Mere mortals musn't mutter that mantra"); 5138544Seric return (IsWiz); 5148544Seric } 515*9339Seric /* 516*9339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 517*9339Seric ** 518*9339Seric ** Parameters: 519*9339Seric ** label -- a string used in error messages 520*9339Seric ** 521*9339Seric ** Returns: 522*9339Seric ** zero in the child 523*9339Seric ** one in the parent 524*9339Seric ** 525*9339Seric ** Side Effects: 526*9339Seric ** none. 527*9339Seric */ 5288544Seric 529*9339Seric runinchild(label) 530*9339Seric char *label; 531*9339Seric { 532*9339Seric int childpid; 533*9339Seric 534*9339Seric childpid = dofork(); 535*9339Seric if (childpid < 0) 536*9339Seric { 537*9339Seric syserr("%s: cannot fork", label); 538*9339Seric return (1); 539*9339Seric } 540*9339Seric if (childpid > 0) 541*9339Seric { 542*9339Seric /* parent -- wait for child to complete */ 543*9339Seric auto int st; 544*9339Seric int i; 545*9339Seric 546*9339Seric while ((i = wait(&st)) != childpid) 547*9339Seric { 548*9339Seric if (i < 0 && errno != EINTR) 549*9339Seric break; 550*9339Seric } 551*9339Seric if (i < 0) 552*9339Seric syserr("%s: lost child", label); 553*9339Seric 554*9339Seric /* if we exited on a QUIT command, complete the process */ 555*9339Seric if (st == (EX_QUIT << 8)) 556*9339Seric finis(); 557*9339Seric 558*9339Seric return (1); 559*9339Seric } 560*9339Seric else 561*9339Seric { 562*9339Seric /* child */ 563*9339Seric InChild = TRUE; 564*9339Seric return (0); 565*9339Seric } 566*9339Seric } 567*9339Seric 5685181Seric # endif SMTP 569