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