blob: 8401359edb56bede1cee91ff5b8c85f3c39d3e2f [file] [log] [blame]
Sean Middleditchda394be2009-03-15 14:19:40 -04001/*
2 * Sean Middleditch
3 * sean@sourcemud.org
4 *
5 * The author or authors of this code dedicate any and all copyright interest
6 * in this code to the public domain. We make this dedication for the benefit
7 * of the public at large and to the detriment of our heirs and successors. We
8 * intend this dedication to be an overt act of relinquishment in perpetuity of
9 * all present and future rights to this code under copyright law.
10 */
11
12#include <sys/socket.h>
13#include <netinet/in.h>
14#include <arpa/inet.h>
15#include <netdb.h>
16#include <poll.h>
17#include <errno.h>
18#include <stdio.h>
19#include <string.h>
20#include <stdlib.h>
21#include <ctype.h>
Sean Middleditcha7896d12009-03-15 20:41:24 -040022#include <termios.h>
Sean Middleditchda394be2009-03-15 14:19:40 -040023#include <unistd.h>
Harald Welte000cfc32010-03-25 21:40:25 +080024#include <libgen.h>
Sean Middleditchda394be2009-03-15 14:19:40 -040025
26#ifdef HAVE_ZLIB
27#include "zlib.h"
28#endif
29
30#include "libtelnet.h"
31
Sean Middleditcha7896d12009-03-15 20:41:24 -040032static struct termios orig_tios;
Sean Middleditchd2466a02009-09-19 14:35:48 -070033static telnet_t *telnet;
Sean Middleditcha7896d12009-03-15 20:41:24 -040034static int do_echo;
35
Sean Middleditch34bb0992009-03-21 00:20:44 -040036static const telnet_telopt_t telopts[] = {
Sean Middleditchbfc641e2009-03-22 16:26:06 -040037 { TELNET_TELOPT_ECHO, TELNET_WONT, TELNET_DO },
Sean Middleditchbfc641e2009-03-22 16:26:06 -040038 { TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
Sean Middleditch2d5c4992009-03-22 22:21:42 -040039 { TELNET_TELOPT_COMPRESS2, TELNET_WONT, TELNET_DO },
40 { TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO },
Sean Middleditch34bb0992009-03-21 00:20:44 -040041 { -1, 0, 0 }
42};
43
Sean Middleditcha7896d12009-03-15 20:41:24 -040044static void _cleanup(void) {
45 tcsetattr(STDOUT_FILENO, TCSADRAIN, &orig_tios);
46}
47
Sean Middleditch8daf7742009-03-19 02:05:24 -040048static void _input(char *buffer, int size) {
49 static char crlf[] = { '\r', '\n' };
Sean Middleditche7c44262009-03-15 20:59:48 -040050 int i;
51
52 for (i = 0; i != size; ++i) {
53 /* if we got a CR or LF, replace with CRLF
54 * NOTE that usually you'd get a CR in UNIX, but in raw
55 * mode we get LF instead (not sure why)
56 */
57 if (buffer[i] == '\r' || buffer[i] == '\n') {
58 if (do_echo)
Sean Middleditch54c1e3e2009-08-31 15:04:39 -070059 printf("\r\n");
Sean Middleditchd2466a02009-09-19 14:35:48 -070060 telnet_send(telnet, crlf, 2);
Sean Middleditche7c44262009-03-15 20:59:48 -040061 } else {
62 if (do_echo)
Sean Middleditch54c1e3e2009-08-31 15:04:39 -070063 putchar(buffer[i]);
Sean Middleditchd2466a02009-09-19 14:35:48 -070064 telnet_send(telnet, buffer + i, 1);
Sean Middleditche7c44262009-03-15 20:59:48 -040065 }
66 }
Sean Middleditch54c1e3e2009-08-31 15:04:39 -070067 fflush(stdout);
Sean Middleditche7c44262009-03-15 20:59:48 -040068}
69
Sean Middleditch340a51b2009-03-19 02:08:46 -040070static void _send(int sock, const char *buffer, size_t size) {
Sean Middleditchda394be2009-03-15 14:19:40 -040071 int rs;
72
73 /* send data */
74 while (size > 0) {
75 if ((rs = send(sock, buffer, size, 0)) == -1) {
76 fprintf(stderr, "send() failed: %s\n", strerror(errno));
77 exit(1);
78 } else if (rs == 0) {
79 fprintf(stderr, "send() unexpectedly returned 0\n");
80 exit(1);
81 }
82
83 /* update pointer and size to see if we've got more to send */
84 buffer += rs;
85 size -= rs;
86 }
87}
88
Sean Middleditchf65f27d2009-03-19 02:32:04 -040089static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
Sean Middleditch812358d2009-03-15 23:24:03 -040090 void *user_data) {
Sean Middleditchda394be2009-03-15 14:19:40 -040091 int sock = *(int*)user_data;
92
93 switch (ev->type) {
94 /* data received */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040095 case TELNET_EV_DATA:
Sean Middleditch4adfdf32009-08-31 15:06:09 -070096 printf("%.*s", (int)ev->size, ev->buffer);
Sean Middleditchda394be2009-03-15 14:19:40 -040097 break;
98 /* data must be sent */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040099 case TELNET_EV_SEND:
Sean Middleditchda394be2009-03-15 14:19:40 -0400100 _send(sock, ev->buffer, ev->size);
101 break;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400102 /* request to enable remote feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400103 case TELNET_EV_WILL:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400104 /* we'll agree to turn off our echo if server wants us to stop */
Sean Middleditch34bb0992009-03-21 00:20:44 -0400105 if (ev->telopt == TELNET_TELOPT_ECHO)
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400106 do_echo = 0;
Sean Middleditcha7896d12009-03-15 20:41:24 -0400107 break;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400108 /* notification of disabling remote feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400109 case TELNET_EV_WONT:
110 if (ev->telopt == TELNET_TELOPT_ECHO)
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400111 do_echo = 1;
112 break;
113 /* request to enable local feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400114 case TELNET_EV_DO:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400115 break;
116 /* demand to disable local feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400117 case TELNET_EV_DONT:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400118 break;
Sean Middleditcha7896d12009-03-15 20:41:24 -0400119 /* respond to particular subnegotiations */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400120 case TELNET_EV_SUBNEGOTIATION:
Sean Middleditcheb950a82009-03-22 23:04:28 -0400121 /* if they just asked for our terminal type, response with it */
Sean Middleditcha7896d12009-03-15 20:41:24 -0400122 /* respond with our terminal type */
Sean Middleditcheb950a82009-03-22 23:04:28 -0400123 if (ev->telopt == TELNET_TELOPT_TTYPE &&
124 ev->argc >= 1 && ev->argv[0][0] == TELNET_TTYPE_SEND) {
125 telnet_format_sb(telnet, TELNET_TELOPT_TTYPE, 1,
126 TELNET_TTYPE_IS, getenv("TERM"));
Sean Middleditcha7896d12009-03-15 20:41:24 -0400127 }
Sean Middleditchda394be2009-03-15 14:19:40 -0400128 break;
129 /* error */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400130 case TELNET_EV_ERROR:
Sean Middleditch340a51b2009-03-19 02:08:46 -0400131 fprintf(stderr, "ERROR: %s\n", ev->buffer);
Sean Middleditchda394be2009-03-15 14:19:40 -0400132 exit(1);
133 default:
134 /* ignore */
135 break;
136 }
137}
138
Harald Weltea3c62232010-03-25 21:33:21 +0800139extern int ipaccess_telnet_auth(int sock);
140
Sean Middleditchda394be2009-03-15 14:19:40 -0400141int main(int argc, char **argv) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400142 char buffer[512];
Sean Middleditchda394be2009-03-15 14:19:40 -0400143 int rs;
144 int sock;
145 struct sockaddr_in addr;
146 struct pollfd pfd[2];
Sean Middleditchda394be2009-03-15 14:19:40 -0400147 struct addrinfo *ai;
148 struct addrinfo hints;
Sean Middleditcha7896d12009-03-15 20:41:24 -0400149 struct termios tios;
Sean Middleditchda394be2009-03-15 14:19:40 -0400150
151 /* check usage */
152 if (argc != 3) {
Harald Welte000cfc32010-03-25 21:40:25 +0800153 fprintf(stderr, "Usage:\n %s <host> <port>\n",
154 basename(argv[0]));
Sean Middleditchda394be2009-03-15 14:19:40 -0400155 return 1;
156 }
157
158 /* look up server host */
159 memset(&hints, 0, sizeof(hints));
160 hints.ai_family = AF_UNSPEC;
161 hints.ai_socktype = SOCK_STREAM;
162 if ((rs = getaddrinfo(argv[1], argv[2], &hints, &ai)) != 0) {
163 fprintf(stderr, "getaddrinfo() failed for %s: %s\n", argv[1],
164 gai_strerror(rs));
165 return 1;
166 }
167
168 /* create server socket */
169 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
170 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
171 return 1;
172 }
173
174 /* bind server socket */
175 memset(&addr, 0, sizeof(addr));
176 addr.sin_family = AF_INET;
177 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
178 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
179 return 1;
180 }
181
182 /* connect */
183 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
184 fprintf(stderr, "server() failed: %s\n", strerror(errno));
185 return 1;
186 }
187
188 /* free address lookup info */
189 freeaddrinfo(ai);
190
Harald Weltea3c62232010-03-25 21:33:21 +0800191#if NANO_BTS_CLI_CLIENT
192 ipaccess_telnet_auth(sock);
193#endif
194
Sean Middleditcha7896d12009-03-15 20:41:24 -0400195 /* get current terminal settings, set raw mode, make sure we
196 * register atexit handler to restore terminal settings
197 */
198 tcgetattr(STDOUT_FILENO, &orig_tios);
199 atexit(_cleanup);
200 tios = orig_tios;
201 cfmakeraw(&tios);
202 tcsetattr(STDOUT_FILENO, TCSADRAIN, &tios);
203
204 /* set input echoing on by default */
205 do_echo = 1;
206
Sean Middleditchda394be2009-03-15 14:19:40 -0400207 /* initialize telnet box */
Sean Middleditchd2466a02009-09-19 14:35:48 -0700208 telnet = telnet_init(telopts, _event_handler, 0, &sock);
Sean Middleditchda394be2009-03-15 14:19:40 -0400209
210 /* initialize poll descriptors */
211 memset(pfd, 0, sizeof(pfd));
212 pfd[0].fd = STDIN_FILENO;
213 pfd[0].events = POLLIN;
214 pfd[1].fd = sock;
215 pfd[1].events = POLLIN;
216
217 /* loop while both connections are open */
218 while (poll(pfd, 2, -1) != -1) {
219 /* read from stdin */
220 if (pfd[0].revents & POLLIN) {
221 if ((rs = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) {
Sean Middleditche7c44262009-03-15 20:59:48 -0400222 _input(buffer, rs);
Sean Middleditchda394be2009-03-15 14:19:40 -0400223 } else if (rs == 0) {
224 break;
225 } else {
226 fprintf(stderr, "recv(server) failed: %s\n",
227 strerror(errno));
228 exit(1);
229 }
230 }
231
232 /* read from client */
233 if (pfd[1].revents & POLLIN) {
234 if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
Sean Middleditchd2466a02009-09-19 14:35:48 -0700235 telnet_recv(telnet, buffer, rs);
Sean Middleditchda394be2009-03-15 14:19:40 -0400236 } else if (rs == 0) {
237 break;
238 } else {
239 fprintf(stderr, "recv(client) failed: %s\n",
240 strerror(errno));
241 exit(1);
242 }
243 }
244 }
245
246 /* clean up */
Sean Middleditchd2466a02009-09-19 14:35:48 -0700247 telnet_free(telnet);
Sean Middleditchda394be2009-03-15 14:19:40 -0400248 close(sock);
249
250 return 0;
251}