builtins.c revision 77685
1109905Smarkm/*-
270657Sobrien * Copyright (c) 1983, 1991, 1993, 1994
370657Sobrien *	The Regents of the University of California.  All rights reserved.
470657Sobrien *
570657Sobrien * Redistribution and use in source and binary forms, with or without
670657Sobrien * modification, are permitted provided that the following conditions
770657Sobrien * are met:
870657Sobrien * 1. Redistributions of source code must retain the above copyright
970657Sobrien *    notice, this list of conditions and the following disclaimer.
1070657Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1170657Sobrien *    notice, this list of conditions and the following disclaimer in the
1270657Sobrien *    documentation and/or other materials provided with the distribution.
1370657Sobrien *
1470657Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1570657Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1670657Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1770657Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1870657Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1970657Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2070657Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2170657Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2270657Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2370657Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2470657Sobrien * SUCH DAMAGE.
2570657Sobrien *
2670657Sobrien * $FreeBSD: head/usr.sbin/inetd/builtins.c 77685 2001-06-04 11:47:08Z dwmalone $
2770657Sobrien *
2870657Sobrien */
2970657Sobrien
3070657Sobrien#include <sys/filio.h>
3170657Sobrien#include <sys/ioccom.h>
3270657Sobrien#include <sys/param.h>
3370657Sobrien#include <sys/stat.h>
3470657Sobrien#include <sys/socket.h>
3570657Sobrien#include <sys/sysctl.h>
3670657Sobrien#include <sys/ucred.h>
3770657Sobrien#include <sys/uio.h>
3870657Sobrien#include <sys/utsname.h>
3970657Sobrien
4070657Sobrien#include <ctype.h>
4170657Sobrien#include <err.h>
42216338Sdim#include <errno.h>
43216338Sdim#include <fcntl.h>
44216338Sdim#include <limits.h>
45100167Smarkm#include <pwd.h>
4670657Sobrien#include <signal.h>
4770657Sobrien#include <stdlib.h>
4870657Sobrien#include <string.h>
49100167Smarkm#include <sysexits.h>
5070657Sobrien#include <syslog.h>
5170657Sobrien#include <unistd.h>
52100167Smarkm
5393399Smarkm#include "inetd.h"
5470657Sobrien
55232832Skibextern int	 debug;
5670657Sobrienextern struct servtab *servtab;
5780740Smp
5880740Smpchar ring[128];
5980740Smpchar *endring;
60209869Snwhitehorn
61209869Snwhitehorn
6270657Sobrienstruct biltin biltins[] = {
6370657Sobrien	/* Echo received data */
6470657Sobrien	{ "echo",	SOCK_STREAM,	1, -1,	echo_stream },
6570657Sobrien	{ "echo",	SOCK_DGRAM,	0, 1,	echo_dg },
6670657Sobrien
6770657Sobrien	/* Internet /dev/null */
6870657Sobrien	{ "discard",	SOCK_STREAM,	1, -1,	discard_stream },
6970657Sobrien	{ "discard",	SOCK_DGRAM,	0, 1,	discard_dg },
7080740Smp
7170657Sobrien	/* Return 32 bit time since 1900 */
72100167Smarkm	{ "time",	SOCK_STREAM,	0, -1,	machtime_stream },
73100167Smarkm	{ "time",	SOCK_DGRAM,	0, 1,	machtime_dg },
7493036Sobrien
7593036Sobrien	/* Return human-readable time */
7693036Sobrien	{ "daytime",	SOCK_STREAM,	0, -1,	daytime_stream },
77100167Smarkm	{ "daytime",	SOCK_DGRAM,	0, 1,	daytime_dg },
7870657Sobrien
79100167Smarkm	/* Familiar character generator */
80100167Smarkm	{ "chargen",	SOCK_STREAM,	1, -1,	chargen_stream },
81100167Smarkm	{ "chargen",	SOCK_DGRAM,	0, 1,	chargen_dg },
8270657Sobrien
8370657Sobrien	{ "tcpmux",	SOCK_STREAM,	1, -1,	(void (*)())tcpmux },
84245133Skib
8570657Sobrien	{ "auth",	SOCK_STREAM,	1, -1,	ident_stream },
8670657Sobrien
8770657Sobrien	{ NULL }
8870657Sobrien};
8970657Sobrien
9070657Sobrien/*
91133754Sdfr * RFC864 Character Generator Protocol. Generates character data without
92133754Sdfr * any regard for input.
9370657Sobrien */
9470657Sobrien
9570657Sobrienvoid
9670657Sobrieninitring()
9770657Sobrien{
98232832Skib	int i;
99232832Skib
100232832Skib	endring = ring;
10170657Sobrien
10270657Sobrien	for (i = 0; i <= 128; ++i)
10370657Sobrien		if (isprint(i))
10470657Sobrien			*endring++ = i;
10570657Sobrien}
10670657Sobrien
10770657Sobrien/* ARGSUSED */
108void
109chargen_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 */
146void
147chargen_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 */
184void
185daytime_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 */
210void
211daytime_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 */
230void
231discard_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 */
241void
242discard_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 */
265void
266echo_dg(s, sep)			/* Echo service -- echo data back */
267	int s;
268	struct servtab *sep;
269{
270	char buffer[65536]; /* Should be sizeof(max datagram). */
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 */
287void
288echo_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/* RFC 1413 says the following are the only errors you can return. */
310#define ID_INVALID	"INVALID-PORT"	/* Port number improperly specified. */
311#define ID_NOUSER	"NO-USER"	/* Port not in use/not identifable. */
312#define ID_HIDDEN	"HIDDEN-USER"	/* Hiden at user's request. */
313#define ID_UNKNOWN	"UNKNOWN-ERROR"	/* Everything else. */
314
315/* ARGSUSED */
316void
317iderror(lport, fport, s, er)	/* Generic ident_stream error-sending func */
318	int lport, fport, s;
319	char *er;
320{
321	char *p;
322
323	asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er);
324	if (p == NULL) {
325		syslog(LOG_ERR, "asprintf: %m");
326		exit(EX_OSERR);
327	}
328	send(s, p, strlen(p), MSG_EOF);
329	free(p);
330
331	exit(0);
332}
333
334/* ARGSUSED */
335void
336ident_stream(s, sep)		/* Ident service (AKA "auth") */
337	int s;
338	struct servtab *sep;
339{
340	struct utsname un;
341	struct stat sb;
342	struct sockaddr_in sin[2];
343#ifdef INET6
344	struct sockaddr_in6 sin6[2];
345#endif
346	struct sockaddr_storage ss[2];
347	struct xucred uc;
348	struct timeval tv = {
349		10,
350		0
351	}, to;
352	struct passwd *pw = NULL;
353	fd_set fdset;
354	char buf[BUFSIZE], *p, **av, *osname = NULL, e;
355	char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */
356	socklen_t socklen;
357	ssize_t ssize;
358	size_t size, bufsiz;
359	int c, fflag = 0, nflag = 0, rflag = 0, argc = 0;
360	int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen;
361	u_short lport, fport;
362
363	inetd_setproctitle(sep->se_service, s);
364	/*
365	 * Reset getopt() since we are a fork() but not an exec() from
366	 * a parent which used getopt() already.
367	 */
368	optind = 1;
369	optreset = 1;
370	/*
371	 * Take the internal argument vector and count it out to make an
372	 * argument count for getopt. This can be used for any internal
373	 * service to read arguments and use getopt() easily.
374	 */
375	for (av = sep->se_argv; *av; av++)
376		argc++;
377	if (argc) {
378		int sec, usec;
379		size_t i;
380		u_int32_t random;
381
382		while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1)
383			switch (c) {
384			case 'd':
385				if (!gflag)
386					strlcpy(idbuf, optarg, sizeof(idbuf));
387				break;
388			case 'f':
389				fflag = 1;
390				break;
391			case 'F':
392				fflag = 1;
393				Fflag=1;
394				break;
395			case 'g':
396				gflag = 1;
397				random = 0;	/* Shush, compiler. */
398				/*
399				 * The number of bits in "random" divided
400				 * by the number of bits needed per iteration
401				 * gives a more optimal way to reload the
402				 * random number only when necessary.
403				 *
404				 * 32 bits from arc4random corrisponds to
405				 * about 6 base-36 digits, so we reseed evey 6.
406				 */
407				for (i = 0; i < sizeof(idbuf) - 1; i++) {
408					static const char *const base36 =
409					    "0123456789"
410					    "abcdefghijklmnopqrstuvwxyz";
411					if (i % 6 == 0)
412						random = arc4random();
413					idbuf[i] = base36[random % 36];
414					random /= 36;
415				}
416				idbuf[i] = '\0';
417				break;
418			case 'i':
419				iflag = 1;
420				break;
421			case 'n':
422				nflag = 1;
423				break;
424			case 'o':
425				osname = optarg;
426				break;
427			case 'r':
428				rflag = 1;
429				break;
430			case 't':
431				switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
432				case 2:
433					tv.tv_usec = usec;
434					/* FALLTHROUGH */
435				case 1:
436					tv.tv_sec = sec;
437					break;
438				default:
439					if (debug)
440						warnx("bad -t argument");
441					break;
442				}
443				break;
444			default:
445				break;
446			}
447	}
448	if (osname == NULL) {
449		if (uname(&un) == -1)
450			iderror(0, 0, s, ID_UNKNOWN);
451		osname = un.sysname;
452	}
453
454	/*
455	 * We're going to prepare for and execute reception of a
456	 * packet of data from the user. The data is in the format
457	 * "local_port , foreign_port\r\n" (with local being the
458	 * server's port and foreign being the client's.)
459	 */
460	gettimeofday(&to, NULL);
461	to.tv_sec += tv.tv_sec;
462	to.tv_usec += tv.tv_usec;
463	if (to.tv_usec >= 1000000) {
464		to.tv_usec -= 1000000;
465		to.tv_sec++;
466	}
467
468	size = 0;
469	bufsiz = sizeof(buf) - 1;
470	FD_ZERO(&fdset);
471 	while (bufsiz > 0) {
472		gettimeofday(&tv, NULL);
473		tv.tv_sec = to.tv_sec - tv.tv_sec;
474		tv.tv_usec = to.tv_usec - tv.tv_usec;
475		if (tv.tv_usec < 0) {
476			tv.tv_usec += 1000000;
477			tv.tv_sec--;
478		}
479		if (tv.tv_sec < 0)
480			break;
481		FD_SET(s, &fdset);
482		if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
483			iderror(0, 0, s, ID_UNKNOWN);
484		if (ioctl(s, FIONREAD, &onreadlen) == -1)
485			iderror(0, 0, s, ID_UNKNOWN);
486		if (onreadlen > bufsiz)
487			onreadlen = bufsiz;
488		ssize = read(s, &buf[size], (size_t)onreadlen);
489		if (ssize == -1)
490			iderror(0, 0, s, ID_UNKNOWN);
491		else if (ssize == 0)
492			break;
493		bufsiz -= ssize;
494		size += ssize;
495		if (memchr(&buf[size - ssize], '\n', ssize) != NULL)
496			break;
497 	}
498	buf[size] = '\0';
499	/* Read two characters, and check for a delimiting character */
500	if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
501		iderror(0, 0, s, ID_INVALID);
502
503	/* Send garbage? */
504	if (gflag)
505		goto printit;
506
507	/*
508	 * If not "real" (-r), send a HIDDEN-USER error for everything.
509	 * If -d is used to set a fallback username, this is used to
510	 * override it, and the fallback is returned instead.
511	 */
512	if (!rflag) {
513		if (*idbuf == '\0')
514			iderror(lport, fport, s, ID_HIDDEN);
515		goto printit;
516	}
517
518	/*
519	 * We take the input and construct an array of two sockaddr_ins
520	 * which contain the local address information and foreign
521	 * address information, respectively, used to look up the
522	 * credentials for the socket (which are returned by the
523	 * sysctl "net.inet.tcp.getcred" when we call it.)
524	 */
525	socklen = sizeof(ss[0]);
526	if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
527		iderror(lport, fport, s, ID_UNKNOWN);
528	socklen = sizeof(ss[1]);
529	if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
530		iderror(lport, fport, s, ID_UNKNOWN);
531	if (ss[0].ss_family != ss[1].ss_family)
532		iderror(lport, fport, s, ID_UNKNOWN);
533	size = sizeof(uc);
534	switch (ss[0].ss_family) {
535	case AF_INET:
536		sin[0] = *(struct sockaddr_in *)&ss[0];
537		sin[0].sin_port = htons(lport);
538		sin[1] = *(struct sockaddr_in *)&ss[1];
539		sin[1].sin_port = htons(fport);
540		if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin,
541				 sizeof(sin)) == -1)
542			getcredfail = errno;
543		break;
544#ifdef INET6
545	case AF_INET6:
546		sin6[0] = *(struct sockaddr_in6 *)&ss[0];
547		sin6[0].sin6_port = htons(lport);
548		sin6[1] = *(struct sockaddr_in6 *)&ss[1];
549		sin6[1].sin6_port = htons(fport);
550		if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
551				 sizeof(sin6)) == -1)
552			getcredfail = errno;
553		break;
554#endif
555	default: /* should not reach here */
556		getcredfail = EAFNOSUPPORT;
557		break;
558	}
559	if (getcredfail != 0) {
560		if (*idbuf == '\0')
561			iderror(lport, fport, s,
562			    getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN);
563		goto printit;
564	}
565
566	/* Look up the pw to get the username and home directory*/
567	errno = 0;
568	pw = getpwuid(uc.cr_uid);
569	if (pw == NULL)
570		iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN);
571
572	if (iflag)
573		snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid);
574	else
575		strlcpy(idbuf, pw->pw_name, sizeof(idbuf));
576
577	/*
578	 * If enabled, we check for a file named ".noident" in the user's
579	 * home directory. If found, we return HIDDEN-USER.
580	 */
581	if (nflag) {
582		if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
583			iderror(lport, fport, s, ID_UNKNOWN);
584		if (lstat(p, &sb) == 0) {
585			free(p);
586			iderror(lport, fport, s, ID_HIDDEN);
587		}
588		free(p);
589	}
590
591	/*
592	 * Here, if enabled, we read a user's ".fakeid" file in their
593	 * home directory. It consists of a line containing the name
594	 * they want.
595	 */
596	if (fflag) {
597		int fakeid_fd;
598
599		/*
600		 * Here we set ourself to effectively be the user, so we don't
601		 * open any files we have no permission to open, especially
602		 * symbolic links to sensitive root-owned files or devices.
603		 */
604		if (initgroups(pw->pw_name, pw->pw_gid) == -1)
605			iderror(lport, fport, s, ID_UNKNOWN);
606		if (seteuid(pw->pw_uid) == -1)
607			iderror(lport, fport, s, ID_UNKNOWN);
608		/*
609		 * We can't stat() here since that would be a race
610		 * condition.
611		 * Therefore, we open the file we have permissions to open
612		 * and if it's not a regular file, we close it and end up
613		 * returning the user's real username.
614		 */
615		if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
616			iderror(lport, fport, s, ID_UNKNOWN);
617		fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
618		free(p);
619		if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 ||
620		    !S_ISREG(sb.st_mode))
621			goto fakeid_fail;
622
623		if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0)
624			goto fakeid_fail;
625		buf[ssize] = '\0';
626
627		/*
628		 * Usually, the file will have the desired identity
629		 * in the form "identity\n". Allow for leading white
630		 * space and trailing white space/end of line.
631		 */
632		p = buf;
633		p += strspn(p, " \t");
634		p[strcspn(p, " \t\r\n")] = '\0';
635		if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */
636			p[MAXLOGNAME - 1] = '\0';
637
638		/*
639		 * If the name is a zero-length string or matches it
640		 * the id or name of another user (unless permitted by -F)
641		 * then it is invalid.
642		 */
643		if (*p == '\0')
644			goto fakeid_fail;
645		if (!Fflag) {
646			if (iflag) {
647				if (p[strspn(p, "0123456789")] == '\0' &&
648				    getpwuid(atoi(p)) != NULL)
649					goto fakeid_fail;
650			} else {
651				if (getpwnam(p) != NULL)
652					goto fakeid_fail;
653			}
654		}
655
656		strlcpy(idbuf, p, sizeof(idbuf));
657
658fakeid_fail:
659		if (fakeid_fd != -1)
660			close(fakeid_fd);
661	}
662
663printit:
664	/* Finally, we make and send the reply. */
665	if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
666	    idbuf) == -1) {
667		syslog(LOG_ERR, "asprintf: %m");
668		exit(EX_OSERR);
669	}
670	send(s, p, strlen(p), MSG_EOF);
671	free(p);
672
673	exit(0);
674}
675
676/*
677 * RFC738 Time Server.
678 * Return a machine readable date and time, in the form of the
679 * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
680 * returns the number of seconds since midnight, Jan 1, 1970,
681 * we must add 2208988800 seconds to this figure to make up for
682 * some seventy years Bell Labs was asleep.
683 */
684
685unsigned long
686machtime()
687{
688	struct timeval tv;
689
690	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
691		if (debug)
692			warnx("unable to get time of day");
693		return (0L);
694	}
695#define	OFFSET ((u_long)25567 * 24*60*60)
696	return (htonl((long)(tv.tv_sec + OFFSET)));
697#undef OFFSET
698}
699
700/* ARGSUSED */
701void
702machtime_dg(s, sep)
703	int s;
704	struct servtab *sep;
705{
706	unsigned long result;
707	struct sockaddr_storage ss;
708	socklen_t size;
709
710	size = sizeof(ss);
711	if (recvfrom(s, (char *)&result, sizeof(result), 0,
712		     (struct sockaddr *)&ss, &size) < 0)
713		return;
714
715	if (check_loop((struct sockaddr *)&ss, sep))
716		return;
717
718	result = machtime();
719	(void) sendto(s, (char *) &result, sizeof(result), 0,
720		      (struct sockaddr *)&ss, size);
721}
722
723/* ARGSUSED */
724void
725machtime_stream(s, sep)
726	int s;
727	struct servtab *sep;
728{
729	unsigned long result;
730
731	result = machtime();
732	(void) send(s, (char *) &result, sizeof(result), MSG_EOF);
733}
734
735/*
736 * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
737 * services based on the service name sent.
738 *
739 *  Based on TCPMUX.C by Mark K. Lottor November 1988
740 *  sri-nic::ps:<mkl>tcpmux.c
741 */
742
743#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
744#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
745
746static int		/* # of characters upto \r,\n or \0 */
747getline(fd, buf, len)
748	int fd;
749	char *buf;
750	int len;
751{
752	int count = 0, n;
753	struct sigaction sa;
754
755	sa.sa_flags = 0;
756	sigemptyset(&sa.sa_mask);
757	sa.sa_handler = SIG_DFL;
758	sigaction(SIGALRM, &sa, (struct sigaction *)0);
759	do {
760		alarm(10);
761		n = read(fd, buf, len-count);
762		alarm(0);
763		if (n == 0)
764			return (count);
765		if (n < 0)
766			return (-1);
767		while (--n >= 0) {
768			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
769				return (count);
770			count++;
771			buf++;
772		}
773	} while (count < len);
774	return (count);
775}
776
777struct servtab *
778tcpmux(s)
779	int s;
780{
781	struct servtab *sep;
782	char service[MAX_SERV_LEN+1];
783	int len;
784
785	/* Get requested service name */
786	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
787		strwrite(s, "-Error reading service name\r\n");
788		return (NULL);
789	}
790	service[len] = '\0';
791
792	if (debug)
793		warnx("tcpmux: someone wants %s", service);
794
795	/*
796	 * Help is a required command, and lists available services,
797	 * one per line.
798	 */
799	if (!strcasecmp(service, "help")) {
800		for (sep = servtab; sep; sep = sep->se_next) {
801			if (!ISMUX(sep))
802				continue;
803			(void)write(s,sep->se_service,strlen(sep->se_service));
804			strwrite(s, "\r\n");
805		}
806		return (NULL);
807	}
808
809	/* Try matching a service in inetd.conf with the request */
810	for (sep = servtab; sep; sep = sep->se_next) {
811		if (!ISMUX(sep))
812			continue;
813		if (!strcasecmp(service, sep->se_service)) {
814			if (ISMUXPLUS(sep)) {
815				strwrite(s, "+Go\r\n");
816			}
817			return (sep);
818		}
819	}
820	strwrite(s, "-Service not available\r\n");
821	return (NULL);
822}
823