19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 311728Seric # include <signal.h> 44549Seric 55181Seric # ifndef SMTP 6*12612Seric SCCSID(@(#)srvrsmtp.c 3.50 05/20/83 (no SMTP)); 75181Seric # else SMTP 84556Seric 9*12612Seric SCCSID(@(#)srvrsmtp.c 3.50 05/20/83); 105181Seric 114549Seric /* 124549Seric ** SMTP -- run the SMTP protocol. 134549Seric ** 144549Seric ** Parameters: 154549Seric ** none. 164549Seric ** 174549Seric ** Returns: 184549Seric ** never. 194549Seric ** 204549Seric ** Side Effects: 214549Seric ** Reads commands from the input channel and processes 224549Seric ** them. 234549Seric */ 244549Seric 254549Seric struct cmd 264549Seric { 274549Seric char *cmdname; /* command name */ 284549Seric int cmdcode; /* internal code, see below */ 294549Seric }; 304549Seric 314549Seric /* values for cmdcode */ 324549Seric # define CMDERROR 0 /* bad command */ 334549Seric # define CMDMAIL 1 /* mail -- designate sender */ 344976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 354549Seric # define CMDDATA 3 /* data -- send message text */ 369339Seric # define CMDRSET 4 /* rset -- reset state */ 379339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 389339Seric # define CMDHELP 6 /* help -- give usage info */ 399339Seric # define CMDNOOP 7 /* noop -- do nothing */ 409339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 419339Seric # define CMDHELO 9 /* helo -- be polite */ 429339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 439339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 449339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 459339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 469339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 479339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 489339Seric # define CMDDBGSHELL 16 /* shell -- give us a shell */ 494549Seric 504549Seric static struct cmd CmdTab[] = 514549Seric { 524549Seric "mail", CMDMAIL, 534976Seric "rcpt", CMDRCPT, 544549Seric "data", CMDDATA, 554549Seric "rset", CMDRSET, 564549Seric "vrfy", CMDVRFY, 577762Seric "expn", CMDVRFY, 584549Seric "help", CMDHELP, 594549Seric "noop", CMDNOOP, 604549Seric "quit", CMDQUIT, 614976Seric "helo", CMDHELO, 628544Seric "verb", CMDVERB, 639314Seric "onex", CMDONEX, 645003Seric # ifdef DEBUG 659339Seric "showq", CMDDBGQSHOW, 668544Seric "debug", CMDDBGDEBUG, 678544Seric "kill", CMDDBGKILL, 688544Seric "wiz", CMDDBGWIZ, 699339Seric "shell", CMDDBGSHELL, 705003Seric # endif DEBUG 714549Seric NULL, CMDERROR, 724549Seric }; 734549Seric 748544Seric # ifdef DEBUG 758544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 768544Seric char *WizWord = NULL; /* the wizard word to compare against */ 778544Seric # endif DEBUG 789339Seric bool InChild = FALSE; /* true if running in a subprocess */ 799378Seric bool OneXact = FALSE; /* one xaction only this run */ 8011146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 8111146Seric 829339Seric #define EX_QUIT 22 /* special code for QUIT command */ 838544Seric 844549Seric smtp() 854549Seric { 864549Seric register char *p; 878544Seric register struct cmd *c; 884549Seric char *cmd; 894549Seric extern char *skipword(); 904549Seric extern bool sameword(); 914549Seric bool hasmail; /* mail command received */ 924713Seric int rcps; /* number of recipients */ 935003Seric auto ADDRESS *vrfyqueue; 94*12612Seric ADDRESS *a; 958544Seric char inp[MAXLINE]; 967124Seric extern char Version[]; 977356Seric extern tick(); 988544Seric extern bool iswiz(); 999349Seric extern char *arpadate(); 10011151Seric extern char *macvalue(); 101*12612Seric extern ADDRESS *recipient(); 1024549Seric 1035003Seric hasmail = FALSE; 1044713Seric rcps = 0; 1057363Seric if (OutChannel != stdout) 1067363Seric { 1077363Seric /* arrange for debugging output to go to remote host */ 1087363Seric (void) close(1); 1097363Seric (void) dup(fileno(OutChannel)); 1107363Seric } 11111931Seric settime(); 11210708Seric expand("$e", inp, &inp[sizeof inp], CurEnv); 11310708Seric message("220", inp); 1144549Seric for (;;) 1154549Seric { 116*12612Seric /* arrange for backout */ 117*12612Seric if (setjmp(TopFrame) > 0 && InChild) 118*12612Seric finis(); 119*12612Seric QuickAbort = FALSE; 120*12612Seric HoldErrs = FALSE; 121*12612Seric 1227356Seric /* setup for the read */ 1236907Seric CurEnv->e_to = NULL; 1244577Seric Errors = 0; 1257275Seric (void) fflush(stdout); 1267356Seric 1277356Seric /* read the input line */ 1287685Seric p = sfgets(inp, sizeof inp, InChannel); 1297356Seric 1307685Seric /* handle errors */ 1317356Seric if (p == NULL) 1327356Seric { 1334549Seric /* end of file, just die */ 1344558Seric message("421", "%s Lost input channel", HostName); 1354549Seric finis(); 1364549Seric } 1374549Seric 1384549Seric /* clean up end of line */ 1394558Seric fixcrlf(inp, TRUE); 1404549Seric 1414713Seric /* echo command to transcript */ 1429545Seric if (CurEnv->e_xfp != NULL) 1439545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1444713Seric 1454549Seric /* break off command */ 1464549Seric for (p = inp; isspace(*p); p++) 1474549Seric continue; 1484549Seric cmd = p; 1494549Seric while (*++p != '\0' && !isspace(*p)) 1504549Seric continue; 1514549Seric if (*p != '\0') 1524549Seric *p++ = '\0'; 1534549Seric 1544549Seric /* decode command */ 1554549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1564549Seric { 1574549Seric if (sameword(c->cmdname, cmd)) 1584549Seric break; 1594549Seric } 1604549Seric 1614549Seric /* process command */ 1624549Seric switch (c->cmdcode) 1634549Seric { 1644976Seric case CMDHELO: /* hello -- introduce yourself */ 16511146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 16611146Seric { 16711146Seric char buf[MAXNAME]; 16811146Seric 16911146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 17011146Seric define('s', newstr(buf), CurEnv); 17111146Seric } 17211146Seric else 17311146Seric define('s', newstr(p), CurEnv); 1744997Seric message("250", "%s Hello %s, pleased to meet you", 1754997Seric HostName, p); 1764976Seric break; 1774976Seric 1784549Seric case CMDMAIL: /* mail -- designate sender */ 17911151Seric /* force a sending host even if no HELO given */ 18011151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 18111151Seric define('s', RealHostName, CurEnv); 18211151Seric 1839314Seric /* check for validity of this command */ 1844558Seric if (hasmail) 1854558Seric { 1864558Seric message("503", "Sender already specified"); 1874558Seric break; 1884558Seric } 1899339Seric if (InChild) 1909339Seric { 1919339Seric syserr("Nested MAIL command"); 1929339Seric exit(0); 1939339Seric } 1949339Seric 1959339Seric /* fork a subprocess to process this command */ 1969339Seric if (runinchild("SMTP-MAIL") > 0) 1979339Seric break; 1989339Seric initsys(); 1999339Seric 2009339Seric /* child -- go do the processing */ 2014549Seric p = skipword(p, "from"); 2024549Seric if (p == NULL) 2034549Seric break; 2044549Seric setsender(p); 2054577Seric if (Errors == 0) 2064549Seric { 2074549Seric message("250", "Sender ok"); 2084549Seric hasmail = TRUE; 2094549Seric } 2109339Seric else if (InChild) 2119339Seric finis(); 2124549Seric break; 2134549Seric 2144976Seric case CMDRCPT: /* rcpt -- designate recipient */ 215*12612Seric if (setjmp(TopFrame) > 0) 216*12612Seric break; 217*12612Seric QuickAbort = TRUE; 2184549Seric p = skipword(p, "to"); 2194549Seric if (p == NULL) 2204549Seric break; 221*12612Seric a = parseaddr(p, (ADDRESS *) NULL, 1); 222*12612Seric if (a == NULL) 223*12612Seric break; 224*12612Seric a = recipient(a, &CurEnv->e_sendqueue); 2259390Seric CurEnv->e_flags &= ~EF_FATALERRS; 226*12612Seric if (Errors != 0) 227*12612Seric break; 228*12612Seric 229*12612Seric /* no errors during parsing, but might be a duplicate */ 230*12612Seric CurEnv->e_to = p; 231*12612Seric if (!bitset(QBADADDR, a->q_flags)) 232*12612Seric message("250", "Recipient ok"); 233*12612Seric else 2344549Seric { 235*12612Seric /* punt -- should keep message in ADDRESS.... */ 236*12612Seric message("550", "Addressee unknown"); 2374549Seric } 238*12612Seric CurEnv->e_to = NULL; 239*12612Seric rcps++; 2404549Seric break; 2414549Seric 2424549Seric case CMDDATA: /* data -- text of mail */ 2434976Seric if (!hasmail) 2444549Seric { 2454976Seric message("503", "Need MAIL command"); 2464976Seric break; 2474549Seric } 2484713Seric else if (rcps <= 0) 2494549Seric { 2504976Seric message("503", "Need RCPT (recipient)"); 2514976Seric break; 2524549Seric } 2534976Seric 2544976Seric /* collect the text of the message */ 2554976Seric collect(TRUE); 2564976Seric if (Errors != 0) 2574976Seric break; 2584976Seric 2598238Seric /* 2608238Seric ** Arrange to send to everyone. 2618238Seric ** If sending to multiple people, mail back 2628238Seric ** errors rather than reporting directly. 2638238Seric ** In any case, don't mail back errors for 2648238Seric ** anything that has happened up to 2658238Seric ** now (the other end will do this). 26610197Seric ** Truncate our transcript -- the mail has gotten 26710197Seric ** to us successfully, and if we have 26810197Seric ** to mail this back, it will be easier 26910197Seric ** on the reader. 2708238Seric ** Then send to everyone. 2718238Seric ** Finally give a reply code. If an error has 2728238Seric ** already been given, don't mail a 2738238Seric ** message back. 2749339Seric ** We goose error returns by clearing error bit. 2758238Seric */ 2768238Seric 2774976Seric if (rcps != 1) 2789378Seric { 2799378Seric HoldErrs = TRUE; 2809378Seric ErrorMode == EM_MAIL; 2819378Seric } 2829339Seric CurEnv->e_flags &= ~EF_FATALERRS; 28310197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2844976Seric 2854976Seric /* send to all recipients */ 2869283Seric sendall(CurEnv, SendMode); 2876907Seric CurEnv->e_to = NULL; 2884976Seric 2898238Seric /* issue success if appropriate and reset */ 2908238Seric if (Errors == 0 || HoldErrs) 2919283Seric message("250", "Ok"); 2928238Seric else 2939339Seric CurEnv->e_flags &= ~EF_FATALERRS; 2949339Seric 2959339Seric /* if in a child, pop back to our parent */ 2969339Seric if (InChild) 2979339Seric finis(); 2984549Seric break; 2994549Seric 3004549Seric case CMDRSET: /* rset -- reset state */ 3014549Seric message("250", "Reset state"); 3029339Seric if (InChild) 3039339Seric finis(); 3049339Seric break; 3054549Seric 3064549Seric case CMDVRFY: /* vrfy -- verify address */ 3079339Seric if (runinchild("SMTP-VRFY") > 0) 3089339Seric break; 3095003Seric vrfyqueue = NULL; 3107762Seric QuickAbort = TRUE; 3119619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3127762Seric if (Errors != 0) 3139339Seric { 3149339Seric if (InChild) 3159339Seric finis(); 3167762Seric break; 3179339Seric } 3185003Seric while (vrfyqueue != NULL) 3195003Seric { 3205003Seric register ADDRESS *a = vrfyqueue->q_next; 3215003Seric char *code; 3225003Seric 3237685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3245003Seric a = a->q_next; 3255003Seric 3267685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3275003Seric { 3285003Seric if (a != NULL) 3295003Seric code = "250-"; 3305003Seric else 3315003Seric code = "250"; 3325003Seric if (vrfyqueue->q_fullname == NULL) 3335003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3345003Seric else 3355003Seric message(code, "%s <%s>", 3365003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3375003Seric } 3385003Seric else if (a == NULL) 3395003Seric message("554", "Self destructive alias loop"); 3405003Seric vrfyqueue = a; 3415003Seric } 3429339Seric if (InChild) 3439339Seric finis(); 3444549Seric break; 3454549Seric 3464549Seric case CMDHELP: /* help -- give user info */ 3474577Seric if (*p == '\0') 3484577Seric p = "SMTP"; 3494577Seric help(p); 3504549Seric break; 3514549Seric 3524549Seric case CMDNOOP: /* noop -- do nothing */ 3534549Seric message("200", "OK"); 3544549Seric break; 3554549Seric 3564549Seric case CMDQUIT: /* quit -- leave mail */ 3574549Seric message("221", "%s closing connection", HostName); 3589339Seric if (InChild) 3599339Seric ExitStat = EX_QUIT; 3604549Seric finis(); 3614549Seric 3628544Seric case CMDVERB: /* set verbose mode */ 3638544Seric Verbose = TRUE; 3648544Seric message("200", "Verbose mode"); 3658544Seric break; 3668544Seric 3679314Seric case CMDONEX: /* doing one transaction only */ 3689378Seric OneXact = TRUE; 3699314Seric message("200", "Only one transaction"); 3709314Seric break; 3719314Seric 3725003Seric # ifdef DEBUG 3739339Seric case CMDDBGQSHOW: /* show queues */ 3746907Seric printf("Send Queue="); 3756907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3765003Seric break; 3777275Seric 3787275Seric case CMDDBGDEBUG: /* set debug mode */ 3797676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3807676Seric tTflag(p); 3817676Seric message("200", "Debug set"); 3827275Seric break; 3837275Seric 3847282Seric case CMDDBGKILL: /* kill the parent */ 3858544Seric if (!iswiz()) 3868544Seric break; 3877282Seric if (kill(MotherPid, SIGTERM) >= 0) 3887282Seric message("200", "Mother is dead"); 3897282Seric else 3907282Seric message("500", "Can't kill Mom"); 3917282Seric break; 3928544Seric 3939339Seric case CMDDBGSHELL: /* give us an interactive shell */ 3949339Seric if (!iswiz()) 3959339Seric break; 3969378Seric if (fileno(InChannel) != 0) 3979378Seric { 3989378Seric (void) close(0); 3999378Seric (void) dup(fileno(InChannel)); 40010346Seric if (fileno(InChannel) != fileno(OutChannel)) 40110346Seric (void) fclose(InChannel); 4029378Seric InChannel = stdin; 4039378Seric } 4049378Seric if (fileno(OutChannel) != 1) 4059378Seric { 4069378Seric (void) close(1); 4079378Seric (void) dup(fileno(OutChannel)); 4089378Seric (void) fclose(OutChannel); 4099378Seric OutChannel = stdout; 4109378Seric } 41110346Seric (void) close(2); 41210346Seric (void) dup(1); 4139339Seric execl("/bin/csh", "sendmail", 0); 4149339Seric execl("/bin/sh", "sendmail", 0); 4159339Seric message("500", "Can't"); 4169378Seric exit(EX_UNAVAILABLE); 4179339Seric 4188544Seric case CMDDBGWIZ: /* become a wizard */ 4198544Seric if (WizWord != NULL) 4208544Seric { 4218544Seric char seed[3]; 4228544Seric extern char *crypt(); 4238544Seric 4248544Seric strncpy(seed, WizWord, 2); 4258544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 4268544Seric { 4278544Seric message("500", "You are no wizard!"); 4288544Seric break; 4298544Seric } 4308544Seric } 4318544Seric IsWiz = TRUE; 4328544Seric message("200", "Please pass, oh mighty wizard"); 4338544Seric break; 4345003Seric # endif DEBUG 4355003Seric 4364549Seric case CMDERROR: /* unknown command */ 4374549Seric message("500", "Command unrecognized"); 4384549Seric break; 4394549Seric 4404549Seric default: 4414549Seric syserr("smtp: unknown code %d", c->cmdcode); 4424549Seric break; 4434549Seric } 4444549Seric } 4454549Seric } 4464549Seric /* 4474549Seric ** SKIPWORD -- skip a fixed word. 4484549Seric ** 4494549Seric ** Parameters: 4504549Seric ** p -- place to start looking. 4514549Seric ** w -- word to skip. 4524549Seric ** 4534549Seric ** Returns: 4544549Seric ** p following w. 4554549Seric ** NULL on error. 4564549Seric ** 4574549Seric ** Side Effects: 4584549Seric ** clobbers the p data area. 4594549Seric */ 4604549Seric 4614549Seric static char * 4624549Seric skipword(p, w) 4634549Seric register char *p; 4644549Seric char *w; 4654549Seric { 4664549Seric register char *q; 4674549Seric extern bool sameword(); 4684549Seric 4694549Seric /* find beginning of word */ 4704549Seric while (isspace(*p)) 4714549Seric p++; 4724549Seric q = p; 4734549Seric 4744549Seric /* find end of word */ 4754549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4764549Seric p++; 4774549Seric while (isspace(*p)) 4784549Seric *p++ = '\0'; 4794549Seric if (*p != ':') 4804549Seric { 4814549Seric syntax: 4824549Seric message("501", "Syntax error"); 4834549Seric Errors++; 4844549Seric return (NULL); 4854549Seric } 4864549Seric *p++ = '\0'; 4874549Seric while (isspace(*p)) 4884549Seric p++; 4894549Seric 4904549Seric /* see if the input word matches desired word */ 4914549Seric if (!sameword(q, w)) 4924549Seric goto syntax; 4934549Seric 4944549Seric return (p); 4954549Seric } 4964577Seric /* 4974577Seric ** HELP -- implement the HELP command. 4984577Seric ** 4994577Seric ** Parameters: 5004577Seric ** topic -- the topic we want help for. 5014577Seric ** 5024577Seric ** Returns: 5034577Seric ** none. 5044577Seric ** 5054577Seric ** Side Effects: 5064577Seric ** outputs the help file to message output. 5074577Seric */ 5084577Seric 5094577Seric help(topic) 5104577Seric char *topic; 5114577Seric { 5124577Seric register FILE *hf; 5134577Seric int len; 5144577Seric char buf[MAXLINE]; 5154577Seric bool noinfo; 5164577Seric 5178269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5184577Seric { 5194577Seric /* no help */ 52011931Seric errno = 0; 5214577Seric message("502", "HELP not implemented"); 5224577Seric return; 5234577Seric } 5244577Seric 5254577Seric len = strlen(topic); 5264577Seric makelower(topic); 5274577Seric noinfo = TRUE; 5284577Seric 5294577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5304577Seric { 5314577Seric if (strncmp(buf, topic, len) == 0) 5324577Seric { 5334577Seric register char *p; 5344577Seric 5354577Seric p = index(buf, '\t'); 5364577Seric if (p == NULL) 5374577Seric p = buf; 5384577Seric else 5394577Seric p++; 5404577Seric fixcrlf(p, TRUE); 5414577Seric message("214-", p); 5424577Seric noinfo = FALSE; 5434577Seric } 5444577Seric } 5454577Seric 5464577Seric if (noinfo) 5474577Seric message("504", "HELP topic unknown"); 5484577Seric else 5494577Seric message("214", "End of HELP info"); 5504628Seric (void) fclose(hf); 5514577Seric } 5528544Seric /* 5538544Seric ** ISWIZ -- tell us if we are a wizard 5548544Seric ** 5558544Seric ** If not, print a nasty message. 5568544Seric ** 5578544Seric ** Parameters: 5588544Seric ** none. 5598544Seric ** 5608544Seric ** Returns: 5618544Seric ** TRUE if we are a wizard. 5628544Seric ** FALSE if we are not a wizard. 5638544Seric ** 5648544Seric ** Side Effects: 5658544Seric ** Prints a 500 exit stat if we are not a wizard. 5668544Seric */ 5675181Seric 5688544Seric bool 5698544Seric iswiz() 5708544Seric { 5718544Seric if (!IsWiz) 5728544Seric message("500", "Mere mortals musn't mutter that mantra"); 5738544Seric return (IsWiz); 5748544Seric } 5759339Seric /* 5769339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5779339Seric ** 5789339Seric ** Parameters: 5799339Seric ** label -- a string used in error messages 5809339Seric ** 5819339Seric ** Returns: 5829339Seric ** zero in the child 5839339Seric ** one in the parent 5849339Seric ** 5859339Seric ** Side Effects: 5869339Seric ** none. 5879339Seric */ 5888544Seric 5899339Seric runinchild(label) 5909339Seric char *label; 5919339Seric { 5929339Seric int childpid; 5939339Seric 5949378Seric if (OneXact) 5959378Seric return (0); 5969378Seric 5979339Seric childpid = dofork(); 5989339Seric if (childpid < 0) 5999339Seric { 6009339Seric syserr("%s: cannot fork", label); 6019339Seric return (1); 6029339Seric } 6039339Seric if (childpid > 0) 6049339Seric { 6059339Seric auto int st; 6069339Seric 6079378Seric /* parent -- wait for child to complete */ 6089378Seric st = waitfor(childpid); 6099378Seric if (st == -1) 6109339Seric syserr("%s: lost child", label); 6119339Seric 6129339Seric /* if we exited on a QUIT command, complete the process */ 6139339Seric if (st == (EX_QUIT << 8)) 6149339Seric finis(); 6159339Seric 6169339Seric return (1); 6179339Seric } 6189339Seric else 6199339Seric { 6209339Seric /* child */ 6219339Seric InChild = TRUE; 6229545Seric clearenvelope(CurEnv); 6239339Seric return (0); 6249339Seric } 6259339Seric } 6269339Seric 6275181Seric # endif SMTP 628