16039Seric # include <errno.h> 24535Seric # include "sendmail.h" 34535Seric 45978Seric #ifndef DAEMON 5*9610Seric SCCSID(@(#)daemon.c 3.36 12/13/82 (w/o daemon mode)); 65978Seric #else 74535Seric 8*9610Seric #include <sys/socket.h> 9*9610Seric #include <netinet/in.h> 10*9610Seric #include <netdb.h> 11*9610Seric #include <wait.h> 125978Seric 13*9610Seric SCCSID(@(#)daemon.c 3.36 12/13/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 56*9610Seric struct sockaddr_in SendmailAddress; 57*9610Seric 584535Seric getrequests() 594535Seric { 60*9610Seric int s; 61*9610Seric int t; 627117Seric union wait status; 637117Seric int numconnections = 0; 64*9610Seric struct sockaddr otherend; 65*9610Seric register struct servent *sp; 667117Seric 67*9610Seric /* 68*9610Seric ** Set up the address for the mailer. 69*9610Seric */ 70*9610Seric 71*9610Seric sp = getservbyname("smtp", "tcp"); 72*9610Seric if (sp == NULL) 73*9610Seric { 74*9610Seric syserr("server \"smtp\" unknown"); 75*9610Seric return (-1); 76*9610Seric } 77*9610Seric SendmailAddress.sin_family = AF_INET; 78*9610Seric SendmailAddress.sin_addr.s_addr = INADDR_ANY; 79*9610Seric SendmailAddress.sin_port = htons(sp->s_port); 80*9610Seric 81*9610Seric /* 82*9610Seric ** Try to actually open the connection. 83*9610Seric */ 84*9610Seric 85*9610Seric # ifdef DEBUG 86*9610Seric if (tTd(15, 1)) 87*9610Seric printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 88*9610Seric # endif DEBUG 89*9610Seric 90*9610Seric /* get a socket for the SMTP connection */ 91*9610Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 92*9610Seric if (s < 0) 93*9610Seric { 94*9610Seric /* probably another daemon already */ 95*9610Seric syserr("getrequests: can't create socket"); 96*9610Seric severe: 97*9610Seric # ifdef LOG 98*9610Seric if (LogLevel > 0) 99*9610Seric syslog(LOG_SALERT, "cannot get connection"); 100*9610Seric # endif LOG 101*9610Seric finis(); 102*9610Seric } 103*9610Seric if (bind(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 104*9610Seric { 105*9610Seric syserr("getrequests: cannot bind"); 106*9610Seric close(s); 107*9610Seric goto severe; 108*9610Seric } 109*9610Seric listen(s, 10); 110*9610Seric 111*9610Seric # ifdef DEBUG 112*9610Seric if (tTd(15, 1)) 113*9610Seric printf("getrequests: %d\n", s); 114*9610Seric # endif DEBUG 115*9610Seric 1164631Seric for (;;) 1174631Seric { 118*9610Seric /* wait for a connection */ 1194636Seric register int pid; 1204631Seric 121*9610Seric do 122*9610Seric { 123*9610Seric auto int lotherend; 124*9610Seric struct sockaddr otherend; 1254631Seric 126*9610Seric errno = 0; 127*9610Seric lotherend = sizeof otherend; 128*9610Seric t = accept(s, &otherend, &lotherend, 0); 129*9610Seric } while (t < 0 && errno == EINTR); 130*9610Seric if (t < 0) 1315978Seric { 132*9610Seric syserr("getrequests: accept"); 133*9610Seric sleep(5); 134*9610Seric continue; 1355978Seric } 1364631Seric 1375978Seric /* 1385978Seric ** Create a subprocess to process the mail. 1395978Seric */ 1405978Seric 1415978Seric # ifdef DEBUG 1427677Seric if (tTd(15, 2)) 143*9610Seric 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); 151*9610Seric (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 162*9610Seric close(s); 163*9610Seric InChannel = fdopen(t, "r"); 164*9610Seric 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) */ 190*9610Seric (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 } 251*9610Seric else 252*9610Seric { 253*9610Seric register struct hostent *hp = gethostbyname(host); 254*9610Seric 255*9610Seric if (hp == 0) 256*9610Seric return (EX_NOHOST); 257*9610Seric bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 258*9610Seric } 259*9610Seric 260*9610Seric /* 261*9610Seric ** Determine the port number. 262*9610Seric */ 263*9610Seric 2646633Seric if (port == 0) 265*9610Seric { 266*9610Seric register struct servent *sp = getservbyname("smtp", "tcp"); 267*9610Seric 268*9610Seric if (sp == NULL) 269*9610Seric { 270*9610Seric syserr("makeconnection: server \"smtp\" unknown"); 271*9610Seric return (EX_OSFILE); 272*9610Seric } 273*9610Seric port = sp->s_port; 274*9610Seric } 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 */ 298*9610Seric 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