16039Seric # include <errno.h> 24535Seric # include "sendmail.h" 34535Seric 45978Seric #ifndef DAEMON 5*9740Ssam SCCSID(@(#)daemon.c 3.37 12/16/82 (w/o daemon mode)); 65978Seric #else 74535Seric 89610Seric #include <sys/socket.h> 99610Seric #include <netinet/in.h> 109610Seric #include <netdb.h> 119610Seric #include <wait.h> 125978Seric 13*9740Ssam SCCSID(@(#)daemon.c 3.37 12/16/82 (with daemon mode)); 145978Seric 154535Seric /* 164535Seric ** DAEMON.C -- routines to use when running as a daemon. 177556Seric ** 187556Seric ** This entire file is highly dependent on the 4.2 BSD 197556Seric ** interprocess communication primitives. No attempt has 207556Seric ** been made to make this file portable to Version 7, 217556Seric ** Version 6, MPX files, etc. If you should try such a 227556Seric ** thing yourself, I recommend chucking the entire file 237556Seric ** and starting from scratch. Basic semantics are: 247556Seric ** 257556Seric ** getrequests() 267556Seric ** Opens a port and initiates a connection. 277556Seric ** Returns in a child. Must set InChannel and 287556Seric ** OutChannel appropriately. 297556Seric ** makeconnection(host, port, outfile, infile) 307556Seric ** Make a connection to the named host on the given 317556Seric ** port. Set *outfile and *infile to the files 327556Seric ** appropriate for communication. Returns zero on 337556Seric ** success, else an exit status describing the 347556Seric ** error. 357556Seric ** 367556Seric ** The semantics of both of these should be clean. 374535Seric */ 384535Seric /* 394535Seric ** GETREQUESTS -- open mail IPC port and get requests. 404535Seric ** 414535Seric ** Parameters: 424535Seric ** none. 434535Seric ** 444535Seric ** Returns: 454535Seric ** none. 464535Seric ** 474535Seric ** Side Effects: 484535Seric ** Waits until some interesting activity occurs. When 494535Seric ** it does, a child is created to process it, and the 504535Seric ** parent waits for completion. Return from this 514535Seric ** routine is always in the child. 524535Seric */ 534535Seric 547117Seric # define MAXCONNS 4 /* maximum simultaneous sendmails */ 557117Seric 569610Seric struct sockaddr_in SendmailAddress; 579610Seric 584535Seric getrequests() 594535Seric { 609610Seric int s; 619610Seric int t; 627117Seric union wait status; 637117Seric int numconnections = 0; 649610Seric struct sockaddr otherend; 659610Seric register struct servent *sp; 667117Seric 679610Seric /* 689610Seric ** Set up the address for the mailer. 699610Seric */ 709610Seric 719610Seric sp = getservbyname("smtp", "tcp"); 729610Seric if (sp == NULL) 739610Seric { 749610Seric syserr("server \"smtp\" unknown"); 759610Seric return (-1); 769610Seric } 779610Seric SendmailAddress.sin_family = AF_INET; 789610Seric SendmailAddress.sin_addr.s_addr = INADDR_ANY; 79*9740Ssam SendmailAddress.sin_port = sp->s_port; 809610Seric 819610Seric /* 829610Seric ** Try to actually open the connection. 839610Seric */ 849610Seric 859610Seric # ifdef DEBUG 869610Seric if (tTd(15, 1)) 879610Seric printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 889610Seric # endif DEBUG 899610Seric 909610Seric /* get a socket for the SMTP connection */ 919610Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 929610Seric if (s < 0) 939610Seric { 949610Seric /* probably another daemon already */ 959610Seric syserr("getrequests: can't create socket"); 969610Seric severe: 979610Seric # ifdef LOG 989610Seric if (LogLevel > 0) 999610Seric syslog(LOG_SALERT, "cannot get connection"); 1009610Seric # endif LOG 1019610Seric finis(); 1029610Seric } 1039610Seric if (bind(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 1049610Seric { 1059610Seric syserr("getrequests: cannot bind"); 1069610Seric close(s); 1079610Seric goto severe; 1089610Seric } 1099610Seric listen(s, 10); 1109610Seric 1119610Seric # ifdef DEBUG 1129610Seric if (tTd(15, 1)) 1139610Seric printf("getrequests: %d\n", s); 1149610Seric # endif DEBUG 1159610Seric 1164631Seric for (;;) 1174631Seric { 1189610Seric /* wait for a connection */ 1194636Seric register int pid; 1204631Seric 1219610Seric do 1229610Seric { 1239610Seric auto int lotherend; 1249610Seric struct sockaddr otherend; 1254631Seric 1269610Seric errno = 0; 1279610Seric lotherend = sizeof otherend; 1289610Seric t = accept(s, &otherend, &lotherend, 0); 1299610Seric } while (t < 0 && errno == EINTR); 1309610Seric if (t < 0) 1315978Seric { 1329610Seric syserr("getrequests: accept"); 1339610Seric sleep(5); 1349610Seric continue; 1355978Seric } 1364631Seric 1375978Seric /* 1385978Seric ** Create a subprocess to process the mail. 1395978Seric */ 1405978Seric 1415978Seric # ifdef DEBUG 1427677Seric if (tTd(15, 2)) 1439610Seric printf("getrequests: forking (fd = %d)\n", t); 1445978Seric # endif DEBUG 1455978Seric 1464636Seric pid = fork(); 1474636Seric if (pid < 0) 1484631Seric { 1494636Seric syserr("daemon: cannot fork"); 1504636Seric sleep(10); 1519610Seric (void) close(t); 1524636Seric continue; 1534631Seric } 1544631Seric 1554636Seric if (pid == 0) 1564631Seric { 1574636Seric /* 1584636Seric ** CHILD -- return to caller. 1594636Seric ** Verify calling user id if possible here. 1604636Seric */ 1614631Seric 1629610Seric close(s); 1639610Seric InChannel = fdopen(t, "r"); 1649610Seric OutChannel = fdopen(t, "w"); 1655978Seric # ifdef DEBUG 1667677Seric if (tTd(15, 2)) 1675978Seric printf("getreq: returning\n"); 1685978Seric # endif DEBUG 1697876Seric # ifdef LOG 1707876Seric if (LogLevel > 11) 1717876Seric syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 1727876Seric # endif LOG 1734636Seric return; 1744631Seric } 1754631Seric 1764636Seric /* 1774636Seric ** PARENT -- wait for child to terminate. 1784636Seric ** Perhaps we should allow concurrent processing? 1794636Seric */ 1804631Seric 1815978Seric # ifdef DEBUG 1827677Seric if (tTd(15, 2)) 1835978Seric { 1845978Seric sleep(2); 1855978Seric printf("getreq: parent waiting\n"); 1865978Seric } 1875978Seric # endif DEBUG 1885978Seric 1897117Seric /* close the port so that others will hang (for a while) */ 1909610Seric (void) close(t); 1917117Seric 1927117Seric /* pick up old zombies; implement load limiting */ 1937117Seric numconnections++; 1947117Seric while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 1957117Seric numconnections--; 1964631Seric } 1974631Seric } 1985978Seric /* 1996039Seric ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 2006039Seric ** 2016039Seric ** Parameters: 2026039Seric ** host -- the name of the host. 2036633Seric ** port -- the port number to connect to. 2046039Seric ** outfile -- a pointer to a place to put the outfile 2056039Seric ** descriptor. 2066039Seric ** infile -- ditto for infile. 2076039Seric ** 2086039Seric ** Returns: 2096039Seric ** An exit code telling whether the connection could be 2106039Seric ** made and if not why not. 2116039Seric ** 2126039Seric ** Side Effects: 2136039Seric ** none. 2146039Seric */ 2155978Seric 2166633Seric makeconnection(host, port, outfile, infile) 2176039Seric char *host; 2187286Seric u_short port; 2196039Seric FILE **outfile; 2206039Seric FILE **infile; 2216039Seric { 2226039Seric register int s; 2236039Seric 2246039Seric /* 2256039Seric ** Set up the address for the mailer. 2269308Seric ** Accept "[a.b.c.d]" syntax for host name. 2276039Seric */ 2286039Seric 2299308Seric if (host[0] == '[') 2309308Seric { 2319308Seric long hid = 0; 2329308Seric int i, j; 2339308Seric register char *p = host; 2349308Seric 2359308Seric for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 2369308Seric { 2379308Seric j = 0; 2389308Seric while (isdigit(*++p)) 2399308Seric j = j * 10 + (*p - '0'); 2409308Seric if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 2419308Seric break; 2429308Seric hid |= j << ((3 - i) * 8); 2439308Seric } 2449308Seric if (i >= 0 || *p != ']' || *++p != '\0') 2459308Seric { 2469308Seric usrerr("Invalid numeric domain spec \"%s\"", host); 2479308Seric return (EX_NOHOST); 2489308Seric } 2499308Seric SendmailAddress.sin_addr.s_addr = hid; 2509308Seric } 2519610Seric else 2529610Seric { 2539610Seric register struct hostent *hp = gethostbyname(host); 2549610Seric 2559610Seric if (hp == 0) 2569610Seric return (EX_NOHOST); 2579610Seric bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 2589610Seric } 2599610Seric 2609610Seric /* 2619610Seric ** Determine the port number. 2629610Seric */ 2639610Seric 2646633Seric if (port == 0) 2659610Seric { 2669610Seric register struct servent *sp = getservbyname("smtp", "tcp"); 2679610Seric 2689610Seric if (sp == NULL) 2699610Seric { 2709610Seric syserr("makeconnection: server \"smtp\" unknown"); 2719610Seric return (EX_OSFILE); 2729610Seric } 2739610Seric port = sp->s_port; 2749610Seric } 2757117Seric SendmailAddress.sin_port = htons(port); 2766039Seric 2776039Seric /* 2786039Seric ** Try to actually open the connection. 2796039Seric */ 2806039Seric 2816039Seric # ifdef DEBUG 2827677Seric if (tTd(16, 1)) 2836039Seric printf("makeconnection (%s)\n", host); 2846039Seric # endif DEBUG 2856039Seric 2869310Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 2876039Seric if (s < 0) 2886039Seric { 2896039Seric syserr("makeconnection: no socket"); 2906039Seric goto failure; 2916039Seric } 2926039Seric 2936039Seric # ifdef DEBUG 2947677Seric if (tTd(16, 1)) 2956039Seric printf("makeconnection: %d\n", s); 2966039Seric # endif DEBUG 2979546Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 2989610Seric SendmailAddress.sin_family = AF_INET; 2999310Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 3009310Seric if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 3016039Seric { 3026039Seric /* failure, decide if temporary or not */ 3036039Seric failure: 3046039Seric switch (errno) 3056039Seric { 3066039Seric case EISCONN: 3076039Seric case ETIMEDOUT: 3086897Seric case EINPROGRESS: 3096897Seric case EALREADY: 3106897Seric case EADDRINUSE: 3116897Seric case ENETDOWN: 3126897Seric case ENETRESET: 3136897Seric case ENOBUFS: 3147204Seric case ECONNREFUSED: 3156039Seric /* there are others, I'm sure..... */ 3166039Seric return (EX_TEMPFAIL); 3176039Seric 3186039Seric default: 3196039Seric return (EX_UNAVAILABLE); 3206039Seric } 3216039Seric } 3226039Seric 3236039Seric /* connection ok, put it into canonical form */ 3246039Seric *outfile = fdopen(s, "w"); 3256039Seric *infile = fdopen(s, "r"); 3266039Seric 3276039Seric return (0); 3286039Seric } 3296039Seric 3305978Seric #endif DAEMON 331