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