/* * 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" struct conn_t { const char *name; int sock; struct libtelnet_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(unsigned char *buffer, unsigned int size) { unsigned int 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("<%02X>\n", (int)buffer[i]); else printf("<%02X>", (int)buffer[i]); } } void libtelnet_data_cb(struct libtelnet_t *telnet, unsigned char *buffer, unsigned int size, void *user_data) { struct conn_t *conn = (struct conn_t*)user_data; printf("%s DATA: ", conn->name); print_buffer(buffer, size); printf("\e[0m\n"); libtelnet_send_data(&conn->remote->telnet, buffer, size, conn->remote); } void libtelnet_send_cb(struct libtelnet_t *telnet, unsigned char *buffer, unsigned int size, void *user_data) { struct conn_t *conn = (struct conn_t*)user_data; int rs; /* DONT SPAM printf("%s SEND: ", conn->name); print_buffer(buffer, size); printf("\e[0m\n"); */ /* send data */ while (size > 0) { if ((rs = send(conn->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; } } void libtelnet_command_cb(struct libtelnet_t *telnet, unsigned char cmd, void *user_data) { struct conn_t *conn = (struct conn_t*)user_data; printf("%s IAC %s\e[0m\n", conn->name, get_cmd(cmd)); libtelnet_send_command(&conn->remote->telnet, cmd, conn->remote); } void libtelnet_negotiate_cb(struct libtelnet_t *telnet, unsigned char cmd, unsigned char opt, void *user_data) { struct conn_t *conn = (struct conn_t*)user_data; printf("%s IAC %s %d (%s)\e[0m\n", conn->name, get_cmd(cmd), (int)opt, get_opt(opt)); /* FIXME: HACK: allow MCCP2 from server without passing it * through to client. this is temporary until libtelnet supports * the server-end of MCCP2. */ if (cmd == LIBTELNET_WILL && opt == LIBTELNET_OPTION_COMPRESS2) { libtelnet_send_negotiate(&conn->telnet, LIBTELNET_DO, opt, conn); return; } libtelnet_send_negotiate(&conn->remote->telnet, cmd, opt, conn->remote); } void libtelnet_subrequest_cb(struct libtelnet_t *telnet, unsigned char type, unsigned char *buffer, unsigned int size, void *user_data) { struct conn_t *conn = (struct conn_t*)user_data; printf("%s SUB %d (%s)", conn->name, (int)type, get_opt(type)); if (size > 0) { printf(": "); print_buffer(buffer, size); } printf("\e[0m\n"); libtelnet_send_subrequest(&conn->remote->telnet, type, buffer, size, conn->remote); } void libtelnet_error_cb(struct libtelnet_t *telnet, enum libtelnet_error_t error, void *user_data) { struct conn_t *conn = (struct conn_t*)user_data; printf("%s ERROR: %d\e[0m\n", conn->name, (int)error); exit(1); } int main(int argc, char **argv) { unsigned char buffer[512]; int listen_sock; int rs; struct sockaddr_in addr; socklen_t addrlen; struct pollfd pfd[2]; struct conn_t server; struct conn_t client; /* check usage */ if (argc != 4) { fprintf(stderr, "Usage:\n ./telnet-proxy " "\n"); return 1; } /* 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(strtol(argv[3], 0, 10)); if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { fprintf(stderr, "bind() failed: %s\n", strerror(errno)); return 1; } /* wait for client */ if (listen(listen_sock, 5) == -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; } /* create server socket */ if ((server.sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "socket() failed: %s\n", strerror(errno)); return 1; } /* connect to server */ 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; } memset(&addr, 0, sizeof(addr)); if (inet_pton(AF_INET, argv[1], &addr.sin_addr) != 1) { fprintf(stderr, "inet_pton() failed: %s\n", strerror(errno)); return 1; } addr.sin_family = AF_INET; addr.sin_port = htons(strtol(argv[2], 0, 10)); if (connect(server.sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { fprintf(stderr, "server() failed: %s\n", strerror(errno)); return 1; } /* initialize connection structs */ server.name = "\e[31mSERVER"; server.remote = &client; client.name = "\e[34mCLIENT"; client.remote = &server; /* initialize telnet boxes */ libtelnet_init(&server.telnet); libtelnet_init(&client.telnet); /* 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) { libtelnet_push(&server.telnet, buffer, rs, (void*)&server); } else if (rs == 0) { printf("%s DISCONNECTED\e[0m\n", server.name); break; } else { 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) { libtelnet_push(&client.telnet, buffer, rs, (void*)&client); } else if (rs == 0) { printf("%s DISCONNECTED\e[0m\n", client.name); break; } else { fprintf(stderr, "recv(client) failed: %s\n", strerror(errno)); exit(1); } } } /* clean up */ libtelnet_free(&server.telnet); libtelnet_free(&client.telnet); close(server.sock); close(client.sock); close(listen_sock); return 0; }