19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 34549Seric 45181Seric # ifndef SMTP 5*9349Seric SCCSID(@(#)srvrsmtp.c 3.38 11/24/82 (no SMTP)); 65181Seric # else SMTP 74556Seric 8*9349Seric SCCSID(@(#)srvrsmtp.c 3.38 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 */ 359339Seric # define CMDRSET 4 /* rset -- reset state */ 369339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 379339Seric # define CMDHELP 6 /* help -- give usage info */ 389339Seric # define CMDNOOP 7 /* noop -- do nothing */ 399339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 409339Seric # define CMDHELO 9 /* helo -- be polite */ 419339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 429339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 439339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 449339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 459339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 469339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 479339Seric # 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 649339Seric "showq", CMDDBGQSHOW, 658544Seric "debug", CMDDBGDEBUG, 668544Seric "kill", CMDDBGKILL, 678544Seric "wiz", CMDDBGWIZ, 689339Seric "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 779339Seric bool InChild = FALSE; /* true if running in a subprocess */ 789339Seric #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 */ 918544Seric char inp[MAXLINE]; 927124Seric extern char Version[]; 937356Seric extern tick(); 948544Seric extern bool iswiz(); 95*9349Seric extern char *arpadate(); 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, 106*9349Seric Version, arpadate((char *) 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 */ 1319339Seric if (Xscript != NULL) 1329339Seric 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 /* check for validity of this command */ 1614558Seric if (hasmail) 1624558Seric { 1634558Seric message("503", "Sender already specified"); 1644558Seric break; 1654558Seric } 1669339Seric if (InChild) 1679339Seric { 1689339Seric syserr("Nested MAIL command"); 1699339Seric exit(0); 1709339Seric } 1719339Seric 1729339Seric /* fork a subprocess to process this command */ 1739339Seric if (runinchild("SMTP-MAIL") > 0) 1749339Seric break; 1759339Seric initsys(); 1769339Seric 1779339Seric /* child -- go do the processing */ 1784549Seric p = skipword(p, "from"); 1794549Seric if (p == NULL) 1804549Seric break; 1814549Seric setsender(p); 1824577Seric if (Errors == 0) 1834549Seric { 1844549Seric message("250", "Sender ok"); 1854549Seric hasmail = TRUE; 1864549Seric } 1879339Seric else if (InChild) 1889339Seric finis(); 1894549Seric break; 1904549Seric 1914976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1924549Seric p = skipword(p, "to"); 1934549Seric if (p == NULL) 1944549Seric break; 1958081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1964577Seric if (Errors == 0) 1974549Seric { 1986057Seric message("250", "%s... Recipient ok", p); 1994713Seric rcps++; 2004549Seric } 2014549Seric break; 2024549Seric 2034549Seric case CMDDATA: /* data -- text of mail */ 2044976Seric if (!hasmail) 2054549Seric { 2064976Seric message("503", "Need MAIL command"); 2074976Seric break; 2084549Seric } 2094713Seric else if (rcps <= 0) 2104549Seric { 2114976Seric message("503", "Need RCPT (recipient)"); 2124976Seric break; 2134549Seric } 2144976Seric 2154976Seric /* collect the text of the message */ 2164976Seric collect(TRUE); 2174976Seric if (Errors != 0) 2184976Seric break; 2194976Seric 2208238Seric /* 2218238Seric ** Arrange to send to everyone. 2228238Seric ** If sending to multiple people, mail back 2238238Seric ** errors rather than reporting directly. 2248238Seric ** In any case, don't mail back errors for 2258238Seric ** anything that has happened up to 2268238Seric ** now (the other end will do this). 2278238Seric ** Then send to everyone. 2288238Seric ** Finally give a reply code. If an error has 2298238Seric ** already been given, don't mail a 2308238Seric ** message back. 2319339Seric ** We goose error returns by clearing error bit. 2328238Seric */ 2338238Seric 2344976Seric if (rcps != 1) 2354976Seric HoldErrs = MailBack = TRUE; 2369339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2374976Seric 2384976Seric /* send to all recipients */ 2399283Seric sendall(CurEnv, SendMode); 2406907Seric CurEnv->e_to = NULL; 2414976Seric 2428238Seric /* issue success if appropriate and reset */ 2438238Seric if (Errors == 0 || HoldErrs) 2448238Seric { 2458238Seric HoldErrs = FALSE; 2469283Seric message("250", "Ok"); 2478238Seric } 2488238Seric else 2499339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2509339Seric 2519339Seric /* if in a child, pop back to our parent */ 2529339Seric if (InChild) 2539339Seric finis(); 2544549Seric break; 2554549Seric 2564549Seric case CMDRSET: /* rset -- reset state */ 2574549Seric message("250", "Reset state"); 2589339Seric if (InChild) 2599339Seric finis(); 2609339Seric break; 2614549Seric 2624549Seric case CMDVRFY: /* vrfy -- verify address */ 2639339Seric if (runinchild("SMTP-VRFY") > 0) 2649339Seric break; 2655003Seric vrfyqueue = NULL; 2667762Seric QuickAbort = TRUE; 2678081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2687762Seric if (Errors != 0) 2699339Seric { 2709339Seric if (InChild) 2719339Seric finis(); 2727762Seric break; 2739339Seric } 2745003Seric while (vrfyqueue != NULL) 2755003Seric { 2765003Seric register ADDRESS *a = vrfyqueue->q_next; 2775003Seric char *code; 2785003Seric 2797685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2805003Seric a = a->q_next; 2815003Seric 2827685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2835003Seric { 2845003Seric if (a != NULL) 2855003Seric code = "250-"; 2865003Seric else 2875003Seric code = "250"; 2885003Seric if (vrfyqueue->q_fullname == NULL) 2895003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2905003Seric else 2915003Seric message(code, "%s <%s>", 2925003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2935003Seric } 2945003Seric else if (a == NULL) 2955003Seric message("554", "Self destructive alias loop"); 2965003Seric vrfyqueue = a; 2975003Seric } 2989339Seric if (InChild) 2999339Seric finis(); 3004549Seric break; 3014549Seric 3024549Seric case CMDHELP: /* help -- give user info */ 3034577Seric if (*p == '\0') 3044577Seric p = "SMTP"; 3054577Seric help(p); 3064549Seric break; 3074549Seric 3084549Seric case CMDNOOP: /* noop -- do nothing */ 3094549Seric message("200", "OK"); 3104549Seric break; 3114549Seric 3124549Seric case CMDQUIT: /* quit -- leave mail */ 3134549Seric message("221", "%s closing connection", HostName); 3149339Seric if (InChild) 3159339Seric ExitStat = EX_QUIT; 3164549Seric finis(); 3174549Seric 3188544Seric case CMDVERB: /* set verbose mode */ 3198544Seric Verbose = TRUE; 3208544Seric message("200", "Verbose mode"); 3218544Seric break; 3228544Seric 3239314Seric case CMDONEX: /* doing one transaction only */ 3249314Seric onexact = TRUE; 3259314Seric message("200", "Only one transaction"); 3269314Seric break; 3279314Seric 3285003Seric # ifdef DEBUG 3299339Seric case CMDDBGQSHOW: /* show queues */ 3306907Seric printf("Send Queue="); 3316907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3325003Seric break; 3337275Seric 3347275Seric case CMDDBGDEBUG: /* set debug mode */ 3357676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3367676Seric tTflag(p); 3377676Seric message("200", "Debug set"); 3387275Seric break; 3397275Seric 3407282Seric case CMDDBGKILL: /* kill the parent */ 3418544Seric if (!iswiz()) 3428544Seric break; 3437282Seric if (kill(MotherPid, SIGTERM) >= 0) 3447282Seric message("200", "Mother is dead"); 3457282Seric else 3467282Seric message("500", "Can't kill Mom"); 3477282Seric break; 3488544Seric 3499339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3509339Seric if (!iswiz()) 3519339Seric break; 3529339Seric execl("/bin/csh", "sendmail", 0); 3539339Seric execl("/bin/sh", "sendmail", 0); 3549339Seric message("500", "Can't"); 3559339Seric break; 3569339Seric 3578544Seric case CMDDBGWIZ: /* become a wizard */ 3588544Seric if (WizWord != NULL) 3598544Seric { 3608544Seric char seed[3]; 3618544Seric extern char *crypt(); 3628544Seric 3638544Seric strncpy(seed, WizWord, 2); 3648544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3658544Seric { 3668544Seric message("500", "You are no wizard!"); 3678544Seric break; 3688544Seric } 3698544Seric } 3708544Seric IsWiz = TRUE; 3718544Seric message("200", "Please pass, oh mighty wizard"); 3728544Seric break; 3735003Seric # endif DEBUG 3745003Seric 3754549Seric case CMDERROR: /* unknown command */ 3764549Seric message("500", "Command unrecognized"); 3774549Seric break; 3784549Seric 3794549Seric default: 3804549Seric syserr("smtp: unknown code %d", c->cmdcode); 3814549Seric break; 3824549Seric } 3834549Seric } 3844549Seric } 3854549Seric /* 3864549Seric ** SKIPWORD -- skip a fixed word. 3874549Seric ** 3884549Seric ** Parameters: 3894549Seric ** p -- place to start looking. 3904549Seric ** w -- word to skip. 3914549Seric ** 3924549Seric ** Returns: 3934549Seric ** p following w. 3944549Seric ** NULL on error. 3954549Seric ** 3964549Seric ** Side Effects: 3974549Seric ** clobbers the p data area. 3984549Seric */ 3994549Seric 4004549Seric static char * 4014549Seric skipword(p, w) 4024549Seric register char *p; 4034549Seric char *w; 4044549Seric { 4054549Seric register char *q; 4064549Seric extern bool sameword(); 4074549Seric 4084549Seric /* find beginning of word */ 4094549Seric while (isspace(*p)) 4104549Seric p++; 4114549Seric q = p; 4124549Seric 4134549Seric /* find end of word */ 4144549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4154549Seric p++; 4164549Seric while (isspace(*p)) 4174549Seric *p++ = '\0'; 4184549Seric if (*p != ':') 4194549Seric { 4204549Seric syntax: 4214549Seric message("501", "Syntax error"); 4224549Seric Errors++; 4234549Seric return (NULL); 4244549Seric } 4254549Seric *p++ = '\0'; 4264549Seric while (isspace(*p)) 4274549Seric p++; 4284549Seric 4294549Seric /* see if the input word matches desired word */ 4304549Seric if (!sameword(q, w)) 4314549Seric goto syntax; 4324549Seric 4334549Seric return (p); 4344549Seric } 4354577Seric /* 4364577Seric ** HELP -- implement the HELP command. 4374577Seric ** 4384577Seric ** Parameters: 4394577Seric ** topic -- the topic we want help for. 4404577Seric ** 4414577Seric ** Returns: 4424577Seric ** none. 4434577Seric ** 4444577Seric ** Side Effects: 4454577Seric ** outputs the help file to message output. 4464577Seric */ 4474577Seric 4484577Seric help(topic) 4494577Seric char *topic; 4504577Seric { 4514577Seric register FILE *hf; 4524577Seric int len; 4534577Seric char buf[MAXLINE]; 4544577Seric bool noinfo; 4554577Seric 4568269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4574577Seric { 4584577Seric /* no help */ 4594577Seric message("502", "HELP not implemented"); 4604577Seric return; 4614577Seric } 4624577Seric 4634577Seric len = strlen(topic); 4644577Seric makelower(topic); 4654577Seric noinfo = TRUE; 4664577Seric 4674577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4684577Seric { 4694577Seric if (strncmp(buf, topic, len) == 0) 4704577Seric { 4714577Seric register char *p; 4724577Seric 4734577Seric p = index(buf, '\t'); 4744577Seric if (p == NULL) 4754577Seric p = buf; 4764577Seric else 4774577Seric p++; 4784577Seric fixcrlf(p, TRUE); 4794577Seric message("214-", p); 4804577Seric noinfo = FALSE; 4814577Seric } 4824577Seric } 4834577Seric 4844577Seric if (noinfo) 4854577Seric message("504", "HELP topic unknown"); 4864577Seric else 4874577Seric message("214", "End of HELP info"); 4884628Seric (void) fclose(hf); 4894577Seric } 4908544Seric /* 4918544Seric ** ISWIZ -- tell us if we are a wizard 4928544Seric ** 4938544Seric ** If not, print a nasty message. 4948544Seric ** 4958544Seric ** Parameters: 4968544Seric ** none. 4978544Seric ** 4988544Seric ** Returns: 4998544Seric ** TRUE if we are a wizard. 5008544Seric ** FALSE if we are not a wizard. 5018544Seric ** 5028544Seric ** Side Effects: 5038544Seric ** Prints a 500 exit stat if we are not a wizard. 5048544Seric */ 5055181Seric 5068544Seric bool 5078544Seric iswiz() 5088544Seric { 5098544Seric if (!IsWiz) 5108544Seric message("500", "Mere mortals musn't mutter that mantra"); 5118544Seric return (IsWiz); 5128544Seric } 5139339Seric /* 5149339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5159339Seric ** 5169339Seric ** Parameters: 5179339Seric ** label -- a string used in error messages 5189339Seric ** 5199339Seric ** Returns: 5209339Seric ** zero in the child 5219339Seric ** one in the parent 5229339Seric ** 5239339Seric ** Side Effects: 5249339Seric ** none. 5259339Seric */ 5268544Seric 5279339Seric runinchild(label) 5289339Seric char *label; 5299339Seric { 5309339Seric int childpid; 5319339Seric 5329339Seric childpid = dofork(); 5339339Seric if (childpid < 0) 5349339Seric { 5359339Seric syserr("%s: cannot fork", label); 5369339Seric return (1); 5379339Seric } 5389339Seric if (childpid > 0) 5399339Seric { 5409339Seric /* parent -- wait for child to complete */ 5419339Seric auto int st; 5429339Seric int i; 5439339Seric 5449339Seric while ((i = wait(&st)) != childpid) 5459339Seric { 5469339Seric if (i < 0 && errno != EINTR) 5479339Seric break; 5489339Seric } 5499339Seric if (i < 0) 5509339Seric syserr("%s: lost child", label); 5519339Seric 5529339Seric /* if we exited on a QUIT command, complete the process */ 5539339Seric if (st == (EX_QUIT << 8)) 5549339Seric finis(); 5559339Seric 5569339Seric return (1); 5579339Seric } 5589339Seric else 5599339Seric { 5609339Seric /* child */ 5619339Seric InChild = TRUE; 5629339Seric return (0); 5639339Seric } 5649339Seric } 5659339Seric 5665181Seric # endif SMTP 567