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