[BusyBox] vodz telnetd applet

David Kimdon dwhedon at gordian.com
Thu Sep 13 10:35:45 UTC 2001


> So if we can separate all the server processes into a separate binary, I think
> that would be a good thing.  We could still have that be part of the busybox
> package, and still have it use libbb...
> 

The following patch adds telnetd as a separate binary.  For lack of a
better name I call it 'trustybox'.  Debian boot-floppies has lazybox,
perhaps this is a natural extention.

Comments?

-David

P.S. There is one arguably unrelated change, a fix to dpkg's usage
without which telnetd's usage gets messed up.


Index: .cvsignore
===================================================================
RCS file: /var/cvs/busybox/.cvsignore,v
retrieving revision 1.8
diff -u -r1.8 .cvsignore
--- .cvsignore	2001/07/06 19:19:21	1.8
+++ .cvsignore	2001/09/13 16:11:47
@@ -1,3 +1,5 @@
+trustybox
+trustybox.links
 busybox
 busybox.links
 _install
Index: Makefile
===================================================================
RCS file: /var/cvs/busybox/Makefile,v
retrieving revision 1.235
diff -u -r1.235 Makefile
--- Makefile	2001/08/22 04:16:36	1.235
+++ Makefile	2001/09/13 16:11:51
@@ -17,7 +17,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 #
 
-PROG      := busybox
 VERSION   := 0.61.pre
 BUILDTIME := $(shell TZ=UTC date -u "+%Y.%m.%d-%H:%M%z")
 export VERSION
@@ -143,11 +142,11 @@
 ifeq ($(strip $(DODEBUG)),true)
     CFLAGS  += $(WARNINGS) -g -D_GNU_SOURCE
     LDFLAGS += -Wl,-warn-common
-    STRIP    =
+    STRIP    = /bin/true
 else
     CFLAGS  += $(WARNINGS) $(OPTIMIZATION) -fomit-frame-pointer -D_GNU_SOURCE
     LDFLAGS += -s -Wl,-warn-common
-    STRIP    = $(STRIPTOOL) --remove-section=.note --remove-section=.comment $(PROG)
+    STRIP    = $(STRIPTOOL) --remove-section=.note --remove-section=.comment
 endif
 ifeq ($(strip $(DOSTATIC)),true)
     LDFLAGS += --static
@@ -211,6 +210,8 @@
 -include applet_source_list
 
 OBJECTS   = $(APPLET_SOURCES:.c=.o) busybox.o usage.o applets.o
+TB_OBJECTS   = $(TB_APPLET_SOURCES:.c=.o)
+TB_MAIN = tb_busybox.o tb_usage.o tb_applets.o
 CFLAGS    += $(CROSS_CFLAGS)
 CFLAGS    += -DBB_VER='"$(VERSION)"'
 CFLAGS    += -DBB_BT='"$(BUILDTIME)"'
@@ -259,7 +260,7 @@
 endif
 
 LIBBB_MSRC=libbb/messages.c
-LIBBB_MESSAGES= full_version name_too_long omitting_directory not_a_directory \
+LIBBB_MESSAGES= full_version tb_full_version name_too_long omitting_directory not_a_directory \
 memory_exhausted invalid_date invalid_option io_error dash_dash_help \
 write_error too_few_args name_longer_than_foo unknown can_not_create_raw_socket
 LIBBB_MOBJ=$(patsubst %,$(LIBBB)/%.o, $(LIBBB_MESSAGES))
@@ -276,10 +277,10 @@
 
 .EXPORT_ALL_VARIABLES:
 
-all: applet_source_list busybox busybox.links doc
+all: applet_source_list $(ALL) doc
 
 applet_source_list: busybox.sh Config.h
-	(echo -n "APPLET_SOURCES := "; BB_SRC_DIR=$(BB_SRC_DIR) $(SHELL) $^) > $@
+	(BB_SRC_DIR=$(BB_SRC_DIR) $(SHELL) $^) > $@
 
 doc: olddoc
 
@@ -344,23 +345,37 @@
 
 busybox: $(PWD_LIB) $(LIBBB_LIB) $(OBJECTS) 
 	$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBBB_LIB) $(PWD_LIB) $(LIBRARIES)
-	$(STRIP)
+	$(STRIP) $@
 
+trustybox: $(PWD_LIB) $(LIBBB_LIB) $(TB_OBJECTS) $(TB_MAIN)
+	$(CC) $(LDFLAGS) -o $@ $(TB_OBJECTS) $(TB_MAIN) $(LIBBB_LIB) $(PWD_LIB) $(LIBRARIES)
+	$(STRIP) $@
+
 # Without VPATH, rule expands to "/bin/sh busybox.mkll Config.h applets.h"
 # but with VPATH, some or all of those file names are resolved to the
 # directories in which they live.
 busybox.links: busybox.mkll Config.h applets.h
 	- $(SHELL) $^ >$@
 
+trustybox.links: busybox.mkll tb_Config.h applets.h
+	- $(SHELL) $^ >$@
+
 nfsmount.o cmdedit.o: %.o: %.h
 ash.o hush.o lash.o msh.o: cmdedit.h
-$(OBJECTS): %.o: %.c Config.h busybox.h applets.h Makefile
+$(TB_OBJECTS) $(OBJECTS): %.o: %.c Config.h busybox.h applets.h Makefile
 ifeq ($(strip $(BB_SRC_DIR)),)
 	$(CC) $(CFLAGS) -I. $(patsubst %,-I%,$(subst :, ,$(BB_SRC_DIR))) -c $< -o $*.o
 else
 	$(CC) $(CFLAGS) -I- -I. $(patsubst %,-I%,$(subst :, ,$(BB_SRC_DIR))) -c $< -o $*.o
 endif
 
+$(TB_MAIN) : tb_%.o: %.c Config.h busybox.h applets.h Makefile
+ifeq ($(strip $(BB_SRC_DIR)),)
+	$(CC) $(CFLAGS) -DTRUSTYBOX -I. $(patsubst %,-I%,$(subst :, ,$(BB_SRC_DIR))) -c $< -o tb_$*.o
+else
+	$(CC) $(CFLAGS) -DTRUSTYBOX -I- -I. $(patsubst %,-I%,$(subst :, ,$(BB_SRC_DIR))) -c $< -o tb_$*.o
+endif
+
 $(PWD_OBJS): %.o: %.c Config.h busybox.h applets.h Makefile
 	- mkdir -p $(PWD_GRP)
 	$(CC) $(CFLAGS) $(PWD_CFLAGS) -c $< -o $*.o
@@ -404,22 +419,25 @@
 	    docs/busybox.pdf docs/busybox.net/busybox.html
 	- rm -f multibuild.log Config.h.orig *.gdb *.elf
 	- rm -rf docs/busybox _install libpwd.a libbb.a pod2htm*
-	- rm -f busybox.links libbb/loop.h *~ slist.mk core applet_source_list
+	- rm -f busybox.links trustybox.links libbb/loop.h *~ slist.mk core applet_source_list
 	- find -name \*.o -exec rm -f {} \;
 
 distclean: clean
-	- rm -f busybox applet_source_list
+	- rm -f busybox trustybox applet_source_list
 	- cd tests && $(MAKE) distclean
+
+install: $(INSTALL)
 
-install: install.sh busybox busybox.links
-	$(SHELL) $< $(PREFIX)
+install-%: install.sh % %.links
+	$(SHELL) $< $(PREFIX) $*
 
-install-hardlinks: install.sh busybox busybox.links
-	$(SHELL) $< $(PREFIX) --hardlinks
+install-%-hardlinks: install.sh % %.links
+	$(SHELL) $< $(PREFIX) $* --hardlinks
 
 debug_pristine:
 	@ echo VPATH=\"$(VPATH)\"
 	@ echo OBJECTS=\"$(OBJECTS)\"
+	@ echo TB_OBJECTS=\"$(TB_OBJECTS)\"
 
 dist release: distclean doc
 	cd ..;					\
Index: applets.c
===================================================================
RCS file: /var/cvs/busybox/applets.c,v
retrieving revision 1.9
diff -u -r1.9 applets.c
--- applets.c	2001/08/27 17:19:38	1.9
+++ applets.c	2001/09/13 16:11:51
@@ -55,7 +55,12 @@
 	if(*usage_string == 0)
 		format_string = "%s\n\nNo help available.\n\n";
 	fprintf(stderr, format_string,
-			full_version, applet_using->name, usage_string);
+#ifdef TRUSTYBOX
+			tb_full_version,
+#else
+			full_version, 
+#endif
+			applet_using->name, usage_string);
 	exit(EXIT_FAILURE);
 }
 
@@ -85,7 +90,7 @@
 	if ((applet_using = find_applet_by_name(name)) != NULL) {
 		applet_name = applet_using->name;
 		if (argv[1] && strcmp(argv[1], "--help") == 0) {
-			if (strcmp(applet_using->name, "busybox")==0) {
+			if (strcmp(applet_using->name, BINNAME)==0) {
 				if(argv[2])
 				  applet_using = find_applet_by_name(argv[2]);
 				 else
@@ -100,7 +105,7 @@
 	}
 	/* Just in case they have renamed busybox - Check argv[1] */
 	if (recurse_level == 1) {
-		run_applet_by_name("busybox", argc, argv);
+		run_applet_by_name(BINNAME, argc, argv);
 	}
 	recurse_level--;
 }
Index: applets.h
===================================================================
RCS file: /var/cvs/busybox/applets.h,v
retrieving revision 1.40
diff -u -r1.40 applets.h
--- applets.h	2001/08/27 18:55:05	1.40
+++ applets.h	2001/09/13 16:11:52
@@ -64,7 +64,9 @@
 #ifdef BB_BASENAME
 	APPLET(basename, basename_main, _BB_DIR_USR_BIN)
 #endif
+#ifndef TRUSTYBOX
 	APPLET_NOUSAGE("busybox", busybox_main, _BB_DIR_BIN)
+#endif
 #ifdef BB_CAT
 	APPLET(cat, cat_main, _BB_DIR_BIN)
 #endif
@@ -410,6 +415,9 @@
 #ifdef BB_TELNET
 	APPLET(telnet, telnet_main, _BB_DIR_USR_BIN)
 #endif
+#ifdef TB_TELNETD
+	APPLET(telnetd, telnetd_main, _BB_DIR_USR_SBIN)
+#endif
 #ifdef BB_TEST
 	APPLET(test, test_main, _BB_DIR_USR_BIN)
 #endif
@@ -427,6 +435,9 @@
 #endif
 #ifdef BB_TRUE_FALSE
 	APPLET(true, true_main, _BB_DIR_BIN)
+#endif
+#ifdef TRUSTYBOX
+	APPLET_NOUSAGE("trustybox", busybox_main, _BB_DIR_BIN)
 #endif
 #ifdef BB_TTY
 	APPLET(tty, tty_main, _BB_DIR_USR_BIN)
Index: busybox.c
===================================================================
RCS file: /var/cvs/busybox/busybox.c,v
retrieving revision 1.135
diff -u -r1.135 busybox.c
--- busybox.c	2001/08/27 15:02:32	1.135
+++ busybox.c	2001/09/13 16:11:53
@@ -134,13 +134,8 @@
 		const struct BB_applet *a = applets;
 
 		fprintf(stderr, "%s\n\n"
-				"Usage: busybox [function] [arguments]...\n"
-				"   or: [function] [arguments]...\n\n"
-				"\tBusyBox is a multi-call binary that combines many common Unix\n"
-				"\tutilities into a single executable.  Most people will create a\n"
-				"\tlink to busybox for each function they wish to use, and BusyBox\n"
-				"\twill act like whatever it was invoked as.\n" 
-				"\nCurrently defined functions:\n", full_version);
+				BB_USAGE
+				full_version);
 
 		while (a->name != 0) {
 			col +=
Index: busybox.h
===================================================================
RCS file: /var/cvs/busybox/busybox.h,v
retrieving revision 1.46
diff -u -r1.46 busybox.h
--- busybox.h	2001/07/19 15:00:14	1.46
+++ busybox.h	2001/09/13 16:11:53
@@ -24,7 +24,27 @@
 #ifndef	_BB_INTERNAL_H_
 #define	_BB_INTERNAL_H_    1
 
+#ifdef TRUSTYBOX
+#include "tb_Config.h"
+#define BINNAME "trustybox"
+#define BB_USAGE "Usage: trustybox [function] [arguments]...\n" \
+				"   or: [function] [arguments]...\n\n" \
+				"\tTrustyBox is a multi-call binary that combines many common Unix\n" \
+				"\tutilities into a single executable.  Most people will create a\n" \
+				"\tlink to trustybox for each function they wish to use, and TrustyBox\n" \
+				"\twill act like whatever it was invoked as.\n" \
+				"\nCurrently defined functions:\n",
+#else
 #include "Config.h"
+#define BINNAME "busybox"
+#define BB_USAGE "Usage: busybox [function] [arguments]...\n" \
+				"   or: [function] [arguments]...\n\n" \
+				"\tBusyBox is a multi-call binary that combines many common Unix\n" \
+				"\tutilities into a single executable.  Most people will create a\n" \
+				"\tlink to busybox for each function they wish to use, and BusyBox\n" \
+				"\twill act like whatever it was invoked as.\n" \
+				"\nCurrently defined functions:\n",
+#endif
 
 #include <stdio.h>
 #include <stdarg.h>
@@ -32,6 +52,7 @@
 #include <sys/types.h>
 
 #define BB_BANNER "BusyBox v" BB_VER " (" BB_BT ")"
+#define TB_BANNER "TrustyBox v" BB_VER " (" BB_BT ")"
 
 #ifdef DMALLOC
 #include "dmalloc.h"
Index: busybox.sh
===================================================================
RCS file: /var/cvs/busybox/busybox.sh,v
retrieving revision 1.15
diff -u -r1.15 busybox.sh
--- busybox.sh	2001/05/01 16:24:32	1.15
+++ busybox.sh	2001/09/13 16:11:53
@@ -8,9 +8,28 @@
     sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \
     | tr A-Z a-z | sort
 `
-test "${RAW}" != "" ||  exit
+TB_RAW=` \
+    gcc -E -dM tb_Config.h | \
+    sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<TB_\(.*\)\>/\1.c/gp;' \
+    | tr A-Z a-z | sort
+`
+
 if [ -d "$BB_SRC_DIR" ]; then cd $BB_SRC_DIR; fi
 # By running $RAW through "ls", we avoid listing
 # source files that don't exist.
-ls $RAW 2>/dev/null | tr '\n' ' '
+if [ "${RAW}" != ""  ] ; then
+    APPLETS=`ls $RAW 2>/dev/null | tr '\n' ' '`
+    ALL="busybox busybox.links"
+    INSTALL="install-busybox"
+fi
+
+if [ "${TB_RAW}" != ""  ] ; then
+    TB_APPLETS=`ls $TB_RAW 2>/dev/null | tr '\n' ' '`
+    ALL="${ALL} trustybox trustybox.links"
+    INSTALL="${INSTALL} install-trustybox"
+fi
 
+echo "ALL := ${ALL}"
+echo "INSTALL := ${INSTALL}"
+echo "APPLET_SOURCES := ${APPLETS}"
+echo "TB_APPLET_SOURCES := ${TB_APPLETS}"
Index: install.sh
===================================================================
RCS file: /var/cvs/busybox/install.sh,v
retrieving revision 1.14
diff -u -r1.14 install.sh
--- install.sh	2001/03/08 21:42:11	1.14
+++ install.sh	2001/09/13 16:11:54
@@ -8,36 +8,40 @@
     echo "No installation directory, aborting."
     exit 1;
 fi
-if [ "$2" = "--hardlinks" ]; then
+
+BINARY=$2
+
+h=`sort ${BINARY}.links | uniq`
+
+if [ "$3" = "--hardlinks" ]; then
     linkopts="-f"
 else
     linkopts="-fs"
 fi
-h=`sort busybox.links | uniq`
 
 
-rm -f $prefix/bin/busybox || exit 1
+rm -f $prefix/bin/${BINARY} || exit 1
 mkdir -p $prefix/bin || exit 1
-install -m 755 busybox $prefix/bin/busybox || exit 1
+install -m 755 ${BINARY} $prefix/bin/${BINARY} || exit 1
 
 for i in $h ; do
 	appdir=`dirname $i`
 	mkdir -p $prefix/$appdir || exit 1
 	if [ "$2" = "--hardlinks" ]; then
-	    bb_path="$prefix/bin/busybox"
+	    bb_path="$prefix/bin/${BINARY}"
 	else
 	    case "$appdir" in
 		/)
-		    bb_path="bin/busybox"
+		    bb_path="bin/${BINARY}"
 		;;
 		/bin)
-		    bb_path="busybox"
+		    bb_path="${BINARY}"
 		;;
 		/sbin)
-		    bb_path="../bin/busybox"
+		    bb_path="../bin/${BINARY}"
 		;;
 		/usr/bin|/usr/sbin)
-		    bb_path="../../bin/busybox"
+		    bb_path="../../bin/${BINARY}"
 		;;
 		*)
 		echo "Unknown installation directory: $appdir"
Index: usage.h
===================================================================
RCS file: /var/cvs/busybox/usage.h,v
retrieving revision 1.62
diff -u -r1.62 usage.h
--- usage.h	2001/08/27 18:55:05	1.62
+++ usage.h	2001/09/13 16:12:00
@@ -297,7 +297,7 @@
 	"\t-d\toutput will be in DOS format"
 
 #define dpkg_trivial_usage \
-	"-i package_file\n"
+	"-i package_file\n" \
 	"[-CPru] package_name"
 #define dpkg_full_usage \
 	"\t-i\tInstall the package\n" \
@@ -1579,6 +1593,14 @@
 #define telnet_full_usage \
 	"Telnet is used to establish interactive communication with another\n"\
 	"computer over a network using the TELNET protocol."
+
+#define telnetd_trivial_usage \
+	"[OPTION]"
+#define telnetd_full_usage \
+	"Telnetd listens for incoming TELNET connections on PORT.\n"\
+	"Options:\n" \
+	"\t-p PORT\tlisten for connections on PORT (default 23)\n"\
+	"\t-l LOGIN\texec LOGIN on connect (default /bin/sh)"
 
 #define test_trivial_usage \
 	"EXPRESSION\n  or   [ EXPRESSION ]"
Index: libbb/libbb.h
===================================================================
RCS file: /var/cvs/busybox/libbb/libbb.h,v
retrieving revision 1.61
diff -u -r1.61 libbb.h
--- libbb/libbb.h	2001/08/24 20:35:45	1.61
+++ libbb/libbb.h	2001/09/13 16:12:02
@@ -280,6 +280,7 @@
 };
 
 extern const char *applet_name;
+extern const char * const tb_full_version;
 extern const char * const full_version;
 extern const char * const name_too_long;
 extern const char * const omitting_directory;
Index: libbb/messages.c
===================================================================
RCS file: /var/cvs/busybox/libbb/messages.c,v
retrieving revision 1.5
diff -u -r1.5 messages.c
--- libbb/messages.c	2001/07/12 20:26:32	1.5
+++ libbb/messages.c	2001/09/13 16:12:03
@@ -24,6 +24,9 @@
 #ifdef L_full_version
 	const char * const full_version = BB_BANNER " multi-call binary";
 #endif
+#ifdef L_tb_full_version
+	const char * const tb_full_version = TB_BANNER " multi-call binary";
+#endif
 #ifdef L_name_too_long
 	const char * const name_too_long = "file name too long";
 #endif
--- /dev/null	Thu Nov 30 07:25:58 2000
+++ telnetd.c	Thu Sep  6 09:19:03 2001
@@ -0,0 +1,552 @@
+/* $Id: telnetd.c,v 1.7 2001/08/06 03:13:15 davidw Exp $
+ *
+ * Simple telnet server
+ * Bjorn Wesen, Axis Communications AB (bjornw at axis.com) 
+ *
+ * This file is distributed under the Gnu Public License (GPL),
+ * please see the file LICENSE for further information.
+ * 
+ * ---------------------------------------------------------------------------
+ * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
+ ****************************************************************************
+ *
+ * The telnetd manpage says it all:
+ *
+ *   Telnetd operates by allocating a pseudo-terminal device (see pty(4))  for
+ *   a client, then creating a login process which has the slave side of the
+ *   pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
+ *   master side of the pseudo-terminal, implementing the telnet protocol and
+ *   passing characters between the remote client and the login process.
+ *
+ * Vladimir Oleynik <dzo at simtreas.ru> 2001
+ * 	Set process group corrections, initial busybox port
+ */
+
+/*#define DEBUG 1 */
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <termios.h>
+#ifdef DEBUG
+#define TELCMDS
+#define TELOPTS
+#endif
+#include <arpa/telnet.h>
+#include <ctype.h>
+
+#include "busybox.h"
+
+#define BUFSIZE 4000
+
+static char *loginpath = NULL;
+
+/* shell name and arguments */
+
+static char *argv_init[] = {NULL, NULL};
+
+/* structure that describes a session */
+
+struct tsession {
+	struct tsession *next;
+	int sockfd, ptyfd;
+	int shell_pid;
+	/* two circular buffers */
+	char *buf1, *buf2;
+	int rdidx1, wridx1, size1;
+	int rdidx2, wridx2, size2;
+};
+
+/*
+
+   This is how the buffers are used. The arrows indicate the movement
+   of data.
+
+   +-------+     wridx1++     +------+     rdidx1++     +----------+
+   |       | <--------------  | buf1 | <--------------  |          |
+   |       |     size1--      +------+     size1++      |          |
+   |  pty  |                                            |  socket  |
+   |       |     rdidx2++     +------+     wridx2++     |          |
+   |       |  --------------> | buf2 |  --------------> |          |
+   +-------+     size2++      +------+     size2--      +----------+
+
+   Each session has got two buffers.
+
+*/
+
+static int maxfd;
+
+static struct tsession *sessions;
+
+
+/* 
+
+   Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
+   and must be removed so as to not be interpreted by the terminal).  Make an
+   uninterrupted string of characters fit for the terminal.  Do this by packing
+   all characters meant for the terminal sequentially towards the end of bf. 
+
+   Return a pointer to the beginning of the characters meant for the terminal.
+   and make *processed equal to the number of characters that were actually
+   processed and *num_totty the number of characters that should be sent to
+   the terminal.  
+   
+   Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
+   past (bf + len) then that IAC will be left unprocessed and *processed will be
+   less than len.
+  
+   FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
+   what is the escape character?  We aren't handling that situation here.
+
+  */
+static char *
+remove_iacs(unsigned char *bf, int len, int *processed, int *num_totty) {
+    unsigned char *ptr = bf;
+    unsigned char *totty = bf;
+    unsigned char *end = bf + len;
+   
+    while (ptr < end) {
+	if (*ptr != IAC) {
+	    *totty++ = *ptr++;
+	}
+	else {
+	    if ((ptr+2) < end) {
+		/* the entire IAC is contained in the buffer 
+		   we were asked to process. */
+#ifdef DEBUG
+		fprintf(stderr, "Ignoring IAC %s,%s\n", 
+			*ptr, TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
+#endif
+		ptr += 3;
+	    } else {
+		/* only the beginning of the IAC is in the 
+		   buffer we were asked to process, we can't
+		   process this char. */
+		break;
+	    }
+	}
+    }
+
+    *processed = ptr - bf;
+    *num_totty = totty - bf;
+    /* move the chars meant for the terminal towards the end of the 
+       buffer. */
+    return memmove(ptr - (totty - bf) , bf, totty - bf);
+}
+
+
+static int
+getpty(char *line)
+{
+	int p;
+#ifdef HAVE_DEVPTS_FS
+	p = open("/dev/ptmx", 2);
+	if (p > 0) {
+	    grantpt(p);
+	    unlockpt(p);
+	    strcpy(line, ptsname(p));
+	    return(p);
+	}
+#else
+	struct stat stb;
+	int i;
+	int j;
+
+
+	strcpy(line, "/dev/ptyXX");
+
+	for (i = 0; i < 16; i++) {
+		line[8] = "pqrstuvwxyzabcde"[i];
+		line[9] = '0';
+		if (stat(line, &stb) < 0) {
+			continue;
+		}
+		for (j = 0; j < 16; j++) {
+			line[9] = "0123456789abcdef"[j];
+			if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
+				line[5] = 't';
+				return p;
+			}
+		}
+	}
+#endif /* HAVE_DEVPTS_FS */
+	return -1;
+}
+
+
+static void
+send_iac(struct tsession *ts, unsigned char command, int option)
+{
+	/* We rely on that there is space in the buffer for now.  */
+	char *b = ts->buf2 + ts->rdidx2;
+	*b++ = IAC;
+	*b++ = command;
+	*b++ = option;
+	ts->rdidx2 += 3;
+	ts->size2 += 3;
+}
+
+
+static struct tsession *
+make_new_session(int sockfd)
+{
+	struct termios termbuf;
+	int pty, pid;
+	static char tty_name[32];
+	struct tsession *ts = (struct tsession *)malloc(sizeof(struct tsession));
+
+	ts->buf1 = (char *)malloc(BUFSIZE);
+	ts->buf2 = (char *)malloc(BUFSIZE);
+
+	ts->sockfd = sockfd;
+
+	ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
+	ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
+
+	/* Got a new connection, set up a tty and spawn a shell.  */
+
+	pty = getpty(tty_name);
+
+	if (pty < 0) {
+		fprintf(stderr, "All network ports in use!\n");
+		return 0;
+	}
+
+	if (pty > maxfd)
+		maxfd = pty;
+
+	ts->ptyfd = pty;
+
+	/* Make the telnet client understand we will echo characters so it 
+	 * should not do it locally. We don't tell the client to run linemode,
+	 * because we want to handle line editing and tab completion and other
+	 * stuff that requires char-by-char support.
+	 */
+
+	send_iac(ts, DO, TELOPT_ECHO);
+	send_iac(ts, DO, TELOPT_LFLOW);
+	send_iac(ts, WILL, TELOPT_ECHO);
+	send_iac(ts, WILL, TELOPT_SGA);
+
+
+	if ((pid = fork()) < 0) {
+		perror("fork");
+	}
+	if (pid == 0) {
+		/* In child, open the child's side of the tty.  */
+		int i, t;
+
+		for(i = 0; i <= maxfd; i++)
+			close(i);
+		/* make new process group */
+		if (setsid() < 0)
+			perror_msg_and_die("setsid");
+
+		t = open(tty_name, O_RDWR | O_NOCTTY);
+		if (t < 0)
+			perror_msg_and_die("Could not open tty");
+		dup(0);
+		dup(1);
+
+		tcsetpgrp(0, getpid());
+  
+		/* The pseudo-terminal allocated to the client is configured to operate in
+		 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
+		 */
+
+		tcgetattr(t, &termbuf);
+		termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
+		termbuf.c_oflag |= ONLCR|XTABS;
+		termbuf.c_iflag |= ICRNL;
+		termbuf.c_iflag &= ~IXOFF;
+		/*termbuf.c_lflag &= ~ICANON;*/
+		tcsetattr(t, TCSANOW, &termbuf);
+
+		/* exec shell, with correct argv and env */
+		execv(loginpath, argv_init);
+		
+	    	/* NOT REACHED */
+		perror_msg_and_die("execv");
+	}
+
+	ts->shell_pid = pid;
+
+	return ts;
+}
+
+static void
+free_session(struct tsession *ts)
+{
+	struct tsession *t = sessions;
+
+	/* Unlink this telnet session from the session list.  */
+	if(t == ts)
+		sessions = ts->next;
+	else {
+		while(t->next != ts)
+			t = t->next;
+		t->next = ts->next;
+	}
+
+	free(ts->buf1);
+	free(ts->buf2);
+
+	kill(ts->shell_pid, SIGKILL);
+
+	wait4(ts->shell_pid, NULL, 0, NULL);
+
+	close(ts->ptyfd);
+	close(ts->sockfd);
+
+	if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+		maxfd--;
+	if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+		maxfd--;
+
+	free(ts);
+}
+
+int
+telnetd_main(int argc, char **argv)
+{
+	struct sockaddr_in sa;
+	int master_fd;
+	fd_set rdfdset, wrfdset;
+	int selret;
+	int on = 1;
+	int portnbr = 23;
+	int c;
+
+	/* check if user supplied a port number */
+
+	for (;;) {
+		c = getopt( argc, argv, "p:l:");
+		if (c == EOF) break;
+		switch (c) {
+			case 'p':
+				portnbr = atoi(optarg);
+				break;
+			case 'l':
+				loginpath = strdup (optarg);
+				break;
+			default:
+				show_usage();
+				exit(1);
+		}
+	}
+
+	if (!loginpath)
+	    loginpath = "/bin/sh";
+
+	if (access(loginpath, X_OK) < 0) {
+		fprintf (stderr, "'%s' unavailable, abort.\n", loginpath);
+		exit(0);
+	}
+
+	argv_init[0] = loginpath;
+	sessions = 0;
+
+	/* Grab a TCP socket.  */
+
+	master_fd = socket(AF_INET, SOCK_STREAM, 0);
+	if (master_fd < 0) {
+		perror("socket");
+		return 1;
+	}
+	(void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+	/* Set it to listen to specified port.  */
+
+	memset((void *)&sa, 0, sizeof(sa));
+	sa.sin_family = AF_INET;
+	sa.sin_port = htons(portnbr);
+
+	if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+		perror("bind");
+		return 1;
+	}
+
+	if (listen(master_fd, 1) < 0) {
+		perror("listen");
+		return 1;
+	}
+
+	if (daemon(0, 1) < 0)
+	    perror_msg_and_die("daemon");
+
+
+	maxfd = master_fd;
+
+	do {
+		struct tsession *ts;
+
+		FD_ZERO(&rdfdset);
+		FD_ZERO(&wrfdset);
+
+		/* select on the master socket, all telnet sockets and their
+		 * ptys if there is room in their respective session buffers.
+		 */
+
+		FD_SET(master_fd, &rdfdset);
+
+		ts = sessions;
+		while (ts) {
+			/* buf1 is used from socket to pty
+			 * buf2 is used from pty to socket
+			 */
+			if (ts->size1 > 0) {
+				FD_SET(ts->ptyfd, &wrfdset);  /* can write to pty */
+			}
+			if (ts->size1 < BUFSIZE) {
+				FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
+			}
+			if (ts->size2 > 0) {
+				FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
+			}
+			if (ts->size2 < BUFSIZE) {
+				FD_SET(ts->ptyfd, &rdfdset);  /* can read from pty */
+			}
+			ts = ts->next;
+		}
+
+		selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
+
+		if (!selret)
+			break;
+
+		/* First check for and accept new sessions.  */
+		if (FD_ISSET(master_fd, &rdfdset)) {
+			int fd, salen;
+
+			salen = sizeof(sa);	
+			if ((fd = accept(master_fd, (struct sockaddr *)&sa,
+					 &salen)) < 0) {
+				continue;
+			} else {
+				/* Create a new session and link it into
+				   our active list.  */
+				struct tsession *new_ts = make_new_session(fd);
+				if (new_ts) {
+					new_ts->next = sessions;
+					sessions = new_ts;
+					if (fd > maxfd)
+						maxfd = fd;
+				} else {
+					close(fd);
+				}
+			}
+		}
+
+		/* Then check for data tunneling.  */
+
+		ts = sessions;
+		while (ts) { /* For all sessions...  */
+			int maxlen, w, r;
+			struct tsession *next = ts->next; /* in case we free ts. */
+
+			if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
+			    int processed, num_totty;
+			    char *ptr;
+				/* Write to pty from buffer 1.  */
+				
+				maxlen = MIN(BUFSIZE - ts->wridx1,
+					     ts->size1);
+				ptr = remove_iacs(ts->buf1 + ts->wridx1, maxlen, 
+					&processed, &num_totty);
+		
+				/* the difference between processed and num_totty
+				   is all the iacs we removed from the stream.
+				   Adjust buf1 accordingly. */
+				ts->wridx1 += processed - num_totty;
+				ts->size1 -= processed - num_totty;
+
+				w = write(ts->ptyfd, ptr, num_totty);
+				if (w < 0) {
+					perror("write");
+					free_session(ts);
+					ts = next;
+					continue;
+				}
+				ts->wridx1 += w;
+				ts->size1 -= w;
+				if (ts->wridx1 == BUFSIZE)
+					ts->wridx1 = 0;
+			}
+
+			if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
+				/* Write to socket from buffer 2.  */
+				maxlen = MIN(BUFSIZE - ts->wridx2,
+					     ts->size2);
+				w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
+				if (w < 0) {
+					perror("write");
+					free_session(ts);
+					ts = next;
+					continue;
+				}
+				ts->wridx2 += w;
+				ts->size2 -= w;
+				if (ts->wridx2 == BUFSIZE)
+					ts->wridx2 = 0;
+			}
+
+			if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
+				/* Read from socket to buffer 1. */
+				maxlen = MIN(BUFSIZE - ts->rdidx1,
+					     BUFSIZE - ts->size1);
+				r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
+				if (!r || (r < 0 && errno != EINTR)) {
+					free_session(ts);
+					ts = next;
+					continue;
+				}
+				if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
+					r--;
+					if(!r)
+						continue;
+				}
+				ts->rdidx1 += r;
+				ts->size1 += r;
+				if (ts->rdidx1 == BUFSIZE)
+					ts->rdidx1 = 0;
+			}
+
+			if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
+				/* Read from pty to buffer 2.  */
+				maxlen = MIN(BUFSIZE - ts->rdidx2,
+					     BUFSIZE - ts->size2);
+				r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
+				if (!r || (r < 0 && errno != EINTR)) {
+					free_session(ts);
+					ts = next;
+					continue;
+				}
+				ts->rdidx2 += r;
+				ts->size2 += r;
+				if (ts->rdidx2 == BUFSIZE)
+					ts->rdidx2 = 0;
+			}
+
+			if (ts->size1 == 0) {
+				ts->rdidx1 = 0;
+				ts->wridx1 = 0;
+			}
+			if (ts->size2 == 0) {
+				ts->rdidx2 = 0;
+				ts->wridx2 = 0;
+			}
+			ts = next;
+		}
+
+	} while (1);
+
+	return 0;
+}
--- /dev/null	Thu Nov 30 07:25:58 2000
+++ tb_Config.h	Thu Sep 13 09:18:56 2001
@@ -0,0 +1,11 @@
+/* vi: set sw=4 ts=4: */
+// This file defines the feature set to be compiled into trustybox.
+// When you turn things off here, they won't be compiled in at all.
+//
+//// This file is parsed by sed.  You MUST use single line comments.
+//   i.e.,  //#define BB_BLAH
+//
+//
+// TrutyBox Applications
+
+#define TB_TELNETD





More information about the busybox mailing list