1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #include <errno.h> 10 #include "sendmail.h" 11 12 #ifndef lint 13 #ifdef DAEMON 14 static char sccsid[] = "@(#)daemon.c 5.39 (Berkeley) 10/05/91 (with daemon mode)"; 15 #else 16 static char sccsid[] = "@(#)daemon.c 5.39 (Berkeley) 10/05/91 (without daemon mode)"; 17 #endif 18 #endif /* not lint */ 19 20 int la; /* load average */ 21 22 #ifdef DAEMON 23 24 # include <netdb.h> 25 # include <sys/signal.h> 26 # include <sys/wait.h> 27 # include <sys/time.h> 28 # include <sys/resource.h> 29 30 /* 31 ** DAEMON.C -- routines to use when running as a daemon. 32 ** 33 ** This entire file is highly dependent on the 4.2 BSD 34 ** interprocess communication primitives. No attempt has 35 ** been made to make this file portable to Version 7, 36 ** Version 6, MPX files, etc. If you should try such a 37 ** thing yourself, I recommend chucking the entire file 38 ** and starting from scratch. Basic semantics are: 39 ** 40 ** getrequests() 41 ** Opens a port and initiates a connection. 42 ** Returns in a child. Must set InChannel and 43 ** OutChannel appropriately. 44 ** clrdaemon() 45 ** Close any open files associated with getting 46 ** the connection; this is used when running the queue, 47 ** etc., to avoid having extra file descriptors during 48 ** the queue run and to avoid confusing the network 49 ** code (if it cares). 50 ** makeconnection(host, port, outfile, infile) 51 ** Make a connection to the named host on the given 52 ** port. Set *outfile and *infile to the files 53 ** appropriate for communication. Returns zero on 54 ** success, else an exit status describing the 55 ** error. 56 ** maphostname(hbuf, hbufsize) 57 ** Convert the entry in hbuf into a canonical form. It 58 ** may not be larger than hbufsize. 59 */ 60 /* 61 ** GETREQUESTS -- open mail IPC port and get requests. 62 ** 63 ** Parameters: 64 ** none. 65 ** 66 ** Returns: 67 ** none. 68 ** 69 ** Side Effects: 70 ** Waits until some interesting activity occurs. When 71 ** it does, a child is created to process it, and the 72 ** parent waits for completion. Return from this 73 ** routine is always in the child. The file pointers 74 ** "InChannel" and "OutChannel" should be set to point 75 ** to the communication channel. 76 */ 77 78 struct sockaddr_in SendmailAddress;/* internet address of sendmail */ 79 80 int DaemonSocket = -1; /* fd describing socket */ 81 char *NetName; /* name of home (local?) network */ 82 83 getrequests() 84 { 85 int t; 86 register struct servent *sp; 87 int on = 1; 88 extern void reapchild(); 89 90 /* 91 ** Set up the address for the mailer. 92 */ 93 94 sp = getservbyname("smtp", "tcp"); 95 if (sp == NULL) 96 { 97 syserr("server \"smtp\" unknown"); 98 goto severe; 99 } 100 SendmailAddress.sin_family = AF_INET; 101 SendmailAddress.sin_addr.s_addr = INADDR_ANY; 102 SendmailAddress.sin_port = sp->s_port; 103 104 /* 105 ** Try to actually open the connection. 106 */ 107 108 if (tTd(15, 1)) 109 printf("getrequests: port 0x%x\n", SendmailAddress.sin_port); 110 111 /* get a socket for the SMTP connection */ 112 DaemonSocket = socket(AF_INET, SOCK_STREAM, 0); 113 if (DaemonSocket < 0) 114 { 115 /* probably another daemon already */ 116 syserr("getrequests: can't create socket"); 117 severe: 118 # ifdef LOG 119 if (LogLevel > 0) 120 syslog(LOG_ALERT, "cannot get connection"); 121 # endif LOG 122 finis(); 123 } 124 125 /* turn on network debugging? */ 126 if (tTd(15, 15)) 127 (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); 128 129 (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on); 130 (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on); 131 132 if (bind(DaemonSocket, 133 (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0) 134 { 135 syserr("getrequests: cannot bind"); 136 (void) close(DaemonSocket); 137 goto severe; 138 } 139 if (listen(DaemonSocket, 10) < 0) 140 { 141 syserr("getrequests: cannot listen"); 142 (void) close(DaemonSocket); 143 goto severe; 144 } 145 146 (void) signal(SIGCHLD, reapchild); 147 148 if (tTd(15, 1)) 149 printf("getrequests: %d\n", DaemonSocket); 150 151 for (;;) 152 { 153 register int pid; 154 auto int lotherend; 155 extern int RefuseLA; 156 157 /* see if we are rejecting connections */ 158 while ((la = getla()) > RefuseLA) 159 { 160 setproctitle("rejecting connections: load average: %.2f", (double)la); 161 sleep(5); 162 } 163 164 /* wait for a connection */ 165 setproctitle("accepting connections"); 166 do 167 { 168 errno = 0; 169 lotherend = sizeof RealHostAddr; 170 t = accept(DaemonSocket, 171 (struct sockaddr *)&RealHostAddr, &lotherend); 172 } while (t < 0 && errno == EINTR); 173 if (t < 0) 174 { 175 syserr("getrequests: accept"); 176 sleep(5); 177 continue; 178 } 179 180 /* 181 ** Create a subprocess to process the mail. 182 */ 183 184 if (tTd(15, 2)) 185 printf("getrequests: forking (fd = %d)\n", t); 186 187 pid = fork(); 188 if (pid < 0) 189 { 190 syserr("daemon: cannot fork"); 191 sleep(10); 192 (void) close(t); 193 continue; 194 } 195 196 if (pid == 0) 197 { 198 extern struct hostent *gethostbyaddr(); 199 register struct hostent *hp; 200 char buf[MAXNAME]; 201 202 /* 203 ** CHILD -- return to caller. 204 ** Collect verified idea of sending host. 205 ** Verify calling user id if possible here. 206 */ 207 208 (void) signal(SIGCHLD, SIG_DFL); 209 210 /* determine host name */ 211 hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET); 212 if (hp != NULL) 213 (void) strcpy(buf, hp->h_name); 214 else 215 { 216 extern char *inet_ntoa(); 217 218 /* produce a dotted quad */ 219 (void) sprintf(buf, "[%s]", 220 inet_ntoa(RealHostAddr.sin_addr)); 221 } 222 223 /* should we check for illegal connection here? XXX */ 224 225 RealHostName = newstr(buf); 226 227 (void) close(DaemonSocket); 228 InChannel = fdopen(t, "r"); 229 OutChannel = fdopen(dup(t), "w"); 230 if (tTd(15, 2)) 231 printf("getreq: returning\n"); 232 # ifdef LOG 233 if (LogLevel > 11) 234 syslog(LOG_DEBUG, "connected, pid=%d", getpid()); 235 # endif LOG 236 return; 237 } 238 239 /* close the port so that others will hang (for a while) */ 240 (void) close(t); 241 } 242 /*NOTREACHED*/ 243 } 244 /* 245 ** CLRDAEMON -- reset the daemon connection 246 ** 247 ** Parameters: 248 ** none. 249 ** 250 ** Returns: 251 ** none. 252 ** 253 ** Side Effects: 254 ** releases any resources used by the passive daemon. 255 */ 256 257 clrdaemon() 258 { 259 if (DaemonSocket >= 0) 260 (void) close(DaemonSocket); 261 DaemonSocket = -1; 262 } 263 /* 264 ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. 265 ** 266 ** Parameters: 267 ** host -- the name of the host. 268 ** port -- the port number to connect to. 269 ** outfile -- a pointer to a place to put the outfile 270 ** descriptor. 271 ** infile -- ditto for infile. 272 ** 273 ** Returns: 274 ** An exit code telling whether the connection could be 275 ** made and if not why not. 276 ** 277 ** Side Effects: 278 ** none. 279 */ 280 281 makeconnection(host, port, outfile, infile) 282 char *host; 283 u_short port; 284 FILE **outfile; 285 FILE **infile; 286 { 287 register int i, s; 288 register struct hostent *hp = (struct hostent *)NULL; 289 extern char *inet_ntoa(); 290 int sav_errno; 291 #ifdef NAMED_BIND 292 extern int h_errno; 293 #endif 294 295 /* 296 ** Set up the address for the mailer. 297 ** Accept "[a.b.c.d]" syntax for host name. 298 */ 299 300 #ifdef NAMED_BIND 301 h_errno = 0; 302 #endif 303 errno = 0; 304 305 if (host[0] == '[') 306 { 307 long hid; 308 register char *p = index(host, ']'); 309 310 if (p != NULL) 311 { 312 *p = '\0'; 313 hid = inet_addr(&host[1]); 314 *p = ']'; 315 } 316 if (p == NULL || hid == -1) 317 { 318 usrerr("Invalid numeric domain spec \"%s\"", host); 319 return (EX_NOHOST); 320 } 321 SendmailAddress.sin_addr.s_addr = hid; 322 } 323 else 324 { 325 hp = gethostbyname(host); 326 if (hp == NULL) 327 { 328 #ifdef NAMED_BIND 329 if (errno == ETIMEDOUT || h_errno == TRY_AGAIN) 330 return (EX_TEMPFAIL); 331 332 /* if name server is specified, assume temp fail */ 333 if (errno == ECONNREFUSED && UseNameServer) 334 return (EX_TEMPFAIL); 335 #endif 336 337 /* 338 ** XXX Should look for mail forwarder record here 339 ** XXX if (h_errno == NO_ADDRESS). 340 */ 341 342 return (EX_NOHOST); 343 } 344 bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length); 345 i = 1; 346 } 347 348 /* 349 ** Determine the port number. 350 */ 351 352 if (port != 0) 353 SendmailAddress.sin_port = htons(port); 354 else 355 { 356 register struct servent *sp = getservbyname("smtp", "tcp"); 357 358 if (sp == NULL) 359 { 360 syserr("makeconnection: server \"smtp\" unknown"); 361 return (EX_OSFILE); 362 } 363 SendmailAddress.sin_port = sp->s_port; 364 } 365 366 /* 367 ** Try to actually open the connection. 368 */ 369 370 again: 371 if (tTd(16, 1)) 372 printf("makeconnection (%s [%s])\n", host, 373 inet_ntoa(SendmailAddress.sin_addr)); 374 375 s = socket(AF_INET, SOCK_STREAM, 0); 376 if (s < 0) 377 { 378 syserr("makeconnection: no socket"); 379 sav_errno = errno; 380 goto failure; 381 } 382 383 if (tTd(16, 1)) 384 printf("makeconnection: %d\n", s); 385 386 /* turn on network debugging? */ 387 if (tTd(16, 14)) 388 { 389 int on = 1; 390 (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); 391 } 392 if (CurEnv->e_xfp != NULL) 393 (void) fflush(CurEnv->e_xfp); /* for debugging */ 394 errno = 0; /* for debugging */ 395 SendmailAddress.sin_family = AF_INET; 396 if (connect(s, 397 (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0) 398 { 399 sav_errno = errno; 400 (void) close(s); 401 if (hp && hp->h_addr_list[i]) 402 { 403 bcopy(hp->h_addr_list[i++], 404 (char *)&SendmailAddress.sin_addr, hp->h_length); 405 goto again; 406 } 407 408 /* failure, decide if temporary or not */ 409 failure: 410 switch (sav_errno) 411 { 412 case EISCONN: 413 case ETIMEDOUT: 414 case EINPROGRESS: 415 case EALREADY: 416 case EADDRINUSE: 417 case EHOSTDOWN: 418 case ENETDOWN: 419 case ENETRESET: 420 case ENOBUFS: 421 case ECONNREFUSED: 422 case ECONNRESET: 423 case EHOSTUNREACH: 424 case ENETUNREACH: 425 /* there are others, I'm sure..... */ 426 return (EX_TEMPFAIL); 427 428 case EPERM: 429 /* why is this happening? */ 430 syserr("makeconnection: funny failure, addr=%lx, port=%x", 431 SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port); 432 return (EX_TEMPFAIL); 433 434 default: 435 { 436 extern char *errstring(); 437 438 message(Arpa_Info, "%s", errstring(sav_errno)); 439 return (EX_UNAVAILABLE); 440 } 441 } 442 } 443 444 /* connection ok, put it into canonical form */ 445 *outfile = fdopen(s, "w"); 446 *infile = fdopen(dup(s), "r"); 447 448 return (EX_OK); 449 } 450 /* 451 ** MYHOSTNAME -- return the name of this host. 452 ** 453 ** Parameters: 454 ** hostbuf -- a place to return the name of this host. 455 ** size -- the size of hostbuf. 456 ** 457 ** Returns: 458 ** A list of aliases for this host. 459 ** 460 ** Side Effects: 461 ** none. 462 */ 463 464 char ** 465 myhostname(hostbuf, size) 466 char hostbuf[]; 467 int size; 468 { 469 extern struct hostent *gethostbyname(); 470 struct hostent *hp; 471 472 if (gethostname(hostbuf, size) < 0) 473 { 474 (void) strcpy(hostbuf, "localhost"); 475 } 476 hp = gethostbyname(hostbuf); 477 if (hp != NULL) 478 { 479 (void) strcpy(hostbuf, hp->h_name); 480 return (hp->h_aliases); 481 } 482 else 483 return (NULL); 484 } 485 /* 486 * MAPHOSTNAME -- turn a hostname into canonical form 487 * 488 * Parameters: 489 * hbuf -- a buffer containing a hostname. 490 * hbsize -- the size of hbuf. 491 * 492 * Returns: 493 * TRUE if the host name was mapped. 494 * FALSE otherwise. 495 * 496 * Side Effects: 497 * Looks up the host specified in hbuf. If it is not 498 * the canonical name for that host, replace it with 499 * the canonical name. If the name is unknown, or it 500 * is already the canonical name, leave it unchanged. 501 */ 502 503 bool 504 maphostname(hbuf, hbsize) 505 char *hbuf; 506 int hbsize; 507 { 508 register struct hostent *hp; 509 u_long in_addr; 510 char ptr[256], *cp; 511 struct hostent *gethostbyaddr(); 512 513 /* 514 * If first character is a bracket, then it is an address 515 * lookup. Address is copied into a temporary buffer to 516 * strip the brackets and to preserve hbuf if address is 517 * unknown. 518 */ 519 if (*hbuf != '[') 520 return (getcanonname(hbuf, hbsize)); 521 if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL) 522 return (FALSE); 523 *cp = '\0'; 524 in_addr = inet_addr(&ptr[1]); 525 hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET); 526 if (hp == NULL) 527 return (FALSE); 528 if (strlen(hp->h_name) >= hbsize) 529 hp->h_name[hbsize - 1] = '\0'; 530 (void)strcpy(hbuf, hp->h_name); 531 return (TRUE); 532 } 533 534 # else DAEMON 535 /* code for systems without sophisticated networking */ 536 537 /* 538 ** MYHOSTNAME -- stub version for case of no daemon code. 539 ** 540 ** Can't convert to upper case here because might be a UUCP name. 541 ** 542 ** Mark, you can change this to be anything you want...... 543 */ 544 545 char ** 546 myhostname(hostbuf, size) 547 char hostbuf[]; 548 int size; 549 { 550 register FILE *f; 551 552 hostbuf[0] = '\0'; 553 f = fopen("/usr/include/whoami", "r"); 554 if (f != NULL) 555 { 556 (void) fgets(hostbuf, size, f); 557 fixcrlf(hostbuf, TRUE); 558 (void) fclose(f); 559 } 560 return (NULL); 561 } 562 /* 563 ** MAPHOSTNAME -- turn a hostname into canonical form 564 ** 565 ** Parameters: 566 ** hbuf -- a buffer containing a hostname. 567 ** hbsize -- the size of hbuf. 568 ** 569 ** Returns: 570 ** TRUE if the hostname was mapped. 571 ** FALSE otherwise. 572 ** 573 ** Side Effects: 574 ** Looks up the host specified in hbuf. If it is not 575 ** the canonical name for that host, replace it with 576 ** the canonical name. If the name is unknown, or it 577 ** is already the canonical name, leave it unchanged. 578 */ 579 580 /*ARGSUSED*/ 581 bool 582 maphostname(hbuf, hbsize) 583 char *hbuf; 584 int hbsize; 585 { 586 return (FALSE); 587 } 588 589 #endif DAEMON 590