aboutsummaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
authorSean Middleditch <sean@mojodo.com>2009-11-03 19:06:17 -0800
committerSean Middleditch <sean@mojodo.com>2009-11-03 19:06:17 -0800
commit641df58b6ebaa4230e7c58de242ef022c4b59990 (patch)
tree9f532c982eaa37bf37c100f9a150f8d08fe4cd55 /util
parent9e4f4912e68a46361fbda9335d8efa131eac5a28 (diff)
move util source code to util/ directory
Diffstat (limited to 'util')
-rw-r--r--util/telnet-chatd.c337
-rw-r--r--util/telnet-client.c243
-rw-r--r--util/telnet-proxy.c451
3 files changed, 1031 insertions, 0 deletions
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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#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 <port>\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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <termios.h>
+#include <unistd.h>
+
+#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 <host> <port>\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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#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 <remote ip> <remote port> "
+ "<local port>\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;
+}