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