-/* $Id: telnetd.c,v 1.12 2004/06/22 10:07:17 andersen Exp $
- *
+/* vi: set sw=4 ts=4: */
+/*
* Simple telnet server
* Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
*
- * This file is distributed under the Gnu Public License (GPL),
- * please see the file LICENSE for further information.
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* ---------------------------------------------------------------------------
* (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
*/
/*#define DEBUG 1 */
+#undef DEBUG
+
+#include "busybox.h"
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/ioctl.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 <sys/syslog.h>
-#include "busybox.h"
#define BUFSIZE 4000
-static const char *loginpath
+#ifdef CONFIG_FEATURE_IPV6
+#define SOCKET_TYPE AF_INET6
+typedef struct sockaddr_in6 sockaddr_type;
+#else
+#define SOCKET_TYPE AF_INET
+typedef struct sockaddr_in sockaddr_type;
+#endif
+
+
#ifdef CONFIG_LOGIN
- = "/bin/login";
+static const char *loginpath = "/bin/login";
#else
-;
+static const char *loginpath;
#endif
static const char *issuefile = "/etc/issue.net";
/*
- Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
+ Remove all IAC's from the buffer pointed to by bf (received 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.
*/
static char *
remove_iacs(struct tsession *ts, int *pnum_totty) {
- unsigned char *ptr0 = ts->buf1 + ts->wridx1;
+ unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
unsigned char *ptr = ptr0;
unsigned char *totty = ptr;
unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
struct winsize ws;
if ((ptr+8) >= end)
- break; /* incomplete, can't process */
+ break; /* incomplete, can't process */
ws.ws_col = (ptr[3] << 8) | ptr[4];
ws.ws_row = (ptr[5] << 8) | ptr[6];
(void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
{
int p;
#ifdef CONFIG_FEATURE_DEVPTS
- p = open("/dev/ptmx", 2);
+ p = open("/dev/ptmx", O_RDWR);
if (p > 0) {
+ const char *name;
grantpt(p);
unlockpt(p);
- strcpy(line, ptsname(p));
- return(p);
+ name = ptsname(p);
+ if (!name) {
+ bb_perror_msg("ptsname error (is /dev/pts mounted?)");
+ return -1;
+ }
+ strcpy(line, name);
+ return p;
}
#else
struct stat stb;
}
for (j = 0; j < 16; j++) {
line[9] = j < 10 ? j + '0' : j - 10 + 'a';
- if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Trying to open device: %s\n", line);
+#endif
+ p = open(line, O_RDWR | O_NOCTTY);
+ if (p >= 0) {
line[5] = 't';
return p;
}
struct termios termbuf;
int pty, pid;
char tty_name[32];
- struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
+ struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
ts->buf1 = (char *)(&ts[1]);
ts->buf2 = ts->buf1 + BUFSIZE;
#ifdef CONFIG_FEATURE_TELNETD_INETD
- ts->sockfd_read = 0;
ts->sockfd_write = 1;
#else /* CONFIG_FEATURE_TELNETD_INETD */
ts->sockfd = sockfd;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
- 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) {
- syslog(LOG_ERR, "All network ports in use!");
+ bb_error_msg("all terminals in use");
return 0;
}
send_iac(ts, WILL, TELOPT_ECHO);
send_iac(ts, WILL, TELOPT_SGA);
-
if ((pid = fork()) < 0) {
- syslog(LOG_ERR, "Can`t forking");
+ bb_perror_msg("fork");
}
if (pid == 0) {
/* In child, open the child's side of the tty. */
/* make new process group */
setsid();
- if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
- syslog(LOG_ERR, "Could not open tty");
- exit(1);
- }
+ xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
dup(0);
dup(0);
execv(loginpath, (char *const *)argv_init);
/* NOT REACHED */
- syslog(LOG_ERR, "execv error");
- exit(1);
+ bb_perror_msg_and_die("execv");
}
ts->shell_pid = pid;
struct tsession *t = sessions;
/* Unlink this telnet session from the session list. */
- if(t == ts)
+ if (t == ts)
sessions = ts->next;
else {
while(t->next != ts)
close(ts->ptyfd);
close(ts->sockfd);
- if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+ if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
maxfd--;
- if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
+ if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
maxfd--;
free(ts);
int
telnetd_main(int argc, char **argv)
{
-#ifndef CONFIG_FEATURE_TELNETD_INETD
- struct sockaddr_in sa;
- int master_fd;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
+ unsigned opt;
fd_set rdfdset, wrfdset;
int selret;
#ifndef CONFIG_FEATURE_TELNETD_INETD
+ sockaddr_type sa;
+ int master_fd;
int on = 1;
- int portnbr = 23;
-#endif /* CONFIG_FEATURE_TELNETD_INETD */
- int c;
- static const char options[] =
-#ifdef CONFIG_FEATURE_TELNETD_INETD
- "f:l:";
-#else /* CONFIG_EATURE_TELNETD_INETD */
- "f:l:p:";
+ unsigned portnbr = 23;
+ struct in_addr bind_addr = { .s_addr = 0x0 };
+ char *opt_portnbr, *opt_bindaddr;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
int maxlen, w, r;
loginpath = DEFAULT_SHELL;
#endif
- for (;;) {
- c = getopt( argc, argv, options);
- if (c == EOF) break;
- switch (c) {
- case 'f':
- issuefile = strdup (optarg);
- break;
- case 'l':
- loginpath = strdup (optarg);
- break;
+ /* We use inetd-style operation unconditionally
+ * (no --foreground option), user most likely will
+ * look into syslog for all errors, even early ones.
+ * Direct all output to syslog at once.
+ */
+ openlog(applet_name, 0, LOG_USER);
+ logmode = LOGMODE_SYSLOG;
+
+ opt = getopt32(argc, argv, "f:l:" SKIP_FEATURE_TELNETD_INETD("p:b:"),
+ &issuefile, &loginpath
+ SKIP_FEATURE_TELNETD_INETD(, &opt_portnbr, &opt_bindaddr));
+ //if (opt & 1) // -f
+ //if (opt & 2) // -l
#ifndef CONFIG_FEATURE_TELNETD_INETD
- case 'p':
- portnbr = atoi(optarg);
- break;
+ if (opt & 4) portnbr = xatou16(opt_portnbr); // -p
+ if (opt & 8) // -b
+ if (inet_aton(opt_bindaddr, &bind_addr) == 0) bb_show_usage();
#endif /* CONFIG_FEATURE_TELNETD_INETD */
- default:
- bb_show_usage();
- }
- }
if (access(loginpath, X_OK) < 0) {
- bb_error_msg_and_die ("'%s' unavailable.", loginpath);
+ bb_error_msg_and_die("'%s' unavailable", loginpath);
}
argv_init[0] = loginpath;
- openlog(bb_applet_name, 0, LOG_USER);
-
#ifdef CONFIG_FEATURE_TELNETD_INETD
maxfd = 1;
sessions = make_new_session();
/* Grab a TCP socket. */
- master_fd = socket(AF_INET, SOCK_STREAM, 0);
- if (master_fd < 0) {
- bb_perror_msg_and_die("socket");
- }
+ master_fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
(void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
/* Set it to listen to specified port. */
memset((void *)&sa, 0, sizeof(sa));
+#ifdef CONFIG_FEATURE_IPV6
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(portnbr);
+ /* sa.sin6_addr = bind_addr6; */
+#else
sa.sin_family = AF_INET;
sa.sin_port = htons(portnbr);
+ sa.sin_addr = bind_addr;
+#endif
- if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
- bb_perror_msg_and_die("bind");
- }
-
- if (listen(master_fd, 1) < 0) {
- bb_perror_msg_and_die("listen");
- }
-
- if (daemon(0, 0) < 0)
- bb_perror_msg_and_die("daemon");
-
+ xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa));
+ xlisten(master_fd, 1);
+ xdaemon(0, 0);
maxfd = master_fd;
#endif /* CONFIG_FEATURE_TELNETD_INETD */
- do {
+ while(1) {
struct tsession *ts;
FD_ZERO(&rdfdset);
#ifndef CONFIG_FEATURE_TELNETD_INETD
/* First check for and accept new sessions. */
if (FD_ISSET(master_fd, &rdfdset)) {
- int fd, salen;
+ int fd;
+ socklen_t salen;
salen = sizeof(sa);
- if ((fd = accept(master_fd, (struct sockaddr *)&sa,
- &salen)) < 0) {
+ fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
+ if (fd < 0) {
continue;
} else {
/* Create a new session and link it into
continue;
}
#endif /* CONFIG_FEATURE_TELNETD_INETD */
- if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
+ if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
r--;
- if(!r)
+ if (!r)
continue;
}
ts->rdidx1 += r;
}
#endif /* CONFIG_FEATURE_TELNETD_INETD */
- } while (1);
+ } /* while(1) */
return 0;
}