19339Seric # include <errno.h> 24549Seric # include "sendmail.h" 311728Seric # include <signal.h> 44549Seric 55181Seric # ifndef SMTP 6*19038Seric SCCSID(@(#)srvrsmtp.c 4.11 04/28/85 (no SMTP)); 75181Seric # else SMTP 84556Seric 9*19038Seric SCCSID(@(#)srvrsmtp.c 4.11 04/28/85); 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 */ 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, 685003Seric # endif DEBUG 694549Seric NULL, CMDERROR, 704549Seric }; 714549Seric 728544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 7315596Seric char *WizWord; /* the wizard word to compare against */ 749339Seric bool InChild = FALSE; /* true if running in a subprocess */ 759378Seric bool OneXact = FALSE; /* one xaction only this run */ 7611146Seric char *RealHostName = NULL; /* verified hostname, set in daemon.c */ 7711146Seric 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; 9012612Seric ADDRESS *a; 918544Seric char inp[MAXLINE]; 927124Seric extern char Version[]; 937356Seric extern tick(); 948544Seric extern bool iswiz(); 959349Seric extern char *arpadate(); 9611151Seric extern char *macvalue(); 9712612Seric extern ADDRESS *recipient(); 984549Seric 995003Seric hasmail = FALSE; 1004713Seric rcps = 0; 1017363Seric if (OutChannel != stdout) 1027363Seric { 1037363Seric /* arrange for debugging output to go to remote host */ 1047363Seric (void) close(1); 1057363Seric (void) dup(fileno(OutChannel)); 1067363Seric } 10711931Seric settime(); 10816153Seric expand("\001e", inp, &inp[sizeof inp], CurEnv); 10910708Seric message("220", inp); 1104549Seric for (;;) 1114549Seric { 11212612Seric /* arrange for backout */ 11312612Seric if (setjmp(TopFrame) > 0 && InChild) 11412612Seric finis(); 11512612Seric QuickAbort = FALSE; 11612612Seric HoldErrs = FALSE; 11712612Seric 1187356Seric /* setup for the read */ 1196907Seric CurEnv->e_to = NULL; 1204577Seric Errors = 0; 1217275Seric (void) fflush(stdout); 1227356Seric 1237356Seric /* read the input line */ 1247685Seric p = sfgets(inp, sizeof inp, InChannel); 1257356Seric 1267685Seric /* handle errors */ 1277356Seric if (p == NULL) 1287356Seric { 1294549Seric /* end of file, just die */ 1304558Seric message("421", "%s Lost input channel", HostName); 1314549Seric finis(); 1324549Seric } 1334549Seric 1344549Seric /* clean up end of line */ 1354558Seric fixcrlf(inp, TRUE); 1364549Seric 1374713Seric /* echo command to transcript */ 1389545Seric if (CurEnv->e_xfp != NULL) 1399545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1404713Seric 1414549Seric /* break off command */ 1424549Seric for (p = inp; isspace(*p); p++) 1434549Seric continue; 1444549Seric cmd = p; 1454549Seric while (*++p != '\0' && !isspace(*p)) 1464549Seric continue; 1474549Seric if (*p != '\0') 1484549Seric *p++ = '\0'; 1494549Seric 1504549Seric /* decode command */ 1514549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1524549Seric { 1534549Seric if (sameword(c->cmdname, cmd)) 1544549Seric break; 1554549Seric } 1564549Seric 1574549Seric /* process command */ 1584549Seric switch (c->cmdcode) 1594549Seric { 1604976Seric case CMDHELO: /* hello -- introduce yourself */ 16114877Seric if (sameword(p, HostName)) 16214877Seric { 16314877Seric /* connected to an echo server */ 16414877Seric message("553", "%s I refuse to talk to myself", 16514877Seric HostName); 16614877Seric break; 16714877Seric } 16811146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 16911146Seric { 17011146Seric char buf[MAXNAME]; 17111146Seric 17211146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 17311146Seric define('s', newstr(buf), CurEnv); 17411146Seric } 17511146Seric else 17611146Seric define('s', newstr(p), CurEnv); 1774997Seric message("250", "%s Hello %s, pleased to meet you", 1784997Seric HostName, p); 1794976Seric break; 1804976Seric 1814549Seric case CMDMAIL: /* mail -- designate sender */ 18211151Seric /* force a sending host even if no HELO given */ 18311151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 18411151Seric define('s', RealHostName, CurEnv); 18511151Seric 1869314Seric /* check for validity of this command */ 1874558Seric if (hasmail) 1884558Seric { 1894558Seric message("503", "Sender already specified"); 1904558Seric break; 1914558Seric } 1929339Seric if (InChild) 1939339Seric { 1949339Seric syserr("Nested MAIL command"); 1959339Seric exit(0); 1969339Seric } 1979339Seric 1989339Seric /* fork a subprocess to process this command */ 1999339Seric if (runinchild("SMTP-MAIL") > 0) 2009339Seric break; 2019339Seric initsys(); 2029339Seric 2039339Seric /* child -- go do the processing */ 2044549Seric p = skipword(p, "from"); 2054549Seric if (p == NULL) 2064549Seric break; 2074549Seric setsender(p); 2084577Seric if (Errors == 0) 2094549Seric { 2104549Seric message("250", "Sender ok"); 2114549Seric hasmail = TRUE; 2124549Seric } 2139339Seric else if (InChild) 2149339Seric finis(); 2154549Seric break; 2164549Seric 2174976Seric case CMDRCPT: /* rcpt -- designate recipient */ 21812612Seric if (setjmp(TopFrame) > 0) 21914785Seric { 22014785Seric CurEnv->e_flags &= ~EF_FATALERRS; 22112612Seric break; 22214785Seric } 22312612Seric QuickAbort = TRUE; 2244549Seric p = skipword(p, "to"); 2254549Seric if (p == NULL) 2264549Seric break; 22716140Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0'); 22812612Seric if (a == NULL) 22912612Seric break; 23016886Seric a->q_flags |= QPRIMARY; 23112612Seric a = recipient(a, &CurEnv->e_sendqueue); 23212612Seric if (Errors != 0) 23312612Seric break; 23412612Seric 23512612Seric /* no errors during parsing, but might be a duplicate */ 23612612Seric CurEnv->e_to = p; 23712612Seric if (!bitset(QBADADDR, a->q_flags)) 23812612Seric message("250", "Recipient ok"); 23912612Seric else 2404549Seric { 24112612Seric /* punt -- should keep message in ADDRESS.... */ 24212612Seric message("550", "Addressee unknown"); 2434549Seric } 24412612Seric CurEnv->e_to = NULL; 24512612Seric rcps++; 2464549Seric break; 2474549Seric 2484549Seric case CMDDATA: /* data -- text of mail */ 2494976Seric if (!hasmail) 2504549Seric { 2514976Seric message("503", "Need MAIL command"); 2524976Seric break; 2534549Seric } 2544713Seric else if (rcps <= 0) 2554549Seric { 2564976Seric message("503", "Need RCPT (recipient)"); 2574976Seric break; 2584549Seric } 2594976Seric 2604976Seric /* collect the text of the message */ 2614976Seric collect(TRUE); 2624976Seric if (Errors != 0) 2634976Seric break; 2644976Seric 2658238Seric /* 2668238Seric ** Arrange to send to everyone. 2678238Seric ** If sending to multiple people, mail back 2688238Seric ** errors rather than reporting directly. 2698238Seric ** In any case, don't mail back errors for 2708238Seric ** anything that has happened up to 2718238Seric ** now (the other end will do this). 27210197Seric ** Truncate our transcript -- the mail has gotten 27310197Seric ** to us successfully, and if we have 27410197Seric ** to mail this back, it will be easier 27510197Seric ** on the reader. 2768238Seric ** Then send to everyone. 2778238Seric ** Finally give a reply code. If an error has 2788238Seric ** already been given, don't mail a 2798238Seric ** message back. 2809339Seric ** We goose error returns by clearing error bit. 2818238Seric */ 2828238Seric 2834976Seric if (rcps != 1) 2849378Seric { 2859378Seric HoldErrs = TRUE; 28616886Seric ErrorMode = EM_MAIL; 2879378Seric } 2889339Seric CurEnv->e_flags &= ~EF_FATALERRS; 28910197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 2904976Seric 2914976Seric /* send to all recipients */ 29214877Seric sendall(CurEnv, SM_DEFAULT); 2936907Seric CurEnv->e_to = NULL; 2944976Seric 2958238Seric /* issue success if appropriate and reset */ 2968238Seric if (Errors == 0 || HoldErrs) 2979283Seric message("250", "Ok"); 2988238Seric else 2999339Seric CurEnv->e_flags &= ~EF_FATALERRS; 3009339Seric 3019339Seric /* if in a child, pop back to our parent */ 3029339Seric if (InChild) 3039339Seric finis(); 3044549Seric break; 3054549Seric 3064549Seric case CMDRSET: /* rset -- reset state */ 3074549Seric message("250", "Reset state"); 3089339Seric if (InChild) 3099339Seric finis(); 3109339Seric break; 3114549Seric 3124549Seric case CMDVRFY: /* vrfy -- verify address */ 3139339Seric if (runinchild("SMTP-VRFY") > 0) 3149339Seric break; 3155003Seric vrfyqueue = NULL; 3167762Seric QuickAbort = TRUE; 3179619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3187762Seric if (Errors != 0) 3199339Seric { 3209339Seric if (InChild) 3219339Seric finis(); 3227762Seric break; 3239339Seric } 3245003Seric while (vrfyqueue != NULL) 3255003Seric { 3265003Seric register ADDRESS *a = vrfyqueue->q_next; 3275003Seric char *code; 3285003Seric 3297685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3305003Seric a = a->q_next; 3315003Seric 3327685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3335003Seric { 3345003Seric if (a != NULL) 3355003Seric code = "250-"; 3365003Seric else 3375003Seric code = "250"; 3385003Seric if (vrfyqueue->q_fullname == NULL) 3395003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3405003Seric else 3415003Seric message(code, "%s <%s>", 3425003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3435003Seric } 3445003Seric else if (a == NULL) 3455003Seric message("554", "Self destructive alias loop"); 3465003Seric vrfyqueue = a; 3475003Seric } 3489339Seric if (InChild) 3499339Seric finis(); 3504549Seric break; 3514549Seric 3524549Seric case CMDHELP: /* help -- give user info */ 3534577Seric if (*p == '\0') 3544577Seric p = "SMTP"; 3554577Seric help(p); 3564549Seric break; 3574549Seric 3584549Seric case CMDNOOP: /* noop -- do nothing */ 3594549Seric message("200", "OK"); 3604549Seric break; 3614549Seric 3624549Seric case CMDQUIT: /* quit -- leave mail */ 3634549Seric message("221", "%s closing connection", HostName); 3649339Seric if (InChild) 3659339Seric ExitStat = EX_QUIT; 3664549Seric finis(); 3674549Seric 3688544Seric case CMDVERB: /* set verbose mode */ 3698544Seric Verbose = TRUE; 3708544Seric message("200", "Verbose mode"); 3718544Seric break; 3728544Seric 3739314Seric case CMDONEX: /* doing one transaction only */ 3749378Seric OneXact = TRUE; 3759314Seric message("200", "Only one transaction"); 3769314Seric break; 3779314Seric 3785003Seric # ifdef DEBUG 3799339Seric case CMDDBGQSHOW: /* show queues */ 3806907Seric printf("Send Queue="); 3816907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3825003Seric break; 3837275Seric 3847275Seric case CMDDBGDEBUG: /* set debug mode */ 3857676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3867676Seric tTflag(p); 3877676Seric message("200", "Debug set"); 3887275Seric break; 3897275Seric 3907282Seric case CMDDBGKILL: /* kill the parent */ 3918544Seric if (!iswiz()) 3928544Seric break; 3937282Seric if (kill(MotherPid, SIGTERM) >= 0) 3947282Seric message("200", "Mother is dead"); 3957282Seric else 3967282Seric message("500", "Can't kill Mom"); 3977282Seric break; 3988544Seric 3998544Seric case CMDDBGWIZ: /* become a wizard */ 4008544Seric if (WizWord != NULL) 4018544Seric { 4028544Seric char seed[3]; 4038544Seric extern char *crypt(); 4048544Seric 4058544Seric strncpy(seed, WizWord, 2); 40615596Seric if (strcmp(WizWord, crypt(p, seed)) == 0) 4078544Seric { 40815596Seric IsWiz = TRUE; 40915596Seric message("200", "Please pass, oh mighty wizard"); 4108544Seric break; 4118544Seric } 4128544Seric } 41315596Seric message("500", "You are no wizard!"); 4148544Seric break; 4155003Seric # endif DEBUG 4165003Seric 4174549Seric case CMDERROR: /* unknown command */ 4184549Seric message("500", "Command unrecognized"); 4194549Seric break; 4204549Seric 4214549Seric default: 4224549Seric syserr("smtp: unknown code %d", c->cmdcode); 4234549Seric break; 4244549Seric } 4254549Seric } 4264549Seric } 4274549Seric /* 4284549Seric ** SKIPWORD -- skip a fixed word. 4294549Seric ** 4304549Seric ** Parameters: 4314549Seric ** p -- place to start looking. 4324549Seric ** w -- word to skip. 4334549Seric ** 4344549Seric ** Returns: 4354549Seric ** p following w. 4364549Seric ** NULL on error. 4374549Seric ** 4384549Seric ** Side Effects: 4394549Seric ** clobbers the p data area. 4404549Seric */ 4414549Seric 4424549Seric static char * 4434549Seric skipword(p, w) 4444549Seric register char *p; 4454549Seric char *w; 4464549Seric { 4474549Seric register char *q; 4484549Seric extern bool sameword(); 4494549Seric 4504549Seric /* find beginning of word */ 4514549Seric while (isspace(*p)) 4524549Seric p++; 4534549Seric q = p; 4544549Seric 4554549Seric /* find end of word */ 4564549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4574549Seric p++; 4584549Seric while (isspace(*p)) 4594549Seric *p++ = '\0'; 4604549Seric if (*p != ':') 4614549Seric { 4624549Seric syntax: 4634549Seric message("501", "Syntax error"); 4644549Seric Errors++; 4654549Seric return (NULL); 4664549Seric } 4674549Seric *p++ = '\0'; 4684549Seric while (isspace(*p)) 4694549Seric p++; 4704549Seric 4714549Seric /* see if the input word matches desired word */ 4724549Seric if (!sameword(q, w)) 4734549Seric goto syntax; 4744549Seric 4754549Seric return (p); 4764549Seric } 4774577Seric /* 4784577Seric ** HELP -- implement the HELP command. 4794577Seric ** 4804577Seric ** Parameters: 4814577Seric ** topic -- the topic we want help for. 4824577Seric ** 4834577Seric ** Returns: 4844577Seric ** none. 4854577Seric ** 4864577Seric ** Side Effects: 4874577Seric ** outputs the help file to message output. 4884577Seric */ 4894577Seric 4904577Seric help(topic) 4914577Seric char *topic; 4924577Seric { 4934577Seric register FILE *hf; 4944577Seric int len; 4954577Seric char buf[MAXLINE]; 4964577Seric bool noinfo; 4974577Seric 4988269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4994577Seric { 5004577Seric /* no help */ 50111931Seric errno = 0; 5024577Seric message("502", "HELP not implemented"); 5034577Seric return; 5044577Seric } 5054577Seric 5064577Seric len = strlen(topic); 5074577Seric makelower(topic); 5084577Seric noinfo = TRUE; 5094577Seric 5104577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5114577Seric { 5124577Seric if (strncmp(buf, topic, len) == 0) 5134577Seric { 5144577Seric register char *p; 5154577Seric 5164577Seric p = index(buf, '\t'); 5174577Seric if (p == NULL) 5184577Seric p = buf; 5194577Seric else 5204577Seric p++; 5214577Seric fixcrlf(p, TRUE); 5224577Seric message("214-", p); 5234577Seric noinfo = FALSE; 5244577Seric } 5254577Seric } 5264577Seric 5274577Seric if (noinfo) 5284577Seric message("504", "HELP topic unknown"); 5294577Seric else 5304577Seric message("214", "End of HELP info"); 5314628Seric (void) fclose(hf); 5324577Seric } 5338544Seric /* 5348544Seric ** ISWIZ -- tell us if we are a wizard 5358544Seric ** 5368544Seric ** If not, print a nasty message. 5378544Seric ** 5388544Seric ** Parameters: 5398544Seric ** none. 5408544Seric ** 5418544Seric ** Returns: 5428544Seric ** TRUE if we are a wizard. 5438544Seric ** FALSE if we are not a wizard. 5448544Seric ** 5458544Seric ** Side Effects: 5468544Seric ** Prints a 500 exit stat if we are not a wizard. 5478544Seric */ 5485181Seric 549*19038Seric #ifdef DEBUG 550*19038Seric 5518544Seric bool 5528544Seric iswiz() 5538544Seric { 5548544Seric if (!IsWiz) 5558544Seric message("500", "Mere mortals musn't mutter that mantra"); 5568544Seric return (IsWiz); 5578544Seric } 558*19038Seric 559*19038Seric #endif DEBUG 5609339Seric /* 5619339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5629339Seric ** 5639339Seric ** Parameters: 5649339Seric ** label -- a string used in error messages 5659339Seric ** 5669339Seric ** Returns: 5679339Seric ** zero in the child 5689339Seric ** one in the parent 5699339Seric ** 5709339Seric ** Side Effects: 5719339Seric ** none. 5729339Seric */ 5738544Seric 5749339Seric runinchild(label) 5759339Seric char *label; 5769339Seric { 5779339Seric int childpid; 5789339Seric 57916158Seric if (!OneXact) 5809339Seric { 58116158Seric childpid = dofork(); 58216158Seric if (childpid < 0) 58316158Seric { 58416158Seric syserr("%s: cannot fork", label); 58516158Seric return (1); 58616158Seric } 58716158Seric if (childpid > 0) 58816158Seric { 58916158Seric auto int st; 5909339Seric 59116158Seric /* parent -- wait for child to complete */ 59216158Seric st = waitfor(childpid); 59316158Seric if (st == -1) 59416158Seric syserr("%s: lost child", label); 5959339Seric 59616158Seric /* if we exited on a QUIT command, complete the process */ 59716158Seric if (st == (EX_QUIT << 8)) 59816158Seric finis(); 5999339Seric 60016158Seric return (1); 60116158Seric } 60216158Seric else 60316158Seric { 60416158Seric /* child */ 60516158Seric InChild = TRUE; 60616158Seric } 6079339Seric } 60815256Seric 60916158Seric /* child (or ONEX command specified) */ 61016158Seric clearenvelope(CurEnv); 61115256Seric 61216158Seric /* open alias database */ 61316158Seric initaliases(AliasFile, FALSE); 61416158Seric 61516158Seric return (0); 6169339Seric } 6179339Seric 6185181Seric # endif SMTP 619