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