1 # include <errno.h> 2 # include "sendmail.h" 3 4 #ifndef DAEMON 5 SCCSID(@(#)daemon.c 3.55 07/13/83 (w/o daemon mode)); 6 #else 7 8 #include <sys/socket.h> 9 #include <netinet/in.h> 10 #include <netdb.h> 11 #include <sys/wait.h> 12 13 SCCSID(@(#)daemon.c 3.55 07/13/83 (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 ** clrdaemon() 30 ** Close any open files associated with getting 31 ** the connection; this is used when running the queue, 32 ** etc., to avoid having extra file descriptors during 33 ** the queue run and to avoid confusing the network 34 ** code (if it cares). 35 ** makeconnection(host, port, outfile, infile) 36 ** Make a connection to the named host on the given 37 ** port. Set *outfile and *infile to the files 38 ** appropriate for communication. Returns zero on 39 ** success, else an exit status describing the 40 ** error. 41 ** 42 ** The semantics of both of these should be clean. 43 */ 44 /* 45 ** GETREQUESTS -- open mail IPC port and get requests. 46 ** 47 ** Parameters: 48 ** none. 49 ** 50 ** Returns: 51 ** none. 52 ** 53 ** Side Effects: 54 ** Waits until some interesting activity occurs. When 55 ** it does, a child is created to process it, and the 56 ** parent waits for completion. Return from this 57 ** routine is always in the child. The file pointers 58 ** "InChannel" and "OutChannel" should be set to point 59 ** to the communication channel. 60 */ 61 62 struct sockaddr_in SendmailAddress;/* internet address of sendmail */ 63 int DaemonSocket = -1; /* fd describing socket */ 64 65 getrequests() 66 { 67 int t; 68 union wait status; 69 register struct servent *sp; 70 71 /* 72 ** Set up the address for the mailer. 73 */ 74 75 sp = getservbyname("smtp", "tcp"); 76 if (sp == NULL) 77 { 78 syserr("server \"smtp\" unknown"); 79 goto severe; 80 } 81 SendmailAddress.sin_family = AF_INET; 82 SendmailAddress.sin_addr.s_addr = INADDR_ANY; 83 SendmailAddress.sin_port = sp->s_port; 84 85 /* 86 ** Try to actually open the connection. 87 */ 88 89 # ifdef DEBUG 90 if (tTd(15, 1)) 91 printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 92 # endif DEBUG 93 94 /* get a socket for the SMTP connection */ 95 DaemonSocket = socket(AF_INET, SOCK_STREAM, 0, 0); 96 if (DaemonSocket < 0) 97 { 98 /* probably another daemon already */ 99 syserr("getrequests: can't create socket"); 100 severe: 101 # ifdef LOG 102 if (LogLevel > 0) 103 syslog(LOG_SALERT, "cannot get connection"); 104 # endif LOG 105 finis(); 106 } 107 108 #ifdef DEBUG 109 /* turn on network debugging? */ 110 if (tTd(15, 15)) 111 (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, 0, 0); 112 #endif DEBUG 113 114 if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 115 { 116 syserr("getrequests: cannot bind"); 117 (void) close(DaemonSocket); 118 goto severe; 119 } 120 listen(DaemonSocket, 10); 121 122 # ifdef DEBUG 123 if (tTd(15, 1)) 124 printf("getrequests: %d\n", DaemonSocket); 125 # endif DEBUG 126 127 for (;;) 128 { 129 auto int lotherend; 130 struct sockaddr_in otherend; 131 132 /* wait for a connection */ 133 register int pid; 134 135 do 136 { 137 errno = 0; 138 lotherend = sizeof otherend; 139 t = accept(DaemonSocket, &otherend, &lotherend, 0); 140 } while (t < 0 && errno == EINTR); 141 if (t < 0) 142 { 143 syserr("getrequests: accept"); 144 sleep(5); 145 continue; 146 } 147 148 /* 149 ** Create a subprocess to process the mail. 150 */ 151 152 # ifdef DEBUG 153 if (tTd(15, 2)) 154 printf("getrequests: forking (fd = %d)\n", t); 155 # endif DEBUG 156 157 pid = fork(); 158 if (pid < 0) 159 { 160 syserr("daemon: cannot fork"); 161 sleep(10); 162 (void) close(t); 163 continue; 164 } 165 166 if (pid == 0) 167 { 168 extern struct hostent *gethostbyaddr(); 169 register struct hostent *hp; 170 extern char *RealHostName; /* srvrsmtp.c */ 171 char buf[MAXNAME]; 172 173 /* 174 ** CHILD -- return to caller. 175 ** Collect verified idea of sending host. 176 ** Verify calling user id if possible here. 177 */ 178 179 /* determine host name */ 180 hp = gethostbyaddr(&otherend.sin_addr, sizeof otherend.sin_addr, AF_INET); 181 if (hp != NULL) 182 (void) sprintf(buf, "%s.ARPA", hp->h_name); 183 else 184 /* this should produce a dotted quad */ 185 (void) sprintf(buf, "%lx", otherend.sin_addr.s_addr); 186 RealHostName = newstr(buf); 187 188 (void) close(DaemonSocket); 189 InChannel = fdopen(t, "r"); 190 OutChannel = fdopen(t, "w"); 191 # ifdef DEBUG 192 if (tTd(15, 2)) 193 printf("getreq: returning\n"); 194 # endif DEBUG 195 # ifdef LOG 196 if (LogLevel > 11) 197 syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 198 # endif LOG 199 return; 200 } 201 202 /* 203 ** PARENT -- wait for child to terminate. 204 ** Perhaps we should allow concurrent processing? 205 */ 206 207 # ifdef DEBUG 208 if (tTd(15, 2)) 209 { 210 sleep(2); 211 printf("getreq: parent waiting\n"); 212 } 213 # endif DEBUG 214 215 /* close the port so that others will hang (for a while) */ 216 (void) close(t); 217 218 /* pick up old zombies */ 219 while (wait3(&status, WNOHANG, 0) > 0) 220 continue; 221 } 222 /*NOTREACHED*/ 223 } 224 /* 225 ** CLRDAEMON -- reset the daemon connection 226 ** 227 ** Parameters: 228 ** none. 229 ** 230 ** Returns: 231 ** none. 232 ** 233 ** Side Effects: 234 ** releases any resources used by the passive daemon. 235 */ 236 237 clrdaemon() 238 { 239 if (DaemonSocket >= 0) 240 (void) close(DaemonSocket); 241 DaemonSocket = -1; 242 } 243 /* 244 ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 245 ** 246 ** Parameters: 247 ** host -- the name of the host. 248 ** port -- the port number to connect to. 249 ** outfile -- a pointer to a place to put the outfile 250 ** descriptor. 251 ** infile -- ditto for infile. 252 ** 253 ** Returns: 254 ** An exit code telling whether the connection could be 255 ** made and if not why not. 256 ** 257 ** Side Effects: 258 ** none. 259 */ 260 261 makeconnection(host, port, outfile, infile) 262 char *host; 263 u_short port; 264 FILE **outfile; 265 FILE **infile; 266 { 267 register int s; 268 269 /* 270 ** Set up the address for the mailer. 271 ** Accept "[a.b.c.d]" syntax for host name. 272 */ 273 274 if (host[0] == '[') 275 { 276 long hid; 277 register char *p = index(host, ']'); 278 279 if (p != NULL) 280 { 281 *p = '\0'; 282 hid = inet_addr(&host[1]); 283 *p = ']'; 284 } 285 if (p == NULL || hid == -1) 286 { 287 usrerr("Invalid numeric domain spec \"%s\"", host); 288 return (EX_NOHOST); 289 } 290 SendmailAddress.sin_addr.s_addr = hid; 291 } 292 else 293 { 294 register struct hostent *hp = gethostbyname(host); 295 296 if (hp == NULL) 297 return (EX_NOHOST); 298 bmove(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 299 } 300 301 /* 302 ** Determine the port number. 303 */ 304 305 if (port != 0) 306 SendmailAddress.sin_port = htons(port); 307 else 308 { 309 register struct servent *sp = getservbyname("smtp", "tcp"); 310 311 if (sp == NULL) 312 { 313 syserr("makeconnection: server \"smtp\" unknown"); 314 return (EX_OSFILE); 315 } 316 SendmailAddress.sin_port = sp->s_port; 317 } 318 319 /* 320 ** Try to actually open the connection. 321 */ 322 323 # ifdef DEBUG 324 if (tTd(16, 1)) 325 printf("makeconnection (%s)\n", host); 326 # endif DEBUG 327 328 s = socket(AF_INET, SOCK_STREAM, 0, 0); 329 if (s < 0) 330 { 331 syserr("makeconnection: no socket"); 332 goto failure; 333 } 334 335 # ifdef DEBUG 336 if (tTd(16, 1)) 337 printf("makeconnection: %d\n", s); 338 339 /* turn on network debugging? */ 340 if (tTd(16, 14)) 341 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0); 342 # endif DEBUG 343 (void) fflush(CurEnv->e_xfp); /* for debugging */ 344 SendmailAddress.sin_family = AF_INET; 345 if (connect(s, &SendmailAddress, sizeof SendmailAddress, 0) < 0) 346 { 347 /* failure, decide if temporary or not */ 348 failure: 349 switch (errno) 350 { 351 case EISCONN: 352 case ETIMEDOUT: 353 case EINPROGRESS: 354 case EALREADY: 355 case EADDRINUSE: 356 case EHOSTDOWN: 357 case ENETDOWN: 358 case ENETRESET: 359 case ENOBUFS: 360 case ECONNREFUSED: 361 case ECONNRESET: 362 case EHOSTUNREACH: 363 case ENETUNREACH: 364 /* there are others, I'm sure..... */ 365 return (EX_TEMPFAIL); 366 367 case EPERM: 368 /* why is this happening? */ 369 syserr("makeconnection: funny failure, addr=%lx, port=%x", 370 SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port); 371 /* explicit fall-through */ 372 373 default: 374 return (EX_UNAVAILABLE); 375 } 376 } 377 378 /* connection ok, put it into canonical form */ 379 *outfile = fdopen(s, "w"); 380 *infile = fdopen(s, "r"); 381 382 return (EX_OK); 383 } 384 /* 385 ** MYHOSTNAME -- return the name of this host. 386 ** 387 ** Parameters: 388 ** hostbuf -- a place to return the name of this host. 389 ** size -- the size of hostbuf. 390 ** 391 ** Returns: 392 ** A list of aliases for this host. 393 ** 394 ** Side Effects: 395 ** none. 396 */ 397 398 char ** 399 myhostname(hostbuf, size) 400 char hostbuf[]; 401 int size; 402 { 403 extern struct hostent *gethostbyname(); 404 struct hostent *hp; 405 auto int i = size; 406 register char *p; 407 408 gethostname(hostbuf, &i); 409 hp = gethostbyname(hostbuf); 410 for (p = hostbuf; *p != '\0'; p++) 411 if (islower(*p)) 412 *p -= 'a' - 'A'; 413 if (hp != NULL) 414 return (hp->h_aliases); 415 else 416 return (NULL); 417 } 418 419 # else DAEMON 420 421 /* 422 ** MYHOSTNAME -- stub version for case of no daemon code. 423 ** 424 ** Can't convert to upper case here because might be a UUCP name. 425 ** 426 ** Mark, you can change this to be anything you want...... 427 */ 428 429 char ** 430 myhostname(hostbuf, size) 431 char hostbuf[]; 432 int size; 433 { 434 register FILE *f; 435 436 hostbuf[0] = '\0'; 437 f = fopen("/usr/include/whoami", "r"); 438 if (f != NULL) 439 { 440 (void) fgets(hostbuf, size, f); 441 fixcrlf(hostbuf, TRUE); 442 (void) fclose(f); 443 } 444 return (NULL); 445 } 446 447 #endif DAEMON 448