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