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