blob: 74e894f68f629945062f6ad5beec295f43eed08f [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 Middleditchf65f27d2009-03-19 02:32:04 -040032static telnet_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 Middleditch8daf7742009-03-19 02:05:24 -040039static void _input(char *buffer, int size) {
40 static char crlf[] = { '\r', '\n' };
Sean Middleditche7c44262009-03-15 20:59:48 -040041 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);
Sean Middleditchf65f27d2009-03-19 02:32:04 -040051 telnet_send_data(&telnet, crlf, 2);
Sean Middleditche7c44262009-03-15 20:59:48 -040052 } else {
53 if (do_echo)
54 write(STDOUT_FILENO, buffer + i, 1);
Sean Middleditchf65f27d2009-03-19 02:32:04 -040055 telnet_send_data(&telnet, buffer + i, 1);
Sean Middleditche7c44262009-03-15 20:59:48 -040056 }
57 }
58}
59
Sean Middleditch340a51b2009-03-19 02:08:46 -040060static void _send(int sock, const char *buffer, size_t size) {
Sean Middleditchda394be2009-03-15 14:19:40 -040061 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 Middleditchf65f27d2009-03-19 02:32:04 -040079static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
Sean Middleditch812358d2009-03-15 23:24:03 -040080 void *user_data) {
Sean Middleditchda394be2009-03-15 14:19:40 -040081 int sock = *(int*)user_data;
82
83 switch (ev->type) {
84 /* data received */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040085 case TELNET_EV_DATA:
Sean Middleditchda394be2009-03-15 14:19:40 -040086 write(STDOUT_FILENO, ev->buffer, ev->size);
87 break;
88 /* data must be sent */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040089 case TELNET_EV_SEND:
Sean Middleditchda394be2009-03-15 14:19:40 -040090 _send(sock, ev->buffer, ev->size);
91 break;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040092 /* request to enable remote feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040093 case TELNET_EV_WILL:
Sean Middleditch5b5bc922009-03-15 23:02:10 -040094 /* we accept COMPRESS2 (MCCP) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040095 if (ev->telopt == TELNET_TELOPT_COMPRESS2)
Sean Middleditch5b5bc922009-03-15 23:02:10 -040096 ev->accept = 1;
Sean Middleditcha7896d12009-03-15 20:41:24 -040097
Sean Middleditch5b5bc922009-03-15 23:02:10 -040098 /* we'll agree to turn off our echo if server wants us to stop */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040099 else if (ev->telopt == TELNET_TELOPT_ECHO) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400100 do_echo = 0;
101 ev->accept = 1;
Sean Middleditcha7896d12009-03-15 20:41:24 -0400102 }
103 break;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400104 /* notification of disabling remote feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400105 case TELNET_EV_WONT:
106 if (ev->telopt == TELNET_TELOPT_ECHO)
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400107 do_echo = 1;
108 break;
109 /* request to enable local feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400110 case TELNET_EV_DO:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400111 /* we support the TTYPE option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400112 if (ev->telopt == TELNET_TELOPT_TTYPE)
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400113 ev->accept = 1;
114 break;
115 /* demand to disable local feature (or receipt) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400116 case TELNET_EV_DONT:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400117 break;
Sean Middleditcha7896d12009-03-15 20:41:24 -0400118 /* respond to particular subnegotiations */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400119 case TELNET_EV_SUBNEGOTIATION:
Sean Middleditcha7896d12009-03-15 20:41:24 -0400120 /* respond with our terminal type */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400121 if (ev->telopt == TELNET_TELOPT_TTYPE) {
Sean Middleditcha7896d12009-03-15 20:41:24 -0400122 /* NOTE: we just assume the server sent a legitimate
123 * sub-negotiation, as there really isn't anything else
124 * it's allowed to send
125 */
126 char buffer[64];
127 buffer[0] = 0; /* IS code for RFC 1091 */
128 snprintf(buffer + 1, sizeof(buffer) - 1, "%s", getenv("TERM"));
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400129 telnet_send_subnegotiation(telnet, TELNET_TELOPT_TTYPE,
Sean Middleditch8daf7742009-03-19 02:05:24 -0400130 (char *)buffer, 1 + strlen(buffer + 1));
Sean Middleditcha7896d12009-03-15 20:41:24 -0400131 }
Sean Middleditchda394be2009-03-15 14:19:40 -0400132 break;
133 /* error */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400134 case TELNET_EV_ERROR:
Sean Middleditch340a51b2009-03-19 02:08:46 -0400135 fprintf(stderr, "ERROR: %s\n", ev->buffer);
Sean Middleditchda394be2009-03-15 14:19:40 -0400136 exit(1);
137 default:
138 /* ignore */
139 break;
140 }
141}
142
143int main(int argc, char **argv) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400144 char buffer[512];
Sean Middleditchda394be2009-03-15 14:19:40 -0400145 int rs;
146 int sock;
147 struct sockaddr_in addr;
148 struct pollfd pfd[2];
Sean Middleditchda394be2009-03-15 14:19:40 -0400149 struct addrinfo *ai;
150 struct addrinfo hints;
Sean Middleditcha7896d12009-03-15 20:41:24 -0400151 struct termios tios;
Sean Middleditchda394be2009-03-15 14:19:40 -0400152
153 /* check usage */
154 if (argc != 3) {
155 fprintf(stderr, "Usage:\n ./telnet-client <host> <port>\n");
156 return 1;
157 }
158
159 /* look up server host */
160 memset(&hints, 0, sizeof(hints));
161 hints.ai_family = AF_UNSPEC;
162 hints.ai_socktype = SOCK_STREAM;
163 if ((rs = getaddrinfo(argv[1], argv[2], &hints, &ai)) != 0) {
164 fprintf(stderr, "getaddrinfo() failed for %s: %s\n", argv[1],
165 gai_strerror(rs));
166 return 1;
167 }
168
169 /* create server socket */
170 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
171 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
172 return 1;
173 }
174
175 /* bind server socket */
176 memset(&addr, 0, sizeof(addr));
177 addr.sin_family = AF_INET;
178 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
179 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
180 return 1;
181 }
182
183 /* connect */
184 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
185 fprintf(stderr, "server() failed: %s\n", strerror(errno));
186 return 1;
187 }
188
189 /* free address lookup info */
190 freeaddrinfo(ai);
191
Sean Middleditcha7896d12009-03-15 20:41:24 -0400192 /* get current terminal settings, set raw mode, make sure we
193 * register atexit handler to restore terminal settings
194 */
195 tcgetattr(STDOUT_FILENO, &orig_tios);
196 atexit(_cleanup);
197 tios = orig_tios;
198 cfmakeraw(&tios);
199 tcsetattr(STDOUT_FILENO, TCSADRAIN, &tios);
200
201 /* set input echoing on by default */
202 do_echo = 1;
203
Sean Middleditchda394be2009-03-15 14:19:40 -0400204 /* initialize telnet box */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400205 telnet_init(&telnet, _event_handler, 0, &sock);
Sean Middleditchda394be2009-03-15 14:19:40 -0400206
207 /* initialize poll descriptors */
208 memset(pfd, 0, sizeof(pfd));
209 pfd[0].fd = STDIN_FILENO;
210 pfd[0].events = POLLIN;
211 pfd[1].fd = sock;
212 pfd[1].events = POLLIN;
213
214 /* loop while both connections are open */
215 while (poll(pfd, 2, -1) != -1) {
216 /* read from stdin */
217 if (pfd[0].revents & POLLIN) {
218 if ((rs = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) {
Sean Middleditche7c44262009-03-15 20:59:48 -0400219 _input(buffer, rs);
Sean Middleditchda394be2009-03-15 14:19:40 -0400220 } else if (rs == 0) {
221 break;
222 } else {
223 fprintf(stderr, "recv(server) failed: %s\n",
224 strerror(errno));
225 exit(1);
226 }
227 }
228
229 /* read from client */
230 if (pfd[1].revents & POLLIN) {
231 if ((rs = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400232 telnet_push(&telnet, buffer, rs);
Sean Middleditchda394be2009-03-15 14:19:40 -0400233 } else if (rs == 0) {
234 break;
235 } else {
236 fprintf(stderr, "recv(client) failed: %s\n",
237 strerror(errno));
238 exit(1);
239 }
240 }
241 }
242
243 /* clean up */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400244 telnet_free(&telnet);
Sean Middleditchda394be2009-03-15 14:19:40 -0400245 close(sock);
246
247 return 0;
248}