blob: 0fe3ce0313288fb1bec6b11e75e0e852d6c28eab [file] [log] [blame]
Sean Middleditch6aef0732009-03-12 23:27:35 -04001/*
Sean Middleditch9de15982009-03-14 03:35:49 -04002 * Sean Middleditch
3 * sean@sourcemud.org
4 *
Sean Middleditch6aef0732009-03-12 23:27:35 -04005 * 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
Sean Middleditch4d9444d2009-03-13 22:48:05 -040012#include <malloc.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040013#include <string.h>
Sean Middleditchd922c6f2009-03-14 22:35:01 -040014#include <stdio.h>
15#include <errno.h>
16#include <string.h>
17#include <stdarg.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040018
19#ifdef HAVE_ZLIB
20#include "zlib.h"
21#endif
22
Sean Middleditch29144852009-03-12 23:14:47 -040023#include "libtelnet.h"
24
Sean Middleditch5b5bc922009-03-15 23:02:10 -040025/* RFC1143 state names */
26#define RFC1143_NO 0x00
27#define RFC1143_YES 0x01
28
29#define RFC1143_WANT 0x02
30#define RFC1143_OP 0x04
31
32#define RFC1143_WANTNO (RFC1143_WANT|RFC1143_YES)
33#define RFC1143_WANTYES (RFC1143_WANT|RFC1143_NO)
34#define RFC1143_WANTNO_OP (RFC1143_WANTNO|RFC1143_OP)
35#define RFC1143_WANTYES_OP (RFC1143_WANTYES|RFC1143_OP)
36
Sean Middleditch4d9444d2009-03-13 22:48:05 -040037/* buffer sizes */
38static const unsigned int _buffer_sizes[] = {
39 0,
40 512,
41 2048,
42 8192,
43 16384,
44};
Sean Middleditch812358d2009-03-15 23:24:03 -040045static const unsigned int _buffer_sizes_count = sizeof(_buffer_sizes) /
46 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040047
Sean Middleditch5b5bc922009-03-15 23:02:10 -040048/* event dispatch helper; return value is value of the accept field of the
49 * event struct after dispatch; used for the funky REQUEST event */
Sean Middleditch35b95be2009-03-15 23:46:31 -040050static int _event(libtelnet_t *telnet, libtelnet_event_type_t type,
Sean Middleditch812358d2009-03-15 23:24:03 -040051 unsigned char command, unsigned char telopt, unsigned char *buffer,
52 unsigned int size) {
53 libtelnet_event_t ev;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040054 ev.buffer = buffer;
55 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040056 ev.type = type;
57 ev.command = command;
58 ev.telopt = telopt;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040059 ev.accept = 0;
Sean Middleditch637df7f2009-03-15 12:57:32 -040060
Sean Middleditch9f79cc52009-03-15 13:39:24 -040061 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch5b5bc922009-03-15 23:02:10 -040062
63 return ev.accept;
Sean Middleditch637df7f2009-03-15 12:57:32 -040064}
65
Sean Middleditchd922c6f2009-03-14 22:35:01 -040066/* error generation function */
Sean Middleditchfbe93e32009-03-15 23:39:31 -040067static libtelnet_error_t _error(libtelnet_t *telnet, unsigned line,
68 const char* func, libtelnet_error_t err, int fatal, const char *fmt,
69 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040070 char buffer[512];
71 va_list va;
72
73 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040074 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040075
76 va_start(va, fmt);
77 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
78 fmt, va);
79 va_end(va);
80
Sean Middleditch16992272009-03-15 19:42:03 -040081 _event(telnet, fatal ? LIBTELNET_EV_ERROR : LIBTELNET_EV_WARNING, err,
Sean Middleditch5cf66662009-03-15 20:02:14 -040082 0, (unsigned char *)buffer, strlen(buffer));
Sean Middleditchfbe93e32009-03-15 23:39:31 -040083
84 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040085}
86
Sean Middleditch72cc9642009-03-15 11:50:36 -040087/* initialize the zlib box for a telnet box; if deflate is non-zero, it
88 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchfbe93e32009-03-15 23:39:31 -040089 * (decompression). returns LIBTELNET_EOK on success, something else on
90 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -040091 */
Sean Middleditchfbe93e32009-03-15 23:39:31 -040092libtelnet_error_t _init_zlib(libtelnet_t *telnet, int deflate, int err_fatal) {
93 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -040094 int rs;
95
Sean Middleditchfbe93e32009-03-15 23:39:31 -040096 /* if compression is already enabled, fail loudly */
97 if (telnet->z != 0)
98 return _error(telnet, __LINE__, __func__, LIBTELNET_EBADVAL,
99 err_fatal, "cannot initialize compression twice");
100
Sean Middleditch72cc9642009-03-15 11:50:36 -0400101 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400102 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
103 return _error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400104 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400105
106 /* initialize */
107 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400108 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
109 free(z);
110 return _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS,
111 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400112 }
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400113 telnet->flags |= LIBTELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400114 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400115 if ((rs = inflateInit(z)) != Z_OK) {
116 free(z);
117 return _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS,
118 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400119 }
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400120 telnet->flags &= ~LIBTELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400121 }
122
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400123 telnet->z = z;
124
125 return LIBTELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400126}
127
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400128/* negotiation handling magic */
129static void _negotiate(struct libtelnet_t *telnet, unsigned char cmd,
130 unsigned char telopt) {
131 struct libtelnet_rfc1143_t *qtmp;
132 int q;
133
134 /* in PROXY mode, just pass it thru and do nothing */
135 if (telnet->mode == LIBTELNET_MODE_PROXY) {
136 switch (cmd) {
137 case LIBTELNET_WILL:
138 _event(telnet, LIBTELNET_EV_WILL, cmd, telopt, 0, 0);
139 break;
140 case LIBTELNET_WONT:
141 _event(telnet, LIBTELNET_EV_WONT, cmd, telopt, 0, 0);
142 break;
143 case LIBTELNET_DO:
144 _event(telnet, LIBTELNET_EV_DO, cmd, telopt, 0, 0);
145 break;
146 case LIBTELNET_DONT:
147 _event(telnet, LIBTELNET_EV_DONT, cmd, telopt, 0, 0);
148 break;
149 }
150 return;
151 }
152
153 /* lookup the current state of the option */
154 for (q = 0; q != telnet->q_size; ++q) {
155 if (telnet->q[q].telopt == telopt)
156 break;
157 }
158
159 /* not found */
160 if (q == telnet->q_size) {
161 /* if the option is unfound then it is off on both ends... and there
162 * is no need thus to respond to a WONT/DONT */
163 if (cmd == LIBTELNET_WONT || cmd == LIBTELNET_DONT)
164 return;
165
166 /* we're going to need to track state for it, so grow the queue
167 * and put the telopt into it; bail on allocation error
168 */
169 if ((qtmp = (struct libtelnet_rfc1143_t *)malloc(sizeof(
170 struct libtelnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
171 _error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, 0,
172 "malloc() failed: %s", strerror(errno));
173 return;
174 }
175 telnet->q = qtmp;
176 memset(&telnet->q[telnet->q_size], 0,
177 sizeof(struct libtelnet_rfc1143_t));
178 telnet->q[telnet->q_size].telopt = telopt;
179 q = telnet->q_size;
180 ++telnet->q_size;
181 }
182
183 /* start processing... */
184 switch (cmd) {
185 /* request to enable option on remote end or confirm DO */
186 case LIBTELNET_WILL:
187 switch (telnet->q[q].him) {
188 case RFC1143_NO:
189 if (_event(telnet, LIBTELNET_EV_WILL, cmd, telopt, 0, 0) == 1) {
190 telnet->q[q].him = RFC1143_YES;
191 libtelnet_send_negotiate(telnet, LIBTELNET_DO, telopt);
192 } else
193 libtelnet_send_negotiate(telnet, LIBTELNET_DONT, telopt);
194 break;
195 case RFC1143_YES:
196 break;
197 case RFC1143_WANTNO:
198 telnet->q[q].him = RFC1143_NO;
199 _error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
200 "DONT answered by WILL");
201 break;
202 case RFC1143_WANTNO_OP:
203 telnet->q[q].him = RFC1143_YES;
204 _error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
205 "DONT answered by WILL");
206 break;
207 case RFC1143_WANTYES:
208 telnet->q[q].him = RFC1143_YES;
209 break;
210 case RFC1143_WANTYES_OP:
211 telnet->q[q].him = RFC1143_WANTNO;
212 libtelnet_send_negotiate(telnet, LIBTELNET_DONT, telopt);
213 break;
214 }
215 break;
216
217 /* request to disable option on remote end, confirm DONT, reject DO */
218 case LIBTELNET_WONT:
219 switch (telnet->q[q].him) {
220 case RFC1143_NO:
221 break;
222 case RFC1143_YES:
223 telnet->q[q].him = RFC1143_NO;
224 libtelnet_send_negotiate(telnet, LIBTELNET_DONT, telopt);
225 _event(telnet, LIBTELNET_EV_WONT, 0, telopt,
226 0, 0);
227 break;
228 case RFC1143_WANTNO:
229 telnet->q[q].him = RFC1143_NO;
230 _event(telnet, LIBTELNET_EV_WONT, 0, telopt,
231 0, 0);
232 break;
233 case RFC1143_WANTNO_OP:
234 telnet->q[q].him = RFC1143_WANTYES;
235 _event(telnet, LIBTELNET_EV_DO, 0, telopt,
236 0, 0);
237 break;
238 case RFC1143_WANTYES:
239 case RFC1143_WANTYES_OP:
240 telnet->q[q].him = RFC1143_NO;
241 break;
242 }
243 break;
244
245 /* request to enable option on local end or confirm WILL */
246 case LIBTELNET_DO:
247 switch (telnet->q[q].us) {
248 case RFC1143_NO:
249 if (_event(telnet, LIBTELNET_EV_DO, cmd, telopt, 0, 0) == 1) {
250 telnet->q[q].us = RFC1143_YES;
251 libtelnet_send_negotiate(telnet, LIBTELNET_WILL, telopt);
252 } else
253 libtelnet_send_negotiate(telnet, LIBTELNET_WONT, telopt);
254 break;
255 case RFC1143_YES:
256 break;
257 case RFC1143_WANTNO:
258 telnet->q[q].us = RFC1143_NO;
259 _error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
260 "WONT answered by DO");
261 break;
262 case RFC1143_WANTNO_OP:
263 telnet->q[q].us = RFC1143_YES;
264 _error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
265 "WONT answered by DO");
266 break;
267 case RFC1143_WANTYES:
268 telnet->q[q].us = RFC1143_YES;
269 break;
270 case RFC1143_WANTYES_OP:
271 telnet->q[q].us = RFC1143_WANTNO;
272 libtelnet_send_negotiate(telnet, LIBTELNET_WONT, telopt);
273 break;
274 }
275 break;
276
277 /* request to disable option on local end, confirm WONT, reject WILL */
278 case LIBTELNET_DONT:
279 switch (telnet->q[q].us) {
280 case RFC1143_NO:
281 break;
282 case RFC1143_YES:
283 telnet->q[q].us = RFC1143_NO;
284 libtelnet_send_negotiate(telnet, LIBTELNET_WONT, telopt);
285 _event(telnet, LIBTELNET_EV_DONT, 0, telopt, 0, 0);
286 break;
287 case RFC1143_WANTNO:
288 telnet->q[q].us = RFC1143_NO;
289 _event(telnet, LIBTELNET_EV_WONT, 0, telopt, 0, 0);
290 break;
291 case RFC1143_WANTNO_OP:
292 telnet->q[q].us = RFC1143_WANTYES;
293 _event(telnet, LIBTELNET_EV_WILL, 0, telopt, 0, 0);
294 break;
295 case RFC1143_WANTYES:
296 case RFC1143_WANTYES_OP:
297 telnet->q[q].us = RFC1143_NO;
298 break;
299 }
300 break;
301 }
302}
303
Sean Middleditch29144852009-03-12 23:14:47 -0400304/* initialize a telnet state tracker */
Sean Middleditch812358d2009-03-15 23:24:03 -0400305void libtelnet_init(libtelnet_t *telnet, libtelnet_event_handler_t eh,
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400306 unsigned char flags, void *user_data) {
Sean Middleditch812358d2009-03-15 23:24:03 -0400307 memset(telnet, 0, sizeof(libtelnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400308 telnet->ud = user_data;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400309 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400310 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400311}
312
313/* free up any memory allocated by a state tracker */
Sean Middleditch812358d2009-03-15 23:24:03 -0400314void libtelnet_free(libtelnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400315 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400316 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400317 free(telnet->buffer);
318 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400319 telnet->buffer_size = 0;
320 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400321 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400322
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400323 /* free zlib box */
324 if (telnet->z != 0) {
325 if (telnet->flags & LIBTELNET_PFLAG_DEFLATE)
326 deflateEnd(telnet->z);
327 else
328 inflateEnd(telnet->z);
329 free(telnet->z);
330 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400331 }
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400332
333 /* free RFC1143 queue */
334 if (telnet->q) {
335 free(telnet->q);
336 telnet->q = 0;
337 telnet->q_size = 0;
338 }
Sean Middleditch29144852009-03-12 23:14:47 -0400339}
340
Sean Middleditch51ad6792009-03-13 20:15:59 -0400341/* push a byte into the telnet buffer */
Sean Middleditch812358d2009-03-15 23:24:03 -0400342static libtelnet_error_t _buffer_byte(libtelnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400343 unsigned char byte) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400344 unsigned char *new_buffer;
345 int i;
346
Sean Middleditch51ad6792009-03-13 20:15:59 -0400347 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400348 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400349 /* find the next buffer size */
350 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400351 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400352 break;
353 }
354
355 /* overflow -- can't grow any more */
356 if (i >= _buffer_sizes_count - 1) {
Sean Middleditch16992272009-03-15 19:42:03 -0400357 _error(telnet, __LINE__, __func__, LIBTELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400358 "subnegotiation buffer size limit reached");
Sean Middleditch51ad6792009-03-13 20:15:59 -0400359 libtelnet_free(telnet);
Sean Middleditchf66a7ee2009-03-15 11:54:07 -0400360 return LIBTELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400361 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400362
363 /* (re)allocate buffer */
364 new_buffer = (unsigned char *)realloc(telnet->buffer,
365 _buffer_sizes[i + 1]);
366 if (new_buffer == 0) {
Sean Middleditch16992272009-03-15 19:42:03 -0400367 _error(telnet, __LINE__, __func__, LIBTELNET_ENOMEM, 0,
368 "realloc() failed");
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400369 libtelnet_free(telnet);
Sean Middleditchf66a7ee2009-03-15 11:54:07 -0400370 return LIBTELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400371 }
372
373 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400374 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400375 }
376
377 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400378 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf66a7ee2009-03-15 11:54:07 -0400379 return LIBTELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400380}
381
Sean Middleditch812358d2009-03-15 23:24:03 -0400382static void _process(libtelnet_t *telnet, unsigned char *buffer,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400383 unsigned int size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400384 unsigned char byte;
385 unsigned int i, start;
386 for (i = start = 0; i != size; ++i) {
387 byte = buffer[i];
388 switch (telnet->state) {
389 /* regular data */
Sean Middleditch9de15982009-03-14 03:35:49 -0400390 case LIBTELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400391 /* on an IAC byte, pass through all pending bytes and
392 * switch states */
393 if (byte == LIBTELNET_IAC) {
394 if (i != start)
Sean Middleditch637df7f2009-03-15 12:57:32 -0400395 _event(telnet, LIBTELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400396 i - start);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400397 telnet->state = LIBTELNET_STATE_IAC;
398 }
399 break;
400
401 /* IAC command */
402 case LIBTELNET_STATE_IAC:
403 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400404 /* subnegotiation */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400405 case LIBTELNET_SB:
406 telnet->state = LIBTELNET_STATE_SB;
407 break;
408 /* negotiation commands */
409 case LIBTELNET_WILL:
410 telnet->state = LIBTELNET_STATE_WILL;
411 break;
412 case LIBTELNET_WONT:
413 telnet->state = LIBTELNET_STATE_WONT;
414 break;
415 case LIBTELNET_DO:
416 telnet->state = LIBTELNET_STATE_DO;
417 break;
418 case LIBTELNET_DONT:
419 telnet->state = LIBTELNET_STATE_DONT;
420 break;
421 /* IAC escaping */
422 case LIBTELNET_IAC:
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400423 _event(telnet, LIBTELNET_EV_DATA, 0, 0, &byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400424 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400425 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400426 break;
427 /* some other command */
428 default:
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400429 _event(telnet, LIBTELNET_EV_IAC, byte, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400430 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400431 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400432 }
433 break;
434
435 /* negotiation commands */
436 case LIBTELNET_STATE_DO:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400437 _negotiate(telnet, LIBTELNET_DO, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400438 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400439 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400440 break;
441 case LIBTELNET_STATE_DONT:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400442 _negotiate(telnet, LIBTELNET_DONT, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400443 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400444 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400445 break;
446 case LIBTELNET_STATE_WILL:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400447 _negotiate(telnet, LIBTELNET_WILL, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400448 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400449 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400450 break;
451 case LIBTELNET_STATE_WONT:
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400452 _negotiate(telnet, LIBTELNET_WONT, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400453 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400454 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400455 break;
456
Sean Middleditchda0e6952009-03-15 13:28:09 -0400457 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400458 case LIBTELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400459 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400460 telnet->buffer_pos = 0;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400461 telnet->state = LIBTELNET_STATE_SB_DATA;
462 break;
463
464 /* subnegotiation -- buffer bytes until end request */
465 case LIBTELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400466 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400467 if (byte == LIBTELNET_IAC) {
Sean Middleditchda0e6952009-03-15 13:28:09 -0400468 telnet->state = LIBTELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400469 /* buffer the byte, or bail if we can't */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400470 } else if (_buffer_byte(telnet, byte) != LIBTELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400471 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400472 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400473 }
474 break;
475
Sean Middleditch6b372882009-03-14 13:06:47 -0400476 /* IAC escaping inside a subnegotiation */
Sean Middleditchda0e6952009-03-15 13:28:09 -0400477 case LIBTELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400478 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400479 /* end subnegotiation */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400480 case LIBTELNET_SE:
Sean Middleditch9de15982009-03-14 03:35:49 -0400481 /* return to default state */
482 start = i + 1;
483 telnet->state = LIBTELNET_STATE_DATA;
484
Sean Middleditch9de15982009-03-14 03:35:49 -0400485 /* invoke callback */
Sean Middleditch637df7f2009-03-15 12:57:32 -0400486 _event(telnet, LIBTELNET_EV_SUBNEGOTIATION, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400487 telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
Sean Middleditch9de15982009-03-14 03:35:49 -0400488
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400489#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400490 /* if we are a client or a proxy and just received the
491 * COMPRESS2 begin marker, setup our zlib box and start
492 * handling the compressed stream if it's not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400493 */
Sean Middleditchda0e6952009-03-15 13:28:09 -0400494 if (telnet->sb_telopt == LIBTELNET_TELOPT_COMPRESS2 &&
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400495 telnet->z == 0 &&
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400496 telnet->flags & LIBTELNET_FLAG_PROXY) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400497
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400498 if (_init_zlib(telnet, 0, 1) != LIBTELNET_EOK)
Sean Middleditch9de15982009-03-14 03:35:49 -0400499 break;
Sean Middleditch9de15982009-03-14 03:35:49 -0400500
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400501 /* notify app that compression was enabled */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400502 _event(telnet, LIBTELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400503
Sean Middleditch9de15982009-03-14 03:35:49 -0400504 /* any remaining bytes in the buffer are compressed.
505 * we have to re-invoke libtelnet_push to get those
506 * bytes inflated and abort trying to process the
507 * remaining compressed bytes in the current _process
508 * buffer argument
509 */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400510 libtelnet_push(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400511 return;
512 }
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400513#endif /* HAVE_ZLIB */
514
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400515 break;
516 /* escaped IAC byte */
517 case LIBTELNET_IAC:
518 /* push IAC into buffer */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400519 if (_buffer_byte(telnet, LIBTELNET_IAC) !=
Sean Middleditchf66a7ee2009-03-15 11:54:07 -0400520 LIBTELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400521 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400522 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400523 } else {
Sean Middleditchda0e6952009-03-15 13:28:09 -0400524 telnet->state = LIBTELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400525 }
526 break;
527 /* something else -- protocol error */
528 default:
Sean Middleditch16992272009-03-15 19:42:03 -0400529 _error(telnet, __LINE__, __func__, LIBTELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400530 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400531 byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400532 start = i + 1;
Sean Middleditch9de15982009-03-14 03:35:49 -0400533 telnet->state = LIBTELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400534 break;
535 }
536 break;
537 }
538 }
539
540 /* pass through any remaining bytes */
Sean Middleditchd30fd572009-03-14 12:55:17 -0400541 if (telnet->state == LIBTELNET_STATE_DATA && i != start)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400542 _event(telnet, LIBTELNET_EV_DATA, 0, 0, buffer + start, i - start);
Sean Middleditch29144852009-03-12 23:14:47 -0400543}
544
Sean Middleditch9de15982009-03-14 03:35:49 -0400545/* push a bytes into the state tracker */
Sean Middleditch812358d2009-03-15 23:24:03 -0400546void libtelnet_push(libtelnet_t *telnet, unsigned char *buffer,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400547 unsigned int size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400548#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400549 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400550 if (telnet->z != 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400551 unsigned char inflate_buffer[4096];
552 int rs;
553
554 /* initialize zlib state */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400555 telnet->z->next_in = buffer;
556 telnet->z->avail_in = size;
557 telnet->z->next_out = inflate_buffer;
558 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400559
560 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400561 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400562 /* reset output buffer */
563
564 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400565 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400566
567 /* process the decompressed bytes on success */
568 if (rs == Z_OK || rs == Z_STREAM_END)
569 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400570 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400571 else
Sean Middleditch16992272009-03-15 19:42:03 -0400572 _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
573 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400574
575 /* prepare output buffer for next run */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400576 telnet->z->next_out = inflate_buffer;
577 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400578
579 /* on error (or on end of stream) disable further inflation */
580 if (rs != Z_OK) {
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400581 _event(telnet, LIBTELNET_EV_COMPRESS, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400582
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400583 inflateEnd(telnet->z);
584 free(telnet->z);
585 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400586 break;
587 }
588 }
589
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400590 /* COMPRESS2 is not negotiated, just process */
591 } else
592#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400593 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400594}
595
Sean Middleditch812358d2009-03-15 23:24:03 -0400596static void _send(libtelnet_t *telnet, unsigned char *buffer,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400597 unsigned int size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400598#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400599 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400600 if (telnet->z != 0) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400601 unsigned char deflate_buffer[1024];
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400602 int rs;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400603
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400604 /* initialize z state */
605 telnet->z->next_in = buffer;
606 telnet->z->avail_in = size;
607 telnet->z->next_out = deflate_buffer;
608 telnet->z->avail_out = sizeof(deflate_buffer);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400609
610 /* deflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400611 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400612 /* compress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400613 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditch16992272009-03-15 19:42:03 -0400614 _error(telnet, __LINE__, __func__, LIBTELNET_ECOMPRESS, 1,
615 "deflate() failed: %s", zError(rs));
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400616 deflateEnd(telnet->z);
617 free(telnet->z);
618 telnet->z = 0;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400619 break;
620 }
621
Sean Middleditch637df7f2009-03-15 12:57:32 -0400622 _event(telnet, LIBTELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400623 sizeof(deflate_buffer) - telnet->z->avail_out);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400624
625 /* prepare output buffer for next run */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400626 telnet->z->next_out = deflate_buffer;
627 telnet->z->avail_out = sizeof(deflate_buffer);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400628 }
629
630 /* COMPRESS2 is not negotiated, just send */
631 } else
632#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400633 _event(telnet, LIBTELNET_EV_SEND, 0, 0, buffer, size);
Sean Middleditch9de15982009-03-14 03:35:49 -0400634}
635
Sean Middleditch29144852009-03-12 23:14:47 -0400636/* send an iac command */
Sean Middleditch812358d2009-03-15 23:24:03 -0400637void libtelnet_send_command(libtelnet_t *telnet, unsigned char cmd) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400638 unsigned char bytes[2] = { LIBTELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400639 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400640}
641
642/* send negotiation */
Sean Middleditch812358d2009-03-15 23:24:03 -0400643void libtelnet_send_negotiate(libtelnet_t *telnet, unsigned char cmd,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400644 unsigned char opt) {
Sean Middleditchf9cebec2009-03-13 20:17:31 -0400645 unsigned char bytes[3] = { LIBTELNET_IAC, cmd, opt };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400646 _send(telnet, bytes, 3);
Sean Middleditch29144852009-03-12 23:14:47 -0400647}
648
649/* send non-command data (escapes IAC bytes) */
Sean Middleditch812358d2009-03-15 23:24:03 -0400650void libtelnet_send_data(libtelnet_t *telnet, unsigned char *buffer,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400651 unsigned int size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400652 unsigned int i, l;
Sean Middleditch29144852009-03-12 23:14:47 -0400653 for (l = i = 0; i != size; ++i) {
654 /* dump prior portion of text, send escaped bytes */
655 if (buffer[i] == LIBTELNET_IAC) {
656 /* dump prior text if any */
657 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400658 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400659 l = i + 1;
660
661 /* send escape */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400662 libtelnet_send_command(telnet, LIBTELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400663 }
664 }
665
666 /* send whatever portion of buffer is left */
667 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400668 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400669}
670
671/* send sub-request */
Sean Middleditch812358d2009-03-15 23:24:03 -0400672void libtelnet_send_subnegotiation(libtelnet_t *telnet, unsigned char opt,
673 unsigned char *buffer, unsigned int size) {
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400674 libtelnet_send_command(telnet, LIBTELNET_SB);
675 libtelnet_send_data(telnet, &opt, 1);
676 libtelnet_send_data(telnet, buffer, size);
677 libtelnet_send_command(telnet, LIBTELNET_SE);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400678
679#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400680 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
681 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400682 */
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400683 if (telnet->flags & LIBTELNET_FLAG_PROXY &&
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400684 telnet->z == 0 &&
Sean Middleditch72cc9642009-03-15 11:50:36 -0400685 opt == LIBTELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400686
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400687 if (_init_zlib(telnet, 1, 1) != LIBTELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400688 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400689
690 /* notify app that compression was enabled */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400691 _event(telnet, LIBTELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400692 }
693#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400694}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400695
Sean Middleditch812358d2009-03-15 23:24:03 -0400696void libtelnet_begin_compress2(libtelnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400697#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400698 unsigned char compress2[] = { LIBTELNET_IAC, LIBTELNET_SB,
699 LIBTELNET_TELOPT_COMPRESS2, LIBTELNET_IAC, LIBTELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400700
701 /* don't do this if we've already got a compression stream */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400702 if (telnet->z != 0) {
Sean Middleditch16992272009-03-15 19:42:03 -0400703 _error(telnet, __LINE__, __func__, LIBTELNET_EBADVAL, 0,
704 "compression already enabled");
Sean Middleditch124a1c22009-03-15 13:20:03 -0400705 return;
Sean Middleditch16992272009-03-15 19:42:03 -0400706 }
Sean Middleditch124a1c22009-03-15 13:20:03 -0400707
708 /* attempt to create output stream first, bail if we can't */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400709 if (_init_zlib(telnet, 1, 0) != LIBTELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400710 return;
711
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400712 /* send compression marker. we send directly to the event handler
713 * instead of passing through _send because _send would result in
714 * the compress marker itself being compressed.
715 */
716 _event(telnet, LIBTELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
Sean Middleditch124a1c22009-03-15 13:20:03 -0400717#endif /* HAVE_ZLIB */
718}
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400719
720/* get local state of specific telopt */
721int libtelnet_get_telopt_local (struct libtelnet_t *telnet,
722 unsigned char telopt) {
723 int i;
724 /* search for entry, return state if found */
725 for (i = 0; i != telnet->q_size; ++i)
726 if (telnet->q[i].telopt == telopt)
727 return telnet->q[i].us & RFC1143_YES;
728
729 /* not found... so it's off */
730 return 0;
731}
732
733/* get remote state of specific telopt */
734int libtelnet_get_telopt_remote (struct libtelnet_t *telnet,
735 unsigned char telopt) {
736 int i;
737 /* search for entry, return state if found */
738 for (i = 0; i != telnet->q_size; ++i)
739 if (telnet->q[i].telopt == telopt)
740 return telnet->q[i].him & RFC1143_YES;
741
742 /* not found... so it's off */
743 return 0;
744}