From 0d2ece6a6f9e96941e284bd298023e5bade06c5a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 25 Mar 2010 16:34:39 +0800 Subject: add "ipaccess-telnet" support, i.e. telnetting to an ip.access nanoBTS There is a special MD5-challenge/response authentication after establishing the telnet session to an ip.access nanoBTS / TCP port 3210. The ipaccess-telnet program supports this mode of operation --- util/Makefile.am | 5 +- util/telnet-client-ipa.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 util/telnet-client-ipa.c (limited to 'util') diff --git a/util/Makefile.am b/util/Makefile.am index 4500cea..7134d68 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -1,6 +1,9 @@ AM_CFLAGS = -I.. AM_LDFLAGS = -L.. +ipaccess_telnet_SOURCES = telnet-client-ipa.c ../libtelnet.h +ipaccess_telnet_LDADD = ../libtelnet.la -lssl + telnet_client_SOURCES = telnet-client.c ../libtelnet.h telnet_client_LDADD = ../libtelnet.la @@ -10,4 +13,4 @@ telnet_chatd_LDADD = ../libtelnet.la telnet_proxy_SOURCES = telnet-proxy.c ../libtelnet.h telnet_proxy_LDADD = ../libtelnet.la -bin_PROGRAMS = telnet-client telnet-chatd telnet-proxy +bin_PROGRAMS = telnet-client telnet-chatd telnet-proxy ipaccess-telnet diff --git a/util/telnet-client-ipa.c b/util/telnet-client-ipa.c new file mode 100644 index 0000000..4e22e20 --- /dev/null +++ b/util/telnet-client-ipa.c @@ -0,0 +1,327 @@ +/* + * 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" + +#define NANO_BTS_CLI_CLIENT 1 + +#if NANO_BTS_CLI_CLIENT + #include +#endif + +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; + } +} + +#if NANO_BTS_CLI_CLIENT + +#define KEY "Sh1n30nY0uCra2yD1am0nd" + +void compute_response(unsigned char *ubChallenge, unsigned char *ubResponse) +{ + MD5_CTX md5; + int i; + + MD5_Init(&md5); + + for(i = 0; i < 4; i++) + { + MD5_Update(&md5, (unsigned char *)KEY, strlen(KEY)); + MD5_Update(&md5, ubChallenge, 16); + } + + MD5_Final(ubResponse, &md5); +} + +#endif + +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); + +#if NANO_BTS_CLI_CLIENT + + { + // nanoBTS Challenge/Response + + unsigned char ubResponse[18]; + + #define CLI_USER "CLICLIENT\n" + + // send client name + + if ((rs = send(sock, CLI_USER, strlen(CLI_USER), 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); + } + + // receive challenge + + if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) { + if(rs != 18 || buffer[0] != '<' || buffer[17] != '>') { + fprintf(stderr, "unexpected response\n"); + exit(1); + } + } else if (rs == 0) { + fprintf(stderr, "recv(client) unexpectedly returned 0\n"); + exit(1); + } else { + fprintf(stderr, "recv(client) failed: %s\n", + strerror(errno)); + exit(1); + } + + // calculate response + + memset(&ubResponse, 0, sizeof(ubResponse)); + compute_response(buffer + 1, ubResponse + 1); + ubResponse[0] = '<'; + ubResponse[17] = '>'; + + // send response + + if ((rs = send(sock, ubResponse, sizeof(ubResponse), 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); + } + } + +#endif + + /* 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; +} + -- cgit v1.2.3