14549Seric # include "sendmail.h" 24549Seric 35181Seric # ifndef SMTP 4*7676Seric SCCSID(@(#)srvrsmtp.c 3.24 08/08/82 (no SMTP)); 55181Seric # else SMTP 64556Seric 7*7676Seric SCCSID(@(#)srvrsmtp.c 3.24 08/08/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 */ 417275Seric # define CMDDBGSHOWQ 12 /* _showq -- show send queue (DEBUG) */ 427275Seric # define CMDDBGDEBUG 13 /* _debug -- set debug mode */ 437275Seric # define CMDDBGVERBOSE 14 /* _verbose -- go into verbose mode */ 447282Seric # define CMDDBGKILL 15 /* _kill -- kill sendmail */ 454549Seric 464549Seric static struct cmd CmdTab[] = 474549Seric { 484549Seric "mail", CMDMAIL, 494976Seric "rcpt", CMDRCPT, 504976Seric "mrcp", CMDRCPT, /* for old MTP compatability */ 514549Seric "data", CMDDATA, 524549Seric "rset", CMDRSET, 534549Seric "vrfy", CMDVRFY, 544549Seric "help", CMDHELP, 554549Seric "noop", CMDNOOP, 564549Seric "quit", CMDQUIT, 574577Seric "mrsq", CMDMRSQ, 584976Seric "helo", CMDHELO, 595003Seric # ifdef DEBUG 607275Seric "_showq", CMDDBGSHOWQ, 617275Seric "_debug", CMDDBGDEBUG, 627275Seric "_verbose", CMDDBGVERBOSE, 637282Seric "_kill", CMDDBGKILL, 645003Seric # endif DEBUG 654549Seric NULL, CMDERROR, 664549Seric }; 674549Seric 684549Seric smtp() 694549Seric { 704549Seric char inp[MAXLINE]; 714549Seric register char *p; 724549Seric struct cmd *c; 734549Seric char *cmd; 744549Seric extern char *skipword(); 754549Seric extern bool sameword(); 764549Seric bool hasmail; /* mail command received */ 774713Seric int rcps; /* number of recipients */ 785003Seric auto ADDRESS *vrfyqueue; 797124Seric extern char Version[]; 807356Seric extern tick(); 814549Seric 825003Seric hasmail = FALSE; 834713Seric rcps = 0; 847363Seric if (OutChannel != stdout) 857363Seric { 867363Seric /* arrange for debugging output to go to remote host */ 877363Seric (void) close(1); 887363Seric (void) dup(fileno(OutChannel)); 897363Seric } 907124Seric message("220", "%s Sendmail version %s at your service", HostName, Version); 917356Seric (void) signal(SIGALRM, tick); 924549Seric for (;;) 934549Seric { 947356Seric /* setup for the read */ 956907Seric CurEnv->e_to = NULL; 964577Seric Errors = 0; 977275Seric (void) fflush(stdout); 987356Seric 997356Seric /* arrange a timeout */ 1007356Seric if (setjmp(TickFrame) != 0) 1014549Seric { 1027356Seric message("421", "%s timed out", HostName); 1037356Seric finis(); 1047356Seric } 1057356Seric (void) alarm(ReadTimeout); 1067356Seric 1077356Seric /* read the input line */ 1087356Seric p = fgets(inp, sizeof inp, InChannel); 1097356Seric 1107356Seric /* clear the timeout and handle errors */ 1117356Seric (void) alarm(0); 1127356Seric if (p == NULL) 1137356Seric { 1144549Seric /* end of file, just die */ 1154558Seric message("421", "%s Lost input channel", HostName); 1164549Seric finis(); 1174549Seric } 1184549Seric 1194549Seric /* clean up end of line */ 1204558Seric fixcrlf(inp, TRUE); 1214549Seric 1224713Seric /* echo command to transcript */ 1237557Seric fprintf(Xscript, "<<< %s\n", inp); 1244713Seric 1254549Seric /* break off command */ 1264549Seric for (p = inp; isspace(*p); p++) 1274549Seric continue; 1284549Seric cmd = p; 1294549Seric while (*++p != '\0' && !isspace(*p)) 1304549Seric continue; 1314549Seric if (*p != '\0') 1324549Seric *p++ = '\0'; 1334549Seric 1344549Seric /* decode command */ 1354549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1364549Seric { 1374549Seric if (sameword(c->cmdname, cmd)) 1384549Seric break; 1394549Seric } 1404549Seric 1414549Seric /* process command */ 1424549Seric switch (c->cmdcode) 1434549Seric { 1444976Seric case CMDHELO: /* hello -- introduce yourself */ 1455187Seric define('s', newstr(p)); 1464997Seric message("250", "%s Hello %s, pleased to meet you", 1474997Seric HostName, p); 1484976Seric break; 1494976Seric 1504549Seric case CMDMAIL: /* mail -- designate sender */ 1514558Seric if (hasmail) 1524558Seric { 1534558Seric message("503", "Sender already specified"); 1544558Seric break; 1554558Seric } 1564549Seric p = skipword(p, "from"); 1574549Seric if (p == NULL) 1584549Seric break; 1594549Seric if (index(p, ',') != NULL) 1604549Seric { 1614549Seric message("501", "Source routing not implemented"); 1624549Seric Errors++; 1634549Seric break; 1644549Seric } 1654549Seric setsender(p); 1664577Seric if (Errors == 0) 1674549Seric { 1684549Seric message("250", "Sender ok"); 1694549Seric hasmail = TRUE; 1704549Seric } 1714549Seric break; 1724549Seric 1734976Seric case CMDRCPT: /* rcpt -- designate recipient */ 1744549Seric p = skipword(p, "to"); 1754549Seric if (p == NULL) 1764549Seric break; 1774549Seric if (index(p, ',') != NULL) 1784549Seric { 1794549Seric message("501", "Source routing not implemented"); 1804549Seric Errors++; 1814549Seric break; 1824549Seric } 1836907Seric sendto(p, 1, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1844577Seric if (Errors == 0) 1854549Seric { 1866057Seric message("250", "%s... Recipient ok", p); 1874713Seric rcps++; 1884549Seric } 1894549Seric break; 1904549Seric 1914549Seric case CMDDATA: /* data -- text of mail */ 1924976Seric if (!hasmail) 1934549Seric { 1944976Seric message("503", "Need MAIL command"); 1954976Seric break; 1964549Seric } 1974713Seric else if (rcps <= 0) 1984549Seric { 1994976Seric message("503", "Need RCPT (recipient)"); 2004976Seric break; 2014549Seric } 2024976Seric 2034976Seric /* collect the text of the message */ 2044976Seric collect(TRUE); 2054976Seric if (Errors != 0) 2064976Seric break; 2074976Seric 2084976Seric /* if sending to multiple people, mail back errors */ 2094976Seric if (rcps != 1) 2104976Seric HoldErrs = MailBack = TRUE; 2114976Seric 2124976Seric /* send to all recipients */ 2137046Seric sendall(CurEnv, FALSE); 2144976Seric 2154976Seric /* reset strange modes */ 2164976Seric HoldErrs = FALSE; 2176907Seric CurEnv->e_to = NULL; 2184976Seric 2194976Seric /* issue success if appropriate */ 2204976Seric if (Errors == 0 || rcps != 1) 2214976Seric message("250", "Sent"); 2224549Seric break; 2234549Seric 2244549Seric case CMDRSET: /* rset -- reset state */ 2254549Seric message("250", "Reset state"); 2264549Seric finis(); 2274549Seric 2284549Seric case CMDVRFY: /* vrfy -- verify address */ 2295003Seric vrfyqueue = NULL; 2305003Seric sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue); 2315003Seric while (vrfyqueue != NULL) 2325003Seric { 2335003Seric register ADDRESS *a = vrfyqueue->q_next; 2345003Seric char *code; 2355003Seric 2365003Seric while (a != NULL && bitset(QDONTSEND, a->q_flags)) 2375003Seric a = a->q_next; 2385003Seric 2395003Seric if (!bitset(QDONTSEND, vrfyqueue->q_flags)) 2405003Seric { 2415003Seric if (a != NULL) 2425003Seric code = "250-"; 2435003Seric else 2445003Seric code = "250"; 2455003Seric if (vrfyqueue->q_fullname == NULL) 2465003Seric message(code, "<%s>", vrfyqueue->q_paddr); 2475003Seric else 2485003Seric message(code, "%s <%s>", 2495003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 2505003Seric } 2515003Seric else if (a == NULL) 2525003Seric message("554", "Self destructive alias loop"); 2535003Seric vrfyqueue = a; 2545003Seric } 2554549Seric break; 2564549Seric 2574549Seric case CMDHELP: /* help -- give user info */ 2584577Seric if (*p == '\0') 2594577Seric p = "SMTP"; 2604577Seric help(p); 2614549Seric break; 2624549Seric 2634549Seric case CMDNOOP: /* noop -- do nothing */ 2644549Seric message("200", "OK"); 2654549Seric break; 2664549Seric 2674549Seric case CMDQUIT: /* quit -- leave mail */ 2684549Seric message("221", "%s closing connection", HostName); 2694549Seric finis(); 2704549Seric 2714577Seric case CMDMRSQ: /* mrsq -- negotiate protocol */ 2724577Seric if (*p == 'R' || *p == 'T') 2734577Seric { 2744577Seric /* recipients first or text first */ 2754577Seric message("200", "%c ok, please continue", *p); 2764577Seric } 2774577Seric else if (*p == '?') 2784577Seric { 2794577Seric /* what do I prefer? anything, anytime */ 2804577Seric message("215", "R Recipients first is my choice"); 2814577Seric } 2824577Seric else if (*p == '\0') 2834577Seric { 2844577Seric /* no meaningful scheme */ 2854577Seric message("200", "okey dokie boobie"); 2864577Seric } 2874577Seric else 2884577Seric { 2894577Seric /* bad argument */ 2904577Seric message("504", "Scheme unknown"); 2914577Seric } 2924577Seric break; 2934577Seric 2945003Seric # ifdef DEBUG 2955003Seric case CMDDBGSHOWQ: /* show queues */ 2966907Seric printf("Send Queue="); 2976907Seric printaddr(CurEnv->e_sendqueue, TRUE); 2985003Seric break; 2997275Seric 3007275Seric case CMDDBGDEBUG: /* set debug mode */ 301*7676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 302*7676Seric tTflag(p); 303*7676Seric message("200", "Debug set"); 3047275Seric break; 3057275Seric 3067275Seric case CMDDBGVERBOSE: /* set verbose mode */ 3077275Seric Verbose = TRUE; 3087275Seric message("200", "Verbose mode"); 3097275Seric break; 3107282Seric 3117282Seric case CMDDBGKILL: /* kill the parent */ 3127282Seric if (kill(MotherPid, SIGTERM) >= 0) 3137282Seric message("200", "Mother is dead"); 3147282Seric else 3157282Seric message("500", "Can't kill Mom"); 3167282Seric break; 3175003Seric # endif DEBUG 3185003Seric 3194549Seric case CMDERROR: /* unknown command */ 3204549Seric message("500", "Command unrecognized"); 3214549Seric break; 3224549Seric 3234549Seric default: 3244549Seric syserr("smtp: unknown code %d", c->cmdcode); 3254549Seric break; 3264549Seric } 3274549Seric } 3284549Seric } 3294549Seric /* 3304549Seric ** SKIPWORD -- skip a fixed word. 3314549Seric ** 3324549Seric ** Parameters: 3334549Seric ** p -- place to start looking. 3344549Seric ** w -- word to skip. 3354549Seric ** 3364549Seric ** Returns: 3374549Seric ** p following w. 3384549Seric ** NULL on error. 3394549Seric ** 3404549Seric ** Side Effects: 3414549Seric ** clobbers the p data area. 3424549Seric */ 3434549Seric 3444549Seric static char * 3454549Seric skipword(p, w) 3464549Seric register char *p; 3474549Seric char *w; 3484549Seric { 3494549Seric register char *q; 3504549Seric extern bool sameword(); 3514549Seric 3524549Seric /* find beginning of word */ 3534549Seric while (isspace(*p)) 3544549Seric p++; 3554549Seric q = p; 3564549Seric 3574549Seric /* find end of word */ 3584549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 3594549Seric p++; 3604549Seric while (isspace(*p)) 3614549Seric *p++ = '\0'; 3624549Seric if (*p != ':') 3634549Seric { 3644549Seric syntax: 3654549Seric message("501", "Syntax error"); 3664549Seric Errors++; 3674549Seric return (NULL); 3684549Seric } 3694549Seric *p++ = '\0'; 3704549Seric while (isspace(*p)) 3714549Seric p++; 3724549Seric 3734549Seric /* see if the input word matches desired word */ 3744549Seric if (!sameword(q, w)) 3754549Seric goto syntax; 3764549Seric 3774549Seric return (p); 3784549Seric } 3794577Seric /* 3804577Seric ** HELP -- implement the HELP command. 3814577Seric ** 3824577Seric ** Parameters: 3834577Seric ** topic -- the topic we want help for. 3844577Seric ** 3854577Seric ** Returns: 3864577Seric ** none. 3874577Seric ** 3884577Seric ** Side Effects: 3894577Seric ** outputs the help file to message output. 3904577Seric */ 3914577Seric 3924577Seric help(topic) 3934577Seric char *topic; 3944577Seric { 3954577Seric register FILE *hf; 3964577Seric int len; 3974577Seric char buf[MAXLINE]; 3984577Seric bool noinfo; 3994582Seric extern char *HelpFile; 4004577Seric 4014582Seric hf = fopen(HelpFile, "r"); 4024577Seric if (hf == NULL) 4034577Seric { 4044577Seric /* no help */ 4054577Seric message("502", "HELP not implemented"); 4064577Seric return; 4074577Seric } 4084577Seric 4094577Seric len = strlen(topic); 4104577Seric makelower(topic); 4114577Seric noinfo = TRUE; 4124577Seric 4134577Seric while (fgets(buf, sizeof buf, hf) != NULL) 4144577Seric { 4154577Seric if (strncmp(buf, topic, len) == 0) 4164577Seric { 4174577Seric register char *p; 4184577Seric 4194577Seric p = index(buf, '\t'); 4204577Seric if (p == NULL) 4214577Seric p = buf; 4224577Seric else 4234577Seric p++; 4244577Seric fixcrlf(p, TRUE); 4254577Seric message("214-", p); 4264577Seric noinfo = FALSE; 4274577Seric } 4284577Seric } 4294577Seric 4304577Seric if (noinfo) 4314577Seric message("504", "HELP topic unknown"); 4324577Seric else 4334577Seric message("214", "End of HELP info"); 4344628Seric (void) fclose(hf); 4354577Seric } 4365181Seric 4375181Seric # endif SMTP 438