19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 34549Seric 45181Seric # ifndef SMTP 5*10708Seric SCCSID(@(#)srvrsmtp.c 3.45 02/03/83 (no SMTP)); 65181Seric # else SMTP 74556Seric 8*10708Seric SCCSID(@(#)srvrsmtp.c 3.45 02/03/83); 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 */ 789378Seric bool OneXact = FALSE; /* one xaction only this run */ 799339Seric #define EX_QUIT 22 /* special code for QUIT command */ 808544Seric 814549Seric smtp() 824549Seric { 834549Seric register char *p; 848544Seric register struct cmd *c; 854549Seric char *cmd; 864549Seric extern char *skipword(); 874549Seric extern bool sameword(); 884549Seric bool hasmail; /* mail command received */ 894713Seric int rcps; /* number of recipients */ 905003Seric auto ADDRESS *vrfyqueue; 918544Seric char inp[MAXLINE]; 927124Seric extern char Version[]; 937356Seric extern tick(); 948544Seric extern bool iswiz(); 959349Seric 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 } 105*10708Seric expand("$e", inp, &inp[sizeof inp], CurEnv); 106*10708Seric message("220", inp); 1077762Seric (void) setjmp(TopFrame); 1087762Seric QuickAbort = FALSE; 1099390Seric HoldErrs = FALSE; 1104549Seric for (;;) 1114549Seric { 1127356Seric /* setup for the read */ 1136907Seric CurEnv->e_to = NULL; 1144577Seric Errors = 0; 1157275Seric (void) fflush(stdout); 1167356Seric 1177356Seric /* read the input line */ 1187685Seric p = sfgets(inp, sizeof inp, InChannel); 1197356Seric 1207685Seric /* handle errors */ 1217356Seric if (p == NULL) 1227356Seric { 1234549Seric /* end of file, just die */ 1244558Seric message("421", "%s Lost input channel", HostName); 1254549Seric finis(); 1264549Seric } 1274549Seric 1284549Seric /* clean up end of line */ 1294558Seric fixcrlf(inp, TRUE); 1304549Seric 1314713Seric /* echo command to transcript */ 1329545Seric if (CurEnv->e_xfp != NULL) 1339545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1344713Seric 1354549Seric /* break off command */ 1364549Seric for (p = inp; isspace(*p); p++) 1374549Seric continue; 1384549Seric cmd = p; 1394549Seric while (*++p != '\0' && !isspace(*p)) 1404549Seric continue; 1414549Seric if (*p != '\0') 1424549Seric *p++ = '\0'; 1434549Seric 1444549Seric /* decode command */ 1454549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1464549Seric { 1474549Seric if (sameword(c->cmdname, cmd)) 1484549Seric break; 1494549Seric } 1504549Seric 1514549Seric /* process command */ 1524549Seric switch (c->cmdcode) 1534549Seric { 1544976Seric case CMDHELO: /* hello -- introduce yourself */ 1559378Seric define('s', newstr(p), CurEnv); 1564997Seric message("250", "%s Hello %s, pleased to meet you", 1574997Seric HostName, p); 1584976Seric break; 1594976Seric 1604549Seric case CMDMAIL: /* mail -- designate sender */ 1619314Seric /* check for validity of this command */ 1624558Seric if (hasmail) 1634558Seric { 1644558Seric message("503", "Sender already specified"); 1654558Seric break; 1664558Seric } 1679339Seric if (InChild) 1689339Seric { 1699339Seric syserr("Nested MAIL command"); 1709339Seric exit(0); 1719339Seric } 1729339Seric 1739339Seric /* fork a subprocess to process this command */ 1749339Seric if (runinchild("SMTP-MAIL") > 0) 1759339Seric break; 1769339Seric initsys(); 1779339Seric 1789339Seric /* child -- go do the processing */ 1794549Seric p = skipword(p, "from"); 1804549Seric if (p == NULL) 1814549Seric break; 1824549Seric setsender(p); 1834577Seric if (Errors == 0) 1844549Seric { 1854549Seric message("250", "Sender ok"); 1864549Seric hasmail = TRUE; 1874549Seric } 1889339Seric else if (InChild) 1899339Seric finis(); 1904549Seric break; 1914549Seric 1924976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1934549Seric p = skipword(p, "to"); 1944549Seric if (p == NULL) 1954549Seric break; 1969619Seric sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1979390Seric CurEnv->e_flags &= ~EF_FATALERRS; 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). 22910197Seric ** Truncate our transcript -- the mail has gotten 23010197Seric ** to us successfully, and if we have 23110197Seric ** to mail this back, it will be easier 23210197Seric ** on the reader. 2338238Seric ** Then send to everyone. 2348238Seric ** Finally give a reply code. If an error has 2358238Seric ** already been given, don't mail a 2368238Seric ** message back. 2379339Seric ** We goose error returns by clearing error bit. 2388238Seric */ 2398238Seric 2404976Seric if (rcps != 1) 2419378Seric { 2429378Seric HoldErrs = TRUE; 2439378Seric ErrorMode == EM_MAIL; 2449378Seric } 2459339Seric CurEnv->e_flags &= ~EF_FATALERRS; 24610197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2474976Seric 2484976Seric /* send to all recipients */ 2499283Seric sendall(CurEnv, SendMode); 2506907Seric CurEnv->e_to = NULL; 2514976Seric 2528238Seric /* issue success if appropriate and reset */ 2538238Seric if (Errors == 0 || HoldErrs) 2549283Seric message("250", "Ok"); 2558238Seric else 2569339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2579339Seric 2589339Seric /* if in a child, pop back to our parent */ 2599339Seric if (InChild) 2609339Seric finis(); 2614549Seric break; 2624549Seric 2634549Seric case CMDRSET: /* rset -- reset state */ 2644549Seric message("250", "Reset state"); 2659339Seric if (InChild) 2669339Seric finis(); 2679339Seric break; 2684549Seric 2694549Seric case CMDVRFY: /* vrfy -- verify address */ 2709339Seric if (runinchild("SMTP-VRFY") > 0) 2719339Seric break; 2725003Seric vrfyqueue = NULL; 2737762Seric QuickAbort = TRUE; 2749619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 2757762Seric if (Errors != 0) 2769339Seric { 2779339Seric if (InChild) 2789339Seric finis(); 2797762Seric break; 2809339Seric } 2815003Seric while (vrfyqueue != NULL) 2825003Seric { 2835003Seric register ADDRESS *a = vrfyqueue->q_next; 2845003Seric char *code; 2855003Seric 2867685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2875003Seric a = a->q_next; 2885003Seric 2897685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2905003Seric { 2915003Seric if (a != NULL) 2925003Seric code = "250-"; 2935003Seric else 2945003Seric code = "250"; 2955003Seric if (vrfyqueue->q_fullname == NULL) 2965003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2975003Seric else 2985003Seric message(code, "%s <%s>", 2995003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3005003Seric } 3015003Seric else if (a == NULL) 3025003Seric message("554", "Self destructive alias loop"); 3035003Seric vrfyqueue = a; 3045003Seric } 3059339Seric if (InChild) 3069339Seric finis(); 3074549Seric break; 3084549Seric 3094549Seric case CMDHELP: /* help -- give user info */ 3104577Seric if (*p == '\0') 3114577Seric p = "SMTP"; 3124577Seric help(p); 3134549Seric break; 3144549Seric 3154549Seric case CMDNOOP: /* noop -- do nothing */ 3164549Seric message("200", "OK"); 3174549Seric break; 3184549Seric 3194549Seric case CMDQUIT: /* quit -- leave mail */ 3204549Seric message("221", "%s closing connection", HostName); 3219339Seric if (InChild) 3229339Seric ExitStat = EX_QUIT; 3234549Seric finis(); 3244549Seric 3258544Seric case CMDVERB: /* set verbose mode */ 3268544Seric Verbose = TRUE; 3278544Seric message("200", "Verbose mode"); 3288544Seric break; 3298544Seric 3309314Seric case CMDONEX: /* doing one transaction only */ 3319378Seric OneXact = TRUE; 3329314Seric message("200", "Only one transaction"); 3339314Seric break; 3349314Seric 3355003Seric # ifdef DEBUG 3369339Seric case CMDDBGQSHOW: /* show queues */ 3376907Seric printf("Send Queue="); 3386907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3395003Seric break; 3407275Seric 3417275Seric case CMDDBGDEBUG: /* set debug mode */ 3427676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3437676Seric tTflag(p); 3447676Seric message("200", "Debug set"); 3457275Seric break; 3467275Seric 3477282Seric case CMDDBGKILL: /* kill the parent */ 3488544Seric if (!iswiz()) 3498544Seric break; 3507282Seric if (kill(MotherPid, SIGTERM) >= 0) 3517282Seric message("200", "Mother is dead"); 3527282Seric else 3537282Seric message("500", "Can't kill Mom"); 3547282Seric break; 3558544Seric 3569339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3579339Seric if (!iswiz()) 3589339Seric break; 3599378Seric if (fileno(InChannel) != 0) 3609378Seric { 3619378Seric (void) close(0); 3629378Seric (void) dup(fileno(InChannel)); 36310346Seric if (fileno(InChannel) != fileno(OutChannel)) 36410346Seric (void) fclose(InChannel); 3659378Seric InChannel = stdin; 3669378Seric } 3679378Seric if (fileno(OutChannel) != 1) 3689378Seric { 3699378Seric (void) close(1); 3709378Seric (void) dup(fileno(OutChannel)); 3719378Seric (void) fclose(OutChannel); 3729378Seric OutChannel = stdout; 3739378Seric } 37410346Seric (void) close(2); 37510346Seric (void) dup(1); 3769339Seric execl("/bin/csh", "sendmail", 0); 3779339Seric execl("/bin/sh", "sendmail", 0); 3789339Seric message("500", "Can't"); 3799378Seric exit(EX_UNAVAILABLE); 3809339Seric 3818544Seric case CMDDBGWIZ: /* become a wizard */ 3828544Seric if (WizWord != NULL) 3838544Seric { 3848544Seric char seed[3]; 3858544Seric extern char *crypt(); 3868544Seric 3878544Seric strncpy(seed, WizWord, 2); 3888544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 3898544Seric { 3908544Seric message("500", "You are no wizard!"); 3918544Seric break; 3928544Seric } 3938544Seric } 3948544Seric IsWiz = TRUE; 3958544Seric message("200", "Please pass, oh mighty wizard"); 3968544Seric break; 3975003Seric # endif DEBUG 3985003Seric 3994549Seric case CMDERROR: /* unknown command */ 4004549Seric message("500", "Command unrecognized"); 4014549Seric break; 4024549Seric 4034549Seric default: 4044549Seric syserr("smtp: unknown code %d", c->cmdcode); 4054549Seric break; 4064549Seric } 4074549Seric } 4084549Seric } 4094549Seric /* 4104549Seric ** SKIPWORD -- skip a fixed word. 4114549Seric ** 4124549Seric ** Parameters: 4134549Seric ** p -- place to start looking. 4144549Seric ** w -- word to skip. 4154549Seric ** 4164549Seric ** Returns: 4174549Seric ** p following w. 4184549Seric ** NULL on error. 4194549Seric ** 4204549Seric ** Side Effects: 4214549Seric ** clobbers the p data area. 4224549Seric */ 4234549Seric 4244549Seric static char * 4254549Seric skipword(p, w) 4264549Seric register char *p; 4274549Seric char *w; 4284549Seric { 4294549Seric register char *q; 4304549Seric extern bool sameword(); 4314549Seric 4324549Seric /* find beginning of word */ 4334549Seric while (isspace(*p)) 4344549Seric p++; 4354549Seric q = p; 4364549Seric 4374549Seric /* find end of word */ 4384549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4394549Seric p++; 4404549Seric while (isspace(*p)) 4414549Seric *p++ = '\0'; 4424549Seric if (*p != ':') 4434549Seric { 4444549Seric syntax: 4454549Seric message("501", "Syntax error"); 4464549Seric Errors++; 4474549Seric return (NULL); 4484549Seric } 4494549Seric *p++ = '\0'; 4504549Seric while (isspace(*p)) 4514549Seric p++; 4524549Seric 4534549Seric /* see if the input word matches desired word */ 4544549Seric if (!sameword(q, w)) 4554549Seric goto syntax; 4564549Seric 4574549Seric return (p); 4584549Seric } 4594577Seric /* 4604577Seric ** HELP -- implement the HELP command. 4614577Seric ** 4624577Seric ** Parameters: 4634577Seric ** topic -- the topic we want help for. 4644577Seric ** 4654577Seric ** Returns: 4664577Seric ** none. 4674577Seric ** 4684577Seric ** Side Effects: 4694577Seric ** outputs the help file to message output. 4704577Seric */ 4714577Seric 4724577Seric help(topic) 4734577Seric char *topic; 4744577Seric { 4754577Seric register FILE *hf; 4764577Seric int len; 4774577Seric char buf[MAXLINE]; 4784577Seric bool noinfo; 4794577Seric 4808269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4814577Seric { 4824577Seric /* no help */ 4834577Seric message("502", "HELP not implemented"); 4844577Seric return; 4854577Seric } 4864577Seric 4874577Seric len = strlen(topic); 4884577Seric makelower(topic); 4894577Seric noinfo = TRUE; 4904577Seric 4914577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4924577Seric { 4934577Seric if (strncmp(buf, topic, len) == 0) 4944577Seric { 4954577Seric register char *p; 4964577Seric 4974577Seric p = index(buf, '\t'); 4984577Seric if (p == NULL) 4994577Seric p = buf; 5004577Seric else 5014577Seric p++; 5024577Seric fixcrlf(p, TRUE); 5034577Seric message("214-", p); 5044577Seric noinfo = FALSE; 5054577Seric } 5064577Seric } 5074577Seric 5084577Seric if (noinfo) 5094577Seric message("504", "HELP topic unknown"); 5104577Seric else 5114577Seric message("214", "End of HELP info"); 5124628Seric (void) fclose(hf); 5134577Seric } 5148544Seric /* 5158544Seric ** ISWIZ -- tell us if we are a wizard 5168544Seric ** 5178544Seric ** If not, print a nasty message. 5188544Seric ** 5198544Seric ** Parameters: 5208544Seric ** none. 5218544Seric ** 5228544Seric ** Returns: 5238544Seric ** TRUE if we are a wizard. 5248544Seric ** FALSE if we are not a wizard. 5258544Seric ** 5268544Seric ** Side Effects: 5278544Seric ** Prints a 500 exit stat if we are not a wizard. 5288544Seric */ 5295181Seric 5308544Seric bool 5318544Seric iswiz() 5328544Seric { 5338544Seric if (!IsWiz) 5348544Seric message("500", "Mere mortals musn't mutter that mantra"); 5358544Seric return (IsWiz); 5368544Seric } 5379339Seric /* 5389339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5399339Seric ** 5409339Seric ** Parameters: 5419339Seric ** label -- a string used in error messages 5429339Seric ** 5439339Seric ** Returns: 5449339Seric ** zero in the child 5459339Seric ** one in the parent 5469339Seric ** 5479339Seric ** Side Effects: 5489339Seric ** none. 5499339Seric */ 5508544Seric 5519339Seric runinchild(label) 5529339Seric char *label; 5539339Seric { 5549339Seric int childpid; 5559339Seric 5569378Seric if (OneXact) 5579378Seric return (0); 5589378Seric 5599339Seric childpid = dofork(); 5609339Seric if (childpid < 0) 5619339Seric { 5629339Seric syserr("%s: cannot fork", label); 5639339Seric return (1); 5649339Seric } 5659339Seric if (childpid > 0) 5669339Seric { 5679339Seric auto int st; 5689339Seric 5699378Seric /* parent -- wait for child to complete */ 5709378Seric st = waitfor(childpid); 5719378Seric if (st == -1) 5729339Seric syserr("%s: lost child", label); 5739339Seric 5749339Seric /* if we exited on a QUIT command, complete the process */ 5759339Seric if (st == (EX_QUIT << 8)) 5769339Seric finis(); 5779339Seric 5789339Seric return (1); 5799339Seric } 5809339Seric else 5819339Seric { 5829339Seric /* child */ 5839339Seric InChild = TRUE; 5849545Seric clearenvelope(CurEnv); 5859339Seric return (0); 5869339Seric } 5879339Seric } 5889339Seric 5895181Seric # endif SMTP 590