/* * 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) write(STDOUT_FILENO, crlf, 2); telnet_send(&telnet, crlf, 2); } else { if (do_echo) write(STDOUT_FILENO, buffer + i, 1); telnet_send(&telnet, buffer + i, 1); } } } 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: write(STDOUT_FILENO, ev->buffer, ev->size); 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: /* respond with our terminal type */ if (ev->telopt == TELNET_TELOPT_TTYPE) { /* NOTE: we just assume the server sent a legitimate * sub-negotiation, as there really isn't anything else * it's allowed to send */ char buffer[64]; buffer[0] = 0; /* IS code for RFC 1091 */ snprintf(buffer + 1, sizeof(buffer) - 1, "%s", getenv("TERM")); telnet_subnegotiation(telnet, TELNET_TELOPT_TTYPE, buffer, strlen(getenv("TERM")) + 1); } 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_init(&telnet, 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; }