blob: 2a82338fe8c8686a7917edcc727fabc765b1605f [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>
24
25#ifdef HAVE_ZLIB
26#include "zlib.h"
27#endif
28
29#include "libtelnet.h"
30
Sean Middleditcha7896d12009-03-15 20:41:24 -040031static struct termios orig_tios;
Sean Middleditch812358d2009-03-15 23:24:03 -040032static libtelnet_t telnet;
Sean Middleditcha7896d12009-03-15 20:41:24 -040033static int do_echo;
34
35static void _cleanup(void) {
36 tcsetattr(STDOUT_FILENO, TCSADRAIN, &orig_tios);
37}
38
Sean Middleditche7c44262009-03-15 20:59:48 -040039static void _input(unsigned char *buffer, int size) {
40 static unsigned char crlf[] = { '\r', '\n' };
41 int i;
42
43 for (i = 0; i != size; ++i) {
44 /* if we got a CR or LF, replace with CRLF
45 * NOTE that usually you'd get a CR in UNIX, but in raw
46 * mode we get LF instead (not sure why)
47 */
48 if (buffer[i] == '\r' || buffer[i] == '\n') {
49 if (do_echo)
50 write(STDOUT_FILENO, crlf, 2);
51 libtelnet_send_data(&telnet, crlf, 2);
52 } else {
53 if (do_echo)
54 write(STDOUT_FILENO, buffer + i, 1);
55 libtelnet_send_data(&telnet, buffer + i, 1);
56 }
57 }
58}
59
Sean Middleditchda394be2009-03-15 14:19:40 -040060static void _send(int sock, unsigned char *buffer, unsigned int size) {
61 int rs;
62
63 /* send data */
64 while (size > 0) {
65 if ((rs = send(sock, buffer, size, 0)) == -1) {
66 fprintf(stderr, "send() failed: %s\n", strerror(errno));
67 exit(1);
68 } else if (rs == 0) {
69 fprintf(stderr, "send() unexpectedly returned 0\n");
70 exit(1);
71 }
72
73 /* update pointer and size to see if we've got more to send */
74 buffer += rs;
75 size -= rs;
76 }
77}
78
Sean Middleditch812358d2009-03-15 23:24:03 -040079static void _event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
80 void *user_data) {
Sean Middleditchda394be2009-03-15 14:19:40 -040081 int sock = *(int*)user_data;
82
83 switch (ev->type) {
84 /* data received */
85 case LIBTELNET_EV_DATA:
86 write(STDOUT_FILENO, ev->buffer, ev->size);
87 break;
88 /* data must be sent */
89 case LIBTELNET_EV_SEND:
90 _send(sock, ev->buffer, ev->size);
91 break;
92 /* accept any options we want */
93 case LIBTELNET_EV_NEGOTIATE:
Sean Middleditcha7896d12009-03-15 20:41:24 -040094 switch (ev->command) {
95 case LIBTELNET_WILL:
96 switch (ev->telopt) {
97 /* accept request to enable compression */
98 case LIBTELNET_TELOPT_COMPRESS2:
99 libtelnet_send_negotiate(telnet, LIBTELNET_DO, ev->telopt);
100 break;
101 /* server "promises" to echo, so turn off local echo */
102 case LIBTELNET_TELOPT_ECHO:
103 do_echo = 0;
104 libtelnet_send_negotiate(telnet, LIBTELNET_DO, ev->telopt);
105 break;
106 /* unknown -- reject */
107 default:
108 libtelnet_send_negotiate(telnet, LIBTELNET_DONT, ev->telopt);
109 break;
110 }
111 break;
112
113 case LIBTELNET_WONT:
114 switch (ev->telopt) {
115 /* server wants us to do echoing, by telling us it won't */
116 case LIBTELNET_TELOPT_ECHO:
117 do_echo = 1;
118 libtelnet_send_negotiate(telnet, LIBTELNET_DONT, ev->telopt);
119 break;
120 }
121 break;
122
123 case LIBTELNET_DO:
124 switch (ev->telopt) {
125 /* accept request to enable terminal-type requests */
126 case LIBTELNET_TELOPT_TTYPE:
127 libtelnet_send_negotiate(telnet, LIBTELNET_WILL, ev->telopt);
128 break;
129 /* unknown - reject */
130 default:
131 libtelnet_send_negotiate(telnet, LIBTELNET_WONT, ev->telopt);
132 break;
133 }
134 break;
135
136 case LIBTELNET_DONT:
137 /* ignore for now */
138 break;
139 }
140 break;
141 /* respond to particular subnegotiations */
142 case LIBTELNET_EV_SUBNEGOTIATION:
143 /* respond with our terminal type */
144 if (ev->telopt == LIBTELNET_TELOPT_TTYPE) {
145 /* NOTE: we just assume the server sent a legitimate
146 * sub-negotiation, as there really isn't anything else
147 * it's allowed to send
148 */
149 char buffer[64];
150 buffer[0] = 0; /* IS code for RFC 1091 */
151 snprintf(buffer + 1, sizeof(buffer) - 1, "%s", getenv("TERM"));
152 libtelnet_send_subnegotiation(telnet, LIBTELNET_TELOPT_TTYPE,
153 (unsigned char *)buffer, 1 + strlen(buffer + 1));
154 }
Sean Middleditchda394be2009-03-15 14:19:40 -0400155 break;
156 /* error */
157 case LIBTELNET_EV_ERROR:
158 fprintf(stderr, "ERROR: %.*s\n", ev->size, ev->buffer);
159 exit(1);
160 default:
161 /* ignore */
162 break;
163 }
164}
165
166int main(int argc, char **argv) {
167 unsigned char buffer[512];
168 int rs;
169 int sock;
170 struct sockaddr_in addr;
171 struct pollfd pfd[2];
Sean Middleditchda394be2009-03-15 14:19:40 -0400172 struct addrinfo *ai;
173 struct addrinfo hints;
Sean Middleditcha7896d12009-03-15 20:41:24 -0400174 struct termios tios;
Sean Middleditchda394be2009-03-15 14:19:40 -0400175
176 /* check usage */
177 if (argc != 3) {
178 fprintf(stderr, "Usage:\n ./telnet-client <host> <port>\n");
179 return 1;
180 }
181
182 /* look up server host */
183 memset(&hints, 0, sizeof(hints));
184 hints.ai_family = AF_UNSPEC;
185 hints.ai_socktype = SOCK_STREAM;
186 if ((rs = getaddrinfo(argv[1], argv[2], &hints, &ai)) != 0) {
187 fprintf(stderr, "getaddrinfo() failed for %s: %s\n", argv[1],
188 gai_strerror(rs));
189 return 1;
190 }
191
192 /* create server socket */
193 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
194 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
195 return 1;
196 }
197
198 /* bind server socket */
199 memset(&addr, 0, sizeof(addr));
200 addr.sin_family = AF_INET;
201 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
202 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
203 return 1;
204 }
205
206 /* connect */
207 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
208 fprintf(stderr, "server() failed: %s\n", strerror(errno));
209 return 1;
210 }
211
212 /* free address lookup info */
213 freeaddrinfo(ai);
214
Sean Middleditcha7896d12009-03-15 20:41:24 -0400215 /* get current terminal settings, set raw mode, make sure we
216 * register atexit handler to restore terminal settings
217 */
218 tcgetattr(STDOUT_FILENO, &orig_tios);
219 atexit(_cleanup);
220 tios = orig_tios;
221 cfmakeraw(&tios);
222 tcsetattr(STDOUT_FILENO, TCSADRAIN, &tios);
223
224 /* set input echoing on by default */
225 do_echo = 1;
226
Sean Middleditchda394be2009-03-15 14:19:40 -0400227 /* initialize telnet box */
228 libtelnet_init(&telnet, _event_handler, LIBTELNET_MODE_CLIENT, &sock);
229
230 /* initialize poll descriptors */
231 memset(pfd, 0, sizeof(pfd));
232 pfd[0].fd = STDIN_FILENO;
233 pfd[0].events = POLLIN;
234 pfd[1].fd = sock;
235 pfd[1].events = POLLIN;
236
237 /* loop while both connections are open */
238 while (poll(pfd, 2, -1) != -1) {
239 /* read from stdin */
240 if (pfd[0].revents & POLLIN) {
241 if ((rs = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) {
Sean Middleditche7c44262009-03-15 20:59:48 -0400242 _input(buffer, rs);
Sean Middleditchda394be2009-03-15 14:19:40 -0400243 } else if (rs == 0) {
244 break;
245 } else {
246 fprintf(stderr, "recv(server) failed: %s\n",
247 strerror(errno));
248 exit(1);
249 }
250 }
251
252 /* read from client */
253 if (pfd[1].revents & POLLIN) {
254 if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
255 libtelnet_push(&telnet, buffer, rs);
256 } else if (rs == 0) {
257 break;
258 } else {
259 fprintf(stderr, "recv(client) failed: %s\n",
260 strerror(errno));
261 exit(1);
262 }
263 }
264 }
265
266 /* clean up */
267 libtelnet_free(&telnet);
268 close(sock);
269
270 return 0;
271}