1 # include <errno.h> 2 # include "sendmail.h" 3 4 #ifndef DAEMON 5 SCCSID(@(#)daemon.c 3.24 08/24/82 (w/o daemon mode)); 6 #else 7 8 # include <sys/socket.h> 9 # include <net/in.h> 10 # include <wait.h> 11 12 SCCSID(@(#)daemon.c 3.24 08/24/82 (with daemon mode)); 13 14 /* 15 ** DAEMON.C -- routines to use when running as a daemon. 16 ** 17 ** This entire file is highly dependent on the 4.2 BSD 18 ** interprocess communication primitives. No attempt has 19 ** been made to make this file portable to Version 7, 20 ** Version 6, MPX files, etc. If you should try such a 21 ** thing yourself, I recommend chucking the entire file 22 ** and starting from scratch. Basic semantics are: 23 ** 24 ** getrequests() 25 ** Opens a port and initiates a connection. 26 ** Returns in a child. Must set InChannel and 27 ** OutChannel appropriately. 28 ** makeconnection(host, port, outfile, infile) 29 ** Make a connection to the named host on the given 30 ** port. Set *outfile and *infile to the files 31 ** appropriate for communication. Returns zero on 32 ** success, else an exit status describing the 33 ** error. 34 ** 35 ** The semantics of both of these should be clean. 36 */ 37 /* 38 ** GETREQUESTS -- open mail IPC port and get requests. 39 ** 40 ** Parameters: 41 ** none. 42 ** 43 ** Returns: 44 ** none. 45 ** 46 ** Side Effects: 47 ** Waits until some interesting activity occurs. When 48 ** it does, a child is created to process it, and the 49 ** parent waits for completion. Return from this 50 ** routine is always in the child. 51 */ 52 53 # define MAXCONNS 4 /* maximum simultaneous sendmails */ 54 55 getrequests() 56 { 57 union wait status; 58 int numconnections = 0; 59 60 for (;;) 61 { 62 register int pid; 63 register int port; 64 65 /* 66 ** Wait for a connection. 67 */ 68 69 while ((port = getconnection()) < 0) 70 { 71 syserr("getrequests: getconnection failed"); 72 finis(); 73 } 74 75 /* 76 ** Create a subprocess to process the mail. 77 */ 78 79 # ifdef DEBUG 80 if (tTd(15, 2)) 81 printf("getrequests: forking (port = %d)\n", port); 82 # endif DEBUG 83 84 pid = fork(); 85 if (pid < 0) 86 { 87 syserr("daemon: cannot fork"); 88 sleep(10); 89 (void) close(port); 90 continue; 91 } 92 93 if (pid == 0) 94 { 95 /* 96 ** CHILD -- return to caller. 97 ** Verify calling user id if possible here. 98 */ 99 100 InChannel = fdopen(port, "r"); 101 OutChannel = fdopen(port, "w"); 102 # ifdef DEBUG 103 if (tTd(15, 2)) 104 printf("getreq: returning\n"); 105 # endif DEBUG 106 # ifdef LOG 107 if (LogLevel > 11) 108 syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 109 # endif LOG 110 return; 111 } 112 113 /* 114 ** PARENT -- wait for child to terminate. 115 ** Perhaps we should allow concurrent processing? 116 */ 117 118 # ifdef DEBUG 119 if (tTd(15, 2)) 120 { 121 sleep(2); 122 printf("getreq: parent waiting\n"); 123 } 124 # endif DEBUG 125 126 /* close the port so that others will hang (for a while) */ 127 (void) close(port); 128 129 /* pick up old zombies; implement load limiting */ 130 numconnections++; 131 while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 132 numconnections--; 133 } 134 } 135 /* 136 ** GETCONNECTION -- make a connection with the outside world 137 ** 138 ** Parameters: 139 ** none. 140 ** 141 ** Returns: 142 ** The port for mail traffic. 143 ** 144 ** Side Effects: 145 ** Waits for a connection. 146 */ 147 148 #define IPPORT_PLAYPORT 3055 /* random number */ 149 150 struct sockaddr_in SendmailAddress = { AF_INET, IPPORT_SMTP }; 151 152 getconnection() 153 { 154 register int s; 155 struct sockaddr otherend; 156 157 /* 158 ** Set up the address for the mailer. 159 */ 160 161 SendmailAddress.sin_addr.s_addr = 0; 162 SendmailAddress.sin_port = IPPORT_SMTP; 163 # ifdef DEBUG 164 if (tTd(15, 15)) 165 SendmailAddress.sin_port = IPPORT_PLAYPORT; 166 # endif DEBUG 167 SendmailAddress.sin_port = htons(SendmailAddress.sin_port); 168 169 /* 170 ** Try to actually open the connection. 171 */ 172 173 # ifdef DEBUG 174 if (tTd(15, 1)) 175 printf("getconnection\n"); 176 # endif DEBUG 177 178 for (;;) 179 { 180 int acptcnt; 181 182 /* get a socket for the SMTP connection */ 183 s = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN); 184 if (s < 0) 185 { 186 /* probably another daemon already */ 187 syserr("getconnection: can't create socket"); 188 break; 189 } 190 191 # ifdef DEBUG 192 if (tTd(15, 1)) 193 printf("getconnection: %d\n", s); 194 # endif DEBUG 195 196 /* wait for a connection */ 197 (void) time(&CurTime); 198 acptcnt = 0; 199 do 200 { 201 long now; 202 203 errno = 0; 204 (void) accept(s, &otherend); 205 (void) time(&now); 206 if (now == CurTime) 207 { 208 if(++acptcnt > 2) 209 { 210 syserr("wild accept"); 211 /* abort(); */ 212 break; 213 } 214 } 215 else 216 { 217 CurTime = now; 218 acptcnt = 0; 219 } 220 } while (errno == ETIMEDOUT || errno == EINTR); 221 if (errno == 0) 222 break; 223 syserr("getconnection: accept"); 224 (void) close(s); 225 sleep(20); 226 } 227 228 return (s); 229 } 230 /* 231 ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 232 ** 233 ** Parameters: 234 ** host -- the name of the host. 235 ** port -- the port number to connect to. 236 ** outfile -- a pointer to a place to put the outfile 237 ** descriptor. 238 ** infile -- ditto for infile. 239 ** 240 ** Returns: 241 ** An exit code telling whether the connection could be 242 ** made and if not why not. 243 ** 244 ** Side Effects: 245 ** none. 246 */ 247 248 makeconnection(host, port, outfile, infile) 249 char *host; 250 u_short port; 251 FILE **outfile; 252 FILE **infile; 253 { 254 register int s; 255 256 /* 257 ** Set up the address for the mailer. 258 */ 259 260 if ((SendmailAddress.sin_addr.s_addr = rhost(&host)) == -1) 261 return (EX_NOHOST); 262 if (port == 0) 263 port = IPPORT_SMTP; 264 SendmailAddress.sin_port = htons(port); 265 266 /* 267 ** Try to actually open the connection. 268 */ 269 270 # ifdef DEBUG 271 if (tTd(16, 1)) 272 printf("makeconnection (%s)\n", host); 273 # endif DEBUG 274 275 s = socket(SOCK_STREAM, 0, (struct sockaddr_in *) 0, 0); 276 if (s < 0) 277 { 278 syserr("makeconnection: no socket"); 279 goto failure; 280 } 281 282 # ifdef DEBUG 283 if (tTd(16, 1)) 284 printf("makeconnection: %d\n", s); 285 # endif DEBUG 286 (void) fflush(Xscript); /* for debugging */ 287 if (connect(s, &SendmailAddress) < 0) 288 { 289 /* failure, decide if temporary or not */ 290 failure: 291 switch (errno) 292 { 293 case EISCONN: 294 case ETIMEDOUT: 295 case EINPROGRESS: 296 case EALREADY: 297 case EADDRINUSE: 298 case ENETDOWN: 299 case ENETRESET: 300 case ENOBUFS: 301 case ECONNREFUSED: 302 /* there are others, I'm sure..... */ 303 return (EX_TEMPFAIL); 304 305 default: 306 return (EX_UNAVAILABLE); 307 } 308 } 309 310 /* connection ok, put it into canonical form */ 311 *outfile = fdopen(s, "w"); 312 *infile = fdopen(s, "r"); 313 314 return (0); 315 } 316 317 #endif DAEMON 318