14549Seric # include "sendmail.h" 24549Seric 35181Seric # ifndef SMTP 4*8544Seric SCCSID(@(#)srvrsmtp.c 3.34 10/16/82 (no SMTP)); 55181Seric # else SMTP 64556Seric 7*8544Seric SCCSID(@(#)srvrsmtp.c 3.34 10/16/82); 85181Seric 94549Seric /* 104549Seric ** SMTP -- run the SMTP protocol. 114549Seric ** 124549Seric ** Parameters: 134549Seric ** none. 144549Seric ** 154549Seric ** Returns: 164549Seric ** never. 174549Seric ** 184549Seric ** Side Effects: 194549Seric ** Reads commands from the input channel and processes 204549Seric ** them. 214549Seric */ 224549Seric 234549Seric struct cmd 244549Seric { 254549Seric char *cmdname; /* command name */ 264549Seric int cmdcode; /* internal code, see below */ 274549Seric }; 284549Seric 294549Seric /* values for cmdcode */ 304549Seric # define CMDERROR 0 /* bad command */ 314549Seric # define CMDMAIL 1 /* mail -- designate sender */ 324976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 334549Seric # define CMDDATA 3 /* data -- send message text */ 344549Seric # define CMDRSET 5 /* rset -- reset state */ 354549Seric # define CMDVRFY 6 /* vrfy -- verify address */ 364549Seric # define CMDHELP 7 /* help -- give usage info */ 374549Seric # define CMDNOOP 8 /* noop -- do nothing */ 384549Seric # define CMDQUIT 9 /* quit -- close connection and die */ 394577Seric # define CMDMRSQ 10 /* mrsq -- for old mtp compat only */ 404976Seric # define CMDHELO 11 /* helo -- be polite */ 41*8544Seric # define CMDDBGSHOWQ 12 /* showq -- show send queue (DEBUG) */ 42*8544Seric # define CMDDBGDEBUG 13 /* debug -- set debug mode */ 43*8544Seric # define CMDVERB 14 /* verb -- go into verbose mode */ 44*8544Seric # define CMDDBGKILL 15 /* kill -- kill sendmail */ 45*8544Seric # define CMDDBGWIZ 16 /* wiz -- become a wizard */ 464549Seric 474549Seric static struct cmd CmdTab[] = 484549Seric { 494549Seric "mail", CMDMAIL, 504976Seric "rcpt", CMDRCPT, 514976Seric "mrcp", CMDRCPT, /* for old MTP compatability */ 524549Seric "data", CMDDATA, 534549Seric "rset", CMDRSET, 544549Seric "vrfy", CMDVRFY, 557762Seric "expn", CMDVRFY, 564549Seric "help", CMDHELP, 574549Seric "noop", CMDNOOP, 584549Seric "quit", CMDQUIT, 594577Seric "mrsq", CMDMRSQ, 604976Seric "helo", CMDHELO, 61*8544Seric "verb", CMDVERB, 625003Seric # ifdef DEBUG 63*8544Seric "showq", CMDDBGSHOWQ, 64*8544Seric "debug", CMDDBGDEBUG, 65*8544Seric "kill", CMDDBGKILL, 66*8544Seric "wiz", CMDDBGWIZ, 675003Seric # endif DEBUG 684549Seric NULL, CMDERROR, 694549Seric }; 704549Seric 71*8544Seric # ifdef DEBUG 72*8544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 73*8544Seric char *WizWord = NULL; /* the wizard word to compare against */ 74*8544Seric # endif DEBUG 75*8544Seric 764549Seric smtp() 774549Seric { 784549Seric register char *p; 79*8544Seric register struct cmd *c; 804549Seric char *cmd; 814549Seric extern char *skipword(); 824549Seric extern bool sameword(); 834549Seric bool hasmail; /* mail command received */ 844713Seric int rcps; /* number of recipients */ 855003Seric auto ADDRESS *vrfyqueue; 86*8544Seric char inp[MAXLINE]; 877124Seric extern char Version[]; 887356Seric extern tick(); 89*8544Seric extern bool iswiz(); 904549Seric 915003Seric hasmail = FALSE; 924713Seric rcps = 0; 937363Seric if (OutChannel != stdout) 947363Seric { 957363Seric /* arrange for debugging output to go to remote host */ 967363Seric (void) close(1); 977363Seric (void) dup(fileno(OutChannel)); 987363Seric } 997851Seric message("220", "%s Sendmail %s ready at %s", HostName, 1007797Seric Version, arpadate(NULL)); 1017762Seric (void) setjmp(TopFrame); 1027762Seric QuickAbort = FALSE; 1034549Seric for (;;) 1044549Seric { 1057356Seric /* setup for the read */ 1066907Seric CurEnv->e_to = NULL; 1074577Seric Errors = 0; 1087275Seric (void) fflush(stdout); 1097356Seric 1107356Seric /* read the input line */ 1117685Seric p = sfgets(inp, sizeof inp, InChannel); 1127356Seric 1137685Seric /* handle errors */ 1147356Seric if (p == NULL) 1157356Seric { 1164549Seric /* end of file, just die */ 1174558Seric message("421", "%s Lost input channel", HostName); 1184549Seric finis(); 1194549Seric } 1204549Seric 1214549Seric /* clean up end of line */ 1224558Seric fixcrlf(inp, TRUE); 1234549Seric 1244713Seric /* echo command to transcript */ 1257557Seric fprintf(Xscript, "<<< %s\n", inp); 1264713Seric 1274549Seric /* break off command */ 1284549Seric for (p = inp; isspace(*p); p++) 1294549Seric continue; 1304549Seric cmd = p; 1314549Seric while (*++p != '\0' && !isspace(*p)) 1324549Seric continue; 1334549Seric if (*p != '\0') 1344549Seric *p++ = '\0'; 1354549Seric 1364549Seric /* decode command */ 1374549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1384549Seric { 1394549Seric if (sameword(c->cmdname, cmd)) 1404549Seric break; 1414549Seric } 1424549Seric 1434549Seric /* process command */ 1444549Seric switch (c->cmdcode) 1454549Seric { 1464976Seric case CMDHELO: /* hello -- introduce yourself */ 1475187Seric define('s', newstr(p)); 1484997Seric message("250", "%s Hello %s, pleased to meet you", 1494997Seric HostName, p); 1504976Seric break; 1514976Seric 1524549Seric case CMDMAIL: /* mail -- designate sender */ 1534558Seric if (hasmail) 1544558Seric { 1554558Seric message("503", "Sender already specified"); 1564558Seric break; 1574558Seric } 1584549Seric p = skipword(p, "from"); 1594549Seric if (p == NULL) 1604549Seric break; 1614549Seric if (index(p, ',') != NULL) 1624549Seric { 1634549Seric message("501", "Source routing not implemented"); 1644549Seric Errors++; 1654549Seric break; 1664549Seric } 1674549Seric setsender(p); 1684577Seric if (Errors == 0) 1694549Seric { 1704549Seric message("250", "Sender ok"); 1714549Seric hasmail = TRUE; 1724549Seric } 1734549Seric break; 1744549Seric 1754976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1764549Seric p = skipword(p, "to"); 1774549Seric if (p == NULL) 1784549Seric break; 1798081Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1804577Seric if (Errors == 0) 1814549Seric { 1826057Seric message("250", "%s... Recipient ok", p); 1834713Seric rcps++; 1844549Seric } 1854549Seric break; 1864549Seric 1874549Seric case CMDDATA: /* data -- text of mail */ 1884976Seric if (!hasmail) 1894549Seric { 1904976Seric message("503", "Need MAIL command"); 1914976Seric break; 1924549Seric } 1934713Seric else if (rcps <= 0) 1944549Seric { 1954976Seric message("503", "Need RCPT (recipient)"); 1964976Seric break; 1974549Seric } 1984976Seric 1994976Seric /* collect the text of the message */ 2004976Seric collect(TRUE); 2014976Seric if (Errors != 0) 2024976Seric break; 2034976Seric 2048238Seric /* 2058238Seric ** Arrange to send to everyone. 2068238Seric ** If sending to multiple people, mail back 2078238Seric ** errors rather than reporting directly. 2088238Seric ** In any case, don't mail back errors for 2098238Seric ** anything that has happened up to 2108238Seric ** now (the other end will do this). 2118238Seric ** Then send to everyone. 2128238Seric ** Finally give a reply code. If an error has 2138238Seric ** already been given, don't mail a 2148238Seric ** message back. 2158238Seric ** We goose error returns by clearing FatalErrors. 2168238Seric */ 2178238Seric 2184976Seric if (rcps != 1) 2194976Seric HoldErrs = MailBack = TRUE; 2208238Seric FatalErrors = FALSE; 2214976Seric 2224976Seric /* send to all recipients */ 2237046Seric sendall(CurEnv, FALSE); 2246907Seric CurEnv->e_to = NULL; 2254976Seric 2268238Seric /* issue success if appropriate and reset */ 2278238Seric if (Errors == 0 || HoldErrs) 2288238Seric { 2298238Seric HoldErrs = FALSE; 2304976Seric message("250", "Sent"); 2318238Seric } 2328238Seric else 2338238Seric FatalErrors = FALSE; 2344549Seric break; 2354549Seric 2364549Seric case CMDRSET: /* rset -- reset state */ 2374549Seric message("250", "Reset state"); 2384549Seric finis(); 2394549Seric 2404549Seric case CMDVRFY: /* vrfy -- verify address */ 2415003Seric vrfyqueue = NULL; 2427762Seric QuickAbort = TRUE; 2438081Seric sendto(p, (ADDRESS *) NULL, &vrfyqueue); 2447762Seric if (Errors != 0) 2457762Seric break; 2465003Seric while (vrfyqueue != NULL) 2475003Seric { 2485003Seric register ADDRESS *a = vrfyqueue->q_next; 2495003Seric char *code; 2505003Seric 2517685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 2525003Seric a = a->q_next; 2535003Seric 2547685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 2555003Seric { 2565003Seric if (a != NULL) 2575003Seric code = "250-"; 2585003Seric else 2595003Seric code = "250"; 2605003Seric if (vrfyqueue->q_fullname == NULL) 2615003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2625003Seric else 2635003Seric message(code, "%s <%s>", 2645003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2655003Seric } 2665003Seric else if (a == NULL) 2675003Seric message("554", "Self destructive alias loop"); 2685003Seric vrfyqueue = a; 2695003Seric } 2704549Seric break; 2714549Seric 2724549Seric case CMDHELP: /* help -- give user info */ 2734577Seric if (*p == '\0') 2744577Seric p = "SMTP"; 2754577Seric help(p); 2764549Seric break; 2774549Seric 2784549Seric case CMDNOOP: /* noop -- do nothing */ 2794549Seric message("200", "OK"); 2804549Seric break; 2814549Seric 2824549Seric case CMDQUIT: /* quit -- leave mail */ 2834549Seric message("221", "%s closing connection", HostName); 2844549Seric finis(); 2854549Seric 2864577Seric case CMDMRSQ: /* mrsq -- negotiate protocol */ 2874577Seric if (*p == 'R' || *p == 'T') 2884577Seric { 2894577Seric /* recipients first or text first */ 2904577Seric message("200", "%c ok, please continue", *p); 2914577Seric } 2924577Seric else if (*p == '?') 2934577Seric { 2944577Seric /* what do I prefer? anything, anytime */ 2954577Seric message("215", "R Recipients first is my choice"); 2964577Seric } 2974577Seric else if (*p == '\0') 2984577Seric { 2994577Seric /* no meaningful scheme */ 3004577Seric message("200", "okey dokie boobie"); 3014577Seric } 3024577Seric else 3034577Seric { 3044577Seric /* bad argument */ 3054577Seric message("504", "Scheme unknown"); 3064577Seric } 3074577Seric break; 3084577Seric 309*8544Seric case CMDVERB: /* set verbose mode */ 310*8544Seric Verbose = TRUE; 311*8544Seric message("200", "Verbose mode"); 312*8544Seric break; 313*8544Seric 3145003Seric # ifdef DEBUG 3155003Seric case CMDDBGSHOWQ: /* show queues */ 3166907Seric printf("Send Queue="); 3176907Seric printaddr(CurEnv->e_sendqueue, TRUE); 3185003Seric break; 3197275Seric 3207275Seric case CMDDBGDEBUG: /* set debug mode */ 3217676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 3227676Seric tTflag(p); 3237676Seric message("200", "Debug set"); 3247275Seric break; 3257275Seric 3267282Seric case CMDDBGKILL: /* kill the parent */ 327*8544Seric if (!iswiz()) 328*8544Seric break; 3297282Seric if (kill(MotherPid, SIGTERM) >= 0) 3307282Seric message("200", "Mother is dead"); 3317282Seric else 3327282Seric message("500", "Can't kill Mom"); 3337282Seric break; 334*8544Seric 335*8544Seric case CMDDBGWIZ: /* become a wizard */ 336*8544Seric if (WizWord != NULL) 337*8544Seric { 338*8544Seric char seed[3]; 339*8544Seric extern char *crypt(); 340*8544Seric 341*8544Seric strncpy(seed, WizWord, 2); 342*8544Seric if (strcmp(WizWord, crypt(p, seed)) != 0) 343*8544Seric { 344*8544Seric message("500", "You are no wizard!"); 345*8544Seric break; 346*8544Seric } 347*8544Seric } 348*8544Seric IsWiz = TRUE; 349*8544Seric message("200", "Please pass, oh mighty wizard"); 350*8544Seric break; 3515003Seric # endif DEBUG 3525003Seric 3534549Seric case CMDERROR: /* unknown command */ 3544549Seric message("500", "Command unrecognized"); 3554549Seric break; 3564549Seric 3574549Seric default: 3584549Seric syserr("smtp: unknown code %d", c->cmdcode); 3594549Seric break; 3604549Seric } 3614549Seric } 3624549Seric } 3634549Seric /* 3644549Seric ** SKIPWORD -- skip a fixed word. 3654549Seric ** 3664549Seric ** Parameters: 3674549Seric ** p -- place to start looking. 3684549Seric ** w -- word to skip. 3694549Seric ** 3704549Seric ** Returns: 3714549Seric ** p following w. 3724549Seric ** NULL on error. 3734549Seric ** 3744549Seric ** Side Effects: 3754549Seric ** clobbers the p data area. 3764549Seric */ 3774549Seric 3784549Seric static char * 3794549Seric skipword(p, w) 3804549Seric register char *p; 3814549Seric char *w; 3824549Seric { 3834549Seric register char *q; 3844549Seric extern bool sameword(); 3854549Seric 3864549Seric /* find beginning of word */ 3874549Seric while (isspace(*p)) 3884549Seric p++; 3894549Seric q = p; 3904549Seric 3914549Seric /* find end of word */ 3924549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 3934549Seric p++; 3944549Seric while (isspace(*p)) 3954549Seric *p++ = '\0'; 3964549Seric if (*p != ':') 3974549Seric { 3984549Seric syntax: 3994549Seric message("501", "Syntax error"); 4004549Seric Errors++; 4014549Seric return (NULL); 4024549Seric } 4034549Seric *p++ = '\0'; 4044549Seric while (isspace(*p)) 4054549Seric p++; 4064549Seric 4074549Seric /* see if the input word matches desired word */ 4084549Seric if (!sameword(q, w)) 4094549Seric goto syntax; 4104549Seric 4114549Seric return (p); 4124549Seric } 4134577Seric /* 4144577Seric ** HELP -- implement the HELP command. 4154577Seric ** 4164577Seric ** Parameters: 4174577Seric ** topic -- the topic we want help for. 4184577Seric ** 4194577Seric ** Returns: 4204577Seric ** none. 4214577Seric ** 4224577Seric ** Side Effects: 4234577Seric ** outputs the help file to message output. 4244577Seric */ 4254577Seric 4264577Seric help(topic) 4274577Seric char *topic; 4284577Seric { 4294577Seric register FILE *hf; 4304577Seric int len; 4314577Seric char buf[MAXLINE]; 4324577Seric bool noinfo; 4334577Seric 4348269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 4354577Seric { 4364577Seric /* no help */ 4374577Seric message("502", "HELP not implemented"); 4384577Seric return; 4394577Seric } 4404577Seric 4414577Seric len = strlen(topic); 4424577Seric makelower(topic); 4434577Seric noinfo = TRUE; 4444577Seric 4454577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4464577Seric { 4474577Seric if (strncmp(buf, topic, len) == 0) 4484577Seric { 4494577Seric register char *p; 4504577Seric 4514577Seric p = index(buf, '\t'); 4524577Seric if (p == NULL) 4534577Seric p = buf; 4544577Seric else 4554577Seric p++; 4564577Seric fixcrlf(p, TRUE); 4574577Seric message("214-", p); 4584577Seric noinfo = FALSE; 4594577Seric } 4604577Seric } 4614577Seric 4624577Seric if (noinfo) 4634577Seric message("504", "HELP topic unknown"); 4644577Seric else 4654577Seric message("214", "End of HELP info"); 4664628Seric (void) fclose(hf); 4674577Seric } 468*8544Seric /* 469*8544Seric ** ISWIZ -- tell us if we are a wizard 470*8544Seric ** 471*8544Seric ** If not, print a nasty message. 472*8544Seric ** 473*8544Seric ** Parameters: 474*8544Seric ** none. 475*8544Seric ** 476*8544Seric ** Returns: 477*8544Seric ** TRUE if we are a wizard. 478*8544Seric ** FALSE if we are not a wizard. 479*8544Seric ** 480*8544Seric ** Side Effects: 481*8544Seric ** Prints a 500 exit stat if we are not a wizard. 482*8544Seric */ 4835181Seric 484*8544Seric bool 485*8544Seric iswiz() 486*8544Seric { 487*8544Seric if (!IsWiz) 488*8544Seric message("500", "Mere mortals musn't mutter that mantra"); 489*8544Seric return (IsWiz); 490*8544Seric } 491*8544Seric 4925181Seric # endif SMTP 493