xref: /freebsd/usr.sbin/inetd/builtins.c (revision 273c26a3c3bea87a241d6879abd4f991db180bf0)
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	get_line(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 get_line(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 = get_line(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