1 /*- 2 * Copyright (c) 1983, 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/filio.h> 31 #include <sys/ioccom.h> 32 #include <sys/param.h> 33 #include <sys/stat.h> 34 #include <sys/socket.h> 35 #include <sys/sysctl.h> 36 #include <sys/ucred.h> 37 #include <sys/uio.h> 38 #include <sys/utsname.h> 39 40 #include <ctype.h> 41 #include <err.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <limits.h> 45 #include <pwd.h> 46 #include <signal.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <sysexits.h> 50 #include <syslog.h> 51 #include <unistd.h> 52 53 #include "inetd.h" 54 55 void chargen_dg(int, struct servtab *); 56 void chargen_stream(int, struct servtab *); 57 void daytime_dg(int, struct servtab *); 58 void daytime_stream(int, struct servtab *); 59 void discard_dg(int, struct servtab *); 60 void discard_stream(int, struct servtab *); 61 void echo_dg(int, struct servtab *); 62 void echo_stream(int, struct servtab *); 63 static int getline(int, char *, int); 64 void iderror(int, int, int, const char *); 65 void ident_stream(int, struct servtab *); 66 void initring(void); 67 uint32_t machtime(void); 68 void machtime_dg(int, struct servtab *); 69 void machtime_stream(int, struct servtab *); 70 71 char ring[128]; 72 char *endring; 73 74 75 struct biltin biltins[] = { 76 /* Echo received data */ 77 { "echo", SOCK_STREAM, 1, -1, echo_stream }, 78 { "echo", SOCK_DGRAM, 0, 1, echo_dg }, 79 80 /* Internet /dev/null */ 81 { "discard", SOCK_STREAM, 1, -1, discard_stream }, 82 { "discard", SOCK_DGRAM, 0, 1, discard_dg }, 83 84 /* Return 32 bit time since 1900 */ 85 { "time", SOCK_STREAM, 0, -1, machtime_stream }, 86 { "time", SOCK_DGRAM, 0, 1, machtime_dg }, 87 88 /* Return human-readable time */ 89 { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, 90 { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, 91 92 /* Familiar character generator */ 93 { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, 94 { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, 95 96 { "tcpmux", SOCK_STREAM, 1, -1, (bi_fn_t *)tcpmux }, 97 98 { "auth", SOCK_STREAM, 1, -1, ident_stream }, 99 100 { NULL, 0, 0, 0, NULL } 101 }; 102 103 /* 104 * RFC864 Character Generator Protocol. Generates character data without 105 * any regard for input. 106 */ 107 108 void 109 initring(void) 110 { 111 int i; 112 113 endring = ring; 114 115 for (i = 0; i <= 128; ++i) 116 if (isprint(i)) 117 *endring++ = i; 118 } 119 120 /* Character generator 121 * The RFC says that we should send back a random number of 122 * characters chosen from the range 0 to 512. We send LINESIZ+2. 123 */ 124 /* ARGSUSED */ 125 void 126 chargen_dg(int s, struct servtab *sep) 127 { 128 struct sockaddr_storage ss; 129 static char *rs; 130 int len; 131 socklen_t size; 132 char text[LINESIZ+2]; 133 134 if (endring == 0) { 135 initring(); 136 rs = ring; 137 } 138 139 size = sizeof(ss); 140 if (recvfrom(s, text, sizeof(text), 0, 141 (struct sockaddr *)&ss, &size) < 0) 142 return; 143 144 if (check_loop((struct sockaddr *)&ss, sep)) 145 return; 146 147 if ((len = endring - rs) >= LINESIZ) 148 memmove(text, rs, LINESIZ); 149 else { 150 memmove(text, rs, len); 151 memmove(text + len, ring, LINESIZ - len); 152 } 153 if (++rs == endring) 154 rs = ring; 155 text[LINESIZ] = '\r'; 156 text[LINESIZ + 1] = '\n'; 157 (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size); 158 } 159 160 /* Character generator */ 161 /* ARGSUSED */ 162 void 163 chargen_stream(int s, struct servtab *sep) 164 { 165 int len; 166 char *rs, text[LINESIZ+2]; 167 168 inetd_setproctitle(sep->se_service, s); 169 170 if (!endring) { 171 initring(); 172 rs = ring; 173 } 174 175 text[LINESIZ] = '\r'; 176 text[LINESIZ + 1] = '\n'; 177 for (rs = ring;;) { 178 if ((len = endring - rs) >= LINESIZ) 179 memmove(text, rs, LINESIZ); 180 else { 181 memmove(text, rs, len); 182 memmove(text + len, ring, LINESIZ - len); 183 } 184 if (++rs == endring) 185 rs = ring; 186 if (write(s, text, sizeof(text)) != sizeof(text)) 187 break; 188 } 189 exit(0); 190 } 191 192 /* 193 * RFC867 Daytime Protocol. Sends the current date and time as an ascii 194 * character string without any regard for input. 195 */ 196 197 /* Return human-readable time of day */ 198 /* ARGSUSED */ 199 void 200 daytime_dg(int s, struct servtab *sep) 201 { 202 char buffer[256]; 203 time_t now; 204 struct sockaddr_storage ss; 205 socklen_t size; 206 207 now = time((time_t *) 0); 208 209 size = sizeof(ss); 210 if (recvfrom(s, buffer, sizeof(buffer), 0, 211 (struct sockaddr *)&ss, &size) < 0) 212 return; 213 214 if (check_loop((struct sockaddr *)&ss, sep)) 215 return; 216 217 (void) sprintf(buffer, "%.24s\r\n", ctime(&now)); 218 (void) sendto(s, buffer, strlen(buffer), 0, 219 (struct sockaddr *)&ss, size); 220 } 221 222 /* Return human-readable time of day */ 223 /* ARGSUSED */ 224 void 225 daytime_stream(int s, struct servtab *sep __unused) 226 { 227 char buffer[256]; 228 time_t now; 229 230 now = time((time_t *) 0); 231 232 (void) sprintf(buffer, "%.24s\r\n", ctime(&now)); 233 (void) send(s, buffer, strlen(buffer), MSG_EOF); 234 } 235 236 /* 237 * RFC863 Discard Protocol. Any data received is thrown away and no response 238 * is sent. 239 */ 240 241 /* Discard service -- ignore data */ 242 /* ARGSUSED */ 243 void 244 discard_dg(int s, struct servtab *sep __unused) 245 { 246 char buffer[BUFSIZE]; 247 248 (void) read(s, buffer, sizeof(buffer)); 249 } 250 251 /* Discard service -- ignore data */ 252 /* ARGSUSED */ 253 void 254 discard_stream(int s, struct servtab *sep) 255 { 256 int ret; 257 char buffer[BUFSIZE]; 258 259 inetd_setproctitle(sep->se_service, s); 260 while (1) { 261 while ((ret = read(s, buffer, sizeof(buffer))) > 0) 262 ; 263 if (ret == 0 || errno != EINTR) 264 break; 265 } 266 exit(0); 267 } 268 269 /* 270 * RFC862 Echo Protocol. Any data received is sent back to the sender as 271 * received. 272 */ 273 274 /* Echo service -- echo data back */ 275 /* ARGSUSED */ 276 void 277 echo_dg(int s, struct servtab *sep) 278 { 279 char buffer[65536]; /* Should be sizeof(max datagram). */ 280 int i; 281 socklen_t size; 282 struct sockaddr_storage ss; 283 284 size = sizeof(ss); 285 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 286 (struct sockaddr *)&ss, &size)) < 0) 287 return; 288 289 if (check_loop((struct sockaddr *)&ss, sep)) 290 return; 291 292 (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size); 293 } 294 295 /* Echo service -- echo data back */ 296 /* ARGSUSED */ 297 void 298 echo_stream(int s, struct servtab *sep) 299 { 300 char buffer[BUFSIZE]; 301 int i; 302 303 inetd_setproctitle(sep->se_service, s); 304 while ((i = read(s, buffer, sizeof(buffer))) > 0 && 305 write(s, buffer, i) > 0) 306 ; 307 exit(0); 308 } 309 310 /* 311 * RFC1413 Identification Protocol. Given a TCP port number pair, return a 312 * character string which identifies the owner of that connection on the 313 * server's system. Extended to allow for ~/.fakeid support and ~/.noident 314 * support. 315 */ 316 317 /* RFC 1413 says the following are the only errors you can return. */ 318 #define ID_INVALID "INVALID-PORT" /* Port number improperly specified. */ 319 #define ID_NOUSER "NO-USER" /* Port not in use/not identifable. */ 320 #define ID_HIDDEN "HIDDEN-USER" /* Hiden at user's request. */ 321 #define ID_UNKNOWN "UNKNOWN-ERROR" /* Everything else. */ 322 323 /* Generic ident_stream error-sending func */ 324 /* ARGSUSED */ 325 void 326 iderror(int lport, int fport, int s, const char *er) 327 { 328 char *p; 329 330 asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er); 331 if (p == NULL) { 332 syslog(LOG_ERR, "asprintf: %m"); 333 exit(EX_OSERR); 334 } 335 send(s, p, strlen(p), MSG_EOF); 336 free(p); 337 338 exit(0); 339 } 340 341 /* Ident service (AKA "auth") */ 342 /* ARGSUSED */ 343 void 344 ident_stream(int s, struct servtab *sep) 345 { 346 struct utsname un; 347 struct stat sb; 348 struct sockaddr_in sin4[2]; 349 #ifdef INET6 350 struct sockaddr_in6 sin6[2]; 351 #endif 352 struct sockaddr_storage ss[2]; 353 struct xucred uc; 354 struct timeval tv = { 355 10, 356 0 357 }, to; 358 struct passwd *pw = NULL; 359 fd_set fdset; 360 char buf[BUFSIZE], *p, **av, *osname = NULL, e; 361 char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */ 362 socklen_t socklen; 363 ssize_t ssize; 364 size_t size, bufsiz; 365 int c, fflag = 0, nflag = 0, rflag = 0, argc = 0; 366 int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen; 367 u_short lport, fport; 368 369 inetd_setproctitle(sep->se_service, s); 370 /* 371 * Reset getopt() since we are a fork() but not an exec() from 372 * a parent which used getopt() already. 373 */ 374 optind = 1; 375 optreset = 1; 376 /* 377 * Take the internal argument vector and count it out to make an 378 * argument count for getopt. This can be used for any internal 379 * service to read arguments and use getopt() easily. 380 */ 381 for (av = sep->se_argv; *av; av++) 382 argc++; 383 if (argc) { 384 int sec, usec; 385 size_t i; 386 u_int32_t rnd32; 387 388 while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1) 389 switch (c) { 390 case 'd': 391 if (!gflag) 392 strlcpy(idbuf, optarg, sizeof(idbuf)); 393 break; 394 case 'f': 395 fflag = 1; 396 break; 397 case 'F': 398 fflag = 1; 399 Fflag=1; 400 break; 401 case 'g': 402 gflag = 1; 403 rnd32 = 0; /* Shush, compiler. */ 404 /* 405 * The number of bits in "rnd32" divided 406 * by the number of bits needed per iteration 407 * gives a more optimal way to reload the 408 * random number only when necessary. 409 * 410 * 32 bits from arc4random corresponds to 411 * about 6 base-36 digits, so we reseed evey 6. 412 */ 413 for (i = 0; i < sizeof(idbuf) - 1; i++) { 414 static const char *const base36 = 415 "0123456789" 416 "abcdefghijklmnopqrstuvwxyz"; 417 if (i % 6 == 0) 418 rnd32 = arc4random(); 419 idbuf[i] = base36[rnd32 % 36]; 420 rnd32 /= 36; 421 } 422 idbuf[i] = '\0'; 423 break; 424 case 'i': 425 iflag = 1; 426 break; 427 case 'n': 428 nflag = 1; 429 break; 430 case 'o': 431 osname = optarg; 432 break; 433 case 'r': 434 rflag = 1; 435 break; 436 case 't': 437 switch (sscanf(optarg, "%d.%d", &sec, &usec)) { 438 case 2: 439 tv.tv_usec = usec; 440 /* FALLTHROUGH */ 441 case 1: 442 tv.tv_sec = sec; 443 break; 444 default: 445 if (debug) 446 warnx("bad -t argument"); 447 break; 448 } 449 break; 450 default: 451 break; 452 } 453 } 454 if (osname == NULL) { 455 if (uname(&un) == -1) 456 iderror(0, 0, s, ID_UNKNOWN); 457 osname = un.sysname; 458 } 459 460 /* 461 * We're going to prepare for and execute reception of a 462 * packet of data from the user. The data is in the format 463 * "local_port , foreign_port\r\n" (with local being the 464 * server's port and foreign being the client's.) 465 */ 466 gettimeofday(&to, NULL); 467 to.tv_sec += tv.tv_sec; 468 to.tv_usec += tv.tv_usec; 469 if (to.tv_usec >= 1000000) { 470 to.tv_usec -= 1000000; 471 to.tv_sec++; 472 } 473 474 size = 0; 475 bufsiz = sizeof(buf) - 1; 476 FD_ZERO(&fdset); 477 while (bufsiz > 0) { 478 gettimeofday(&tv, NULL); 479 tv.tv_sec = to.tv_sec - tv.tv_sec; 480 tv.tv_usec = to.tv_usec - tv.tv_usec; 481 if (tv.tv_usec < 0) { 482 tv.tv_usec += 1000000; 483 tv.tv_sec--; 484 } 485 if (tv.tv_sec < 0) 486 break; 487 FD_SET(s, &fdset); 488 if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 489 iderror(0, 0, s, ID_UNKNOWN); 490 if (ioctl(s, FIONREAD, &onreadlen) == -1) 491 iderror(0, 0, s, ID_UNKNOWN); 492 if ((size_t)onreadlen > bufsiz) 493 onreadlen = bufsiz; 494 ssize = read(s, &buf[size], (size_t)onreadlen); 495 if (ssize == -1) 496 iderror(0, 0, s, ID_UNKNOWN); 497 else if (ssize == 0) 498 break; 499 bufsiz -= ssize; 500 size += ssize; 501 if (memchr(&buf[size - ssize], '\n', ssize) != NULL) 502 break; 503 } 504 buf[size] = '\0'; 505 /* Read two characters, and check for a delimiting character */ 506 if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e)) 507 iderror(0, 0, s, ID_INVALID); 508 509 /* Send garbage? */ 510 if (gflag) 511 goto printit; 512 513 /* 514 * If not "real" (-r), send a HIDDEN-USER error for everything. 515 * If -d is used to set a fallback username, this is used to 516 * override it, and the fallback is returned instead. 517 */ 518 if (!rflag) { 519 if (*idbuf == '\0') 520 iderror(lport, fport, s, ID_HIDDEN); 521 goto printit; 522 } 523 524 /* 525 * We take the input and construct an array of two sockaddr_ins 526 * which contain the local address information and foreign 527 * address information, respectively, used to look up the 528 * credentials for the socket (which are returned by the 529 * sysctl "net.inet.tcp.getcred" when we call it.) 530 */ 531 socklen = sizeof(ss[0]); 532 if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) 533 iderror(lport, fport, s, ID_UNKNOWN); 534 socklen = sizeof(ss[1]); 535 if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) 536 iderror(lport, fport, s, ID_UNKNOWN); 537 if (ss[0].ss_family != ss[1].ss_family) 538 iderror(lport, fport, s, ID_UNKNOWN); 539 size = sizeof(uc); 540 switch (ss[0].ss_family) { 541 case AF_INET: 542 sin4[0] = *(struct sockaddr_in *)&ss[0]; 543 sin4[0].sin_port = htons(lport); 544 sin4[1] = *(struct sockaddr_in *)&ss[1]; 545 sin4[1].sin_port = htons(fport); 546 if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin4, 547 sizeof(sin4)) == -1) 548 getcredfail = errno; 549 break; 550 #ifdef INET6 551 case AF_INET6: 552 sin6[0] = *(struct sockaddr_in6 *)&ss[0]; 553 sin6[0].sin6_port = htons(lport); 554 sin6[1] = *(struct sockaddr_in6 *)&ss[1]; 555 sin6[1].sin6_port = htons(fport); 556 if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6, 557 sizeof(sin6)) == -1) 558 getcredfail = errno; 559 break; 560 #endif 561 default: /* should not reach here */ 562 getcredfail = EAFNOSUPPORT; 563 break; 564 } 565 if (getcredfail != 0 || uc.cr_version != XUCRED_VERSION) { 566 if (*idbuf == '\0') 567 iderror(lport, fport, s, 568 getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN); 569 goto printit; 570 } 571 572 /* Look up the pw to get the username and home directory*/ 573 errno = 0; 574 pw = getpwuid(uc.cr_uid); 575 if (pw == NULL) 576 iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN); 577 578 if (iflag) 579 snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid); 580 else 581 strlcpy(idbuf, pw->pw_name, sizeof(idbuf)); 582 583 /* 584 * If enabled, we check for a file named ".noident" in the user's 585 * home directory. If found, we return HIDDEN-USER. 586 */ 587 if (nflag) { 588 if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) 589 iderror(lport, fport, s, ID_UNKNOWN); 590 if (lstat(p, &sb) == 0) { 591 free(p); 592 iderror(lport, fport, s, ID_HIDDEN); 593 } 594 free(p); 595 } 596 597 /* 598 * Here, if enabled, we read a user's ".fakeid" file in their 599 * home directory. It consists of a line containing the name 600 * they want. 601 */ 602 if (fflag) { 603 int fakeid_fd; 604 605 /* 606 * Here we set ourself to effectively be the user, so we don't 607 * open any files we have no permission to open, especially 608 * symbolic links to sensitive root-owned files or devices. 609 */ 610 if (initgroups(pw->pw_name, pw->pw_gid) == -1) 611 iderror(lport, fport, s, ID_UNKNOWN); 612 if (seteuid(pw->pw_uid) == -1) 613 iderror(lport, fport, s, ID_UNKNOWN); 614 /* 615 * We can't stat() here since that would be a race 616 * condition. 617 * Therefore, we open the file we have permissions to open 618 * and if it's not a regular file, we close it and end up 619 * returning the user's real username. 620 */ 621 if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) 622 iderror(lport, fport, s, ID_UNKNOWN); 623 fakeid_fd = open(p, O_RDONLY | O_NONBLOCK); 624 free(p); 625 if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 || 626 !S_ISREG(sb.st_mode)) 627 goto fakeid_fail; 628 629 if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0) 630 goto fakeid_fail; 631 buf[ssize] = '\0'; 632 633 /* 634 * Usually, the file will have the desired identity 635 * in the form "identity\n". Allow for leading white 636 * space and trailing white space/end of line. 637 */ 638 p = buf; 639 p += strspn(p, " \t"); 640 p[strcspn(p, " \t\r\n")] = '\0'; 641 if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */ 642 p[MAXLOGNAME - 1] = '\0'; 643 644 /* 645 * If the name is a zero-length string or matches it 646 * the id or name of another user (unless permitted by -F) 647 * then it is invalid. 648 */ 649 if (*p == '\0') 650 goto fakeid_fail; 651 if (!Fflag) { 652 if (iflag) { 653 if (p[strspn(p, "0123456789")] == '\0' && 654 getpwuid(atoi(p)) != NULL) 655 goto fakeid_fail; 656 } else { 657 if (getpwnam(p) != NULL) 658 goto fakeid_fail; 659 } 660 } 661 662 strlcpy(idbuf, p, sizeof(idbuf)); 663 664 fakeid_fail: 665 if (fakeid_fd != -1) 666 close(fakeid_fd); 667 } 668 669 printit: 670 /* Finally, we make and send the reply. */ 671 if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, 672 idbuf) == -1) { 673 syslog(LOG_ERR, "asprintf: %m"); 674 exit(EX_OSERR); 675 } 676 send(s, p, strlen(p), MSG_EOF); 677 free(p); 678 679 exit(0); 680 } 681 682 /* 683 * RFC738/868 Time Server. 684 * Return a machine readable date and time, in the form of the 685 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 686 * returns the number of seconds since midnight, Jan 1, 1970, 687 * we must add 2208988800 seconds to this figure to make up for 688 * some seventy years Bell Labs was asleep. 689 */ 690 691 uint32_t 692 machtime(void) 693 { 694 695 #define OFFSET ((uint32_t)25567 * 24*60*60) 696 return (htonl((uint32_t)(time(NULL) + OFFSET))); 697 #undef OFFSET 698 } 699 700 /* ARGSUSED */ 701 void 702 machtime_dg(int s, struct servtab *sep) 703 { 704 uint32_t result; 705 struct sockaddr_storage ss; 706 socklen_t size; 707 708 size = sizeof(ss); 709 if (recvfrom(s, (char *)&result, sizeof(result), 0, 710 (struct sockaddr *)&ss, &size) < 0) 711 return; 712 713 if (check_loop((struct sockaddr *)&ss, sep)) 714 return; 715 716 result = machtime(); 717 (void) sendto(s, (char *) &result, sizeof(result), 0, 718 (struct sockaddr *)&ss, size); 719 } 720 721 /* ARGSUSED */ 722 void 723 machtime_stream(int s, struct servtab *sep __unused) 724 { 725 uint32_t result; 726 727 result = machtime(); 728 (void) send(s, (char *) &result, sizeof(result), MSG_EOF); 729 } 730 731 /* 732 * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to 733 * services based on the service name sent. 734 * 735 * Based on TCPMUX.C by Mark K. Lottor November 1988 736 * sri-nic::ps:<mkl>tcpmux.c 737 */ 738 739 #define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 740 #define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 741 742 static int /* # of characters up to \r,\n or \0 */ 743 getline(int fd, char *buf, int len) 744 { 745 int count = 0, n; 746 struct sigaction sa; 747 748 sa.sa_flags = 0; 749 sigemptyset(&sa.sa_mask); 750 sa.sa_handler = SIG_DFL; 751 sigaction(SIGALRM, &sa, (struct sigaction *)0); 752 do { 753 alarm(10); 754 n = read(fd, buf, len-count); 755 alarm(0); 756 if (n == 0) 757 return (count); 758 if (n < 0) 759 return (-1); 760 while (--n >= 0) { 761 if (*buf == '\r' || *buf == '\n' || *buf == '\0') 762 return (count); 763 count++; 764 buf++; 765 } 766 } while (count < len); 767 return (count); 768 } 769 770 struct servtab * 771 tcpmux(int s) 772 { 773 struct servtab *sep; 774 char service[MAX_SERV_LEN+1]; 775 int len; 776 777 /* Get requested service name */ 778 if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 779 strwrite(s, "-Error reading service name\r\n"); 780 return (NULL); 781 } 782 service[len] = '\0'; 783 784 if (debug) 785 warnx("tcpmux: someone wants %s", service); 786 787 /* 788 * Help is a required command, and lists available services, 789 * one per line. 790 */ 791 if (!strcasecmp(service, "help")) { 792 for (sep = servtab; sep; sep = sep->se_next) { 793 if (!ISMUX(sep)) 794 continue; 795 (void)write(s,sep->se_service,strlen(sep->se_service)); 796 strwrite(s, "\r\n"); 797 } 798 return (NULL); 799 } 800 801 /* Try matching a service in inetd.conf with the request */ 802 for (sep = servtab; sep; sep = sep->se_next) { 803 if (!ISMUX(sep)) 804 continue; 805 if (!strcasecmp(service, sep->se_service)) { 806 if (ISMUXPLUS(sep)) { 807 strwrite(s, "+Go\r\n"); 808 } 809 return (sep); 810 } 811 } 812 strwrite(s, "-Service not available\r\n"); 813 return (NULL); 814 } 815