1 /* $Id: telnetd.c,v 1.2 2002/11/10 22:26:19 bug1 Exp $
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
6 * This file is distributed under the Gnu Public License (GPL),
7 * please see the file LICENSE for further information.
9 * ---------------------------------------------------------------------------
10 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
11 ****************************************************************************
13 * The telnetd manpage says it all:
15 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
16 * a client, then creating a login process which has the slave side of the
17 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
18 * master side of the pseudo-terminal, implementing the telnet protocol and
19 * passing characters between the remote client and the login process.
21 * Vladimir Oleynik <dzo@simtreas.ru> 2001
22 * Set process group corrections, initial busybox port
28 #include <sys/socket.h>
34 #include <netinet/in.h>
43 #include <arpa/telnet.h>
45 #include <sys/syslog.h>
51 static const char *loginpath = "/bin/sh";
53 /* shell name and arguments */
55 static const char *argv_init[] = {NULL, NULL};
57 /* structure that describes a session */
60 struct tsession *next;
63 /* two circular buffers */
65 int rdidx1, wridx1, size1;
66 int rdidx2, wridx2, size2;
71 This is how the buffers are used. The arrows indicate the movement
74 +-------+ wridx1++ +------+ rdidx1++ +----------+
75 | | <-------------- | buf1 | <-------------- | |
76 | | size1-- +------+ size1++ | |
78 | | rdidx2++ +------+ wridx2++ | |
79 | | --------------> | buf2 | --------------> | |
80 +-------+ size2++ +------+ size2-- +----------+
82 Each session has got two buffers.
88 static struct tsession *sessions;
93 Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
94 and must be removed so as to not be interpreted by the terminal). Make an
95 uninterrupted string of characters fit for the terminal. Do this by packing
96 all characters meant for the terminal sequentially towards the end of bf.
98 Return a pointer to the beginning of the characters meant for the terminal.
99 and make *num_totty the number of characters that should be sent to
102 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
103 past (bf + len) then that IAC will be left unprocessed and *processed will be
106 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
107 what is the escape character? We aren't handling that situation here.
111 remove_iacs(struct tsession *ts, int *pnum_totty) {
112 unsigned char *ptr0 = ts->buf1 + ts->wridx1;
113 unsigned char *ptr = ptr0;
114 unsigned char *totty = ptr;
115 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
125 /* the entire IAC is contained in the buffer
126 we were asked to process. */
128 fprintf(stderr, "Ignoring IAC %s,%s\n",
129 *ptr, TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
133 /* only the beginning of the IAC is in the
134 buffer we were asked to process, we can't
135 process this char. */
141 processed = ptr - ptr0;
142 num_totty = totty - ptr0;
143 /* the difference between processed and num_to tty
144 is all the iacs we removed from the stream.
145 Adjust buf1 accordingly. */
146 ts->wridx1 += processed - num_totty;
147 ts->size1 -= processed - num_totty;
148 *pnum_totty = num_totty;
149 /* move the chars meant for the terminal towards the end of the
151 return memmove(ptr - num_totty, ptr0, num_totty);
159 #ifdef CONFIG_FEATURE_DEVPTS
160 p = open("/dev/ptmx", 2);
164 strcpy(line, ptsname(p));
172 strcpy(line, "/dev/ptyXX");
174 for (i = 0; i < 16; i++) {
175 line[8] = "pqrstuvwxyzabcde"[i];
177 if (stat(line, &stb) < 0) {
180 for (j = 0; j < 16; j++) {
181 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
182 if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
188 #endif /* CONFIG_FEATURE_DEVPTS */
194 send_iac(struct tsession *ts, unsigned char command, int option)
196 /* We rely on that there is space in the buffer for now. */
197 char *b = ts->buf2 + ts->rdidx2;
206 static struct tsession *
207 make_new_session(int sockfd)
209 struct termios termbuf;
212 struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
214 ts->buf1 = (char *)(&ts[1]);
215 ts->buf2 = ts->buf1 + BUFSIZE;
219 ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
220 ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
222 /* Got a new connection, set up a tty and spawn a shell. */
224 pty = getpty(tty_name);
227 syslog_msg(LOG_USER, LOG_ERR, "All network ports in use!");
236 /* Make the telnet client understand we will echo characters so it
237 * should not do it locally. We don't tell the client to run linemode,
238 * because we want to handle line editing and tab completion and other
239 * stuff that requires char-by-char support.
242 send_iac(ts, DO, TELOPT_ECHO);
243 send_iac(ts, DO, TELOPT_LFLOW);
244 send_iac(ts, WILL, TELOPT_ECHO);
245 send_iac(ts, WILL, TELOPT_SGA);
248 if ((pid = fork()) < 0) {
249 syslog_msg(LOG_USER, LOG_ERR, "Can`t forking");
252 /* In child, open the child's side of the tty. */
255 for(i = 0; i <= maxfd; i++)
257 /* make new process group */
260 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
261 syslog_msg(LOG_USER, LOG_ERR, "Could not open tty");
267 tcsetpgrp(0, getpid());
269 /* The pseudo-terminal allocated to the client is configured to operate in
270 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
273 tcgetattr(0, &termbuf);
274 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
275 termbuf.c_oflag |= ONLCR|XTABS;
276 termbuf.c_iflag |= ICRNL;
277 termbuf.c_iflag &= ~IXOFF;
278 /*termbuf.c_lflag &= ~ICANON;*/
279 tcsetattr(0, TCSANOW, &termbuf);
281 /* exec shell, with correct argv and env */
282 execv(loginpath, (char *const *)argv_init);
285 syslog_msg(LOG_USER, LOG_ERR, "execv error");
295 free_session(struct tsession *ts)
297 struct tsession *t = sessions;
299 /* Unlink this telnet session from the session list. */
308 kill(ts->shell_pid, SIGKILL);
310 wait4(ts->shell_pid, NULL, 0, NULL);
315 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
317 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
324 telnetd_main(int argc, char **argv)
326 struct sockaddr_in sa;
328 fd_set rdfdset, wrfdset;
334 /* check if user supplied a port number */
337 c = getopt( argc, argv, "p:l:");
341 portnbr = atoi(optarg);
344 loginpath = strdup (optarg);
351 if (access(loginpath, X_OK) < 0) {
352 error_msg_and_die ("'%s' unavailable.", loginpath);
355 argv_init[0] = loginpath;
358 /* Grab a TCP socket. */
360 master_fd = socket(AF_INET, SOCK_STREAM, 0);
362 perror_msg_and_die("socket");
364 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
366 /* Set it to listen to specified port. */
368 memset((void *)&sa, 0, sizeof(sa));
369 sa.sin_family = AF_INET;
370 sa.sin_port = htons(portnbr);
372 if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
373 perror_msg_and_die("bind");
376 if (listen(master_fd, 1) < 0) {
377 perror_msg_and_die("listen");
380 if (daemon(0, 0) < 0)
381 perror_msg_and_die("daemon");
392 /* select on the master socket, all telnet sockets and their
393 * ptys if there is room in their respective session buffers.
396 FD_SET(master_fd, &rdfdset);
400 /* buf1 is used from socket to pty
401 * buf2 is used from pty to socket
404 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
406 if (ts->size1 < BUFSIZE) {
407 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
410 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
412 if (ts->size2 < BUFSIZE) {
413 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
418 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
423 /* First check for and accept new sessions. */
424 if (FD_ISSET(master_fd, &rdfdset)) {
428 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
432 /* Create a new session and link it into
434 struct tsession *new_ts = make_new_session(fd);
436 new_ts->next = sessions;
446 /* Then check for data tunneling. */
449 while (ts) { /* For all sessions... */
451 struct tsession *next = ts->next; /* in case we free ts. */
453 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
456 /* Write to pty from buffer 1. */
458 ptr = remove_iacs(ts, &num_totty);
460 w = write(ts->ptyfd, ptr, num_totty);
468 if (ts->wridx1 == BUFSIZE)
472 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
473 /* Write to socket from buffer 2. */
474 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
475 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
483 if (ts->wridx2 == BUFSIZE)
487 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
488 /* Read from socket to buffer 1. */
489 maxlen = MIN(BUFSIZE - ts->rdidx1,
490 BUFSIZE - ts->size1);
491 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
492 if (!r || (r < 0 && errno != EINTR)) {
497 if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
504 if (ts->rdidx1 == BUFSIZE)
508 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
509 /* Read from pty to buffer 2. */
510 maxlen = MIN(BUFSIZE - ts->rdidx2,
511 BUFSIZE - ts->size2);
512 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
513 if (!r || (r < 0 && errno != EINTR)) {
520 if (ts->rdidx2 == BUFSIZE)
524 if (ts->size1 == 0) {
528 if (ts->size2 == 0) {