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