From 266f447e3cea4b0b716f38163e680c64cc016a0f Mon Sep 17 00:00:00 2001 From: Ilya Belyy Date: Tue, 4 Mar 2008 01:33:33 +0900 Subject: [PATCH] basic telnet server --- server.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 server.c diff --git a/server.c b/server.c new file mode 100644 index 0000000..e79f1f8 --- /dev/null +++ b/server.c @@ -0,0 +1,306 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MYPORT 3456 + +#define BACKLOG 3 // how many pending connections queue will hold + + +enum +{ + // RFC 854 + TN_IAC = 0xFF, + TN_WILL = 0xFB, + TN_WONT = 0xFC, + TN_DO = 0xFD, + TN_DONT = 0xFE, + TN_SB = 0xFA, + TN_SE = 0xF0, + // RFC 857 + TN_ECHO = 0x01, + // RFC 858 + TN_SUPPRESS_GA = 0x03, + // RFC 1073 + TN_NAWS = 0x1F +}; + + + +static int translate_telnet_input (uint8_t *buf, int len, int pty) +{ + int i = 0, j = 0; + while (i < len) + { + // CR + if (buf[i] == '\r') + { + // translate it to LF + buf[j++] = '\n'; + + if (++i >= len) break; // XXX + + // CR LF and CR 0 are translated to LF + // translation is already done, so we only have to skip it here + if (buf[i] == '\n' || buf[i] == 0) i++; + + continue; + } + + // telnet command + if (buf[i] == TN_IAC) + { + if (++i >= len) break; // XXX + + // it's just an escaped data octet + if (buf[i] == TN_IAC) + { + buf[j++] = buf[i++]; + continue; + } + + // some confirmation + if (buf[i] == TN_WILL || buf[i] == TN_DO) + { + i += 2; // XXX just skip it for now + } + + // something is rejected + if (buf[i] == TN_WONT || buf[i] == TN_DONT) + { + i += 2; // XXX just skip it for now + } + + // some subsection begins + if (buf[i] == TN_SB) + { + if (++i >= len) break; // XXX + + // terminal window size + if (buf[i] == TN_NAWS) + { + struct winsize ws; + + if (i + 7 > len) break; // XXX + + // XXX IAC/255 + ws.ws_col = (buf[i + 1] << 8) | buf[i + 2]; + ws.ws_row = (buf[i + 3] << 8) | buf[i + 4]; +printf("term %dx%d\n",ws.ws_col,ws.ws_row); + + // skip IAC SE + i += 7; + + ioctl (pty, TIOCSWINSZ, &ws); + + continue; + } + + // XXX + } + + continue; + } + + // an ordinary octet, just copy it + buf[j++] = buf[i++]; + } + + return j; +} + + + +#define SIZE 32*1024 +#define CMD "./fdepths" +//#define CMD "sh" +static void handle_connect (int sck) +{ + int pty_master, pty_slave; + int pid; + + + if (openpty(&pty_master, &pty_slave, NULL, NULL, NULL) == -1) + { + perror ("openpty failed"); + return; + } +printf("%s\n", ttyname(pty_slave)); + + if ((pid = fork()) < 0) + { + perror ("fork failed"); + return; + } + + // child + if (pid == 0) + { + dup2 (pty_slave, 0); // reassign stdin to pty + dup2 (pty_slave, 1); // reassign stdout to pty + dup2 (pty_slave, 2); // reassign stderr to pty + // these are not needed anymore + close (pty_slave); + close (pty_master); + close (sck); + +sleep(1); +//execlp ("angband", "angband", "-mgcu", NULL); + execlp (CMD, CMD, NULL); + + // exec should never return + perror ("child failed"); + return; + } + + + // parent + + close (pty_slave); + + { + uint8_t req[] = + { + TN_IAC, TN_WILL, TN_SUPPRESS_GA, // enable full-duplex mode (RFC 858) + TN_IAC, TN_DO, TN_NAWS, // request window size info (RFC 1073) + TN_IAC, TN_WILL, TN_ECHO // request to disable local echo (RFC 857) + }; + send (sck, req, sizeof(req) / sizeof(uint8_t), 0); + } + +// XXX logs +FILE *ffr=fopen("TELNET_raw","wb");setbuf(ffr,NULL); +FILE *ffp=fopen("TELNET_in","wb");setbuf(ffp,NULL); + while (1) + { + fd_set rfds; + FD_ZERO (&rfds); + FD_SET (pty_master, &rfds); + FD_SET (sck, &rfds); + + int sn = (sck > pty_master) ? sck : pty_master; + int sl = select(sn + 1, &rfds, NULL, NULL, NULL); + if (sl == -1) + { + perror ("select"); + break; + } + + if (sl) + { + uint8_t buf[SIZE]; + + if (FD_ISSET(pty_master, &rfds)) + { + int nr = read(pty_master, buf, SIZE); + if (nr <= 0) break; + + send (sck, buf, nr, 0); + } + + if (FD_ISSET(sck, &rfds)) + { + int nr = recv(sck, buf, SIZE, 0); + if (nr <= 0) break; +fwrite(buf,nr,1,ffr); + nr = translate_telnet_input(buf, nr, pty_master); +fwrite(buf,nr,1,ffp); + if (nr > 0) write (pty_master, buf, nr); + } + } + } +fclose(ffr);fclose(ffp); +} + + + +static void sigchld_handler (int s) +{ + while (waitpid(-1, NULL, WNOHANG) > 0); +} + + + +int main (void) +{ + int sockfd, new_fd; // listen on sock_fd, new connection on new_fd + struct sockaddr_in my_addr; // my address information + struct sockaddr_in their_addr; // connector's address information + struct sigaction sa; + int yes = 1; + + + sa.sa_handler = sigchld_handler; // reap all dead processes + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &sa, NULL) == -1) + { + perror ("sigaction"); + exit (1); + } + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror ("socket"); + exit (1); + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + { + perror ("setsockopt"); + exit (1); + } + + my_addr.sin_family = AF_INET; // host byte order + my_addr.sin_port = htons(MYPORT); // short, network byte order + my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP + memset (&(my_addr.sin_zero), 0, 8); // zero the rest of the struct + + if (bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == -1) + { + perror ("bind"); + exit (1); + } + + if (listen(sockfd, BACKLOG) == -1) + { + perror ("listen"); + exit (1); + } + + +// while (1) + { + socklen_t sin_size = sizeof(struct sockaddr_in); + if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) + { + perror("accept"); + exit (1); + } + + if (fork() == 0) + { + close (sockfd); + handle_connect (new_fd); + close (new_fd); + return 0; + } + + close (new_fd); +printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr)); + } + + return 0; +} + -- 2.11.4.GIT