blob: 044230516b61f5e39f8fd4d88d45f1df1130a2d4 [file] [log] [blame]
Sean Middleditch6aef0732009-03-12 23:27:35 -04001/*
Sean Middleditchae39cee2009-03-22 22:47:30 -04002 * libtelnet 0.9
3 *
Sean Middleditch9de15982009-03-14 03:35:49 -04004 * Sean Middleditch
5 * sean@sourcemud.org
6 *
Sean Middleditch6aef0732009-03-12 23:27:35 -04007 * The author or authors of this code dedicate any and all copyright interest
8 * in this code to the public domain. We make this dedication for the benefit
9 * of the public at large and to the detriment of our heirs and successors. We
10 * intend this dedication to be an overt act of relinquishment in perpetuity of
11 * all present and future rights to this code under copyright law.
12 */
13
Sean Middleditch4d9444d2009-03-13 22:48:05 -040014#include <malloc.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040015#include <string.h>
Sean Middleditchd922c6f2009-03-14 22:35:01 -040016#include <stdio.h>
17#include <errno.h>
18#include <string.h>
19#include <stdarg.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040020
Sean Middleditch2d5c4992009-03-22 22:21:42 -040021#ifdef HAVE_ALLOCA
22#include <alloca.h>
23#endif
24
Sean Middleditch9de15982009-03-14 03:35:49 -040025#ifdef HAVE_ZLIB
Sean Middleditch2d5c4992009-03-22 22:21:42 -040026#include <zlib.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040027#endif
28
Sean Middleditch29144852009-03-12 23:14:47 -040029#include "libtelnet.h"
30
Sean Middleditch9b07e922009-03-19 18:18:44 -040031/* inlinable functions */
32#if __GNUC__ || __STDC_VERSION__ >= 199901L
Sean Middleditch53a86612009-03-22 17:43:29 -040033# define INLINE __inline__
Sean Middleditch9b07e922009-03-19 18:18:44 -040034#else
35# define INLINE
36#endif
37
Sean Middleditchd2466a02009-09-19 14:35:48 -070038/* telnet state */
39struct telnet_t {
40 /* user data */
41 void *ud;
42 /* telopt support table */
43 const telnet_telopt_t *telopts;
44 /* event handler */
45 telnet_event_handler_t eh;
46#ifdef HAVE_ZLIB
47 /* zlib (mccp2) compression */
48 z_stream *z;
49#endif
50 /* RFC1143 option negotiation states */
51 struct telnet_rfc1143_t *q;
52 /* sub-request buffer */
53 char *buffer;
54 /* current size of the buffer */
55 size_t buffer_size;
56 /* current buffer write position (also length of buffer data) */
57 size_t buffer_pos;
58 /* current state */
59 enum telnet_state_t state;
60 /* option flags */
61 unsigned char flags;
62 /* current subnegotiation telopt */
63 unsigned char sb_telopt;
64 /* length of RFC1143 queue */
65 unsigned char q_size;
66};
67
Sean Middleditch90e79da2009-03-19 15:17:13 -040068/* RFC1143 option negotiation state */
69typedef struct telnet_rfc1143_t {
70 unsigned char telopt;
71 char us:4, him:4;
72} telnet_rfc1143_t;
73
Sean Middleditch5b5bc922009-03-15 23:02:10 -040074/* RFC1143 state names */
Sean Middleditch7491bf42009-03-20 23:52:18 -040075#define Q_NO 0
76#define Q_YES 1
77#define Q_WANTNO 2
78#define Q_WANTYES 3
79#define Q_WANTNO_OP 4
80#define Q_WANTYES_OP 5
Sean Middleditch5b5bc922009-03-15 23:02:10 -040081
Sean Middleditch4d9444d2009-03-13 22:48:05 -040082/* buffer sizes */
Sean Middleditch8c111792009-03-22 22:44:38 -040083static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
Sean Middleditch340a51b2009-03-19 02:08:46 -040084static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040085 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040086
Sean Middleditch34bb0992009-03-21 00:20:44 -040087/* event dispatch helper */
88static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040089 unsigned char command, unsigned char telopt,
Sean Middleditche5327da2009-03-21 00:54:50 -040090 const char *buffer, size_t size, const char **argv, size_t argc) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040091 telnet_event_t ev;
Sean Middleditche5327da2009-03-21 00:54:50 -040092 ev.argv = argv;
93 ev.argc = argc;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040094 ev.buffer = buffer;
95 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040096 ev.type = type;
97 ev.command = command;
98 ev.telopt = telopt;
Sean Middleditch637df7f2009-03-15 12:57:32 -040099
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400100 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch637df7f2009-03-15 12:57:32 -0400101}
102
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400103/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400104static telnet_error_t _error(telnet_t *telnet, unsigned line,
105 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400106 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400107 char buffer[512];
108 va_list va;
109
110 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -0400111 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400112
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400113 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400114 va_start(va, fmt);
115 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
116 fmt, va);
117 va_end(va);
118
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400119 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400120 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditche5327da2009-03-21 00:54:50 -0400121 0, buffer, strlen(buffer), 0, 0);
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400122
123 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400124}
125
Sean Middleditche5b47592009-03-16 17:37:43 -0400126#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400127/* initialize the zlib box for a telnet box; if deflate is non-zero, it
128 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400129 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400130 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -0400131 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400132telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400133 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400134 int rs;
135
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400136 /* if compression is already enabled, fail loudly */
137 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400138 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400139 err_fatal, "cannot initialize compression twice");
140
Sean Middleditch72cc9642009-03-15 11:50:36 -0400141 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400142 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400143 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400144 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400145
146 /* initialize */
147 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400148 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
149 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400150 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400151 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400152 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400153 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400154 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400155 if ((rs = inflateInit(z)) != Z_OK) {
156 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400157 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400158 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400159 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400160 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400161 }
162
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400163 telnet->z = z;
164
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400165 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400166}
Sean Middleditche5b47592009-03-16 17:37:43 -0400167#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400168
Sean Middleditch8b788962009-03-16 01:06:27 -0400169/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400170static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400171 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400172#ifdef HAVE_ZLIB
173 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400174 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400175 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400176 int rs;
177
178 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400179 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400180 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400181 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400182 telnet->z->avail_out = sizeof(deflate_buffer);
183
184 /* deflate until buffer exhausted and all output is produced */
185 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
186 /* compress */
187 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400188 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400189 "deflate() failed: %s", zError(rs));
190 deflateEnd(telnet->z);
191 free(telnet->z);
192 telnet->z = 0;
193 break;
194 }
195
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400196 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditche5327da2009-03-21 00:54:50 -0400197 sizeof(deflate_buffer) - telnet->z->avail_out, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400198
199 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400200 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400201 telnet->z->avail_out = sizeof(deflate_buffer);
202 }
203
204 /* COMPRESS2 is not negotiated, just send */
205 } else
206#endif /* HAVE_ZLIB */
Sean Middleditche5327da2009-03-21 00:54:50 -0400207 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400208}
209
Sean Middleditch34bb0992009-03-21 00:20:44 -0400210/* check if we support a particular telopt; if us is non-zero, we
211 * check if we (local) supports it, otherwise we check if he (remote)
212 * supports it. return non-zero if supported, zero if not supported.
213 */
214static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
215 int us) {
216 int i;
217
218 /* if we have no telopts table, we obviously don't support it */
219 if (telnet->telopts == 0)
220 return 0;
221
222 /* loop unti found or end marker (us and him both 0) */
Sean Middleditchbfc641e2009-03-22 16:26:06 -0400223 for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
224 if (telnet->telopts[i].telopt == telopt) {
225 if (us && telnet->telopts[i].us == TELNET_WILL)
226 return 1;
227 else if (!us && telnet->telopts[i].him == TELNET_DO)
228 return 1;
229 else
230 return 0;
231 }
232 }
Sean Middleditch34bb0992009-03-21 00:20:44 -0400233
234 /* not found, so not supported */
235 return 0;
236}
237
Sean Middleditch8b788962009-03-16 01:06:27 -0400238/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400239static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
240 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400241 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400242 int i;
243
244 /* search for entry */
245 for (i = 0; i != telnet->q_size; ++i)
246 if (telnet->q[i].telopt == telopt)
247 return telnet->q[i];
248
249 /* not found, return empty value */
250 return empty;
251}
252
253/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400254static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
255 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400256 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400257 int i;
258
259 /* search for entry */
260 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400261 if (telnet->q[i].telopt == telopt) {
262 telnet->q[i].us = us;
263 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400264 return;
265 }
266 }
267
268 /* we're going to need to track state for it, so grow the queue
Sean Middleditch1e80f522009-03-22 18:24:18 -0400269 * by 4 (four) elements and put the telopt into it; bail on allocation
270 * error. we go by four because it seems like a reasonable guess as
271 * to the number of enabled options for most simple code, and it
272 * allows for an acceptable number of reallocations for complex code.
Sean Middleditch8b788962009-03-16 01:06:27 -0400273 */
Sean Middleditcha63ff7f2009-04-03 01:27:06 -0400274 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q,
275 sizeof(telnet_rfc1143_t) * (telnet->q_size + 4))) == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400276 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400277 "malloc() failed: %s", strerror(errno));
278 return;
279 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400280 memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
Sean Middleditch8b788962009-03-16 01:06:27 -0400281 telnet->q = qtmp;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400282 telnet->q[telnet->q_size].telopt = telopt;
283 telnet->q[telnet->q_size].us = us;
284 telnet->q[telnet->q_size].him = him;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400285 telnet->q_size += 4;
Sean Middleditch8b788962009-03-16 01:06:27 -0400286}
287
Sean Middleditch90e79da2009-03-19 15:17:13 -0400288/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400289static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400290 unsigned char telopt) {
291 char bytes[3] = { TELNET_IAC, cmd, telopt };
292 _send(telnet, bytes, 3);
293}
294
Sean Middleditch8b788962009-03-16 01:06:27 -0400295/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400296static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400297 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400298
299 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400300 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400301 switch ((int)telnet->state) {
302 case TELNET_STATE_WILL:
Sean Middleditche5327da2009-03-21 00:54:50 -0400303 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400304 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400305 case TELNET_STATE_WONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400306 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400307 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400308 case TELNET_STATE_DO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400309 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400310 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400311 case TELNET_STATE_DONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400312 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400313 break;
314 }
315 return;
316 }
317
318 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400319 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400320
321 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400322 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400323 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400324 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400325 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400326 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400327 if (_check_telopt(telnet, telopt, 0)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400328 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400329 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400330 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400331 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400332 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400333 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400334 case Q_WANTNO:
335 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400336 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400337 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400338 "DONT answered by WILL");
339 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400340 case Q_WANTNO_OP:
341 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400342 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400343 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400344 "DONT answered by WILL");
345 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400346 case Q_WANTYES:
347 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400348 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400349 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400350 case Q_WANTYES_OP:
351 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400352 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400353 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400354 break;
355 }
356 break;
357
358 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400359 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400360 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400361 case Q_YES:
362 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400363 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400364 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400365 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400366 case Q_WANTNO:
367 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400368 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400369 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400370 case Q_WANTNO_OP:
371 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400372 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400373 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400374 case Q_WANTYES:
375 case Q_WANTYES_OP:
376 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400377 break;
378 }
379 break;
380
381 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400382 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400383 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400384 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400385 if (_check_telopt(telnet, telopt, 1)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400386 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400387 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400388 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400389 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400390 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400391 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400392 case Q_WANTNO:
393 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400394 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400395 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400396 "WONT answered by DO");
397 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400398 case Q_WANTNO_OP:
399 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400400 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400401 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400402 "WONT answered by DO");
403 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400404 case Q_WANTYES:
405 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400406 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400407 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400408 case Q_WANTYES_OP:
409 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400410 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400411 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400412 break;
413 }
414 break;
415
416 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400417 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400418 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400419 case Q_YES:
420 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400421 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400422 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400423 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400424 case Q_WANTNO:
425 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400426 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400427 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400428 case Q_WANTNO_OP:
429 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400430 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400431 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400432 case Q_WANTYES:
433 case Q_WANTYES_OP:
434 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400435 break;
436 }
437 break;
438 }
439}
440
Sean Middleditche5327da2009-03-21 00:54:50 -0400441/* process a subnegotiation buffer; return non-zero if the current buffer
442 * must be aborted and reprocessed due to COMPRESS2 being activated
443 */
444static int _subnegotiate(telnet_t *telnet) {
Sean Middleditchb10946c2009-03-22 18:21:14 -0400445 switch (telnet->sb_telopt) {
446#ifdef HAVE_ZLIB
Sean Middleditche5327da2009-03-21 00:54:50 -0400447 /* received COMPRESS2 begin marker, setup our zlib box and
448 * start handling the compressed stream if it's not already.
449 */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400450 case TELNET_TELOPT_COMPRESS2:
451 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
452 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch8c111792009-03-22 22:44:38 -0400453 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400454
Sean Middleditchb10946c2009-03-22 18:21:14 -0400455 /* standard SB notification */
456 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
457 telnet->buffer, telnet->buffer_pos, 0, 0);
458
459 /* notify app that compression was enabled */
460 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
461 return 1;
462 }
Sean Middleditch8c111792009-03-22 22:44:38 -0400463 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400464#endif /* HAVE_ZLIB */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400465#ifdef HAVE_ALLOCA
Sean Middleditch8c111792009-03-22 22:44:38 -0400466
Sean Middleditchb10946c2009-03-22 18:21:14 -0400467 /* ZMP command */
Sean Middleditch8c111792009-03-22 22:44:38 -0400468 case TELNET_TELOPT_ZMP: {
469 const char **argv, *c;
470 size_t i, argc;
Sean Middleditche5327da2009-03-21 00:54:50 -0400471 /* make sure this is a valid ZMP buffer */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400472 if (telnet->buffer_pos == 0 ||
473 telnet->buffer[telnet->buffer_pos - 1] != 0) {
474 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
475 "incomplete ZMP frame");
476 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
477 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400478 return 0;
Sean Middleditchb10946c2009-03-22 18:21:14 -0400479 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400480
481 /* count arguments */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400482 for (argc = 0, c = telnet->buffer; c != telnet->buffer +
483 telnet->buffer_pos; ++argc)
Sean Middleditche5327da2009-03-21 00:54:50 -0400484 c += strlen(c) + 1;
Sean Middleditche5327da2009-03-21 00:54:50 -0400485
486 /* allocate argument array, bail on error */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400487 if ((argv = (const char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400488 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400489 "alloca() failed: %s", strerror(errno));
Sean Middleditchb10946c2009-03-22 18:21:14 -0400490 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
491 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400492 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400493 }
494
495 /* populate argument array */
496 for (i = 0, c = telnet->buffer; i != argc; ++i) {
497 argv[i] = c;
498 c += strlen(c) + 1;
499 }
500
Sean Middleditchb10946c2009-03-22 18:21:14 -0400501 /* invoke event with our arguments */
502 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
503 telnet->buffer, telnet->buffer_pos, argv, argc);
Sean Middleditch8c111792009-03-22 22:44:38 -0400504 return 0;
505 }
506
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400507 /* any of a number of commands that use the form <BYTE>data<BYTE>data,
508 * including TTYPE, ENVIRON, NEW-ENVIRON, and MSSP
509 */
510 case TELNET_TELOPT_TTYPE:
511 case TELNET_TELOPT_ENVIRON:
512 case TELNET_TELOPT_NEW_ENVIRON:
Sean Middleditch8c111792009-03-22 22:44:38 -0400513 case TELNET_TELOPT_MSSP: {
514 char **argv, *c, *l;
515 size_t i, argc;
516
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400517 /* if we have no data, just pass it through */
518 if (telnet->buffer_pos == 0) {
519 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
520 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400521 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400522 }
523
524 /* very first byte must be in range 0-3 */
525 if ((unsigned)telnet->buffer[0] > 3) {
526 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
527 "telopt %d subneg has invalid data", telnet->sb_telopt);
528 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
529 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400530 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400531 }
532
533 /* count arguments; each argument is preceded by a byte in the
534 * range 0-3, so just count those.
535 * NOTE: we don't support the ENVIRON/NEW-ENVIRON ESC handling
536 * properly at all. guess that's a FIXME.
537 */
538 for (argc = 0, i = 0; i != telnet->buffer_pos; ++i)
539 if ((unsigned)telnet->buffer[i] <= 3)
540 ++argc;
541
542 /* allocate argument array, bail on error */
Sean Middleditch8c111792009-03-22 22:44:38 -0400543 if ((argv = (char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400544 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
545 "alloca() failed: %s", strerror(errno));
546 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
547 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400548 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400549 }
550
551 /* allocate strings in argument array */
552 for (i = 0, l = telnet->buffer; i != argc; ++i) {
Sean Middleditch8c111792009-03-22 22:44:38 -0400553 /* search for end marker */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400554 c = l + 1;
555 while (c != telnet->buffer + telnet->buffer_pos &&
556 (unsigned)*c > 3)
557 ++c;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400558
Sean Middleditch8c111792009-03-22 22:44:38 -0400559 /* allocate space; bail on error */
560 if ((argv[i] = (char *)alloca(c - l + 1)) == 0) {
561 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
562 "alloca() failed: %s", strerror(errno));
563 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
564 telnet->buffer, telnet->buffer_pos, 0, 0);
565 return 0;
566 }
567
568 /* copy data */
569 memcpy(argv[i], l, c - l);
570 argv[i][c - l] = 0;
571
572 /* prepare for next loop */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400573 l = c;
574 }
575
576 /* invoke event with our arguments */
577 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
Sean Middleditch8c111792009-03-22 22:44:38 -0400578 telnet->buffer, telnet->buffer_pos, (const char **)argv, argc);
579 return 0;
580 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400581#endif /* HAVE_ALLOCA */
Sean Middleditch8c111792009-03-22 22:44:38 -0400582
Sean Middleditchb10946c2009-03-22 18:21:14 -0400583 /* other generic subnegotiation */
584 default:
585 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
586 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400587 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400588 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400589}
590
Sean Middleditch29144852009-03-12 23:14:47 -0400591/* initialize a telnet state tracker */
Sean Middleditchd2466a02009-09-19 14:35:48 -0700592telnet_t *telnet_init(const telnet_telopt_t *telopts,
Sean Middleditch34bb0992009-03-21 00:20:44 -0400593 telnet_event_handler_t eh, unsigned char flags, void *user_data) {
Sean Middleditchd2466a02009-09-19 14:35:48 -0700594 /* allocate structure */
595 struct telnet_t *telnet = (telnet_t*)calloc(1, sizeof(telnet_t));
596 if (telnet == 0)
597 return 0;
598
599 /* initialize data */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400600 telnet->ud = user_data;
Sean Middleditch34bb0992009-03-21 00:20:44 -0400601 telnet->telopts = telopts;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400602 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400603 telnet->flags = flags;
Sean Middleditchd2466a02009-09-19 14:35:48 -0700604
605 return telnet;
Sean Middleditch29144852009-03-12 23:14:47 -0400606}
607
608/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400609void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400610 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400611 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400612 free(telnet->buffer);
613 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400614 telnet->buffer_size = 0;
615 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400616 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400617
Sean Middleditche5b47592009-03-16 17:37:43 -0400618#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400619 /* free zlib box */
620 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400621 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400622 deflateEnd(telnet->z);
623 else
624 inflateEnd(telnet->z);
625 free(telnet->z);
626 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400627 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400628#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400629
630 /* free RFC1143 queue */
631 if (telnet->q) {
632 free(telnet->q);
633 telnet->q = 0;
634 telnet->q_size = 0;
635 }
Sean Middleditchd2466a02009-09-19 14:35:48 -0700636
637 /* free the telnet structure itself */
638 free(telnet);
Sean Middleditch29144852009-03-12 23:14:47 -0400639}
640
Sean Middleditch51ad6792009-03-13 20:15:59 -0400641/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400642static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400643 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400644 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400645 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400646
Sean Middleditch51ad6792009-03-13 20:15:59 -0400647 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400648 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400649 /* find the next buffer size */
650 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400651 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400652 break;
653 }
654
655 /* overflow -- can't grow any more */
656 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400657 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400658 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400659 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400660 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400661
662 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400663 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400664 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400665 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400666 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400667 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400668 }
669
670 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400671 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400672 }
673
674 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400675 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400676 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400677}
678
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400679static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400680 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400681 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400682 for (i = start = 0; i != size; ++i) {
683 byte = buffer[i];
684 switch (telnet->state) {
685 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400686 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400687 /* on an IAC byte, pass through all pending bytes and
688 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400689 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400690 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400691 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditche5327da2009-03-21 00:54:50 -0400692 i - start, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400693 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400694 }
695 break;
696
697 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400698 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400699 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400700 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400701 case TELNET_SB:
702 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400703 break;
704 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400705 case TELNET_WILL:
706 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400707 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400708 case TELNET_WONT:
709 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400710 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400711 case TELNET_DO:
712 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400713 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400714 case TELNET_DONT:
715 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400716 break;
717 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400718 case TELNET_IAC:
Sean Middleditche5327da2009-03-21 00:54:50 -0400719 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400720 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400721 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400722 break;
723 /* some other command */
724 default:
Sean Middleditche5327da2009-03-21 00:54:50 -0400725 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400726 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400727 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400728 }
729 break;
730
731 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400732 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400733 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400734 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400735 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400736 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400737 start = i + 1;
738 telnet->state = TELNET_STATE_DATA;
739 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400740
Sean Middleditchda0e6952009-03-15 13:28:09 -0400741 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400742 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400743 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400744 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400745 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400746 break;
747
748 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400749 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400750 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400751 if (byte == TELNET_IAC) {
752 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400753 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400754 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400755 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400756 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400757 }
758 break;
759
Sean Middleditch6b372882009-03-14 13:06:47 -0400760 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400761 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400762 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400763 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400764 case TELNET_SE:
Sean Middleditch8c567352009-03-21 01:07:18 -0400765 /* return to default state */
766 start = i + 1;
767 telnet->state = TELNET_STATE_DATA;
768
Sean Middleditche5327da2009-03-21 00:54:50 -0400769 /* process subnegotiation */
770 if (_subnegotiate(telnet) != 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400771 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400772 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400773 * bytes inflated and abort trying to process the
774 * remaining compressed bytes in the current _process
775 * buffer argument
776 */
Sean Middleditch8c567352009-03-21 01:07:18 -0400777 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400778 return;
779 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400780 break;
781 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400782 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400783 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400784 if (_buffer_byte(telnet, TELNET_IAC) !=
785 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400786 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400787 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400788 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400789 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400790 }
791 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400792 /* something else -- protocol error. attempt to process
793 * content in subnegotiation buffer, then evaluate the
794 * given command as an IAC code.
795 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400796 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400797 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400798 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400799 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400800
Sean Middleditchf6daa852009-03-21 01:16:20 -0400801 /* enter IAC state */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400802 start = i + 1;
Sean Middleditchf6daa852009-03-21 01:16:20 -0400803 telnet->state = TELNET_STATE_IAC;
Sean Middleditche5327da2009-03-21 00:54:50 -0400804
805 /* process subnegotiation; see comment in
806 * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
807 */
808 if (_subnegotiate(telnet) != 0) {
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400809 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditche5327da2009-03-21 00:54:50 -0400810 return;
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400811 } else {
812 /* recursive call to get the current input byte processed
813 * as a regular IAC command. we could use a goto, but
814 * that would be gross.
815 */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400816 _process(telnet, (char *)&byte, 1);
Sean Middleditche5327da2009-03-21 00:54:50 -0400817 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400818 break;
819 }
820 break;
821 }
822 }
823
824 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400825 if (telnet->state == TELNET_STATE_DATA && i != start)
Sean Middleditche5327da2009-03-21 00:54:50 -0400826 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start, 0, 0);
Sean Middleditch29144852009-03-12 23:14:47 -0400827}
828
Sean Middleditch9de15982009-03-14 03:35:49 -0400829/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400830void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400831 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400832#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400833 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400834 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400835 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400836 int rs;
837
838 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400839 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400840 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400841 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400842 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400843
844 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400845 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400846 /* reset output buffer */
847
848 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400849 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400850
851 /* process the decompressed bytes on success */
852 if (rs == Z_OK || rs == Z_STREAM_END)
853 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400854 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400855 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400856 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400857 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400858
859 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400860 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400861 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400862
863 /* on error (or on end of stream) disable further inflation */
864 if (rs != Z_OK) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400865 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400866
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400867 inflateEnd(telnet->z);
868 free(telnet->z);
869 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400870 break;
871 }
872 }
873
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400874 /* COMPRESS2 is not negotiated, just process */
875 } else
876#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400877 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400878}
879
Sean Middleditch29144852009-03-12 23:14:47 -0400880/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400881void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400882 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400883 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400884}
885
886/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400887void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400888 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400889 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400890
891 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400892 if (telnet->flags & TELNET_FLAG_PROXY) {
893 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400894 _send(telnet, bytes, 3);
895 return;
896 }
897
898 /* get current option states */
899 q = _get_rfc1143(telnet, telopt);
900
901 switch (cmd) {
902 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400903 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400904 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400905 case Q_NO:
906 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400907 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400908 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400909 case Q_WANTNO:
910 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400911 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400912 case Q_WANTYES_OP:
913 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400914 break;
915 }
916 break;
917
918 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400919 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400920 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400921 case Q_YES:
922 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400923 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400924 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400925 case Q_WANTYES:
926 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400927 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400928 case Q_WANTNO_OP:
929 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400930 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400931 }
932 break;
933
934 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400935 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400936 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400937 case Q_NO:
938 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400939 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400940 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400941 case Q_WANTNO:
942 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400943 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400944 case Q_WANTYES_OP:
945 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400946 break;
947 }
948 break;
949
950 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400951 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400952 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400953 case Q_YES:
954 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400955 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400956 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400957 case Q_WANTYES:
958 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400959 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400960 case Q_WANTNO_OP:
961 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400962 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400963 }
964 break;
965 }
Sean Middleditch29144852009-03-12 23:14:47 -0400966}
967
968/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400969void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400970 size_t size) {
971 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400972
Sean Middleditch29144852009-03-12 23:14:47 -0400973 for (l = i = 0; i != size; ++i) {
974 /* dump prior portion of text, send escaped bytes */
Sean Middleditch0003f052009-04-27 19:26:05 -0400975 if (buffer[i] == (char)TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400976 /* dump prior text if any */
977 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400978 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400979 l = i + 1;
980
981 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400982 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400983 }
984 }
985
986 /* send whatever portion of buffer is left */
987 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400988 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400989}
990
Sean Middleditch78943842009-03-19 15:22:06 -0400991/* send subnegotiation header */
Sean Middleditch1443ca42009-03-21 00:21:04 -0400992void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch78943842009-03-19 15:22:06 -0400993 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
994 _send(telnet, sb, 3);
995}
996
997
998/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400999void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -04001000 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -04001001 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
1002 static const char se[2] = { TELNET_IAC, TELNET_SE };
1003
1004 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001005 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -04001006 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001007
1008#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -04001009 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
1010 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001011 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001012 if (telnet->flags & TELNET_FLAG_PROXY &&
1013 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -04001014
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001015 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -04001016 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001017
1018 /* notify app that compression was enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -04001019 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001020 }
1021#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -04001022}
Sean Middleditch124a1c22009-03-15 13:20:03 -04001023
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001024void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -04001025#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001026 static const char compress2[] = { TELNET_IAC, TELNET_SB,
1027 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -04001028
Sean Middleditch124a1c22009-03-15 13:20:03 -04001029 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001030 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -04001031 return;
1032
Sean Middleditchfbe93e32009-03-15 23:39:31 -04001033 /* send compression marker. we send directly to the event handler
1034 * instead of passing through _send because _send would result in
1035 * the compress marker itself being compressed.
1036 */
Sean Middleditche5327da2009-03-21 00:54:50 -04001037 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2), 0, 0);
Sean Middleditch2b4bfc42009-03-16 01:25:52 -04001038
1039 /* notify app that compression was successfully enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -04001040 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -04001041#endif /* HAVE_ZLIB */
1042}
Sean Middleditchd58f49f2009-03-16 12:49:35 -04001043
Sean Middleditch4a156042009-03-16 17:10:58 -04001044/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001045int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -04001046 static const char CRLF[] = { '\r', '\n' };
1047 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -04001048 char buffer[4096];
1049 va_list va;
1050 int rs, i, l;
1051
1052 /* format */
1053 va_start(va, fmt);
1054 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1055 va_end(va);
1056
1057 /* send */
1058 for (l = i = 0; i != rs; ++i) {
1059 /* special characters */
Sean Middleditch0003f052009-04-27 19:26:05 -04001060 if (buffer[i] == (char)TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -04001061 buffer[i] == '\n') {
1062 /* dump prior portion of text */
1063 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001064 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001065 l = i + 1;
1066
1067 /* IAC -> IAC IAC */
Sean Middleditch0003f052009-04-27 19:26:05 -04001068 if (buffer[i] == (char)TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001069 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -04001070 /* automatic translation of \r -> CRNUL */
1071 else if (buffer[i] == '\r')
1072 _send(telnet, CRNUL, 2);
1073 /* automatic translation of \n -> CRLF */
1074 else if (buffer[i] == '\n')
1075 _send(telnet, CRLF, 2);
1076 }
1077 }
1078
1079 /* send whatever portion of buffer is left */
1080 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001081 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001082
1083 return rs;
1084}
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001085
Sean Middleditch7491bf42009-03-20 23:52:18 -04001086/* send formatted data through telnet_send */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001087int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
1088 char buffer[4096];
1089 va_list va;
1090 int rs;
1091
1092 /* format */
1093 va_start(va, fmt);
1094 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1095 va_end(va);
1096
1097 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001098 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001099
1100 return rs;
1101}
Sean Middleditche22b4772009-03-22 16:44:40 -04001102
Sean Middleditcheb950a82009-03-22 23:04:28 -04001103/* send formatted subnegotiation data for TTYPE/ENVIRON/NEW-ENVIRON/MSSP */
1104void telnet_format_sb(telnet_t *telnet, unsigned char telopt,
1105 size_t count, ...) {
1106 va_list va;
1107 size_t i;
1108
1109 /* subnegotiation header */
1110 telnet_begin_sb(telnet, telopt);
1111
1112 /* iterate over the arguments pulling out integers and strings */
1113 va_start(va, count);
1114 for (i = 0; i != count; ++i) {
1115 char t;
1116 const char* s;
1117 t = va_arg(va, int);
1118 s = va_arg(va, const char *);
1119 telnet_send(telnet, &t, 1);
1120 telnet_send(telnet, s, strlen(s));
1121 }
1122 va_end(va);
1123
1124 /* footer */
1125 telnet_finish_sb(telnet);
1126}
1127
Sean Middleditche22b4772009-03-22 16:44:40 -04001128/* send ZMP data */
1129void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) {
1130 size_t i;
1131
1132 /* ZMP header */
1133 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1134
1135 /* send out each argument, including trailing NUL byte */
1136 for (i = 0; i != argc; ++i)
1137 telnet_send(telnet, argv[i], strlen(argv[i] + 1));
1138
1139 /* ZMP footer */
1140 telnet_finish_sb(telnet);
1141}
1142
1143/* send ZMP data using varargs */
1144void telnet_send_zmpv(telnet_t *telnet, ...) {
1145 va_list va;
1146 const char* arg;
1147
1148 /* ZMP header */
1149 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1150
1151 /* send out each argument, including trailing NUL byte */
1152 va_start(va, telnet);
1153 while ((arg = va_arg(va, const char *)) != NULL)
1154 telnet_send(telnet, arg, strlen(arg) + 1);
1155 va_end(va);
1156
1157 /* ZMP footer */
1158 telnet_finish_sb(telnet);
1159}