xref: /freebsd/usr.sbin/inetd/builtins.c (revision 6addf2595e5ea95d7cc45233ca44c3faa09520b7)
18391600eSBrian Feldman /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
48391600eSBrian Feldman  * Copyright (c) 1983, 1991, 1993, 1994
58391600eSBrian Feldman  *	The Regents of the University of California.  All rights reserved.
68391600eSBrian Feldman  *
78391600eSBrian Feldman  * Redistribution and use in source and binary forms, with or without
88391600eSBrian Feldman  * modification, are permitted provided that the following conditions
98391600eSBrian Feldman  * are met:
108391600eSBrian Feldman  * 1. Redistributions of source code must retain the above copyright
118391600eSBrian Feldman  *    notice, this list of conditions and the following disclaimer.
128391600eSBrian Feldman  * 2. Redistributions in binary form must reproduce the above copyright
138391600eSBrian Feldman  *    notice, this list of conditions and the following disclaimer in the
148391600eSBrian Feldman  *    documentation and/or other materials provided with the distribution.
158391600eSBrian Feldman  *
168391600eSBrian Feldman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
178391600eSBrian Feldman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188391600eSBrian Feldman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198391600eSBrian Feldman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
208391600eSBrian Feldman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
218391600eSBrian Feldman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
228391600eSBrian Feldman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238391600eSBrian Feldman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
248391600eSBrian Feldman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258391600eSBrian Feldman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268391600eSBrian Feldman  * SUCH DAMAGE.
278391600eSBrian Feldman  */
288391600eSBrian Feldman 
2935ea3970SJuli Mallett #include <sys/cdefs.h>
308391600eSBrian Feldman #include <sys/filio.h>
318391600eSBrian Feldman #include <sys/ioccom.h>
325ff3afceSSheldon Hearn #include <sys/param.h>
335ff3afceSSheldon Hearn #include <sys/stat.h>
345ff3afceSSheldon Hearn #include <sys/socket.h>
355ff3afceSSheldon Hearn #include <sys/sysctl.h>
365ff3afceSSheldon Hearn #include <sys/ucred.h>
375ff3afceSSheldon Hearn #include <sys/uio.h>
388391600eSBrian Feldman #include <sys/utsname.h>
395ff3afceSSheldon Hearn 
405ff3afceSSheldon Hearn #include <ctype.h>
415ff3afceSSheldon Hearn #include <err.h>
425ff3afceSSheldon Hearn #include <errno.h>
436fe761c7SBrian Feldman #include <fcntl.h>
445ff3afceSSheldon Hearn #include <limits.h>
455ff3afceSSheldon Hearn #include <pwd.h>
465ff3afceSSheldon Hearn #include <signal.h>
478391600eSBrian Feldman #include <stdlib.h>
485ff3afceSSheldon Hearn #include <string.h>
4956658bf1SSheldon Hearn #include <sysexits.h>
5056658bf1SSheldon Hearn #include <syslog.h>
515ff3afceSSheldon Hearn #include <unistd.h>
525ff3afceSSheldon Hearn 
535ff3afceSSheldon Hearn #include "inetd.h"
545ff3afceSSheldon Hearn 
554909085fSHiroki Sato static void	chargen_dg(int, struct servtab *);
564909085fSHiroki Sato static void	chargen_stream(int, struct servtab *);
574909085fSHiroki Sato static void	daytime_dg(int, struct servtab *);
584909085fSHiroki Sato static void	daytime_stream(int, struct servtab *);
594909085fSHiroki Sato static void	discard_dg(int, struct servtab *);
604909085fSHiroki Sato static void	discard_stream(int, struct servtab *);
614909085fSHiroki Sato static void	echo_dg(int, struct servtab *);
624909085fSHiroki Sato static void	echo_stream(int, struct servtab *);
63f03ef840SBaptiste Daroussin static int	get_line(int, char *, int);
644909085fSHiroki Sato static void	iderror(int, int, int, const char *);
654909085fSHiroki Sato static void	ident_stream(int, struct servtab *);
664909085fSHiroki Sato static void	initring(void);
674909085fSHiroki Sato static uint32_t	machtime(void);
684909085fSHiroki Sato static void	machtime_dg(int, struct servtab *);
694909085fSHiroki Sato static void	machtime_stream(int, struct servtab *);
705ff3afceSSheldon Hearn 
714909085fSHiroki Sato static char ring[128];
724909085fSHiroki Sato static char *endring;
735ff3afceSSheldon Hearn 
745ff3afceSSheldon Hearn struct biltin biltins[] = {
755ff3afceSSheldon Hearn 	/* Echo received data */
765ff3afceSSheldon Hearn 	{ "echo",	SOCK_STREAM,	1, -1,	echo_stream },
775ff3afceSSheldon Hearn 	{ "echo",	SOCK_DGRAM,	0, 1,	echo_dg },
785ff3afceSSheldon Hearn 
795ff3afceSSheldon Hearn 	/* Internet /dev/null */
805ff3afceSSheldon Hearn 	{ "discard",	SOCK_STREAM,	1, -1,	discard_stream },
815ff3afceSSheldon Hearn 	{ "discard",	SOCK_DGRAM,	0, 1,	discard_dg },
825ff3afceSSheldon Hearn 
83aca66ea0SDavid Malone 	/* Return 32 bit time since 1900 */
845ff3afceSSheldon Hearn 	{ "time",	SOCK_STREAM,	0, -1,	machtime_stream },
855ff3afceSSheldon Hearn 	{ "time",	SOCK_DGRAM,	0, 1,	machtime_dg },
865ff3afceSSheldon Hearn 
875ff3afceSSheldon Hearn 	/* Return human-readable time */
885ff3afceSSheldon Hearn 	{ "daytime",	SOCK_STREAM,	0, -1,	daytime_stream },
895ff3afceSSheldon Hearn 	{ "daytime",	SOCK_DGRAM,	0, 1,	daytime_dg },
905ff3afceSSheldon Hearn 
915ff3afceSSheldon Hearn 	/* Familiar character generator */
925ff3afceSSheldon Hearn 	{ "chargen",	SOCK_STREAM,	1, -1,	chargen_stream },
935ff3afceSSheldon Hearn 	{ "chargen",	SOCK_DGRAM,	0, 1,	chargen_dg },
945ff3afceSSheldon Hearn 
95b585f768SDavid Malone 	{ "tcpmux",	SOCK_STREAM,	1, -1,	(bi_fn_t *)tcpmux },
965ff3afceSSheldon Hearn 
975ff3afceSSheldon Hearn 	{ "auth",	SOCK_STREAM,	1, -1,	ident_stream },
985ff3afceSSheldon Hearn 
99b585f768SDavid Malone 	{ NULL,		0,		0, 0,	NULL }
1005ff3afceSSheldon Hearn };
1015ff3afceSSheldon Hearn 
1029a16e31aSSheldon Hearn /*
1039a16e31aSSheldon Hearn  * RFC864 Character Generator Protocol. Generates character data without
1049a16e31aSSheldon Hearn  * any regard for input.
1059a16e31aSSheldon Hearn  */
1069a16e31aSSheldon Hearn 
1074909085fSHiroki Sato static void
108081713dcSJuli Mallett initring(void)
1095ff3afceSSheldon Hearn {
1105ff3afceSSheldon Hearn 	int i;
1115ff3afceSSheldon Hearn 
1125ff3afceSSheldon Hearn 	endring = ring;
1135ff3afceSSheldon Hearn 
1145ff3afceSSheldon Hearn 	for (i = 0; i <= 128; ++i)
1155ff3afceSSheldon Hearn 		if (isprint(i))
1165ff3afceSSheldon Hearn 			*endring++ = i;
1175ff3afceSSheldon Hearn }
1185ff3afceSSheldon Hearn 
1196a4d12adSDavid Malone /* Character generator
1206a4d12adSDavid Malone  * The RFC says that we should send back a random number of
1216a4d12adSDavid Malone  * characters chosen from the range 0 to 512. We send LINESIZ+2.
1226a4d12adSDavid Malone  */
1235ff3afceSSheldon Hearn /* ARGSUSED */
1244909085fSHiroki Sato static void
1258aea60beSJuli Mallett chargen_dg(int s, struct servtab *sep)
1265ff3afceSSheldon Hearn {
1270cac72f4SYoshinobu Inoue 	struct sockaddr_storage ss;
1285ff3afceSSheldon Hearn 	static char *rs;
129a9a948a9SYoshinobu Inoue 	int len;
130a9a948a9SYoshinobu Inoue 	socklen_t size;
1315ff3afceSSheldon Hearn 	char text[LINESIZ+2];
1325ff3afceSSheldon Hearn 
133f23df319SKyle Evans 	if (endring == NULL)
1345ff3afceSSheldon Hearn 		initring();
135f23df319SKyle Evans 	if (rs == NULL)
1365ff3afceSSheldon Hearn 		rs = ring;
1375ff3afceSSheldon Hearn 
1380cac72f4SYoshinobu Inoue 	size = sizeof(ss);
1395ff3afceSSheldon Hearn 	if (recvfrom(s, text, sizeof(text), 0,
1400cac72f4SYoshinobu Inoue 		     (struct sockaddr *)&ss, &size) < 0)
1415ff3afceSSheldon Hearn 		return;
1425ff3afceSSheldon Hearn 
1430cac72f4SYoshinobu Inoue 	if (check_loop((struct sockaddr *)&ss, sep))
1445ff3afceSSheldon Hearn 		return;
1455ff3afceSSheldon Hearn 
1465ff3afceSSheldon Hearn 	if ((len = endring - rs) >= LINESIZ)
1475ff3afceSSheldon Hearn 		memmove(text, rs, LINESIZ);
1485ff3afceSSheldon Hearn 	else {
1495ff3afceSSheldon Hearn 		memmove(text, rs, len);
1505ff3afceSSheldon Hearn 		memmove(text + len, ring, LINESIZ - len);
1515ff3afceSSheldon Hearn 	}
1525ff3afceSSheldon Hearn 	if (++rs == endring)
1535ff3afceSSheldon Hearn 		rs = ring;
1545ff3afceSSheldon Hearn 	text[LINESIZ] = '\r';
1555ff3afceSSheldon Hearn 	text[LINESIZ + 1] = '\n';
156a9a948a9SYoshinobu Inoue 	(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
1575ff3afceSSheldon Hearn }
1585ff3afceSSheldon Hearn 
1598aea60beSJuli Mallett /* Character generator */
1605ff3afceSSheldon Hearn /* ARGSUSED */
1614909085fSHiroki Sato static void
1628aea60beSJuli Mallett chargen_stream(int s, struct servtab *sep)
1635ff3afceSSheldon Hearn {
1645ff3afceSSheldon Hearn 	int len;
1655ff3afceSSheldon Hearn 	char *rs, text[LINESIZ+2];
1665ff3afceSSheldon Hearn 
1675ff3afceSSheldon Hearn 	inetd_setproctitle(sep->se_service, s);
1685ff3afceSSheldon Hearn 
169483825edSXin LI 	if (!endring)
1705ff3afceSSheldon Hearn 		initring();
1715ff3afceSSheldon Hearn 
1725ff3afceSSheldon Hearn 	text[LINESIZ] = '\r';
1735ff3afceSSheldon Hearn 	text[LINESIZ + 1] = '\n';
1745ff3afceSSheldon Hearn 	for (rs = ring;;) {
1755ff3afceSSheldon Hearn 		if ((len = endring - rs) >= LINESIZ)
1765ff3afceSSheldon Hearn 			memmove(text, rs, LINESIZ);
1775ff3afceSSheldon Hearn 		else {
1785ff3afceSSheldon Hearn 			memmove(text, rs, len);
1795ff3afceSSheldon Hearn 			memmove(text + len, ring, LINESIZ - len);
1805ff3afceSSheldon Hearn 		}
1815ff3afceSSheldon Hearn 		if (++rs == endring)
1825ff3afceSSheldon Hearn 			rs = ring;
1835ff3afceSSheldon Hearn 		if (write(s, text, sizeof(text)) != sizeof(text))
1845ff3afceSSheldon Hearn 			break;
1855ff3afceSSheldon Hearn 	}
1865ff3afceSSheldon Hearn 	exit(0);
1875ff3afceSSheldon Hearn }
1885ff3afceSSheldon Hearn 
1899a16e31aSSheldon Hearn /*
1909a16e31aSSheldon Hearn  * RFC867 Daytime Protocol. Sends the current date and time as an ascii
1919a16e31aSSheldon Hearn  * character string without any regard for input.
1929a16e31aSSheldon Hearn  */
1939a16e31aSSheldon Hearn 
1948aea60beSJuli Mallett /* Return human-readable time of day */
1955ff3afceSSheldon Hearn /* ARGSUSED */
1964909085fSHiroki Sato static void
1978aea60beSJuli Mallett daytime_dg(int s, struct servtab *sep)
1985ff3afceSSheldon Hearn {
1995ff3afceSSheldon Hearn 	char buffer[256];
200b585f768SDavid Malone 	time_t now;
2010cac72f4SYoshinobu Inoue 	struct sockaddr_storage ss;
202a9a948a9SYoshinobu Inoue 	socklen_t size;
2035ff3afceSSheldon Hearn 
204b585f768SDavid Malone 	now = time((time_t *) 0);
2055ff3afceSSheldon Hearn 
2060cac72f4SYoshinobu Inoue 	size = sizeof(ss);
2075ff3afceSSheldon Hearn 	if (recvfrom(s, buffer, sizeof(buffer), 0,
2080cac72f4SYoshinobu Inoue 		     (struct sockaddr *)&ss, &size) < 0)
2095ff3afceSSheldon Hearn 		return;
2105ff3afceSSheldon Hearn 
2110cac72f4SYoshinobu Inoue 	if (check_loop((struct sockaddr *)&ss, sep))
2125ff3afceSSheldon Hearn 		return;
2135ff3afceSSheldon Hearn 
214b585f768SDavid Malone 	(void) sprintf(buffer, "%.24s\r\n", ctime(&now));
2155ff3afceSSheldon Hearn 	(void) sendto(s, buffer, strlen(buffer), 0,
216a9a948a9SYoshinobu Inoue 		      (struct sockaddr *)&ss, size);
2175ff3afceSSheldon Hearn }
2185ff3afceSSheldon Hearn 
2198aea60beSJuli Mallett /* Return human-readable time of day */
2205ff3afceSSheldon Hearn /* ARGSUSED */
2214909085fSHiroki Sato static void
2228aea60beSJuli Mallett daytime_stream(int s, struct servtab *sep __unused)
2235ff3afceSSheldon Hearn {
2245ff3afceSSheldon Hearn 	char buffer[256];
225b585f768SDavid Malone 	time_t now;
2265ff3afceSSheldon Hearn 
227b585f768SDavid Malone 	now = time((time_t *) 0);
2285ff3afceSSheldon Hearn 
229b585f768SDavid Malone 	(void) sprintf(buffer, "%.24s\r\n", ctime(&now));
230a3ad0852SSheldon Hearn 	(void) send(s, buffer, strlen(buffer), MSG_EOF);
2315ff3afceSSheldon Hearn }
2325ff3afceSSheldon Hearn 
2339a16e31aSSheldon Hearn /*
2349a16e31aSSheldon Hearn  * RFC863 Discard Protocol. Any data received is thrown away and no response
2359a16e31aSSheldon Hearn  * is sent.
2369a16e31aSSheldon Hearn  */
2379a16e31aSSheldon Hearn 
2388aea60beSJuli Mallett /* Discard service -- ignore data */
2395ff3afceSSheldon Hearn /* ARGSUSED */
2404909085fSHiroki Sato static void
2412306f8e9SJuli Mallett discard_dg(int s, struct servtab *sep __unused)
2425ff3afceSSheldon Hearn {
2435ff3afceSSheldon Hearn 	char buffer[BUFSIZE];
2445ff3afceSSheldon Hearn 
2455ff3afceSSheldon Hearn 	(void) read(s, buffer, sizeof(buffer));
2465ff3afceSSheldon Hearn }
2475ff3afceSSheldon Hearn 
2488aea60beSJuli Mallett /* Discard service -- ignore data */
2495ff3afceSSheldon Hearn /* ARGSUSED */
2504909085fSHiroki Sato static void
2518aea60beSJuli Mallett discard_stream(int s, struct servtab *sep)
2525ff3afceSSheldon Hearn {
2535ff3afceSSheldon Hearn 	int ret;
2545ff3afceSSheldon Hearn 	char buffer[BUFSIZE];
2555ff3afceSSheldon Hearn 
2565ff3afceSSheldon Hearn 	inetd_setproctitle(sep->se_service, s);
2575ff3afceSSheldon Hearn 	while (1) {
2585ff3afceSSheldon Hearn 		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
2595ff3afceSSheldon Hearn 			;
2605ff3afceSSheldon Hearn 		if (ret == 0 || errno != EINTR)
2615ff3afceSSheldon Hearn 			break;
2625ff3afceSSheldon Hearn 	}
2635ff3afceSSheldon Hearn 	exit(0);
2645ff3afceSSheldon Hearn }
2655ff3afceSSheldon Hearn 
2669a16e31aSSheldon Hearn /*
2679a16e31aSSheldon Hearn  * RFC862 Echo Protocol. Any data received is sent back to the sender as
2689a16e31aSSheldon Hearn  * received.
2699a16e31aSSheldon Hearn  */
2709a16e31aSSheldon Hearn 
2718aea60beSJuli Mallett /* Echo service -- echo data back */
2725ff3afceSSheldon Hearn /* ARGSUSED */
2734909085fSHiroki Sato static void
2748aea60beSJuli Mallett echo_dg(int s, struct servtab *sep)
2755ff3afceSSheldon Hearn {
276d0847e93SDavid Malone 	char buffer[65536]; /* Should be sizeof(max datagram). */
277a9a948a9SYoshinobu Inoue 	int i;
278a9a948a9SYoshinobu Inoue 	socklen_t size;
2790cac72f4SYoshinobu Inoue 	struct sockaddr_storage ss;
2805ff3afceSSheldon Hearn 
2810cac72f4SYoshinobu Inoue 	size = sizeof(ss);
2825ff3afceSSheldon Hearn 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
2830cac72f4SYoshinobu Inoue 			  (struct sockaddr *)&ss, &size)) < 0)
2845ff3afceSSheldon Hearn 		return;
2855ff3afceSSheldon Hearn 
2860cac72f4SYoshinobu Inoue 	if (check_loop((struct sockaddr *)&ss, sep))
2875ff3afceSSheldon Hearn 		return;
2885ff3afceSSheldon Hearn 
289a9a948a9SYoshinobu Inoue 	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
2905ff3afceSSheldon Hearn }
2915ff3afceSSheldon Hearn 
2928aea60beSJuli Mallett /* Echo service -- echo data back */
2935ff3afceSSheldon Hearn /* ARGSUSED */
2944909085fSHiroki Sato static void
2958aea60beSJuli Mallett echo_stream(int s, struct servtab *sep)
2965ff3afceSSheldon Hearn {
2975ff3afceSSheldon Hearn 	char buffer[BUFSIZE];
2985ff3afceSSheldon Hearn 	int i;
2995ff3afceSSheldon Hearn 
3005ff3afceSSheldon Hearn 	inetd_setproctitle(sep->se_service, s);
3015ff3afceSSheldon Hearn 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
3025ff3afceSSheldon Hearn 	    write(s, buffer, i) > 0)
3035ff3afceSSheldon Hearn 		;
3045ff3afceSSheldon Hearn 	exit(0);
3055ff3afceSSheldon Hearn }
3065ff3afceSSheldon Hearn 
3079a16e31aSSheldon Hearn /*
3089a16e31aSSheldon Hearn  * RFC1413 Identification Protocol. Given a TCP port number pair, return a
3099a16e31aSSheldon Hearn  * character string which identifies the owner of that connection on the
3102d878a19SBrian Feldman  * server's system. Extended to allow for ~/.fakeid support and ~/.noident
3112d878a19SBrian Feldman  * support.
3122d878a19SBrian Feldman  */
3132d878a19SBrian Feldman 
3149a0b3389SDavid Malone /* RFC 1413 says the following are the only errors you can return. */
3159a0b3389SDavid Malone #define ID_INVALID	"INVALID-PORT"	/* Port number improperly specified. */
316*6addf259SElyes Haouas #define ID_NOUSER	"NO-USER"	/* Port not in use/not identifiable. */
317*6addf259SElyes Haouas #define ID_HIDDEN	"HIDDEN-USER"	/* Hidden at user's request. */
3189a0b3389SDavid Malone #define ID_UNKNOWN	"UNKNOWN-ERROR"	/* Everything else. */
3199a0b3389SDavid Malone 
3208aea60beSJuli Mallett /* Generic ident_stream error-sending func */
3215ff3afceSSheldon Hearn /* ARGSUSED */
3224909085fSHiroki Sato static void
3238aea60beSJuli Mallett iderror(int lport, int fport, int s, const char *er)
3245ff3afceSSheldon Hearn {
3258391600eSBrian Feldman 	char *p;
3268391600eSBrian Feldman 
3279a0b3389SDavid Malone 	asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er);
32856658bf1SSheldon Hearn 	if (p == NULL) {
3292d878a19SBrian Feldman 		syslog(LOG_ERR, "asprintf: %m");
33056658bf1SSheldon Hearn 		exit(EX_OSERR);
33156658bf1SSheldon Hearn 	}
332a3ad0852SSheldon Hearn 	send(s, p, strlen(p), MSG_EOF);
3338391600eSBrian Feldman 	free(p);
3345ff3afceSSheldon Hearn 
3355ff3afceSSheldon Hearn 	exit(0);
3365ff3afceSSheldon Hearn }
3375ff3afceSSheldon Hearn 
3388aea60beSJuli Mallett /* Ident service (AKA "auth") */
3395ff3afceSSheldon Hearn /* ARGSUSED */
3404909085fSHiroki Sato static void
3418aea60beSJuli Mallett ident_stream(int s, struct servtab *sep)
3425ff3afceSSheldon Hearn {
343ecf12be0SBrian Feldman 	struct utsname un;
344ecf12be0SBrian Feldman 	struct stat sb;
345b585f768SDavid Malone 	struct sockaddr_in sin4[2];
3461a0760ddSYoshinobu Inoue #ifdef INET6
3470cac72f4SYoshinobu Inoue 	struct sockaddr_in6 sin6[2];
3481a0760ddSYoshinobu Inoue #endif
3490cac72f4SYoshinobu Inoue 	struct sockaddr_storage ss[2];
350c0511d3bSBrian Feldman 	struct xucred uc;
3518391600eSBrian Feldman 	struct timeval tv = {
3528391600eSBrian Feldman 		10,
3538391600eSBrian Feldman 		0
3547d37a2e6SDavid Malone 	}, to;
35518338e9eSBrian Feldman 	struct passwd *pw = NULL;
3568391600eSBrian Feldman 	fd_set fdset;
3579a0b3389SDavid Malone 	char buf[BUFSIZE], *p, **av, *osname = NULL, e;
3589a0b3389SDavid Malone 	char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */
3599e72c318SBrian Feldman 	socklen_t socklen;
3609e72c318SBrian Feldman 	ssize_t ssize;
3617d37a2e6SDavid Malone 	size_t size, bufsiz;
3629a0b3389SDavid Malone 	int c, fflag = 0, nflag = 0, rflag = 0, argc = 0;
3639a0b3389SDavid Malone 	int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen;
3645ff3afceSSheldon Hearn 	u_short lport, fport;
3655ff3afceSSheldon Hearn 
3665ff3afceSSheldon Hearn 	inetd_setproctitle(sep->se_service, s);
367019893b4SBrian Feldman 	/*
368019893b4SBrian Feldman 	 * Reset getopt() since we are a fork() but not an exec() from
369019893b4SBrian Feldman 	 * a parent which used getopt() already.
370019893b4SBrian Feldman 	 */
3715ff3afceSSheldon Hearn 	optind = 1;
3725ff3afceSSheldon Hearn 	optreset = 1;
373019893b4SBrian Feldman 	/*
374019893b4SBrian Feldman 	 * Take the internal argument vector and count it out to make an
375019893b4SBrian Feldman 	 * argument count for getopt. This can be used for any internal
376019893b4SBrian Feldman 	 * service to read arguments and use getopt() easily.
377019893b4SBrian Feldman 	 */
3785ff3afceSSheldon Hearn 	for (av = sep->se_argv; *av; av++)
3795ff3afceSSheldon Hearn 		argc++;
3805ff3afceSSheldon Hearn 	if (argc) {
381b52c43b3SBrian Feldman 		int sec, usec;
38218338e9eSBrian Feldman 		size_t i;
383b585f768SDavid Malone 		u_int32_t rnd32;
384b52c43b3SBrian Feldman 
3859a0b3389SDavid Malone 		while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1)
3865ff3afceSSheldon Hearn 			switch (c) {
38718338e9eSBrian Feldman 			case 'd':
3889a0b3389SDavid Malone 				if (!gflag)
3899a0b3389SDavid Malone 					strlcpy(idbuf, optarg, sizeof(idbuf));
39018338e9eSBrian Feldman 				break;
3915ff3afceSSheldon Hearn 			case 'f':
3925ff3afceSSheldon Hearn 				fflag = 1;
3935ff3afceSSheldon Hearn 				break;
39438db6bf3SDavid Malone 			case 'F':
39538db6bf3SDavid Malone 				fflag = 1;
39638db6bf3SDavid Malone 				Fflag=1;
39738db6bf3SDavid Malone 				break;
39818338e9eSBrian Feldman 			case 'g':
39918338e9eSBrian Feldman 				gflag = 1;
400b585f768SDavid Malone 				rnd32 = 0;	/* Shush, compiler. */
4015a5e442aSBrian Feldman 				/*
402b585f768SDavid Malone 				 * The number of bits in "rnd32" divided
4035a5e442aSBrian Feldman 				 * by the number of bits needed per iteration
4045a5e442aSBrian Feldman 				 * gives a more optimal way to reload the
4055a5e442aSBrian Feldman 				 * random number only when necessary.
4065a5e442aSBrian Feldman 				 *
407ae5fafd8STony Finch 				 * 32 bits from arc4random corresponds to
408*6addf259SElyes Haouas 				 * about 6 base-36 digits, so we reseed every 6.
4095a5e442aSBrian Feldman 				 */
4109a0b3389SDavid Malone 				for (i = 0; i < sizeof(idbuf) - 1; i++) {
4119a0b3389SDavid Malone 					static const char *const base36 =
4125a5e442aSBrian Feldman 					    "0123456789"
4135a5e442aSBrian Feldman 					    "abcdefghijklmnopqrstuvwxyz";
4149a0b3389SDavid Malone 					if (i % 6 == 0)
415b585f768SDavid Malone 						rnd32 = arc4random();
416b585f768SDavid Malone 					idbuf[i] = base36[rnd32 % 36];
417b585f768SDavid Malone 					rnd32 /= 36;
41818338e9eSBrian Feldman 				}
4199a0b3389SDavid Malone 				idbuf[i] = '\0';
4209a0b3389SDavid Malone 				break;
4219a0b3389SDavid Malone 			case 'i':
4229a0b3389SDavid Malone 				iflag = 1;
42318338e9eSBrian Feldman 				break;
4242d878a19SBrian Feldman 			case 'n':
4252d878a19SBrian Feldman 				nflag = 1;
4265ff3afceSSheldon Hearn 				break;
4278391600eSBrian Feldman 			case 'o':
4288391600eSBrian Feldman 				osname = optarg;
4298391600eSBrian Feldman 				break;
4302d878a19SBrian Feldman 			case 'r':
4312d878a19SBrian Feldman 				rflag = 1;
4322d878a19SBrian Feldman 				break;
4336f426a27SSheldon Hearn 			case 't':
43456658bf1SSheldon Hearn 				switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
4353d1171b5SSheldon Hearn 				case 2:
4363d1171b5SSheldon Hearn 					tv.tv_usec = usec;
4379a0b3389SDavid Malone 					/* FALLTHROUGH */
438202a2323SBrian Feldman 				case 1:
439202a2323SBrian Feldman 					tv.tv_sec = sec;
440202a2323SBrian Feldman 					break;
441202a2323SBrian Feldman 				default:
4423d1171b5SSheldon Hearn 					if (debug)
44356658bf1SSheldon Hearn 						warnx("bad -t argument");
444202a2323SBrian Feldman 					break;
445202a2323SBrian Feldman 				}
446b52c43b3SBrian Feldman 				break;
4475ff3afceSSheldon Hearn 			default:
4485ff3afceSSheldon Hearn 				break;
4495ff3afceSSheldon Hearn 			}
4505ff3afceSSheldon Hearn 	}
4518391600eSBrian Feldman 	if (osname == NULL) {
452e1c77598SBrian Feldman 		if (uname(&un) == -1)
4539a0b3389SDavid Malone 			iderror(0, 0, s, ID_UNKNOWN);
4548391600eSBrian Feldman 		osname = un.sysname;
4558391600eSBrian Feldman 	}
4569a0b3389SDavid Malone 
457019893b4SBrian Feldman 	/*
458019893b4SBrian Feldman 	 * We're going to prepare for and execute reception of a
459019893b4SBrian Feldman 	 * packet of data from the user. The data is in the format
460019893b4SBrian Feldman 	 * "local_port , foreign_port\r\n" (with local being the
461019893b4SBrian Feldman 	 * server's port and foreign being the client's.)
462019893b4SBrian Feldman 	 */
4637d37a2e6SDavid Malone 	gettimeofday(&to, NULL);
4647d37a2e6SDavid Malone 	to.tv_sec += tv.tv_sec;
465c4483bc0SBrian Feldman 	to.tv_usec += tv.tv_usec;
466c4483bc0SBrian Feldman 	if (to.tv_usec >= 1000000) {
4677d37a2e6SDavid Malone 		to.tv_usec -= 1000000;
4687d37a2e6SDavid Malone 		to.tv_sec++;
4697d37a2e6SDavid Malone 	}
4707d37a2e6SDavid Malone 
4717d37a2e6SDavid Malone 	size = 0;
4727d37a2e6SDavid Malone 	bufsiz = sizeof(buf) - 1;
4738391600eSBrian Feldman 	FD_ZERO(&fdset);
474d517199fSDavid Malone  	while (bufsiz > 0) {
4757d37a2e6SDavid Malone 		gettimeofday(&tv, NULL);
4767d37a2e6SDavid Malone 		tv.tv_sec = to.tv_sec - tv.tv_sec;
4777d37a2e6SDavid Malone 		tv.tv_usec = to.tv_usec - tv.tv_usec;
4787d37a2e6SDavid Malone 		if (tv.tv_usec < 0) {
4797d37a2e6SDavid Malone 			tv.tv_usec += 1000000;
4807d37a2e6SDavid Malone 			tv.tv_sec--;
4817d37a2e6SDavid Malone 		}
4827d37a2e6SDavid Malone 		if (tv.tv_sec < 0)
4837d37a2e6SDavid Malone 			break;
4848391600eSBrian Feldman 		FD_SET(s, &fdset);
4858391600eSBrian Feldman 		if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
4869a0b3389SDavid Malone 			iderror(0, 0, s, ID_UNKNOWN);
4879e72c318SBrian Feldman 		if (ioctl(s, FIONREAD, &onreadlen) == -1)
4889a0b3389SDavid Malone 			iderror(0, 0, s, ID_UNKNOWN);
489b585f768SDavid Malone 		if ((size_t)onreadlen > bufsiz)
4907d37a2e6SDavid Malone 			onreadlen = bufsiz;
4917d37a2e6SDavid Malone 		ssize = read(s, &buf[size], (size_t)onreadlen);
4929e72c318SBrian Feldman 		if (ssize == -1)
4939a0b3389SDavid Malone 			iderror(0, 0, s, ID_UNKNOWN);
494957672f9SDavid Malone 		else if (ssize == 0)
495957672f9SDavid Malone 			break;
4967d37a2e6SDavid Malone 		bufsiz -= ssize;
4977d37a2e6SDavid Malone 		size += ssize;
498d517199fSDavid Malone 		if (memchr(&buf[size - ssize], '\n', ssize) != NULL)
499d517199fSDavid Malone 			break;
5007d37a2e6SDavid Malone  	}
5017d37a2e6SDavid Malone 	buf[size] = '\0';
5027d37a2e6SDavid Malone 	/* Read two characters, and check for a delimiting character */
5037d37a2e6SDavid Malone 	if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
5049a0b3389SDavid Malone 		iderror(0, 0, s, ID_INVALID);
5059a0b3389SDavid Malone 
5069a0b3389SDavid Malone 	/* Send garbage? */
5079a0b3389SDavid Malone 	if (gflag)
50818338e9eSBrian Feldman 		goto printit;
50918338e9eSBrian Feldman 
5107ef719fbSBrian Feldman 	/*
5117ef719fbSBrian Feldman 	 * If not "real" (-r), send a HIDDEN-USER error for everything.
5127ef719fbSBrian Feldman 	 * If -d is used to set a fallback username, this is used to
5137ef719fbSBrian Feldman 	 * override it, and the fallback is returned instead.
5147ef719fbSBrian Feldman 	 */
5157ef719fbSBrian Feldman 	if (!rflag) {
5169a0b3389SDavid Malone 		if (*idbuf == '\0')
5179a0b3389SDavid Malone 			iderror(lport, fport, s, ID_HIDDEN);
5187ef719fbSBrian Feldman 		goto printit;
5197ef719fbSBrian Feldman 	}
5207ef719fbSBrian Feldman 
521019893b4SBrian Feldman 	/*
522019893b4SBrian Feldman 	 * We take the input and construct an array of two sockaddr_ins
523019893b4SBrian Feldman 	 * which contain the local address information and foreign
524019893b4SBrian Feldman 	 * address information, respectively, used to look up the
525019893b4SBrian Feldman 	 * credentials for the socket (which are returned by the
5269a0b3389SDavid Malone 	 * sysctl "net.inet.tcp.getcred" when we call it.)
527019893b4SBrian Feldman 	 */
5289a0b3389SDavid Malone 	socklen = sizeof(ss[0]);
5299a0b3389SDavid Malone 	if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
5309a0b3389SDavid Malone 		iderror(lport, fport, s, ID_UNKNOWN);
5319a0b3389SDavid Malone 	socklen = sizeof(ss[1]);
5329a0b3389SDavid Malone 	if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
5339a0b3389SDavid Malone 		iderror(lport, fport, s, ID_UNKNOWN);
5340cac72f4SYoshinobu Inoue 	if (ss[0].ss_family != ss[1].ss_family)
5359a0b3389SDavid Malone 		iderror(lport, fport, s, ID_UNKNOWN);
5369e72c318SBrian Feldman 	size = sizeof(uc);
5370cac72f4SYoshinobu Inoue 	switch (ss[0].ss_family) {
5380cac72f4SYoshinobu Inoue 	case AF_INET:
539b585f768SDavid Malone 		sin4[0] = *(struct sockaddr_in *)&ss[0];
540b585f768SDavid Malone 		sin4[0].sin_port = htons(lport);
541b585f768SDavid Malone 		sin4[1] = *(struct sockaddr_in *)&ss[1];
542b585f768SDavid Malone 		sin4[1].sin_port = htons(fport);
543b585f768SDavid Malone 		if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin4,
544b585f768SDavid Malone 				 sizeof(sin4)) == -1)
545c4483bc0SBrian Feldman 			getcredfail = errno;
5460cac72f4SYoshinobu Inoue 		break;
5470cac72f4SYoshinobu Inoue #ifdef INET6
5480cac72f4SYoshinobu Inoue 	case AF_INET6:
5490cac72f4SYoshinobu Inoue 		sin6[0] = *(struct sockaddr_in6 *)&ss[0];
5500cac72f4SYoshinobu Inoue 		sin6[0].sin6_port = htons(lport);
5510cac72f4SYoshinobu Inoue 		sin6[1] = *(struct sockaddr_in6 *)&ss[1];
5520cac72f4SYoshinobu Inoue 		sin6[1].sin6_port = htons(fport);
5539e72c318SBrian Feldman 		if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
5540cac72f4SYoshinobu Inoue 				 sizeof(sin6)) == -1)
555c4483bc0SBrian Feldman 			getcredfail = errno;
5560cac72f4SYoshinobu Inoue 		break;
5570cac72f4SYoshinobu Inoue #endif
5580cac72f4SYoshinobu Inoue 	default: /* should not reach here */
559c4483bc0SBrian Feldman 		getcredfail = EAFNOSUPPORT;
5600cac72f4SYoshinobu Inoue 		break;
5610cac72f4SYoshinobu Inoue 	}
56276183f34SDima Dorfman 	if (getcredfail != 0 || uc.cr_version != XUCRED_VERSION) {
5639a0b3389SDavid Malone 		if (*idbuf == '\0')
5649a0b3389SDavid Malone 			iderror(lport, fport, s,
5659a0b3389SDavid Malone 			    getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN);
5669a0b3389SDavid Malone 		goto printit;
5679a0b3389SDavid Malone 	}
5689a0b3389SDavid Malone 
5699a0b3389SDavid Malone 	/* Look up the pw to get the username and home directory*/
570c4483bc0SBrian Feldman 	errno = 0;
57118338e9eSBrian Feldman 	pw = getpwuid(uc.cr_uid);
5729a0b3389SDavid Malone 	if (pw == NULL)
5739a0b3389SDavid Malone 		iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN);
5749a0b3389SDavid Malone 
5759a0b3389SDavid Malone 	if (iflag)
5769a0b3389SDavid Malone 		snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid);
5779a0b3389SDavid Malone 	else
5789a0b3389SDavid Malone 		strlcpy(idbuf, pw->pw_name, sizeof(idbuf));
5799a0b3389SDavid Malone 
580019893b4SBrian Feldman 	/*
581019893b4SBrian Feldman 	 * If enabled, we check for a file named ".noident" in the user's
582019893b4SBrian Feldman 	 * home directory. If found, we return HIDDEN-USER.
583019893b4SBrian Feldman 	 */
5849a0b3389SDavid Malone 	if (nflag) {
585ecf12be0SBrian Feldman 		if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
5869a0b3389SDavid Malone 			iderror(lport, fport, s, ID_UNKNOWN);
587ecf12be0SBrian Feldman 		if (lstat(p, &sb) == 0) {
588ecf12be0SBrian Feldman 			free(p);
5899a0b3389SDavid Malone 			iderror(lport, fport, s, ID_HIDDEN);
5902d878a19SBrian Feldman 		}
591ecf12be0SBrian Feldman 		free(p);
5922d878a19SBrian Feldman 	}
5939a0b3389SDavid Malone 
594019893b4SBrian Feldman 	/*
595019893b4SBrian Feldman 	 * Here, if enabled, we read a user's ".fakeid" file in their
596019893b4SBrian Feldman 	 * home directory. It consists of a line containing the name
597019893b4SBrian Feldman 	 * they want.
598019893b4SBrian Feldman 	 */
5999a0b3389SDavid Malone 	if (fflag) {
6006fe761c7SBrian Feldman 		int fakeid_fd;
6012d878a19SBrian Feldman 
602019893b4SBrian Feldman 		/*
603019893b4SBrian Feldman 		 * Here we set ourself to effectively be the user, so we don't
604019893b4SBrian Feldman 		 * open any files we have no permission to open, especially
605019893b4SBrian Feldman 		 * symbolic links to sensitive root-owned files or devices.
606019893b4SBrian Feldman 		 */
6076fe761c7SBrian Feldman 		if (initgroups(pw->pw_name, pw->pw_gid) == -1)
6089a0b3389SDavid Malone 			iderror(lport, fport, s, ID_UNKNOWN);
6099a0b3389SDavid Malone 		if (seteuid(pw->pw_uid) == -1)
6109a0b3389SDavid Malone 			iderror(lport, fport, s, ID_UNKNOWN);
611019893b4SBrian Feldman 		/*
612c4483bc0SBrian Feldman 		 * We can't stat() here since that would be a race
613c4483bc0SBrian Feldman 		 * condition.
614019893b4SBrian Feldman 		 * Therefore, we open the file we have permissions to open
615019893b4SBrian Feldman 		 * and if it's not a regular file, we close it and end up
616019893b4SBrian Feldman 		 * returning the user's real username.
617019893b4SBrian Feldman 		 */
6189a0b3389SDavid Malone 		if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
6199a0b3389SDavid Malone 			iderror(lport, fport, s, ID_UNKNOWN);
6206fe761c7SBrian Feldman 		fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
621ecf12be0SBrian Feldman 		free(p);
6229a0b3389SDavid Malone 		if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 ||
6239a0b3389SDavid Malone 		    !S_ISREG(sb.st_mode))
6249a0b3389SDavid Malone 			goto fakeid_fail;
6259a0b3389SDavid Malone 
6269a0b3389SDavid Malone 		if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0)
6279a0b3389SDavid Malone 			goto fakeid_fail;
6289a0b3389SDavid Malone 		buf[ssize] = '\0';
6299a0b3389SDavid Malone 
630019893b4SBrian Feldman 		/*
631019893b4SBrian Feldman 		 * Usually, the file will have the desired identity
6329a0b3389SDavid Malone 		 * in the form "identity\n". Allow for leading white
6339a0b3389SDavid Malone 		 * space and trailing white space/end of line.
634019893b4SBrian Feldman 		 */
6359a0b3389SDavid Malone 		p = buf;
6369a0b3389SDavid Malone 		p += strspn(p, " \t");
6379a0b3389SDavid Malone 		p[strcspn(p, " \t\r\n")] = '\0';
6389a0b3389SDavid Malone 		if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */
6399a0b3389SDavid Malone 			p[MAXLOGNAME - 1] = '\0';
640019893b4SBrian Feldman 
6419a0b3389SDavid Malone 		/*
6429a0b3389SDavid Malone 		 * If the name is a zero-length string or matches it
6439a0b3389SDavid Malone 		 * the id or name of another user (unless permitted by -F)
6449a0b3389SDavid Malone 		 * then it is invalid.
6459a0b3389SDavid Malone 		 */
6469a0b3389SDavid Malone 		if (*p == '\0')
6479a0b3389SDavid Malone 			goto fakeid_fail;
6489a0b3389SDavid Malone 		if (!Fflag) {
6499a0b3389SDavid Malone 			if (iflag) {
6501c3b9acfSKyle Evans 				const char *errstr;
6511c3b9acfSKyle Evans 				uid_t uid;
6521c3b9acfSKyle Evans 
6531c3b9acfSKyle Evans 				uid = strtonum(p, 0, UID_MAX, &errstr);
6541c3b9acfSKyle Evans 				if (errstr != NULL)
6551c3b9acfSKyle Evans 					goto fakeid_fail;
6561c3b9acfSKyle Evans 
6571c3b9acfSKyle Evans 				if (getpwuid(uid) != NULL)
6589a0b3389SDavid Malone 					goto fakeid_fail;
6599a0b3389SDavid Malone 			} else {
6609a0b3389SDavid Malone 				if (getpwnam(p) != NULL)
6619a0b3389SDavid Malone 					goto fakeid_fail;
6626fe761c7SBrian Feldman 			}
6639a0b3389SDavid Malone 		}
6649a0b3389SDavid Malone 
6659a0b3389SDavid Malone 		strlcpy(idbuf, p, sizeof(idbuf));
6669a0b3389SDavid Malone 
6679a0b3389SDavid Malone fakeid_fail:
6689a0b3389SDavid Malone 		if (fakeid_fd != -1)
6696fe761c7SBrian Feldman 			close(fakeid_fd);
6709a0b3389SDavid Malone 	}
6719a0b3389SDavid Malone 
6725ff3afceSSheldon Hearn printit:
673019893b4SBrian Feldman 	/* Finally, we make and send the reply. */
6748391600eSBrian Feldman 	if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
6759a0b3389SDavid Malone 	    idbuf) == -1) {
6762d878a19SBrian Feldman 		syslog(LOG_ERR, "asprintf: %m");
6776f426a27SSheldon Hearn 		exit(EX_OSERR);
6786f426a27SSheldon Hearn 	}
679a3ad0852SSheldon Hearn 	send(s, p, strlen(p), MSG_EOF);
6808391600eSBrian Feldman 	free(p);
6815ff3afceSSheldon Hearn 
6825ff3afceSSheldon Hearn 	exit(0);
6835ff3afceSSheldon Hearn }
6845ff3afceSSheldon Hearn 
6855ff3afceSSheldon Hearn /*
6866a4d12adSDavid Malone  * RFC738/868 Time Server.
6875ff3afceSSheldon Hearn  * Return a machine readable date and time, in the form of the
6885ff3afceSSheldon Hearn  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
6895ff3afceSSheldon Hearn  * returns the number of seconds since midnight, Jan 1, 1970,
6905ff3afceSSheldon Hearn  * we must add 2208988800 seconds to this figure to make up for
6915ff3afceSSheldon Hearn  * some seventy years Bell Labs was asleep.
6925ff3afceSSheldon Hearn  */
6935ff3afceSSheldon Hearn 
6944909085fSHiroki Sato static uint32_t
695081713dcSJuli Mallett machtime(void)
6965ff3afceSSheldon Hearn {
6975ff3afceSSheldon Hearn 
698e90fa6a9SDavid Malone #define	OFFSET ((uint32_t)25567 * 24*60*60)
699902d9eafSEd Schouten 	return (htonl((uint32_t)(time(NULL) + OFFSET)));
7005ff3afceSSheldon Hearn #undef OFFSET
7015ff3afceSSheldon Hearn }
7025ff3afceSSheldon Hearn 
7035ff3afceSSheldon Hearn /* ARGSUSED */
7044909085fSHiroki Sato static void
705081713dcSJuli Mallett machtime_dg(int s, struct servtab *sep)
7065ff3afceSSheldon Hearn {
707e90fa6a9SDavid Malone 	uint32_t result;
7080cac72f4SYoshinobu Inoue 	struct sockaddr_storage ss;
709a9a948a9SYoshinobu Inoue 	socklen_t size;
7105ff3afceSSheldon Hearn 
7110cac72f4SYoshinobu Inoue 	size = sizeof(ss);
7125ff3afceSSheldon Hearn 	if (recvfrom(s, (char *)&result, sizeof(result), 0,
7130cac72f4SYoshinobu Inoue 		     (struct sockaddr *)&ss, &size) < 0)
7145ff3afceSSheldon Hearn 		return;
7155ff3afceSSheldon Hearn 
7160cac72f4SYoshinobu Inoue 	if (check_loop((struct sockaddr *)&ss, sep))
7175ff3afceSSheldon Hearn 		return;
7185ff3afceSSheldon Hearn 
7195ff3afceSSheldon Hearn 	result = machtime();
7205ff3afceSSheldon Hearn 	(void) sendto(s, (char *) &result, sizeof(result), 0,
721a9a948a9SYoshinobu Inoue 		      (struct sockaddr *)&ss, size);
7225ff3afceSSheldon Hearn }
7235ff3afceSSheldon Hearn 
7245ff3afceSSheldon Hearn /* ARGSUSED */
7254909085fSHiroki Sato static void
7262306f8e9SJuli Mallett machtime_stream(int s, struct servtab *sep __unused)
7275ff3afceSSheldon Hearn {
728e90fa6a9SDavid Malone 	uint32_t result;
7295ff3afceSSheldon Hearn 
7305ff3afceSSheldon Hearn 	result = machtime();
731a3ad0852SSheldon Hearn 	(void) send(s, (char *) &result, sizeof(result), MSG_EOF);
7325ff3afceSSheldon Hearn }
7335ff3afceSSheldon Hearn 
7345ff3afceSSheldon Hearn /*
7359a16e31aSSheldon Hearn  * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
7369a16e31aSSheldon Hearn  * services based on the service name sent.
7379a16e31aSSheldon Hearn  *
7385ff3afceSSheldon Hearn  *  Based on TCPMUX.C by Mark K. Lottor November 1988
7395ff3afceSSheldon Hearn  *  sri-nic::ps:<mkl>tcpmux.c
7405ff3afceSSheldon Hearn  */
7415ff3afceSSheldon Hearn 
7425ff3afceSSheldon Hearn #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
7435ff3afceSSheldon Hearn #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
7445ff3afceSSheldon Hearn 
7455ff3afceSSheldon Hearn static int		/* # of characters up to \r,\n or \0 */
746f03ef840SBaptiste Daroussin get_line(int fd, char *buf, int len)
7475ff3afceSSheldon Hearn {
7485ff3afceSSheldon Hearn 	int count = 0, n;
7495ff3afceSSheldon Hearn 	struct sigaction sa;
7505ff3afceSSheldon Hearn 
7515ff3afceSSheldon Hearn 	sa.sa_flags = 0;
7525ff3afceSSheldon Hearn 	sigemptyset(&sa.sa_mask);
7535ff3afceSSheldon Hearn 	sa.sa_handler = SIG_DFL;
7545ff3afceSSheldon Hearn 	sigaction(SIGALRM, &sa, (struct sigaction *)0);
7555ff3afceSSheldon Hearn 	do {
7565ff3afceSSheldon Hearn 		alarm(10);
7575ff3afceSSheldon Hearn 		n = read(fd, buf, len-count);
7585ff3afceSSheldon Hearn 		alarm(0);
7595ff3afceSSheldon Hearn 		if (n == 0)
7605ff3afceSSheldon Hearn 			return (count);
7615ff3afceSSheldon Hearn 		if (n < 0)
7625ff3afceSSheldon Hearn 			return (-1);
7635ff3afceSSheldon Hearn 		while (--n >= 0) {
7645ff3afceSSheldon Hearn 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
7655ff3afceSSheldon Hearn 				return (count);
7665ff3afceSSheldon Hearn 			count++;
7675ff3afceSSheldon Hearn 			buf++;
7685ff3afceSSheldon Hearn 		}
7695ff3afceSSheldon Hearn 	} while (count < len);
7705ff3afceSSheldon Hearn 	return (count);
7715ff3afceSSheldon Hearn }
7725ff3afceSSheldon Hearn 
7735ff3afceSSheldon Hearn struct servtab *
774081713dcSJuli Mallett tcpmux(int s)
7755ff3afceSSheldon Hearn {
7765ff3afceSSheldon Hearn 	struct servtab *sep;
7775ff3afceSSheldon Hearn 	char service[MAX_SERV_LEN+1];
7785ff3afceSSheldon Hearn 	int len;
7795ff3afceSSheldon Hearn 
7805ff3afceSSheldon Hearn 	/* Get requested service name */
781f03ef840SBaptiste Daroussin 	if ((len = get_line(s, service, MAX_SERV_LEN)) < 0) {
7825ff3afceSSheldon Hearn 		strwrite(s, "-Error reading service name\r\n");
7835ff3afceSSheldon Hearn 		return (NULL);
7845ff3afceSSheldon Hearn 	}
7855ff3afceSSheldon Hearn 	service[len] = '\0';
7865ff3afceSSheldon Hearn 
7875ff3afceSSheldon Hearn 	if (debug)
7885ff3afceSSheldon Hearn 		warnx("tcpmux: someone wants %s", service);
7895ff3afceSSheldon Hearn 
7905ff3afceSSheldon Hearn 	/*
7915ff3afceSSheldon Hearn 	 * Help is a required command, and lists available services,
7925ff3afceSSheldon Hearn 	 * one per line.
7935ff3afceSSheldon Hearn 	 */
7945ff3afceSSheldon Hearn 	if (!strcasecmp(service, "help")) {
7955ff3afceSSheldon Hearn 		for (sep = servtab; sep; sep = sep->se_next) {
7965ff3afceSSheldon Hearn 			if (!ISMUX(sep))
7975ff3afceSSheldon Hearn 				continue;
7985ff3afceSSheldon Hearn 			(void)write(s,sep->se_service,strlen(sep->se_service));
7995ff3afceSSheldon Hearn 			strwrite(s, "\r\n");
8005ff3afceSSheldon Hearn 		}
8015ff3afceSSheldon Hearn 		return (NULL);
8025ff3afceSSheldon Hearn 	}
8035ff3afceSSheldon Hearn 
8045ff3afceSSheldon Hearn 	/* Try matching a service in inetd.conf with the request */
8055ff3afceSSheldon Hearn 	for (sep = servtab; sep; sep = sep->se_next) {
8065ff3afceSSheldon Hearn 		if (!ISMUX(sep))
8075ff3afceSSheldon Hearn 			continue;
8085ff3afceSSheldon Hearn 		if (!strcasecmp(service, sep->se_service)) {
8095ff3afceSSheldon Hearn 			if (ISMUXPLUS(sep)) {
8105ff3afceSSheldon Hearn 				strwrite(s, "+Go\r\n");
8115ff3afceSSheldon Hearn 			}
8125ff3afceSSheldon Hearn 			return (sep);
8135ff3afceSSheldon Hearn 		}
8145ff3afceSSheldon Hearn 	}
8155ff3afceSSheldon Hearn 	strwrite(s, "-Service not available\r\n");
8165ff3afceSSheldon Hearn 	return (NULL);
8175ff3afceSSheldon Hearn }
818