From 641df58b6ebaa4230e7c58de242ef022c4b59990 Mon Sep 17 00:00:00 2001 From: Sean Middleditch Date: Tue, 3 Nov 2009 19:06:17 -0800 Subject: move util source code to util/ directory --- Makefile.am | 6 +- telnet-chatd.c | 337 -------------------------------------- telnet-client.c | 243 --------------------------- telnet-proxy.c | 451 --------------------------------------------------- util/telnet-chatd.c | 337 ++++++++++++++++++++++++++++++++++++++ util/telnet-client.c | 243 +++++++++++++++++++++++++++ util/telnet-proxy.c | 451 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1034 insertions(+), 1034 deletions(-) delete mode 100644 telnet-chatd.c delete mode 100644 telnet-client.c delete mode 100644 telnet-proxy.c create mode 100644 util/telnet-chatd.c create mode 100644 util/telnet-client.c create mode 100644 util/telnet-proxy.c diff --git a/Makefile.am b/Makefile.am index df72d05..0eccbf3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,11 +16,11 @@ libtelnet_la_LDFLAGS = -version-info 1:0:0 bin_PROGRAMS = telnet-client telnet-chatd telnet-proxy -telnet_client_SOURCES = telnet-client.c libtelnet.h +telnet_client_SOURCES = util/telnet-client.c libtelnet.h telnet_client_LDADD = libtelnet.la -telnet_chatd_SOURCES = telnet-chatd.c libtelnet.h +telnet_chatd_SOURCES = util/telnet-chatd.c libtelnet.h telnet_chatd_LDADD = libtelnet.la -telnet_proxy_SOURCES = telnet-proxy.c libtelnet.h +telnet_proxy_SOURCES = util/telnet-proxy.c libtelnet.h telnet_proxy_LDADD = libtelnet.la diff --git a/telnet-chatd.c b/telnet-chatd.c deleted file mode 100644 index 7bad622..0000000 --- a/telnet-chatd.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Sean Middleditch - * sean@sourcemud.org - * - * The author or authors of this code dedicate any and all copyright interest - * in this code to the public domain. We make this dedication for the benefit - * of the public at large and to the detriment of our heirs and successors. We - * intend this dedication to be an overt act of relinquishment in perpetuity of - * all present and future rights to this code under copyright law. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_ZLIB -#include "zlib.h" -#endif - -#include "libtelnet.h" - -#define MAX_USERS 64 -#define LINEBUFFER_SIZE 256 - -const telnet_telopt_t telopts[] = { - { TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DONT }, - { -1, 0, 0 } -}; - -struct user_t { - char *name; - int sock; - telnet_t *telnet; - char linebuf[256]; - int linepos; -}; - -static struct user_t users[MAX_USERS]; - -static void linebuffer_push(char *buffer, size_t size, int *linepos, - char ch, void (*cb)(const char *line, int overflow, void *ud), - void *ud) { - - /* CRLF -- line terminator */ - if (ch == '\n' && *linepos > 0 && buffer[*linepos - 1] == '\r') { - /* NUL terminate (replaces \r in buffer), notify app, clear */ - buffer[*linepos - 1] = 0; - cb(buffer, 0, ud); - *linepos = 0; - - /* CRNUL -- just a CR */ - } else if (ch == 0 && *linepos > 0 && buffer[*linepos - 1] == '\r') { - /* do nothing, the CR is already in the buffer */ - - /* anything else (including technically invalid CR followed by - * anything besides LF or NUL -- just buffer if we have room - * \r - */ - } else if (*linepos != size) { - buffer[(*linepos)++] = ch; - - /* buffer overflow */ - } else { - /* terminate (NOTE: eats a byte), notify app, clear buffer */ - buffer[size - 1] = 0; - cb(buffer, size - 1, ud); - *linepos = 0; - } -} - -static void _message(const char *from, const char *msg) { - int i; - for (i = 0; i != MAX_USERS; ++i) { - if (users[i].sock != -1) { - telnet_printf(users[i].telnet, "%s: %s\n", from, msg); - } - } -} - -static void _send(int sock, const char *buffer, unsigned int size) { - int rs; - - /* ignore on invalid socket */ - if (sock == -1) - return; - - /* send data */ - while (size > 0) { - if ((rs = send(sock, buffer, size, 0)) == -1) { - if (errno != EINTR && errno != ECONNRESET) { - fprintf(stderr, "send() failed: %s\n", strerror(errno)); - exit(1); - } else { - return; - } - } else if (rs == 0) { - fprintf(stderr, "send() unexpectedly returned 0\n"); - exit(1); - } - - /* update pointer and size to see if we've got more to send */ - buffer += rs; - size -= rs; - } -} - -/* process input line */ -static void _online(const char *line, int overflow, void *ud) { - struct user_t *user = (struct user_t*)ud; - int i; - - /* if the user has no name, this is his "login" */ - if (user->name == 0) { - /* must not be empty, must be at least 32 chars */ - if (strlen(line) == 0 || strlen(line) > 32) { - telnet_printf(user->telnet, "Invalid name.\nEnter name: "); - return; - } - - /* must not already be in use */ - for (i = 0; i != MAX_USERS; ++i) { - if (users[i].name != 0 && strcmp(users[i].name, line) == 0) { - telnet_printf(user->telnet, "Name in use.\nEnter name: "); - return; - } - } - - /* keep name */ - user->name = strdup(line); - telnet_printf(user->telnet, "Welcome, %s!\n", line); - return; - } - - /* if line is "quit" then, well, quit */ - if (strcmp(line, "quit") == 0) { - close(user->sock); - user->sock = -1; - _message(user->name, "** HAS QUIT **"); - free(user->name); - user->name = 0; - return; - } - - /* just a message -- send to all users */ - _message(user->name, line); -} - -static void _input(struct user_t *user, const char *buffer, - unsigned int size) { - unsigned int i; - for (i = 0; i != size; ++i) - linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos, - (char)buffer[i], _online, user); -} - -static void _event_handler(telnet_t *telnet, telnet_event_t *ev, - void *user_data) { - struct user_t *user = (struct user_t*)user_data; - - switch (ev->type) { - /* data received */ - case TELNET_EV_DATA: - _input(user, ev->buffer, ev->size); - break; - /* data must be sent */ - case TELNET_EV_SEND: - _send(user->sock, ev->buffer, ev->size); - break; - /* enable compress2 if accepted by client */ - case TELNET_EV_DO: - if (ev->telopt == TELNET_TELOPT_COMPRESS2) - telnet_begin_compress2(telnet); - break; - /* error */ - case TELNET_EV_ERROR: - close(user->sock); - user->sock = -1; - if (user->name != 0) { - _message(user->name, "** HAS HAD AN ERROR **"); - free(user->name); - user->name = 0; - } - telnet_free(user->telnet); - break; - default: - /* ignore */ - break; - } -} - -int main(int argc, char **argv) { - char buffer[512]; - short listen_port; - int listen_sock; - int rs; - int i; - struct sockaddr_in addr; - socklen_t addrlen; - struct pollfd pfd[MAX_USERS + 1]; - - /* check usage */ - if (argc != 2) { - fprintf(stderr, "Usage:\n ./telnet-chatd \n"); - return 1; - } - - /* initialize data structures */ - memset(&pfd, 0, sizeof(pfd)); - memset(users, 0, sizeof(users)); - for (i = 0; i != MAX_USERS; ++i) - users[i].sock = -1; - - /* parse listening port */ - listen_port = strtol(argv[1], 0, 10); - - /* create listening socket */ - if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - fprintf(stderr, "socket() failed: %s\n", strerror(errno)); - return 1; - } - - /* reuse address option */ - rs = 1; - setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs)); - - /* bind to listening addr/port */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(listen_port); - if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - fprintf(stderr, "bind() failed: %s\n", strerror(errno)); - return 1; - } - - /* listen for clients */ - if (listen(listen_sock, 5) == -1) { - fprintf(stderr, "listen() failed: %s\n", strerror(errno)); - return 1; - } - - printf("LISTENING ON PORT %d\n", listen_port); - - /* initialize listening descriptors */ - pfd[MAX_USERS].fd = listen_sock; - pfd[MAX_USERS].events = POLLIN; - - /* loop for ever */ - for (;;) { - /* prepare for poll */ - for (i = 0; i != MAX_USERS; ++i) { - if (users[i].sock != -1) { - pfd[i].fd = users[i].sock; - pfd[i].events = POLLIN; - } else { - pfd[i].fd = -1; - pfd[i].events = 0; - } - } - - /* poll */ - rs = poll(pfd, MAX_USERS + 1, -1); - if (rs == -1 && errno != EINTR) { - fprintf(stderr, "poll() failed: %s\n", strerror(errno)); - return 1; - } - - /* new connection */ - if (pfd[MAX_USERS].revents & POLLIN) { - /* acept the sock */ - addrlen = sizeof(addr); - if ((rs = accept(listen_sock, (struct sockaddr *)&addr, - &addrlen)) == -1) { - fprintf(stderr, "accept() failed: %s\n", strerror(errno)); - return 1; - } - - printf("Connection received.\n"); - - /* find a free user */ - for (i = 0; i != MAX_USERS; ++i) - if (users[i].sock == -1) - break; - if (i == MAX_USERS) { - printf(" rejected (too many users)\n"); - _send(rs, "Too many users.\r\n", 14); - close(rs); - } - - /* init, welcome */ - users[i].sock = rs; - users[i].telnet = telnet_init(telopts, _event_handler, 0, - &users[i]); - telnet_negotiate(users[i].telnet, TELNET_WILL, - TELNET_TELOPT_COMPRESS2); - telnet_printf(users[i].telnet, "Enter name: "); - } - - /* read from client */ - for (i = 0; i != MAX_USERS; ++i) { - /* skip users that aren't actually connected */ - if (users[i].sock == -1) - continue; - - if (pfd[i].revents & POLLIN) { - if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) { - telnet_recv(users[i].telnet, buffer, rs); - } else if (rs == 0) { - printf("Connection closed.\n"); - close(users[i].sock); - if (users[i].name != 0) { - _message(users[i].name, "** HAS DISCONNECTED **"); - free(users[i].name); - users[i].name = 0; - } - telnet_free(users[i].telnet); - users[i].sock = -1; - break; - } else if (errno != EINTR) { - fprintf(stderr, "recv(client) failed: %s\n", - strerror(errno)); - exit(1); - } - } - } - } - - /* not that we can reach this, but GCC will cry if it's not here */ - return 0; -} diff --git a/telnet-client.c b/telnet-client.c deleted file mode 100644 index 5565656..0000000 --- a/telnet-client.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Sean Middleditch - * sean@sourcemud.org - * - * The author or authors of this code dedicate any and all copyright interest - * in this code to the public domain. We make this dedication for the benefit - * of the public at large and to the detriment of our heirs and successors. We - * intend this dedication to be an overt act of relinquishment in perpetuity of - * all present and future rights to this code under copyright law. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_ZLIB -#include "zlib.h" -#endif - -#include "libtelnet.h" - -static struct termios orig_tios; -static telnet_t *telnet; -static int do_echo; - -static const telnet_telopt_t telopts[] = { - { TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO }, - { TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT }, - { TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO }, - { TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO }, - { -1, 0, 0 } -}; - -static void _cleanup(void) { - tcsetattr(STDOUT_FILENO, TCSADRAIN, &orig_tios); -} - -static void _input(char *buffer, int size) { - static char crlf[] = { '\r', '\n' }; - int i; - - for (i = 0; i != size; ++i) { - /* if we got a CR or LF, replace with CRLF - * NOTE that usually you'd get a CR in UNIX, but in raw - * mode we get LF instead (not sure why) - */ - if (buffer[i] == '\r' || buffer[i] == '\n') { - if (do_echo) - printf("\r\n"); - telnet_send(telnet, crlf, 2); - } else { - if (do_echo) - putchar(buffer[i]); - telnet_send(telnet, buffer + i, 1); - } - } - fflush(stdout); -} - -static void _send(int sock, const char *buffer, size_t size) { - int rs; - - /* send data */ - while (size > 0) { - if ((rs = send(sock, buffer, size, 0)) == -1) { - fprintf(stderr, "send() failed: %s\n", strerror(errno)); - exit(1); - } else if (rs == 0) { - fprintf(stderr, "send() unexpectedly returned 0\n"); - exit(1); - } - - /* update pointer and size to see if we've got more to send */ - buffer += rs; - size -= rs; - } -} - -static void _event_handler(telnet_t *telnet, telnet_event_t *ev, - void *user_data) { - int sock = *(int*)user_data; - - switch (ev->type) { - /* data received */ - case TELNET_EV_DATA: - printf("%.*s", (int)ev->size, ev->buffer); - break; - /* data must be sent */ - case TELNET_EV_SEND: - _send(sock, ev->buffer, ev->size); - break; - /* request to enable remote feature (or receipt) */ - case TELNET_EV_WILL: - /* we'll agree to turn off our echo if server wants us to stop */ - if (ev->telopt == TELNET_TELOPT_ECHO) - do_echo = 0; - break; - /* notification of disabling remote feature (or receipt) */ - case TELNET_EV_WONT: - if (ev->telopt == TELNET_TELOPT_ECHO) - do_echo = 1; - break; - /* request to enable local feature (or receipt) */ - case TELNET_EV_DO: - break; - /* demand to disable local feature (or receipt) */ - case TELNET_EV_DONT: - break; - /* respond to particular subnegotiations */ - case TELNET_EV_SUBNEGOTIATION: - /* if they just asked for our terminal type, response with it */ - /* respond with our terminal type */ - if (ev->telopt == TELNET_TELOPT_TTYPE && - ev->argc >= 1 && ev->argv[0][0] == TELNET_TTYPE_SEND) { - telnet_format_sb(telnet, TELNET_TELOPT_TTYPE, 1, - TELNET_TTYPE_IS, getenv("TERM")); - } - break; - /* error */ - case TELNET_EV_ERROR: - fprintf(stderr, "ERROR: %s\n", ev->buffer); - exit(1); - default: - /* ignore */ - break; - } -} - -int main(int argc, char **argv) { - char buffer[512]; - int rs; - int sock; - struct sockaddr_in addr; - struct pollfd pfd[2]; - struct addrinfo *ai; - struct addrinfo hints; - struct termios tios; - - /* check usage */ - if (argc != 3) { - fprintf(stderr, "Usage:\n ./telnet-client \n"); - return 1; - } - - /* look up server host */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if ((rs = getaddrinfo(argv[1], argv[2], &hints, &ai)) != 0) { - fprintf(stderr, "getaddrinfo() failed for %s: %s\n", argv[1], - gai_strerror(rs)); - return 1; - } - - /* create server socket */ - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - fprintf(stderr, "socket() failed: %s\n", strerror(errno)); - return 1; - } - - /* bind server socket */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - fprintf(stderr, "bind() failed: %s\n", strerror(errno)); - return 1; - } - - /* connect */ - if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { - fprintf(stderr, "server() failed: %s\n", strerror(errno)); - return 1; - } - - /* free address lookup info */ - freeaddrinfo(ai); - - /* get current terminal settings, set raw mode, make sure we - * register atexit handler to restore terminal settings - */ - tcgetattr(STDOUT_FILENO, &orig_tios); - atexit(_cleanup); - tios = orig_tios; - cfmakeraw(&tios); - tcsetattr(STDOUT_FILENO, TCSADRAIN, &tios); - - /* set input echoing on by default */ - do_echo = 1; - - /* initialize telnet box */ - telnet = telnet_init(telopts, _event_handler, 0, &sock); - - /* initialize poll descriptors */ - memset(pfd, 0, sizeof(pfd)); - pfd[0].fd = STDIN_FILENO; - pfd[0].events = POLLIN; - pfd[1].fd = sock; - pfd[1].events = POLLIN; - - /* loop while both connections are open */ - while (poll(pfd, 2, -1) != -1) { - /* read from stdin */ - if (pfd[0].revents & POLLIN) { - if ((rs = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) { - _input(buffer, rs); - } else if (rs == 0) { - break; - } else { - fprintf(stderr, "recv(server) failed: %s\n", - strerror(errno)); - exit(1); - } - } - - /* read from client */ - if (pfd[1].revents & POLLIN) { - if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) { - telnet_recv(telnet, buffer, rs); - } else if (rs == 0) { - break; - } else { - fprintf(stderr, "recv(client) failed: %s\n", - strerror(errno)); - exit(1); - } - } - } - - /* clean up */ - telnet_free(telnet); - close(sock); - - return 0; -} diff --git a/telnet-proxy.c b/telnet-proxy.c deleted file mode 100644 index 04fe28d..0000000 --- a/telnet-proxy.c +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Sean Middleditch - * sean@sourcemud.org - * - * The author or authors of this code dedicate any and all copyright interest - * in this code to the public domain. We make this dedication for the benefit - * of the public at large and to the detriment of our heirs and successors. We - * intend this dedication to be an overt act of relinquishment in perpetuity of - * all present and future rights to this code under copyright law. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_ZLIB -#include "zlib.h" -#endif - -#include "libtelnet.h" - -#ifdef ENABLE_COLOR -# define COLOR_SERVER "\e[35m" -# define COLOR_CLIENT "\e[34m" -# define COLOR_BOLD "\e[1m" -# define COLOR_UNBOLD "\e[22m" -# define COLOR_NORMAL "\e[0m" -#else -# define COLOR_SERVER "" -# define COLOR_CLIENT "" -# define COLOR_BOLD "" -# define COLOR_UNBOLD "" -# define COLOR_NORMAL "" -#endif - -struct conn_t { - const char *name; - int sock; - telnet_t *telnet; - struct conn_t *remote; -}; - -static const char *get_cmd(unsigned char cmd) { - static char buffer[4]; - - switch (cmd) { - case 255: return "IAC"; - case 254: return "DONT"; - case 253: return "DO"; - case 252: return "WONT"; - case 251: return "WILL"; - case 250: return "SB"; - case 249: return "GA"; - case 248: return "EL"; - case 247: return "EC"; - case 246: return "AYT"; - case 245: return "AO"; - case 244: return "IP"; - case 243: return "BREAK"; - case 242: return "DM"; - case 241: return "NOP"; - case 240: return "SE"; - case 239: return "EOR"; - case 238: return "ABORT"; - case 237: return "SUSP"; - case 236: return "xEOF"; - default: - snprintf(buffer, sizeof(buffer), "%d", (int)cmd); - return buffer; - } -} - -static const char *get_opt(unsigned char opt) { - switch (opt) { - case 0: return "BINARY"; - case 1: return "ECHO"; - case 2: return "RCP"; - case 3: return "SGA"; - case 4: return "NAMS"; - case 5: return "STATUS"; - case 6: return "TM"; - case 7: return "RCTE"; - case 8: return "NAOL"; - case 9: return "NAOP"; - case 10: return "NAOCRD"; - case 11: return "NAOHTS"; - case 12: return "NAOHTD"; - case 13: return "NAOFFD"; - case 14: return "NAOVTS"; - case 15: return "NAOVTD"; - case 16: return "NAOLFD"; - case 17: return "XASCII"; - case 18: return "LOGOUT"; - case 19: return "BM"; - case 20: return "DET"; - case 21: return "SUPDUP"; - case 22: return "SUPDUPOUTPUT"; - case 23: return "SNDLOC"; - case 24: return "TTYPE"; - case 25: return "EOR"; - case 26: return "TUID"; - case 27: return "OUTMRK"; - case 28: return "TTYLOC"; - case 29: return "3270REGIME"; - case 30: return "X3PAD"; - case 31: return "NAWS"; - case 32: return "TSPEED"; - case 33: return "LFLOW"; - case 34: return "LINEMODE"; - case 35: return "XDISPLOC"; - case 36: return "ENVIRON"; - case 37: return "AUTHENTICATION"; - case 38: return "ENCRYPT"; - case 39: return "NEW-ENVIRON"; - case 70: return "MSSP"; - case 85: return "COMPRESS"; - case 86: return "COMPRESS2"; - case 93: return "ZMP"; - case 255: return "EXOPL"; - default: return "unknown"; - } -} - -static void print_buffer(const char *buffer, size_t size) { - size_t i; - for (i = 0; i != size; ++i) { - if (buffer[i] == ' ' || (isprint(buffer[i]) && !isspace(buffer[i]))) - printf("%c", (char)buffer[i]); - else if (buffer[i] == '\n') - printf("<" COLOR_BOLD "0x%02X" COLOR_UNBOLD ">\n", - (int)buffer[i]); - else - printf("<" COLOR_BOLD "0x%02X" COLOR_UNBOLD ">", (int)buffer[i]); - } -} - -static void _send(int sock, const char *buffer, size_t size) { - int rs; - - /* send data */ - while (size > 0) { - if ((rs = send(sock, buffer, size, 0)) == -1) { - if (errno != EINTR && errno != ECONNRESET) { - fprintf(stderr, "send() failed: %s\n", strerror(errno)); - exit(1); - } else { - return; - } - } else if (rs == 0) { - fprintf(stderr, "send() unexpectedly returned 0\n"); - exit(1); - } - - /* update pointer and size to see if we've got more to send */ - buffer += rs; - size -= rs; - } -} - -static void _event_handler(telnet_t *telnet, telnet_event_t *ev, - void *user_data) { - struct conn_t *conn = (struct conn_t*)user_data; - - switch (ev->type) { - /* data received */ - case TELNET_EV_DATA: - printf("%s DATA: ", conn->name); - print_buffer(ev->buffer, ev->size); - printf(COLOR_NORMAL "\n"); - - telnet_send(conn->remote->telnet, ev->buffer, ev->size); - break; - /* data must be sent */ - case TELNET_EV_SEND: - /* DONT SPAM - printf("%s SEND: ", conn->name); - print_buffer(ev->buffer, ev->size); - printf(COLOR_BOLD "\n"); - */ - - _send(conn->sock, ev->buffer, ev->size); - break; - /* IAC command */ - case TELNET_EV_IAC: - printf("%s IAC %s" COLOR_NORMAL "\n", conn->name, - get_cmd(ev->command)); - - telnet_iac(conn->remote->telnet, ev->command); - break; - /* negotiation, WILL */ - case TELNET_EV_WILL: - printf("%s IAC WILL %d (%s)" COLOR_NORMAL "\n", conn->name, - (int)ev->telopt, get_opt(ev->telopt)); - telnet_negotiate(conn->remote->telnet, TELNET_WILL, - ev->telopt); - break; - /* negotiation, WONT */ - case TELNET_EV_WONT: - printf("%s IAC WONT %d (%s)" COLOR_NORMAL "\n", conn->name, - (int)ev->telopt, get_opt(ev->telopt)); - telnet_negotiate(conn->remote->telnet, TELNET_WONT, - ev->telopt); - break; - /* negotiation, DO */ - case TELNET_EV_DO: - printf("%s IAC DO %d (%s)" COLOR_NORMAL "\n", conn->name, - (int)ev->telopt, get_opt(ev->telopt)); - telnet_negotiate(conn->remote->telnet, TELNET_DO, - ev->telopt); - break; - case TELNET_EV_DONT: - printf("%s IAC DONT %d (%s)" COLOR_NORMAL "\n", conn->name, - (int)ev->telopt, get_opt(ev->telopt)); - telnet_negotiate(conn->remote->telnet, TELNET_DONT, - ev->telopt); - break; - /* subnegotiation */ - case TELNET_EV_SUBNEGOTIATION: - if (ev->telopt == TELNET_TELOPT_ZMP) { - if (ev->argc != 0) { - size_t i; - printf("%s ZMP [%zi params]", conn->name, ev->argc); - for (i = 0; i != ev->argc; ++i) { - printf(" \""); - print_buffer(ev->argv[i], strlen(ev->argv[i])); - printf("\""); - } - printf(COLOR_NORMAL "\n"); - } else { - printf("%s ZMP (malformed!) [%zi bytes]", - conn->name, ev->size); - print_buffer(ev->buffer, ev->size); - printf(COLOR_NORMAL "\n"); - } - } else if (ev->telopt == TELNET_TELOPT_TTYPE || - ev->telopt == TELNET_TELOPT_ENVIRON || - ev->telopt == TELNET_TELOPT_NEW_ENVIRON || - ev->telopt == TELNET_TELOPT_MSSP) { - size_t i; - printf("%s %s [%zi parts]", conn->name, get_opt(ev->telopt), - ev->argc); - for (i = 0; i != ev->argc; ++i) { - printf(" \""); - print_buffer(ev->argv[i], strlen(ev->argv[i] + 1) + 1); - printf("\""); - } - printf(COLOR_NORMAL "\n"); - } else { - printf("%s SUB %d (%s)", conn->name, (int)ev->telopt, - get_opt(ev->telopt)); - if (ev->size > 0) { - printf(" [%zi bytes]: ", ev->size); - print_buffer(ev->buffer, ev->size); - } - printf(COLOR_NORMAL "\n"); - } - - /* forward */ - telnet_subnegotiation(conn->remote->telnet, ev->telopt, - ev->buffer, ev->size); - break; - /* compression notification */ - case TELNET_EV_COMPRESS: - printf("%s COMPRESSION %s" COLOR_NORMAL "\n", conn->name, - ev->command ? "ON" : "OFF"); - break; - /* warning */ - case TELNET_EV_WARNING: - printf("%s WARNING: %s" COLOR_NORMAL "\n", conn->name, ev->buffer); - break; - /* error */ - case TELNET_EV_ERROR: - printf("%s ERROR: %s" COLOR_NORMAL "\n", conn->name, ev->buffer); - exit(1); - } -} - -int main(int argc, char **argv) { - char buffer[512]; - short listen_port; - int listen_sock; - int rs; - struct sockaddr_in addr; - socklen_t addrlen; - struct pollfd pfd[2]; - struct conn_t server; - struct conn_t client; - struct addrinfo *ai; - struct addrinfo hints; - - /* check usage */ - if (argc != 4) { - fprintf(stderr, "Usage:\n ./telnet-proxy " - "\n"); - return 1; - } - - /* parse listening port */ - listen_port = strtol(argv[3], 0, 10); - - /* loop forever, until user kills process */ - for (;;) { - /* create listening socket */ - if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - fprintf(stderr, "socket() failed: %s\n", strerror(errno)); - return 1; - } - - /* reuse address option */ - rs = 1; - setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs)); - - /* bind to listening addr/port */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(listen_port); - if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - fprintf(stderr, "bind() failed: %s\n", strerror(errno)); - return 1; - } - - printf("LISTENING ON PORT %d\n", listen_port); - - /* wait for client */ - if (listen(listen_sock, 1) == -1) { - fprintf(stderr, "listen() failed: %s\n", strerror(errno)); - return 1; - } - addrlen = sizeof(addr); - if ((client.sock = accept(listen_sock, (struct sockaddr *)&addr, - &addrlen)) == -1) { - fprintf(stderr, "accept() failed: %s\n", strerror(errno)); - return 1; - } - - printf("CLIENT CONNECTION RECEIVED\n"); - - /* stop listening now that we have a client */ - close(listen_sock); - - /* look up server host */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if ((rs = getaddrinfo(argv[1], argv[2], &hints, &ai)) != 0) { - fprintf(stderr, "getaddrinfo() failed for %s: %s\n", argv[1], - gai_strerror(rs)); - return 1; - } - - /* create server socket */ - if ((server.sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - fprintf(stderr, "socket() failed: %s\n", strerror(errno)); - return 1; - } - - /* bind server socket */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - if (bind(server.sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - fprintf(stderr, "bind() failed: %s\n", strerror(errno)); - return 1; - } - - /* connect */ - if (connect(server.sock, ai->ai_addr, ai->ai_addrlen) == -1) { - fprintf(stderr, "server() failed: %s\n", strerror(errno)); - return 1; - } - - /* free address lookup info */ - freeaddrinfo(ai); - - printf("SERVER CONNECTION ESTABLISHED\n"); - - /* initialize connection structs */ - server.name = COLOR_SERVER "SERVER"; - server.remote = &client; - client.name = COLOR_CLIENT "CLIENT"; - client.remote = &server; - - /* initialize telnet boxes */ - server.telnet = telnet_init(0, _event_handler, TELNET_FLAG_PROXY, - &server); - client.telnet = telnet_init(0, _event_handler, TELNET_FLAG_PROXY, - &client); - - /* initialize poll descriptors */ - memset(pfd, 0, sizeof(pfd)); - pfd[0].fd = server.sock; - pfd[0].events = POLLIN; - pfd[1].fd = client.sock; - pfd[1].events = POLLIN; - - /* loop while both connections are open */ - while (poll(pfd, 2, -1) != -1) { - /* read from server */ - if (pfd[0].revents & POLLIN) { - if ((rs = recv(server.sock, buffer, sizeof(buffer), 0)) > 0) { - telnet_recv(server.telnet, buffer, rs); - } else if (rs == 0) { - printf("%s DISCONNECTED" COLOR_NORMAL "\n", server.name); - break; - } else { - if (errno != EINTR && errno != ECONNRESET) { - fprintf(stderr, "recv(server) failed: %s\n", - strerror(errno)); - exit(1); - } - } - } - - /* read from client */ - if (pfd[1].revents & POLLIN) { - if ((rs = recv(client.sock, buffer, sizeof(buffer), 0)) > 0) { - telnet_recv(client.telnet, buffer, rs); - } else if (rs == 0) { - printf("%s DISCONNECTED" COLOR_NORMAL "\n", client.name); - break; - } else { - if (errno != EINTR && errno != ECONNRESET) { - fprintf(stderr, "recv(server) failed: %s\n", - strerror(errno)); - exit(1); - } - } - } - } - - /* clean up */ - telnet_free(server.telnet); - telnet_free(client.telnet); - close(server.sock); - close(client.sock); - - /* all done */ - printf("BOTH CONNECTIONS CLOSED\n"); - } - - /* not that we can reach this, but GCC will cry if it's not here */ - return 0; -} diff --git a/util/telnet-chatd.c b/util/telnet-chatd.c new file mode 100644 index 0000000..7bad622 --- /dev/null +++ b/util/telnet-chatd.c @@ -0,0 +1,337 @@ +/* + * Sean Middleditch + * sean@sourcemud.org + * + * The author or authors of this code dedicate any and all copyright interest + * in this code to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and successors. We + * intend this dedication to be an overt act of relinquishment in perpetuity of + * all present and future rights to this code under copyright law. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ZLIB +#include "zlib.h" +#endif + +#include "libtelnet.h" + +#define MAX_USERS 64 +#define LINEBUFFER_SIZE 256 + +const telnet_telopt_t telopts[] = { + { TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DONT }, + { -1, 0, 0 } +}; + +struct user_t { + char *name; + int sock; + telnet_t *telnet; + char linebuf[256]; + int linepos; +}; + +static struct user_t users[MAX_USERS]; + +static void linebuffer_push(char *buffer, size_t size, int *linepos, + char ch, void (*cb)(const char *line, int overflow, void *ud), + void *ud) { + + /* CRLF -- line terminator */ + if (ch == '\n' && *linepos > 0 && buffer[*linepos - 1] == '\r') { + /* NUL terminate (replaces \r in buffer), notify app, clear */ + buffer[*linepos - 1] = 0; + cb(buffer, 0, ud); + *linepos = 0; + + /* CRNUL -- just a CR */ + } else if (ch == 0 && *linepos > 0 && buffer[*linepos - 1] == '\r') { + /* do nothing, the CR is already in the buffer */ + + /* anything else (including technically invalid CR followed by + * anything besides LF or NUL -- just buffer if we have room + * \r + */ + } else if (*linepos != size) { + buffer[(*linepos)++] = ch; + + /* buffer overflow */ + } else { + /* terminate (NOTE: eats a byte), notify app, clear buffer */ + buffer[size - 1] = 0; + cb(buffer, size - 1, ud); + *linepos = 0; + } +} + +static void _message(const char *from, const char *msg) { + int i; + for (i = 0; i != MAX_USERS; ++i) { + if (users[i].sock != -1) { + telnet_printf(users[i].telnet, "%s: %s\n", from, msg); + } + } +} + +static void _send(int sock, const char *buffer, unsigned int size) { + int rs; + + /* ignore on invalid socket */ + if (sock == -1) + return; + + /* send data */ + while (size > 0) { + if ((rs = send(sock, buffer, size, 0)) == -1) { + if (errno != EINTR && errno != ECONNRESET) { + fprintf(stderr, "send() failed: %s\n", strerror(errno)); + exit(1); + } else { + return; + } + } else if (rs == 0) { + fprintf(stderr, "send() unexpectedly returned 0\n"); + exit(1); + } + + /* update pointer and size to see if we've got more to send */ + buffer += rs; + size -= rs; + } +} + +/* process input line */ +static void _online(const char *line, int overflow, void *ud) { + struct user_t *user = (struct user_t*)ud; + int i; + + /* if the user has no name, this is his "login" */ + if (user->name == 0) { + /* must not be empty, must be at least 32 chars */ + if (strlen(line) == 0 || strlen(line) > 32) { + telnet_printf(user->telnet, "Invalid name.\nEnter name: "); + return; + } + + /* must not already be in use */ + for (i = 0; i != MAX_USERS; ++i) { + if (users[i].name != 0 && strcmp(users[i].name, line) == 0) { + telnet_printf(user->telnet, "Name in use.\nEnter name: "); + return; + } + } + + /* keep name */ + user->name = strdup(line); + telnet_printf(user->telnet, "Welcome, %s!\n", line); + return; + } + + /* if line is "quit" then, well, quit */ + if (strcmp(line, "quit") == 0) { + close(user->sock); + user->sock = -1; + _message(user->name, "** HAS QUIT **"); + free(user->name); + user->name = 0; + return; + } + + /* just a message -- send to all users */ + _message(user->name, line); +} + +static void _input(struct user_t *user, const char *buffer, + unsigned int size) { + unsigned int i; + for (i = 0; i != size; ++i) + linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos, + (char)buffer[i], _online, user); +} + +static void _event_handler(telnet_t *telnet, telnet_event_t *ev, + void *user_data) { + struct user_t *user = (struct user_t*)user_data; + + switch (ev->type) { + /* data received */ + case TELNET_EV_DATA: + _input(user, ev->buffer, ev->size); + break; + /* data must be sent */ + case TELNET_EV_SEND: + _send(user->sock, ev->buffer, ev->size); + break; + /* enable compress2 if accepted by client */ + case TELNET_EV_DO: + if (ev->telopt == TELNET_TELOPT_COMPRESS2) + telnet_begin_compress2(telnet); + break; + /* error */ + case TELNET_EV_ERROR: + close(user->sock); + user->sock = -1; + if (user->name != 0) { + _message(user->name, "** HAS HAD AN ERROR **"); + free(user->name); + user->name = 0; + } + telnet_free(user->telnet); + break; + default: + /* ignore */ + break; + } +} + +int main(int argc, char **argv) { + char buffer[512]; + short listen_port; + int listen_sock; + int rs; + int i; + struct sockaddr_in addr; + socklen_t addrlen; + struct pollfd pfd[MAX_USERS + 1]; + + /* check usage */ + if (argc != 2) { + fprintf(stderr, "Usage:\n ./telnet-chatd \n"); + return 1; + } + + /* initialize data structures */ + memset(&pfd, 0, sizeof(pfd)); + memset(users, 0, sizeof(users)); + for (i = 0; i != MAX_USERS; ++i) + users[i].sock = -1; + + /* parse listening port */ + listen_port = strtol(argv[1], 0, 10); + + /* create listening socket */ + if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "socket() failed: %s\n", strerror(errno)); + return 1; + } + + /* reuse address option */ + rs = 1; + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs)); + + /* bind to listening addr/port */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(listen_port); + if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "bind() failed: %s\n", strerror(errno)); + return 1; + } + + /* listen for clients */ + if (listen(listen_sock, 5) == -1) { + fprintf(stderr, "listen() failed: %s\n", strerror(errno)); + return 1; + } + + printf("LISTENING ON PORT %d\n", listen_port); + + /* initialize listening descriptors */ + pfd[MAX_USERS].fd = listen_sock; + pfd[MAX_USERS].events = POLLIN; + + /* loop for ever */ + for (;;) { + /* prepare for poll */ + for (i = 0; i != MAX_USERS; ++i) { + if (users[i].sock != -1) { + pfd[i].fd = users[i].sock; + pfd[i].events = POLLIN; + } else { + pfd[i].fd = -1; + pfd[i].events = 0; + } + } + + /* poll */ + rs = poll(pfd, MAX_USERS + 1, -1); + if (rs == -1 && errno != EINTR) { + fprintf(stderr, "poll() failed: %s\n", strerror(errno)); + return 1; + } + + /* new connection */ + if (pfd[MAX_USERS].revents & POLLIN) { + /* acept the sock */ + addrlen = sizeof(addr); + if ((rs = accept(listen_sock, (struct sockaddr *)&addr, + &addrlen)) == -1) { + fprintf(stderr, "accept() failed: %s\n", strerror(errno)); + return 1; + } + + printf("Connection received.\n"); + + /* find a free user */ + for (i = 0; i != MAX_USERS; ++i) + if (users[i].sock == -1) + break; + if (i == MAX_USERS) { + printf(" rejected (too many users)\n"); + _send(rs, "Too many users.\r\n", 14); + close(rs); + } + + /* init, welcome */ + users[i].sock = rs; + users[i].telnet = telnet_init(telopts, _event_handler, 0, + &users[i]); + telnet_negotiate(users[i].telnet, TELNET_WILL, + TELNET_TELOPT_COMPRESS2); + telnet_printf(users[i].telnet, "Enter name: "); + } + + /* read from client */ + for (i = 0; i != MAX_USERS; ++i) { + /* skip users that aren't actually connected */ + if (users[i].sock == -1) + continue; + + if (pfd[i].revents & POLLIN) { + if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) { + telnet_recv(users[i].telnet, buffer, rs); + } else if (rs == 0) { + printf("Connection closed.\n"); + close(users[i].sock); + if (users[i].name != 0) { + _message(users[i].name, "** HAS DISCONNECTED **"); + free(users[i].name); + users[i].name = 0; + } + telnet_free(users[i].telnet); + users[i].sock = -1; + break; + } else if (errno != EINTR) { + fprintf(stderr, "recv(client) failed: %s\n", + strerror(errno)); + exit(1); + } + } + } + } + + /* not that we can reach this, but GCC will cry if it's not here */ + return 0; +} diff --git a/util/telnet-client.c b/util/telnet-client.c new file mode 100644 index 0000000..5565656 --- /dev/null +++ b/util/telnet-client.c @@ -0,0 +1,243 @@ +/* + * Sean Middleditch + * sean@sourcemud.org + * + * The author or authors of this code dedicate any and all copyright interest + * in this code to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and successors. We + * intend this dedication to be an overt act of relinquishment in perpetuity of + * all present and future rights to this code under copyright law. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ZLIB +#include "zlib.h" +#endif + +#include "libtelnet.h" + +static struct termios orig_tios; +static telnet_t *telnet; +static int do_echo; + +static const telnet_telopt_t telopts[] = { + { TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO }, + { TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT }, + { TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO }, + { TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO }, + { -1, 0, 0 } +}; + +static void _cleanup(void) { + tcsetattr(STDOUT_FILENO, TCSADRAIN, &orig_tios); +} + +static void _input(char *buffer, int size) { + static char crlf[] = { '\r', '\n' }; + int i; + + for (i = 0; i != size; ++i) { + /* if we got a CR or LF, replace with CRLF + * NOTE that usually you'd get a CR in UNIX, but in raw + * mode we get LF instead (not sure why) + */ + if (buffer[i] == '\r' || buffer[i] == '\n') { + if (do_echo) + printf("\r\n"); + telnet_send(telnet, crlf, 2); + } else { + if (do_echo) + putchar(buffer[i]); + telnet_send(telnet, buffer + i, 1); + } + } + fflush(stdout); +} + +static void _send(int sock, const char *buffer, size_t size) { + int rs; + + /* send data */ + while (size > 0) { + if ((rs = send(sock, buffer, size, 0)) == -1) { + fprintf(stderr, "send() failed: %s\n", strerror(errno)); + exit(1); + } else if (rs == 0) { + fprintf(stderr, "send() unexpectedly returned 0\n"); + exit(1); + } + + /* update pointer and size to see if we've got more to send */ + buffer += rs; + size -= rs; + } +} + +static void _event_handler(telnet_t *telnet, telnet_event_t *ev, + void *user_data) { + int sock = *(int*)user_data; + + switch (ev->type) { + /* data received */ + case TELNET_EV_DATA: + printf("%.*s", (int)ev->size, ev->buffer); + break; + /* data must be sent */ + case TELNET_EV_SEND: + _send(sock, ev->buffer, ev->size); + break; + /* request to enable remote feature (or receipt) */ + case TELNET_EV_WILL: + /* we'll agree to turn off our echo if server wants us to stop */ + if (ev->telopt == TELNET_TELOPT_ECHO) + do_echo = 0; + break; + /* notification of disabling remote feature (or receipt) */ + case TELNET_EV_WONT: + if (ev->telopt == TELNET_TELOPT_ECHO) + do_echo = 1; + break; + /* request to enable local feature (or receipt) */ + case TELNET_EV_DO: + break; + /* demand to disable local feature (or receipt) */ + case TELNET_EV_DONT: + break; + /* respond to particular subnegotiations */ + case TELNET_EV_SUBNEGOTIATION: + /* if they just asked for our terminal type, response with it */ + /* respond with our terminal type */ + if (ev->telopt == TELNET_TELOPT_TTYPE && + ev->argc >= 1 && ev->argv[0][0] == TELNET_TTYPE_SEND) { + telnet_format_sb(telnet, TELNET_TELOPT_TTYPE, 1, + TELNET_TTYPE_IS, getenv("TERM")); + } + break; + /* error */ + case TELNET_EV_ERROR: + fprintf(stderr, "ERROR: %s\n", ev->buffer); + exit(1); + default: + /* ignore */ + break; + } +} + +int main(int argc, char **argv) { + char buffer[512]; + int rs; + int sock; + struct sockaddr_in addr; + struct pollfd pfd[2]; + struct addrinfo *ai; + struct addrinfo hints; + struct termios tios; + + /* check usage */ + if (argc != 3) { + fprintf(stderr, "Usage:\n ./telnet-client \n"); + return 1; + } + + /* look up server host */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if ((rs = getaddrinfo(argv[1], argv[2], &hints, &ai)) != 0) { + fprintf(stderr, "getaddrinfo() failed for %s: %s\n", argv[1], + gai_strerror(rs)); + return 1; + } + + /* create server socket */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "socket() failed: %s\n", strerror(errno)); + return 1; + } + + /* bind server socket */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "bind() failed: %s\n", strerror(errno)); + return 1; + } + + /* connect */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { + fprintf(stderr, "server() failed: %s\n", strerror(errno)); + return 1; + } + + /* free address lookup info */ + freeaddrinfo(ai); + + /* get current terminal settings, set raw mode, make sure we + * register atexit handler to restore terminal settings + */ + tcgetattr(STDOUT_FILENO, &orig_tios); + atexit(_cleanup); + tios = orig_tios; + cfmakeraw(&tios); + tcsetattr(STDOUT_FILENO, TCSADRAIN, &tios); + + /* set input echoing on by default */ + do_echo = 1; + + /* initialize telnet box */ + telnet = telnet_init(telopts, _event_handler, 0, &sock); + + /* initialize poll descriptors */ + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = STDIN_FILENO; + pfd[0].events = POLLIN; + pfd[1].fd = sock; + pfd[1].events = POLLIN; + + /* loop while both connections are open */ + while (poll(pfd, 2, -1) != -1) { + /* read from stdin */ + if (pfd[0].revents & POLLIN) { + if ((rs = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) { + _input(buffer, rs); + } else if (rs == 0) { + break; + } else { + fprintf(stderr, "recv(server) failed: %s\n", + strerror(errno)); + exit(1); + } + } + + /* read from client */ + if (pfd[1].revents & POLLIN) { + if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) { + telnet_recv(telnet, buffer, rs); + } else if (rs == 0) { + break; + } else { + fprintf(stderr, "recv(client) failed: %s\n", + strerror(errno)); + exit(1); + } + } + } + + /* clean up */ + telnet_free(telnet); + close(sock); + + return 0; +} diff --git a/util/telnet-proxy.c b/util/telnet-proxy.c new file mode 100644 index 0000000..04fe28d --- /dev/null +++ b/util/telnet-proxy.c @@ -0,0 +1,451 @@ +/* + * Sean Middleditch + * sean@sourcemud.org + * + * The author or authors of this code dedicate any and all copyright interest + * in this code to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and successors. We + * intend this dedication to be an overt act of relinquishment in perpetuity of + * all present and future rights to this code under copyright law. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ZLIB +#include "zlib.h" +#endif + +#include "libtelnet.h" + +#ifdef ENABLE_COLOR +# define COLOR_SERVER "\e[35m" +# define COLOR_CLIENT "\e[34m" +# define COLOR_BOLD "\e[1m" +# define COLOR_UNBOLD "\e[22m" +# define COLOR_NORMAL "\e[0m" +#else +# define COLOR_SERVER "" +# define COLOR_CLIENT "" +# define COLOR_BOLD "" +# define COLOR_UNBOLD "" +# define COLOR_NORMAL "" +#endif + +struct conn_t { + const char *name; + int sock; + telnet_t *telnet; + struct conn_t *remote; +}; + +static const char *get_cmd(unsigned char cmd) { + static char buffer[4]; + + switch (cmd) { + case 255: return "IAC"; + case 254: return "DONT"; + case 253: return "DO"; + case 252: return "WONT"; + case 251: return "WILL"; + case 250: return "SB"; + case 249: return "GA"; + case 248: return "EL"; + case 247: return "EC"; + case 246: return "AYT"; + case 245: return "AO"; + case 244: return "IP"; + case 243: return "BREAK"; + case 242: return "DM"; + case 241: return "NOP"; + case 240: return "SE"; + case 239: return "EOR"; + case 238: return "ABORT"; + case 237: return "SUSP"; + case 236: return "xEOF"; + default: + snprintf(buffer, sizeof(buffer), "%d", (int)cmd); + return buffer; + } +} + +static const char *get_opt(unsigned char opt) { + switch (opt) { + case 0: return "BINARY"; + case 1: return "ECHO"; + case 2: return "RCP"; + case 3: return "SGA"; + case 4: return "NAMS"; + case 5: return "STATUS"; + case 6: return "TM"; + case 7: return "RCTE"; + case 8: return "NAOL"; + case 9: return "NAOP"; + case 10: return "NAOCRD"; + case 11: return "NAOHTS"; + case 12: return "NAOHTD"; + case 13: return "NAOFFD"; + case 14: return "NAOVTS"; + case 15: return "NAOVTD"; + case 16: return "NAOLFD"; + case 17: return "XASCII"; + case 18: return "LOGOUT"; + case 19: return "BM"; + case 20: return "DET"; + case 21: return "SUPDUP"; + case 22: return "SUPDUPOUTPUT"; + case 23: return "SNDLOC"; + case 24: return "TTYPE"; + case 25: return "EOR"; + case 26: return "TUID"; + case 27: return "OUTMRK"; + case 28: return "TTYLOC"; + case 29: return "3270REGIME"; + case 30: return "X3PAD"; + case 31: return "NAWS"; + case 32: return "TSPEED"; + case 33: return "LFLOW"; + case 34: return "LINEMODE"; + case 35: return "XDISPLOC"; + case 36: return "ENVIRON"; + case 37: return "AUTHENTICATION"; + case 38: return "ENCRYPT"; + case 39: return "NEW-ENVIRON"; + case 70: return "MSSP"; + case 85: return "COMPRESS"; + case 86: return "COMPRESS2"; + case 93: return "ZMP"; + case 255: return "EXOPL"; + default: return "unknown"; + } +} + +static void print_buffer(const char *buffer, size_t size) { + size_t i; + for (i = 0; i != size; ++i) { + if (buffer[i] == ' ' || (isprint(buffer[i]) && !isspace(buffer[i]))) + printf("%c", (char)buffer[i]); + else if (buffer[i] == '\n') + printf("<" COLOR_BOLD "0x%02X" COLOR_UNBOLD ">\n", + (int)buffer[i]); + else + printf("<" COLOR_BOLD "0x%02X" COLOR_UNBOLD ">", (int)buffer[i]); + } +} + +static void _send(int sock, const char *buffer, size_t size) { + int rs; + + /* send data */ + while (size > 0) { + if ((rs = send(sock, buffer, size, 0)) == -1) { + if (errno != EINTR && errno != ECONNRESET) { + fprintf(stderr, "send() failed: %s\n", strerror(errno)); + exit(1); + } else { + return; + } + } else if (rs == 0) { + fprintf(stderr, "send() unexpectedly returned 0\n"); + exit(1); + } + + /* update pointer and size to see if we've got more to send */ + buffer += rs; + size -= rs; + } +} + +static void _event_handler(telnet_t *telnet, telnet_event_t *ev, + void *user_data) { + struct conn_t *conn = (struct conn_t*)user_data; + + switch (ev->type) { + /* data received */ + case TELNET_EV_DATA: + printf("%s DATA: ", conn->name); + print_buffer(ev->buffer, ev->size); + printf(COLOR_NORMAL "\n"); + + telnet_send(conn->remote->telnet, ev->buffer, ev->size); + break; + /* data must be sent */ + case TELNET_EV_SEND: + /* DONT SPAM + printf("%s SEND: ", conn->name); + print_buffer(ev->buffer, ev->size); + printf(COLOR_BOLD "\n"); + */ + + _send(conn->sock, ev->buffer, ev->size); + break; + /* IAC command */ + case TELNET_EV_IAC: + printf("%s IAC %s" COLOR_NORMAL "\n", conn->name, + get_cmd(ev->command)); + + telnet_iac(conn->remote->telnet, ev->command); + break; + /* negotiation, WILL */ + case TELNET_EV_WILL: + printf("%s IAC WILL %d (%s)" COLOR_NORMAL "\n", conn->name, + (int)ev->telopt, get_opt(ev->telopt)); + telnet_negotiate(conn->remote->telnet, TELNET_WILL, + ev->telopt); + break; + /* negotiation, WONT */ + case TELNET_EV_WONT: + printf("%s IAC WONT %d (%s)" COLOR_NORMAL "\n", conn->name, + (int)ev->telopt, get_opt(ev->telopt)); + telnet_negotiate(conn->remote->telnet, TELNET_WONT, + ev->telopt); + break; + /* negotiation, DO */ + case TELNET_EV_DO: + printf("%s IAC DO %d (%s)" COLOR_NORMAL "\n", conn->name, + (int)ev->telopt, get_opt(ev->telopt)); + telnet_negotiate(conn->remote->telnet, TELNET_DO, + ev->telopt); + break; + case TELNET_EV_DONT: + printf("%s IAC DONT %d (%s)" COLOR_NORMAL "\n", conn->name, + (int)ev->telopt, get_opt(ev->telopt)); + telnet_negotiate(conn->remote->telnet, TELNET_DONT, + ev->telopt); + break; + /* subnegotiation */ + case TELNET_EV_SUBNEGOTIATION: + if (ev->telopt == TELNET_TELOPT_ZMP) { + if (ev->argc != 0) { + size_t i; + printf("%s ZMP [%zi params]", conn->name, ev->argc); + for (i = 0; i != ev->argc; ++i) { + printf(" \""); + print_buffer(ev->argv[i], strlen(ev->argv[i])); + printf("\""); + } + printf(COLOR_NORMAL "\n"); + } else { + printf("%s ZMP (malformed!) [%zi bytes]", + conn->name, ev->size); + print_buffer(ev->buffer, ev->size); + printf(COLOR_NORMAL "\n"); + } + } else if (ev->telopt == TELNET_TELOPT_TTYPE || + ev->telopt == TELNET_TELOPT_ENVIRON || + ev->telopt == TELNET_TELOPT_NEW_ENVIRON || + ev->telopt == TELNET_TELOPT_MSSP) { + size_t i; + printf("%s %s [%zi parts]", conn->name, get_opt(ev->telopt), + ev->argc); + for (i = 0; i != ev->argc; ++i) { + printf(" \""); + print_buffer(ev->argv[i], strlen(ev->argv[i] + 1) + 1); + printf("\""); + } + printf(COLOR_NORMAL "\n"); + } else { + printf("%s SUB %d (%s)", conn->name, (int)ev->telopt, + get_opt(ev->telopt)); + if (ev->size > 0) { + printf(" [%zi bytes]: ", ev->size); + print_buffer(ev->buffer, ev->size); + } + printf(COLOR_NORMAL "\n"); + } + + /* forward */ + telnet_subnegotiation(conn->remote->telnet, ev->telopt, + ev->buffer, ev->size); + break; + /* compression notification */ + case TELNET_EV_COMPRESS: + printf("%s COMPRESSION %s" COLOR_NORMAL "\n", conn->name, + ev->command ? "ON" : "OFF"); + break; + /* warning */ + case TELNET_EV_WARNING: + printf("%s WARNING: %s" COLOR_NORMAL "\n", conn->name, ev->buffer); + break; + /* error */ + case TELNET_EV_ERROR: + printf("%s ERROR: %s" COLOR_NORMAL "\n", conn->name, ev->buffer); + exit(1); + } +} + +int main(int argc, char **argv) { + char buffer[512]; + short listen_port; + int listen_sock; + int rs; + struct sockaddr_in addr; + socklen_t addrlen; + struct pollfd pfd[2]; + struct conn_t server; + struct conn_t client; + struct addrinfo *ai; + struct addrinfo hints; + + /* check usage */ + if (argc != 4) { + fprintf(stderr, "Usage:\n ./telnet-proxy " + "\n"); + return 1; + } + + /* parse listening port */ + listen_port = strtol(argv[3], 0, 10); + + /* loop forever, until user kills process */ + for (;;) { + /* create listening socket */ + if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "socket() failed: %s\n", strerror(errno)); + return 1; + } + + /* reuse address option */ + rs = 1; + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs)); + + /* bind to listening addr/port */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(listen_port); + if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "bind() failed: %s\n", strerror(errno)); + return 1; + } + + printf("LISTENING ON PORT %d\n", listen_port); + + /* wait for client */ + if (listen(listen_sock, 1) == -1) { + fprintf(stderr, "listen() failed: %s\n", strerror(errno)); + return 1; + } + addrlen = sizeof(addr); + if ((client.sock = accept(listen_sock, (struct sockaddr *)&addr, + &addrlen)) == -1) { + fprintf(stderr, "accept() failed: %s\n", strerror(errno)); + return 1; + } + + printf("CLIENT CONNECTION RECEIVED\n"); + + /* stop listening now that we have a client */ + close(listen_sock); + + /* look up server host */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if ((rs = getaddrinfo(argv[1], argv[2], &hints, &ai)) != 0) { + fprintf(stderr, "getaddrinfo() failed for %s: %s\n", argv[1], + gai_strerror(rs)); + return 1; + } + + /* create server socket */ + if ((server.sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "socket() failed: %s\n", strerror(errno)); + return 1; + } + + /* bind server socket */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + if (bind(server.sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "bind() failed: %s\n", strerror(errno)); + return 1; + } + + /* connect */ + if (connect(server.sock, ai->ai_addr, ai->ai_addrlen) == -1) { + fprintf(stderr, "server() failed: %s\n", strerror(errno)); + return 1; + } + + /* free address lookup info */ + freeaddrinfo(ai); + + printf("SERVER CONNECTION ESTABLISHED\n"); + + /* initialize connection structs */ + server.name = COLOR_SERVER "SERVER"; + server.remote = &client; + client.name = COLOR_CLIENT "CLIENT"; + client.remote = &server; + + /* initialize telnet boxes */ + server.telnet = telnet_init(0, _event_handler, TELNET_FLAG_PROXY, + &server); + client.telnet = telnet_init(0, _event_handler, TELNET_FLAG_PROXY, + &client); + + /* initialize poll descriptors */ + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = server.sock; + pfd[0].events = POLLIN; + pfd[1].fd = client.sock; + pfd[1].events = POLLIN; + + /* loop while both connections are open */ + while (poll(pfd, 2, -1) != -1) { + /* read from server */ + if (pfd[0].revents & POLLIN) { + if ((rs = recv(server.sock, buffer, sizeof(buffer), 0)) > 0) { + telnet_recv(server.telnet, buffer, rs); + } else if (rs == 0) { + printf("%s DISCONNECTED" COLOR_NORMAL "\n", server.name); + break; + } else { + if (errno != EINTR && errno != ECONNRESET) { + fprintf(stderr, "recv(server) failed: %s\n", + strerror(errno)); + exit(1); + } + } + } + + /* read from client */ + if (pfd[1].revents & POLLIN) { + if ((rs = recv(client.sock, buffer, sizeof(buffer), 0)) > 0) { + telnet_recv(client.telnet, buffer, rs); + } else if (rs == 0) { + printf("%s DISCONNECTED" COLOR_NORMAL "\n", client.name); + break; + } else { + if (errno != EINTR && errno != ECONNRESET) { + fprintf(stderr, "recv(server) failed: %s\n", + strerror(errno)); + exit(1); + } + } + } + } + + /* clean up */ + telnet_free(server.telnet); + telnet_free(client.telnet); + close(server.sock); + close(client.sock); + + /* all done */ + printf("BOTH CONNECTIONS CLOSED\n"); + } + + /* not that we can reach this, but GCC will cry if it's not here */ + return 0; +} -- cgit v1.2.3