1 # include <errno.h> 2 # include "sendmail.h" 3 4 #ifndef DAEMON 5 SCCSID(@(#)daemon.c 3.34 11/21/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.34 11/21/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 # ifdef LOG 72 if (LogLevel > 0) 73 syslog(LOG_SALERT, "cannot get connection"); 74 # endif LOG 75 finis(); 76 } 77 78 /* 79 ** Create a subprocess to process the mail. 80 */ 81 82 # ifdef DEBUG 83 if (tTd(15, 2)) 84 printf("getrequests: forking (port = %d)\n", port); 85 # endif DEBUG 86 87 pid = fork(); 88 if (pid < 0) 89 { 90 syserr("daemon: cannot fork"); 91 sleep(10); 92 (void) close(port); 93 continue; 94 } 95 96 if (pid == 0) 97 { 98 /* 99 ** CHILD -- return to caller. 100 ** Verify calling user id if possible here. 101 */ 102 103 InChannel = fdopen(port, "r"); 104 OutChannel = fdopen(port, "w"); 105 # ifdef DEBUG 106 if (tTd(15, 2)) 107 printf("getreq: returning\n"); 108 # endif DEBUG 109 # ifdef LOG 110 if (LogLevel > 11) 111 syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 112 # endif LOG 113 return; 114 } 115 116 /* 117 ** PARENT -- wait for child to terminate. 118 ** Perhaps we should allow concurrent processing? 119 */ 120 121 # ifdef DEBUG 122 if (tTd(15, 2)) 123 { 124 sleep(2); 125 printf("getreq: parent waiting\n"); 126 } 127 # endif DEBUG 128 129 /* close the port so that others will hang (for a while) */ 130 (void) close(port); 131 132 /* pick up old zombies; implement load limiting */ 133 numconnections++; 134 while (wait3(&status, numconnections < MAXCONNS ? WNOHANG : 0, 0) > 0) 135 numconnections--; 136 } 137 } 138 /* 139 ** GETCONNECTION -- make a connection with the outside world 140 ** 141 ** This routine is horribly contorted to try to get around a bunch 142 ** of 4.1a IPC bugs. There appears to be nothing we can do to make 143 ** it "right" -- the code to interrupt accepts just doesn't work 144 ** right. However, this is an attempt to minimize the probablity 145 ** of problems. 146 ** 147 ** Parameters: 148 ** none. 149 ** 150 ** Returns: 151 ** The port for mail traffic. 152 ** 153 ** Side Effects: 154 ** Waits for a connection. 155 */ 156 157 #define IPPORT_PLAYPORT 3055 /* random number */ 158 159 struct sockaddr_in SendmailAddress = { AF_INET, IPPORT_SMTP }; 160 161 getconnection() 162 { 163 int s; 164 #ifdef NVMUNIX 165 int t; 166 #endif NVMUNIX 167 struct sockaddr otherend; 168 169 /* 170 ** Set up the address for the mailer. 171 */ 172 173 SendmailAddress.sin_addr.s_addr = 0; 174 SendmailAddress.sin_port = IPPORT_SMTP; 175 # ifdef DEBUG 176 if (tTd(15, 15)) 177 SendmailAddress.sin_port = IPPORT_PLAYPORT; 178 # endif DEBUG 179 SendmailAddress.sin_port = htons(SendmailAddress.sin_port); 180 181 /* 182 ** Try to actually open the connection. 183 */ 184 185 # ifdef DEBUG 186 if (tTd(15, 1)) 187 printf("getconnection\n"); 188 # endif DEBUG 189 190 for (;; sleep(15)) 191 { 192 int i; 193 194 /* get a socket for the SMTP connection */ 195 #ifdef NVMUNIX 196 s = socket(AF_INET, SOCK_STREAM, 0, 0); 197 bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 198 listen(s, 10); 199 #else NVMUNIX 200 /* do loop is to avoid 4.1b kernel bug (?) */ 201 i = 60; 202 do 203 { 204 s = socket(SOCK_STREAM, 0, &SendmailAddress, SO_ACCEPTCONN); 205 if (s < 0) 206 sleep(10); 207 } while (--i > 0 && s < 0); 208 #endif NVMUNIX 209 if (s < 0) 210 { 211 /* probably another daemon already */ 212 syserr("getconnection: can't create socket"); 213 return (-1); 214 } 215 216 # ifdef DEBUG 217 if (tTd(15, 1)) 218 printf("getconnection: %d\n", s); 219 # endif DEBUG 220 221 /* wait for a connection */ 222 do 223 { 224 errno = 0; 225 #ifdef NVMUNIX 226 lotherend = sizeof otherend; 227 t = accept(s, &otherend, &lotherend, 0); 228 if (t >= 0) 229 return (t); 230 #else NVMUNIX 231 if (accept(s, &otherend) >= 0) 232 return (s); 233 #endif NVMUNIX 234 } while (errno == EINTR); 235 syserr("getconnection: accept"); 236 sleep(5); 237 (void) close(s); 238 } 239 } 240 /* 241 ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 242 ** 243 ** Parameters: 244 ** host -- the name of the host. 245 ** port -- the port number to connect to. 246 ** outfile -- a pointer to a place to put the outfile 247 ** descriptor. 248 ** infile -- ditto for infile. 249 ** 250 ** Returns: 251 ** An exit code telling whether the connection could be 252 ** made and if not why not. 253 ** 254 ** Side Effects: 255 ** none. 256 */ 257 258 makeconnection(host, port, outfile, infile) 259 char *host; 260 u_short port; 261 FILE **outfile; 262 FILE **infile; 263 { 264 register int s; 265 266 /* 267 ** Set up the address for the mailer. 268 ** Accept "[a.b.c.d]" syntax for host name. 269 */ 270 271 if (host[0] == '[') 272 { 273 long hid = 0; 274 int i, j; 275 register char *p = host; 276 277 for (i = 3; i >= 0 && *p != ']' && *p != '\0'; i--) 278 { 279 j = 0; 280 while (isdigit(*++p)) 281 j = j * 10 + (*p - '0'); 282 if (*p != (i == 0 ? ']' : '.') || j > 255 || j < 0) 283 break; 284 hid |= j << ((3 - i) * 8); 285 } 286 if (i >= 0 || *p != ']' || *++p != '\0') 287 { 288 usrerr("Invalid numeric domain spec \"%s\"", host); 289 return (EX_NOHOST); 290 } 291 SendmailAddress.sin_addr.s_addr = hid; 292 } 293 else if ((SendmailAddress.sin_addr.s_addr = rhost(&host)) == -1) 294 return (EX_NOHOST); 295 if (port == 0) 296 port = IPPORT_SMTP; 297 SendmailAddress.sin_port = htons(port); 298 299 /* 300 ** Try to actually open the connection. 301 */ 302 303 # ifdef DEBUG 304 if (tTd(16, 1)) 305 printf("makeconnection (%s)\n", host); 306 # endif DEBUG 307 308 #ifdef NVMUNIX 309 s = socket(AF_INET, SOCK_STREAM, 0, 0); 310 #else NVMUNIX 311 s = socket(SOCK_STREAM, 0, (struct sockaddr_in *) 0, 0); 312 #endif NVMUNIX 313 if (s < 0) 314 { 315 syserr("makeconnection: no socket"); 316 goto failure; 317 } 318 319 # ifdef DEBUG 320 if (tTd(16, 1)) 321 printf("makeconnection: %d\n", s); 322 # endif DEBUG 323 (void) fflush(Xscript); /* for debugging */ 324 #ifdef NVMUNIX 325 bind(s, &SendmailAddress, sizeof SendmailAddress, 0); 326 if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 327 #else NVMUNIX 328 if (connect(s, &SendmailAddress) < 0) 329 #endif NVMUNIX 330 { 331 /* failure, decide if temporary or not */ 332 failure: 333 switch (errno) 334 { 335 case EISCONN: 336 case ETIMEDOUT: 337 case EINPROGRESS: 338 case EALREADY: 339 case EADDRINUSE: 340 case ENETDOWN: 341 case ENETRESET: 342 case ENOBUFS: 343 case ECONNREFUSED: 344 /* there are others, I'm sure..... */ 345 return (EX_TEMPFAIL); 346 347 default: 348 return (EX_UNAVAILABLE); 349 } 350 } 351 352 /* connection ok, put it into canonical form */ 353 *outfile = fdopen(s, "w"); 354 *infile = fdopen(s, "r"); 355 356 return (0); 357 } 358 359 #endif DAEMON 360