16039Seric # include <errno.h> 24535Seric # include "sendmail.h" 34535Seric 45978Seric #ifndef DAEMON 5*10347Seric SCCSID(@(#)daemon.c 3.46 01/16/83 (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*10347Seric SCCSID(@(#)daemon.c 3.46 01/16/83 (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. 2910206Seric ** clrdaemon() 3010206Seric ** Close any open files associated with getting 3110206Seric ** the connection; this is used when running the queue, 3210206Seric ** etc., to avoid having extra file descriptors during 3310206Seric ** the queue run and to avoid confusing the network 3410206Seric ** code (if it cares). 357556Seric ** makeconnection(host, port, outfile, infile) 367556Seric ** Make a connection to the named host on the given 377556Seric ** port. Set *outfile and *infile to the files 387556Seric ** appropriate for communication. Returns zero on 397556Seric ** success, else an exit status describing the 407556Seric ** error. 417556Seric ** 427556Seric ** The semantics of both of these should be clean. 434535Seric */ 444535Seric /* 454535Seric ** GETREQUESTS -- open mail IPC port and get requests. 464535Seric ** 474535Seric ** Parameters: 484535Seric ** none. 494535Seric ** 504535Seric ** Returns: 514535Seric ** none. 524535Seric ** 534535Seric ** Side Effects: 544535Seric ** Waits until some interesting activity occurs. When 554535Seric ** it does, a child is created to process it, and the 564535Seric ** parent waits for completion. Return from this 579886Seric ** routine is always in the child. The file pointers 589886Seric ** "InChannel" and "OutChannel" should be set to point 599886Seric ** to the communication channel. 604535Seric */ 614535Seric 6210206Seric struct sockaddr_in SendmailAddress;/* internet address of sendmail */ 6310206Seric int DaemonSocket = -1; /* fd describing socket */ 6410342Seric int MaxConnections = 4; /* maximum simultaneous sendmails */ 659610Seric 664535Seric getrequests() 674535Seric { 689610Seric int t; 697117Seric union wait status; 707117Seric int numconnections = 0; 719610Seric register struct servent *sp; 727117Seric 739610Seric /* 749610Seric ** Set up the address for the mailer. 759610Seric */ 769610Seric 779610Seric sp = getservbyname("smtp", "tcp"); 789610Seric if (sp == NULL) 799610Seric { 809610Seric syserr("server \"smtp\" unknown"); 8110167Seric goto severe; 829610Seric } 839610Seric SendmailAddress.sin_family = AF_INET; 849610Seric SendmailAddress.sin_addr.s_addr = INADDR_ANY; 859740Ssam SendmailAddress.sin_port = sp->s_port; 869610Seric 879610Seric /* 889610Seric ** Try to actually open the connection. 899610Seric */ 909610Seric 919610Seric # ifdef DEBUG 929610Seric if (tTd(15, 1)) 939610Seric printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 949610Seric # endif DEBUG 959610Seric 969610Seric /* get a socket for the SMTP connection */ 9710206Seric DaemonSocket = socket(AF_INET, SOCK_STREAM, 0, 0); 9810206Seric if (DaemonSocket < 0) 999610Seric { 1009610Seric /* probably another daemon already */ 1019610Seric syserr("getrequests: can't create socket"); 1029610Seric severe: 1039610Seric # ifdef LOG 1049610Seric if (LogLevel > 0) 1059610Seric syslog(LOG_SALERT, "cannot get connection"); 1069610Seric # endif LOG 1079610Seric finis(); 1089610Seric } 109*10347Seric 110*10347Seric #ifdef DEBUG 111*10347Seric /* turn on network debugging? */ 112*10347Seric if (tTd(15, 15)) 113*10347Seric (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, 0, 0); 114*10347Seric #endif DEBUG 115*10347Seric 11610206Seric if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 1179610Seric { 1189610Seric syserr("getrequests: cannot bind"); 11910206Seric (void) close(DaemonSocket); 1209610Seric goto severe; 1219610Seric } 12210206Seric listen(DaemonSocket, 10); 1239610Seric 1249610Seric # ifdef DEBUG 1259610Seric if (tTd(15, 1)) 12610206Seric printf("getrequests: %d\n", DaemonSocket); 1279610Seric # endif DEBUG 1289610Seric 1294631Seric for (;;) 1304631Seric { 1319610Seric /* wait for a connection */ 1324636Seric register int pid; 1334631Seric 1349610Seric do 1359610Seric { 1369610Seric auto int lotherend; 1379610Seric struct sockaddr otherend; 1384631Seric 1399610Seric errno = 0; 1409610Seric lotherend = sizeof otherend; 14110206Seric t = accept(DaemonSocket, &otherend, &lotherend, 0); 1429610Seric } while (t < 0 && errno == EINTR); 1439610Seric if (t < 0) 1445978Seric { 1459610Seric syserr("getrequests: accept"); 1469610Seric sleep(5); 1479610Seric continue; 1485978Seric } 1494631Seric 1505978Seric /* 1515978Seric ** Create a subprocess to process the mail. 1525978Seric */ 1535978Seric 1545978Seric # ifdef DEBUG 1557677Seric if (tTd(15, 2)) 1569610Seric printf("getrequests: forking (fd = %d)\n", t); 1575978Seric # endif DEBUG 1585978Seric 1594636Seric pid = fork(); 1604636Seric if (pid < 0) 1614631Seric { 1624636Seric syserr("daemon: cannot fork"); 1634636Seric sleep(10); 1649610Seric (void) close(t); 1654636Seric continue; 1664631Seric } 1674631Seric 1684636Seric if (pid == 0) 1694631Seric { 1704636Seric /* 1714636Seric ** CHILD -- return to caller. 1724636Seric ** Verify calling user id if possible here. 1734636Seric */ 1744631Seric 17510206Seric (void) close(DaemonSocket); 1769610Seric InChannel = fdopen(t, "r"); 1779610Seric OutChannel = fdopen(t, "w"); 1785978Seric # ifdef DEBUG 1797677Seric if (tTd(15, 2)) 1805978Seric printf("getreq: returning\n"); 1815978Seric # endif DEBUG 1827876Seric # ifdef LOG 1837876Seric if (LogLevel > 11) 1847876Seric syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 1857876Seric # endif LOG 1864636Seric return; 1874631Seric } 1884631Seric 1894636Seric /* 1904636Seric ** PARENT -- wait for child to terminate. 1914636Seric ** Perhaps we should allow concurrent processing? 1924636Seric */ 1934631Seric 1945978Seric # ifdef DEBUG 1957677Seric if (tTd(15, 2)) 1965978Seric { 1975978Seric sleep(2); 1985978Seric printf("getreq: parent waiting\n"); 1995978Seric } 2005978Seric # endif DEBUG 2015978Seric 2027117Seric /* close the port so that others will hang (for a while) */ 2039610Seric (void) close(t); 2047117Seric 2057117Seric /* pick up old zombies; implement load limiting */ 2067117Seric numconnections++; 20710342Seric while (wait3(&status, numconnections < MaxConnections ? WNOHANG : 0, 0) > 0) 2087117Seric numconnections--; 2094631Seric } 2109886Seric /*NOTREACHED*/ 2114631Seric } 2125978Seric /* 21310206Seric ** CLRDAEMON -- reset the daemon connection 21410206Seric ** 21510206Seric ** Parameters: 21610206Seric ** none. 21710206Seric ** 21810206Seric ** Returns: 21910206Seric ** none. 22010206Seric ** 22110206Seric ** Side Effects: 22210206Seric ** releases any resources used by the passive daemon. 22310206Seric */ 22410206Seric 22510206Seric clrdaemon() 22610206Seric { 22710206Seric if (DaemonSocket >= 0) 22810206Seric (void) close(DaemonSocket); 22910206Seric DaemonSocket = -1; 23010206Seric } 23110206Seric /* 2326039Seric ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 2336039Seric ** 2346039Seric ** Parameters: 2356039Seric ** host -- the name of the host. 2366633Seric ** port -- the port number to connect to. 2376039Seric ** outfile -- a pointer to a place to put the outfile 2386039Seric ** descriptor. 2396039Seric ** infile -- ditto for infile. 2406039Seric ** 2416039Seric ** Returns: 2426039Seric ** An exit code telling whether the connection could be 2436039Seric ** made and if not why not. 2446039Seric ** 2456039Seric ** Side Effects: 2466039Seric ** none. 2476039Seric */ 2485978Seric 2496633Seric makeconnection(host, port, outfile, infile) 2506039Seric char *host; 2517286Seric u_short port; 2526039Seric FILE **outfile; 2536039Seric FILE **infile; 2546039Seric { 2556039Seric register int s; 2566039Seric 2576039Seric /* 2586039Seric ** Set up the address for the mailer. 2599308Seric ** Accept "[a.b.c.d]" syntax for host name. 2606039Seric */ 2616039Seric 2629308Seric if (host[0] == '[') 2639308Seric { 2649308Seric long hid = 0; 2659308Seric int i, j; 2669308Seric register char *p = host; 2679308Seric 2689308Seric for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 2699308Seric { 2709308Seric j = 0; 2719308Seric while (isdigit(*++p)) 2729308Seric j = j * 10 + (*p - '0'); 2739308Seric if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 2749308Seric break; 2759308Seric hid |= j << ((3 - i) * 8); 2769308Seric } 2779308Seric if (i >= 0 || *p != ']' || *++p != '\0') 2789308Seric { 2799308Seric usrerr("Invalid numeric domain spec \"%s\"", host); 2809308Seric return (EX_NOHOST); 2819308Seric } 2829308Seric SendmailAddress.sin_addr.s_addr = hid; 2839308Seric } 2849610Seric else 2859610Seric { 2869610Seric register struct hostent *hp = gethostbyname(host); 2879610Seric 2889610Seric if (hp == 0) 2899610Seric return (EX_NOHOST); 2909610Seric bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 2919610Seric } 2929610Seric 2939610Seric /* 2949610Seric ** Determine the port number. 2959610Seric */ 2969610Seric 29710011Seric if (port != 0) 29810011Seric SendmailAddress.sin_port = htons(port); 29910011Seric else 3009610Seric { 3019610Seric register struct servent *sp = getservbyname("smtp", "tcp"); 3029610Seric 3039610Seric if (sp == NULL) 3049610Seric { 3059610Seric syserr("makeconnection: server \"smtp\" unknown"); 3069610Seric return (EX_OSFILE); 3079610Seric } 30810011Seric SendmailAddress.sin_port = sp->s_port; 3099610Seric } 3106039Seric 3116039Seric /* 3126039Seric ** Try to actually open the connection. 3136039Seric */ 3146039Seric 3156039Seric # ifdef DEBUG 3167677Seric if (tTd(16, 1)) 3176039Seric printf("makeconnection (%s)\n", host); 3186039Seric # endif DEBUG 3196039Seric 3209310Seric s = socket(AF_INET, SOCK_STREAM, 0, 0); 3216039Seric if (s < 0) 3226039Seric { 3236039Seric syserr("makeconnection: no socket"); 3246039Seric goto failure; 3256039Seric } 3266039Seric 3276039Seric # ifdef DEBUG 3287677Seric if (tTd(16, 1)) 3296039Seric printf("makeconnection: %d\n", s); 330*10347Seric 331*10347Seric /* turn on network debugging? */ 332*10347Seric if (tTd(16, 14)) 333*10347Seric (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0); 3346039Seric # endif DEBUG 3359546Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3369610Seric SendmailAddress.sin_family = AF_INET; 3379310Seric bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 3389310Seric if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 3396039Seric { 3406039Seric /* failure, decide if temporary or not */ 3416039Seric failure: 3426039Seric switch (errno) 3436039Seric { 3446039Seric case EISCONN: 3456039Seric case ETIMEDOUT: 3466897Seric case EINPROGRESS: 3476897Seric case EALREADY: 3486897Seric case EADDRINUSE: 34910123Seric case EHOSTDOWN: 3506897Seric case ENETDOWN: 3516897Seric case ENETRESET: 3526897Seric case ENOBUFS: 3537204Seric case ECONNREFUSED: 35410081Seric case EHOSTUNREACH: 35510098Seric case ENETUNREACH: 3566039Seric /* there are others, I'm sure..... */ 3576039Seric return (EX_TEMPFAIL); 3586039Seric 3596039Seric default: 3606039Seric return (EX_UNAVAILABLE); 3616039Seric } 3626039Seric } 3636039Seric 3646039Seric /* connection ok, put it into canonical form */ 3656039Seric *outfile = fdopen(s, "w"); 3666039Seric *infile = fdopen(s, "r"); 3676039Seric 36810098Seric return (EX_OK); 3696039Seric } 3706039Seric 3715978Seric #endif DAEMON 372