xref: /freebsd/usr.sbin/inetd/builtins.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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 int check_loop __P((struct sockaddr *, struct servtab *sep));
62 void inetd_setproctitle __P((char *, int));
63 
64 struct 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 
97 void
98 initring()
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 */
110 void
111 chargen_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 */
148 void
149 chargen_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 */
186 void
187 daytime_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 */
212 void
213 daytime_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 */
232 void
233 discard_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 */
243 void
244 discard_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 */
267 void
268 echo_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 */
289 void
290 echo_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 */
312 void
313 iderror(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 */
331 void
332 ident_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 	if ((to.tv_usec += tv.tv_usec) >= 1000000) {
457 		to.tv_usec -= 1000000;
458 		to.tv_sec++;
459 	}
460 
461 	size = 0;
462 	bufsiz = sizeof(buf) - 1;
463 	FD_ZERO(&fdset);
464  	while (bufsiz > 0 && (size == 0 || buf[size - 1] != '\n')) {
465 		gettimeofday(&tv, NULL);
466 		tv.tv_sec = to.tv_sec - tv.tv_sec;
467 		tv.tv_usec = to.tv_usec - tv.tv_usec;
468 		if (tv.tv_usec < 0) {
469 			tv.tv_usec += 1000000;
470 			tv.tv_sec--;
471 		}
472 		if (tv.tv_sec < 0)
473 			break;
474 		FD_SET(s, &fdset);
475 		if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
476 			iderror(0, 0, s, errno);
477 		if (ioctl(s, FIONREAD, &onreadlen) == -1)
478 			iderror(0, 0, s, errno);
479 		if (onreadlen > bufsiz)
480 			onreadlen = bufsiz;
481 		ssize = read(s, &buf[size], (size_t)onreadlen);
482 		if (ssize == -1)
483 			iderror(0, 0, s, errno);
484 		else if (ssize == 0)
485 			break;
486 		bufsiz -= ssize;
487 		size += ssize;
488  	}
489 	buf[size] = '\0';
490 	/* Read two characters, and check for a delimiting character */
491 	if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
492 		iderror(0, 0, s, 0);
493 	if (gflag) {
494 		cp = garbage;
495 		goto printit;
496 	}
497 
498 	/*
499 	 * If not "real" (-r), send a HIDDEN-USER error for everything.
500 	 * If -d is used to set a fallback username, this is used to
501 	 * override it, and the fallback is returned instead.
502 	 */
503 	if (!rflag) {
504 		if (fallback == NULL)
505 			iderror(lport, fport, s, -1);
506 		else {
507 			cp = fallback;
508 			goto printit;
509 		}
510 	}
511 
512 	/*
513 	 * We take the input and construct an array of two sockaddr_ins
514 	 * which contain the local address information and foreign
515 	 * address information, respectively, used to look up the
516 	 * credentials for the socket (which are returned by the
517 	 * sysctl "net.inet.tcp.getcred" when we call it.) The
518 	 * arrays have been filled in above via get{peer,sock}name(),
519 	 * so right here we are only setting the ports.
520 	 */
521 	if (ss[0].ss_family != ss[1].ss_family)
522 		iderror(lport, fport, s, errno);
523 	size = sizeof(uc);
524 	switch (ss[0].ss_family) {
525 	case AF_INET:
526 		sin[0] = *(struct sockaddr_in *)&ss[0];
527 		sin[0].sin_port = htons(lport);
528 		sin[1] = *(struct sockaddr_in *)&ss[1];
529 		sin[1].sin_port = htons(fport);
530 		if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin,
531 				 sizeof(sin)) == -1)
532 			getcredfail = 1;
533 		break;
534 #ifdef INET6
535 	case AF_INET6:
536 		sin6[0] = *(struct sockaddr_in6 *)&ss[0];
537 		sin6[0].sin6_port = htons(lport);
538 		sin6[1] = *(struct sockaddr_in6 *)&ss[1];
539 		sin6[1].sin6_port = htons(fport);
540 		if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
541 				 sizeof(sin6)) == -1)
542 			getcredfail = 1;
543 		break;
544 #endif
545 	default: /* should not reach here */
546 		getcredfail = 1;
547 		break;
548 	}
549 	if (getcredfail != 0) {
550 		if (fallback == NULL)		/* Use a default, if asked to */
551 			iderror(lport, fport, s, errno);
552 		usedfallback = 1;
553 	} else {
554 		/* Look up the pw to get the username */
555 		pw = getpwuid(uc.cr_uid);
556 	}
557 	if (pw == NULL && !usedfallback)		/* No such user... */
558 		iderror(lport, fport, s, errno);
559 	/*
560 	 * If enabled, we check for a file named ".noident" in the user's
561 	 * home directory. If found, we return HIDDEN-USER.
562 	 */
563 	if (nflag && !usedfallback) {
564 		if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
565 			iderror(lport, fport, s, errno);
566 		if (lstat(p, &sb) == 0) {
567 			free(p);
568 			iderror(lport, fport, s, -1);
569 		}
570 		free(p);
571 	}
572 	/*
573 	 * Here, if enabled, we read a user's ".fakeid" file in their
574 	 * home directory. It consists of a line containing the name
575 	 * they want.
576 	 */
577 	if (fflag && !usedfallback) {
578 		FILE *fakeid = NULL;
579 		int fakeid_fd;
580 
581 		if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
582 			iderror(lport, fport, s, errno);
583 		/*
584 		 * Here we set ourself to effectively be the user, so we don't
585 		 * open any files we have no permission to open, especially
586 		 * symbolic links to sensitive root-owned files or devices.
587 		 */
588 		if (initgroups(pw->pw_name, pw->pw_gid) == -1)
589 			iderror(lport, fport, s, errno);
590 		seteuid(pw->pw_uid);
591 		/*
592 		 * If we were to lstat() here, it would do no good, since it
593 		 * would introduce a race condition and could be defeated.
594 		 * Therefore, we open the file we have permissions to open
595 		 * and if it's not a regular file, we close it and end up
596 		 * returning the user's real username.
597 		 */
598 		fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
599 		free(p);
600 		if ((fakeid = fdopen(fakeid_fd, "r")) != NULL &&
601 		    fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) {
602 			buf[sizeof(buf) - 1] = '\0';
603 			if (fgets(buf, sizeof(buf), fakeid) == NULL) {
604 				cp = pw->pw_name;
605 				fclose(fakeid);
606 				goto printit;
607 			}
608 			fclose(fakeid);
609 			/*
610 			 * Usually, the file will have the desired identity
611 			 * in the form "identity\n", so we use strcspn() to
612 			 * end the string (which fgets() doesn't do.)
613 			 */
614 			buf[strcspn(buf, "\r\n")] = '\0';
615 			cp = buf;
616 			/* Allow for beginning white space... */
617 			while (isspace(*cp))
618 				cp++;
619 			/* ...and ending white space. */
620 			cp[strcspn(cp, " \t")] = '\0';
621 			/* User names of >16 characters are invalid */
622 			if (strlen(cp) > 16)
623 				cp[16] = '\0';
624 			/*
625 			 * If the name is a zero-length string or matches
626 			 * the name of another user, it's invalid, so
627 			 * we will return their real identity instead.
628 			 */
629 
630 			if (!*cp || getpwnam(cp)) {
631 				pw = getpwuid(uc.cr_uid);
632 				if (pw == NULL)
633 					iderror(lport, fport, s, errno);
634 				cp = pw->pw_name;
635 			}
636 		} else
637 			cp = pw->pw_name;
638 		if (fakeid_fd != -1)
639 			close(fakeid_fd);
640 	} else if (!usedfallback)
641 		cp = pw->pw_name;
642 	else
643 		cp = fallback;
644 printit:
645 	/* Finally, we make and send the reply. */
646 	if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
647 	    cp) == -1) {
648 		syslog(LOG_ERR, "asprintf: %m");
649 		exit(EX_OSERR);
650 	}
651 	send(s, p, strlen(p), MSG_EOF);
652 	free(p);
653 
654 	exit(0);
655 }
656 
657 /*
658  * RFC738 Time Server.
659  * Return a machine readable date and time, in the form of the
660  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
661  * returns the number of seconds since midnight, Jan 1, 1970,
662  * we must add 2208988800 seconds to this figure to make up for
663  * some seventy years Bell Labs was asleep.
664  */
665 
666 unsigned long
667 machtime()
668 {
669 	struct timeval tv;
670 
671 	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
672 		if (debug)
673 			warnx("unable to get time of day");
674 		return (0L);
675 	}
676 #define	OFFSET ((u_long)25567 * 24*60*60)
677 	return (htonl((long)(tv.tv_sec + OFFSET)));
678 #undef OFFSET
679 }
680 
681 /* ARGSUSED */
682 void
683 machtime_dg(s, sep)
684 	int s;
685 	struct servtab *sep;
686 {
687 	unsigned long result;
688 	struct sockaddr_storage ss;
689 	socklen_t size;
690 
691 	size = sizeof(ss);
692 	if (recvfrom(s, (char *)&result, sizeof(result), 0,
693 		     (struct sockaddr *)&ss, &size) < 0)
694 		return;
695 
696 	if (check_loop((struct sockaddr *)&ss, sep))
697 		return;
698 
699 	result = machtime();
700 	(void) sendto(s, (char *) &result, sizeof(result), 0,
701 		      (struct sockaddr *)&ss, size);
702 }
703 
704 /* ARGSUSED */
705 void
706 machtime_stream(s, sep)
707 	int s;
708 	struct servtab *sep;
709 {
710 	unsigned long result;
711 
712 	result = machtime();
713 	(void) send(s, (char *) &result, sizeof(result), MSG_EOF);
714 }
715 
716 /*
717  * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
718  * services based on the service name sent.
719  *
720  *  Based on TCPMUX.C by Mark K. Lottor November 1988
721  *  sri-nic::ps:<mkl>tcpmux.c
722  */
723 
724 #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
725 #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
726 
727 static int		/* # of characters upto \r,\n or \0 */
728 getline(fd, buf, len)
729 	int fd;
730 	char *buf;
731 	int len;
732 {
733 	int count = 0, n;
734 	struct sigaction sa;
735 
736 	sa.sa_flags = 0;
737 	sigemptyset(&sa.sa_mask);
738 	sa.sa_handler = SIG_DFL;
739 	sigaction(SIGALRM, &sa, (struct sigaction *)0);
740 	do {
741 		alarm(10);
742 		n = read(fd, buf, len-count);
743 		alarm(0);
744 		if (n == 0)
745 			return (count);
746 		if (n < 0)
747 			return (-1);
748 		while (--n >= 0) {
749 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
750 				return (count);
751 			count++;
752 			buf++;
753 		}
754 	} while (count < len);
755 	return (count);
756 }
757 
758 struct servtab *
759 tcpmux(s)
760 	int s;
761 {
762 	struct servtab *sep;
763 	char service[MAX_SERV_LEN+1];
764 	int len;
765 
766 	/* Get requested service name */
767 	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
768 		strwrite(s, "-Error reading service name\r\n");
769 		return (NULL);
770 	}
771 	service[len] = '\0';
772 
773 	if (debug)
774 		warnx("tcpmux: someone wants %s", service);
775 
776 	/*
777 	 * Help is a required command, and lists available services,
778 	 * one per line.
779 	 */
780 	if (!strcasecmp(service, "help")) {
781 		for (sep = servtab; sep; sep = sep->se_next) {
782 			if (!ISMUX(sep))
783 				continue;
784 			(void)write(s,sep->se_service,strlen(sep->se_service));
785 			strwrite(s, "\r\n");
786 		}
787 		return (NULL);
788 	}
789 
790 	/* Try matching a service in inetd.conf with the request */
791 	for (sep = servtab; sep; sep = sep->se_next) {
792 		if (!ISMUX(sep))
793 			continue;
794 		if (!strcasecmp(service, sep->se_service)) {
795 			if (ISMUXPLUS(sep)) {
796 				strwrite(s, "+Go\r\n");
797 			}
798 			return (sep);
799 		}
800 	}
801 	strwrite(s, "-Service not available\r\n");
802 	return (NULL);
803 }
804