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