blob: fdb7652405809145d1a45c5dec747b5741b46963 [file] [log] [blame]
Sean Middleditch6aef0732009-03-12 23:27:35 -04001/*
Sean Middleditch55655962009-10-24 15:38:46 -07002 * libtelnet - TELNET protocol handling library
Sean Middleditchae39cee2009-03-22 22:47:30 -04003 *
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 Middleditchdb128162009-10-25 02:02:17 -070021#if defined(HAVE_ALLOCA)
22# include <alloca.h>
Sean Middleditch2d5c4992009-03-22 22:21:42 -040023#endif
24
Sean Middleditchdb128162009-10-25 02:02:17 -070025#if defined(HAVE_ZLIB)
26# 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 */
Sean Middleditchdb128162009-10-25 02:02:17 -070032#if defined(__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 Middleditche97ac9e2009-10-24 15:41:05 -070038/* telnet state codes */
39enum telnet_state_t {
40 TELNET_STATE_DATA = 0,
41 TELNET_STATE_IAC,
42 TELNET_STATE_WILL,
43 TELNET_STATE_WONT,
44 TELNET_STATE_DO,
45 TELNET_STATE_DONT,
46 TELNET_STATE_SB,
47 TELNET_STATE_SB_DATA,
48 TELNET_STATE_SB_DATA_IAC
49};
50typedef enum telnet_state_t telnet_state_t;
51
52/* telnet state tracker */
Sean Middleditchd2466a02009-09-19 14:35:48 -070053struct telnet_t {
54 /* user data */
55 void *ud;
56 /* telopt support table */
57 const telnet_telopt_t *telopts;
58 /* event handler */
59 telnet_event_handler_t eh;
Sean Middleditchdb128162009-10-25 02:02:17 -070060#if defined(HAVE_ZLIB)
Sean Middleditchd2466a02009-09-19 14:35:48 -070061 /* zlib (mccp2) compression */
62 z_stream *z;
63#endif
64 /* RFC1143 option negotiation states */
65 struct telnet_rfc1143_t *q;
66 /* sub-request buffer */
67 char *buffer;
68 /* current size of the buffer */
69 size_t buffer_size;
70 /* current buffer write position (also length of buffer data) */
71 size_t buffer_pos;
72 /* current state */
73 enum telnet_state_t state;
74 /* option flags */
75 unsigned char flags;
76 /* current subnegotiation telopt */
77 unsigned char sb_telopt;
78 /* length of RFC1143 queue */
79 unsigned char q_size;
80};
81
Sean Middleditch90e79da2009-03-19 15:17:13 -040082/* RFC1143 option negotiation state */
83typedef struct telnet_rfc1143_t {
84 unsigned char telopt;
85 char us:4, him:4;
86} telnet_rfc1143_t;
87
Sean Middleditch5b5bc922009-03-15 23:02:10 -040088/* RFC1143 state names */
Sean Middleditch7491bf42009-03-20 23:52:18 -040089#define Q_NO 0
90#define Q_YES 1
91#define Q_WANTNO 2
92#define Q_WANTYES 3
93#define Q_WANTNO_OP 4
94#define Q_WANTYES_OP 5
Sean Middleditch5b5bc922009-03-15 23:02:10 -040095
Sean Middleditch4d9444d2009-03-13 22:48:05 -040096/* buffer sizes */
Sean Middleditch8c111792009-03-22 22:44:38 -040097static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
Sean Middleditch340a51b2009-03-19 02:08:46 -040098static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040099 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400100
Sean Middleditch34bb0992009-03-21 00:20:44 -0400101/* event dispatch helper */
102static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400103 unsigned char command, unsigned char telopt,
Sean Middleditche5327da2009-03-21 00:54:50 -0400104 const char *buffer, size_t size, const char **argv, size_t argc) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400105 telnet_event_t ev;
Sean Middleditche5327da2009-03-21 00:54:50 -0400106 ev.argv = argv;
107 ev.argc = argc;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400108 ev.buffer = buffer;
109 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400110 ev.type = type;
111 ev.command = command;
112 ev.telopt = telopt;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400113
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400114 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch637df7f2009-03-15 12:57:32 -0400115}
116
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400117/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400118static telnet_error_t _error(telnet_t *telnet, unsigned line,
119 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400120 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400121 char buffer[512];
122 va_list va;
123
124 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -0400125 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400126
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400127 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400128 va_start(va, fmt);
129 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
130 fmt, va);
131 va_end(va);
132
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400133 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400134 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditche5327da2009-03-21 00:54:50 -0400135 0, buffer, strlen(buffer), 0, 0);
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400136
137 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400138}
139
Sean Middleditchdb128162009-10-25 02:02:17 -0700140#if defined(HAVE_ZLIB)
Sean Middleditch72cc9642009-03-15 11:50:36 -0400141/* initialize the zlib box for a telnet box; if deflate is non-zero, it
142 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400143 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400144 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -0400145 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400146telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400147 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400148 int rs;
149
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400150 /* if compression is already enabled, fail loudly */
151 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400152 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400153 err_fatal, "cannot initialize compression twice");
154
Sean Middleditch72cc9642009-03-15 11:50:36 -0400155 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400156 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400157 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400158 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400159
160 /* initialize */
161 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400162 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
163 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400164 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400165 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400166 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400167 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400168 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400169 if ((rs = inflateInit(z)) != Z_OK) {
170 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400171 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400172 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400173 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400174 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400175 }
176
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400177 telnet->z = z;
178
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400179 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400180}
Sean Middleditchdb128162009-10-25 02:02:17 -0700181#endif /* defined(HAVE_ZLIB) */
Sean Middleditch72cc9642009-03-15 11:50:36 -0400182
Sean Middleditch8b788962009-03-16 01:06:27 -0400183/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400184static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400185 size_t size) {
Sean Middleditchdb128162009-10-25 02:02:17 -0700186#if defined(HAVE_ZLIB)
Sean Middleditch8b788962009-03-16 01:06:27 -0400187 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400188 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400189 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400190 int rs;
191
192 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400193 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400194 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400195 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400196 telnet->z->avail_out = sizeof(deflate_buffer);
197
198 /* deflate until buffer exhausted and all output is produced */
199 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
200 /* compress */
201 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400202 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400203 "deflate() failed: %s", zError(rs));
204 deflateEnd(telnet->z);
205 free(telnet->z);
206 telnet->z = 0;
207 break;
208 }
209
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400210 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditche5327da2009-03-21 00:54:50 -0400211 sizeof(deflate_buffer) - telnet->z->avail_out, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400212
213 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400214 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400215 telnet->z->avail_out = sizeof(deflate_buffer);
216 }
217
218 /* COMPRESS2 is not negotiated, just send */
219 } else
Sean Middleditchdb128162009-10-25 02:02:17 -0700220#endif /* defined(HAVE_ZLIB) */
Sean Middleditche5327da2009-03-21 00:54:50 -0400221 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400222}
223
Sean Middleditche2634bd2010-01-06 16:32:55 -0800224/* to send bags of unsigned chars */
225#define _sendu(t, d, s) _send((t), (const char*)(d), (s))
226
Sean Middleditch34bb0992009-03-21 00:20:44 -0400227/* check if we support a particular telopt; if us is non-zero, we
228 * check if we (local) supports it, otherwise we check if he (remote)
229 * supports it. return non-zero if supported, zero if not supported.
230 */
231static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
232 int us) {
233 int i;
234
235 /* if we have no telopts table, we obviously don't support it */
236 if (telnet->telopts == 0)
237 return 0;
238
239 /* loop unti found or end marker (us and him both 0) */
Sean Middleditchbfc641e2009-03-22 16:26:06 -0400240 for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
241 if (telnet->telopts[i].telopt == telopt) {
242 if (us && telnet->telopts[i].us == TELNET_WILL)
243 return 1;
244 else if (!us && telnet->telopts[i].him == TELNET_DO)
245 return 1;
246 else
247 return 0;
248 }
249 }
Sean Middleditch34bb0992009-03-21 00:20:44 -0400250
251 /* not found, so not supported */
252 return 0;
253}
254
Sean Middleditch8b788962009-03-16 01:06:27 -0400255/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400256static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
257 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400258 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400259 int i;
260
261 /* search for entry */
262 for (i = 0; i != telnet->q_size; ++i)
263 if (telnet->q[i].telopt == telopt)
264 return telnet->q[i];
265
266 /* not found, return empty value */
267 return empty;
268}
269
270/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400271static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
272 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400273 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400274 int i;
275
276 /* search for entry */
277 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400278 if (telnet->q[i].telopt == telopt) {
279 telnet->q[i].us = us;
280 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400281 return;
282 }
283 }
284
285 /* we're going to need to track state for it, so grow the queue
Sean Middleditch1e80f522009-03-22 18:24:18 -0400286 * by 4 (four) elements and put the telopt into it; bail on allocation
287 * error. we go by four because it seems like a reasonable guess as
288 * to the number of enabled options for most simple code, and it
289 * allows for an acceptable number of reallocations for complex code.
Sean Middleditch8b788962009-03-16 01:06:27 -0400290 */
Sean Middleditcha63ff7f2009-04-03 01:27:06 -0400291 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q,
292 sizeof(telnet_rfc1143_t) * (telnet->q_size + 4))) == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400293 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400294 "malloc() failed: %s", strerror(errno));
295 return;
296 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400297 memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
Sean Middleditch8b788962009-03-16 01:06:27 -0400298 telnet->q = qtmp;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400299 telnet->q[telnet->q_size].telopt = telopt;
300 telnet->q[telnet->q_size].us = us;
301 telnet->q[telnet->q_size].him = him;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400302 telnet->q_size += 4;
Sean Middleditch8b788962009-03-16 01:06:27 -0400303}
304
Sean Middleditch90e79da2009-03-19 15:17:13 -0400305/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400306static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400307 unsigned char telopt) {
Sean Middleditche2634bd2010-01-06 16:32:55 -0800308 const unsigned char bytes[3] = { TELNET_IAC, cmd, telopt };
309 _sendu(telnet, bytes, 3);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400310}
311
Sean Middleditch8b788962009-03-16 01:06:27 -0400312/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400313static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400314 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400315
316 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400317 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400318 switch ((int)telnet->state) {
319 case TELNET_STATE_WILL:
Sean Middleditche5327da2009-03-21 00:54:50 -0400320 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400321 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400322 case TELNET_STATE_WONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400323 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400324 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400325 case TELNET_STATE_DO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400326 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400327 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400328 case TELNET_STATE_DONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400329 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400330 break;
331 }
332 return;
333 }
334
335 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400336 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400337
338 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400339 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400340 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400341 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400342 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400343 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400344 if (_check_telopt(telnet, telopt, 0)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400345 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400346 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400347 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400348 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400349 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400350 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400351 case Q_WANTNO:
352 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400353 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400354 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400355 "DONT answered by WILL");
356 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400357 case Q_WANTNO_OP:
358 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400359 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400360 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400361 "DONT answered by WILL");
362 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400363 case Q_WANTYES:
364 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400365 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400366 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400367 case Q_WANTYES_OP:
368 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400369 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400370 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400371 break;
372 }
373 break;
374
375 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400376 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400377 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400378 case Q_YES:
379 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400380 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400381 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400382 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400383 case Q_WANTNO:
384 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400385 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400386 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400387 case Q_WANTNO_OP:
388 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400389 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400390 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400391 case Q_WANTYES:
392 case Q_WANTYES_OP:
393 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400394 break;
395 }
396 break;
397
398 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400399 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400400 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400401 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400402 if (_check_telopt(telnet, telopt, 1)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400403 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400404 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400405 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400406 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400407 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400408 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400409 case Q_WANTNO:
410 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400411 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400412 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400413 "WONT answered by DO");
414 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400415 case Q_WANTNO_OP:
416 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400417 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400418 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400419 "WONT answered by DO");
420 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400421 case Q_WANTYES:
422 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400423 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400424 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400425 case Q_WANTYES_OP:
426 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400427 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400428 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400429 break;
430 }
431 break;
432
433 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400434 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400435 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400436 case Q_YES:
437 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400438 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400439 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400440 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400441 case Q_WANTNO:
442 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400443 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400444 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400445 case Q_WANTNO_OP:
446 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400447 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400448 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400449 case Q_WANTYES:
450 case Q_WANTYES_OP:
451 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400452 break;
453 }
454 break;
455 }
456}
457
Sean Middleditche5327da2009-03-21 00:54:50 -0400458/* process a subnegotiation buffer; return non-zero if the current buffer
459 * must be aborted and reprocessed due to COMPRESS2 being activated
460 */
461static int _subnegotiate(telnet_t *telnet) {
Sean Middleditchb10946c2009-03-22 18:21:14 -0400462 switch (telnet->sb_telopt) {
Sean Middleditchdb128162009-10-25 02:02:17 -0700463#if defined(HAVE_ZLIB)
Sean Middleditche5327da2009-03-21 00:54:50 -0400464 /* received COMPRESS2 begin marker, setup our zlib box and
465 * start handling the compressed stream if it's not already.
466 */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400467 case TELNET_TELOPT_COMPRESS2:
468 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
469 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch8c111792009-03-22 22:44:38 -0400470 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400471
Sean Middleditchb10946c2009-03-22 18:21:14 -0400472 /* standard SB notification */
473 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
474 telnet->buffer, telnet->buffer_pos, 0, 0);
475
476 /* notify app that compression was enabled */
477 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
478 return 1;
479 }
Sean Middleditch8c111792009-03-22 22:44:38 -0400480 return 0;
Sean Middleditchdb128162009-10-25 02:02:17 -0700481#endif /* defined(HAVE_ZLIB) */
482#if defined(HAVE_ALLOCA)
Sean Middleditch8c111792009-03-22 22:44:38 -0400483
Sean Middleditchb10946c2009-03-22 18:21:14 -0400484 /* ZMP command */
Sean Middleditch8c111792009-03-22 22:44:38 -0400485 case TELNET_TELOPT_ZMP: {
486 const char **argv, *c;
487 size_t i, argc;
Sean Middleditche5327da2009-03-21 00:54:50 -0400488 /* make sure this is a valid ZMP buffer */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400489 if (telnet->buffer_pos == 0 ||
490 telnet->buffer[telnet->buffer_pos - 1] != 0) {
491 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
492 "incomplete ZMP frame");
493 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
494 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400495 return 0;
Sean Middleditchb10946c2009-03-22 18:21:14 -0400496 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400497
498 /* count arguments */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400499 for (argc = 0, c = telnet->buffer; c != telnet->buffer +
500 telnet->buffer_pos; ++argc)
Sean Middleditche5327da2009-03-21 00:54:50 -0400501 c += strlen(c) + 1;
Sean Middleditche5327da2009-03-21 00:54:50 -0400502
503 /* allocate argument array, bail on error */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400504 if ((argv = (const char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400505 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400506 "alloca() failed: %s", strerror(errno));
Sean Middleditchb10946c2009-03-22 18:21:14 -0400507 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
508 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400509 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400510 }
511
512 /* populate argument array */
513 for (i = 0, c = telnet->buffer; i != argc; ++i) {
514 argv[i] = c;
515 c += strlen(c) + 1;
516 }
517
Sean Middleditchb10946c2009-03-22 18:21:14 -0400518 /* invoke event with our arguments */
519 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
520 telnet->buffer, telnet->buffer_pos, argv, argc);
Sean Middleditch8c111792009-03-22 22:44:38 -0400521 return 0;
522 }
523
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400524 /* any of a number of commands that use the form <BYTE>data<BYTE>data,
525 * including TTYPE, ENVIRON, NEW-ENVIRON, and MSSP
526 */
527 case TELNET_TELOPT_TTYPE:
528 case TELNET_TELOPT_ENVIRON:
529 case TELNET_TELOPT_NEW_ENVIRON:
Sean Middleditch8c111792009-03-22 22:44:38 -0400530 case TELNET_TELOPT_MSSP: {
531 char **argv, *c, *l;
532 size_t i, argc;
533
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400534 /* if we have no data, just pass it through */
535 if (telnet->buffer_pos == 0) {
536 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
537 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400538 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400539 }
540
541 /* very first byte must be in range 0-3 */
542 if ((unsigned)telnet->buffer[0] > 3) {
543 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
544 "telopt %d subneg has invalid data", telnet->sb_telopt);
545 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
546 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400547 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400548 }
549
550 /* count arguments; each argument is preceded by a byte in the
551 * range 0-3, so just count those.
552 * NOTE: we don't support the ENVIRON/NEW-ENVIRON ESC handling
553 * properly at all. guess that's a FIXME.
554 */
555 for (argc = 0, i = 0; i != telnet->buffer_pos; ++i)
556 if ((unsigned)telnet->buffer[i] <= 3)
557 ++argc;
558
559 /* allocate argument array, bail on error */
Sean Middleditch8c111792009-03-22 22:44:38 -0400560 if ((argv = (char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400561 _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);
Sean Middleditch8c111792009-03-22 22:44:38 -0400565 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400566 }
567
568 /* allocate strings in argument array */
569 for (i = 0, l = telnet->buffer; i != argc; ++i) {
Sean Middleditch8c111792009-03-22 22:44:38 -0400570 /* search for end marker */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400571 c = l + 1;
572 while (c != telnet->buffer + telnet->buffer_pos &&
573 (unsigned)*c > 3)
574 ++c;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400575
Sean Middleditch8c111792009-03-22 22:44:38 -0400576 /* allocate space; bail on error */
577 if ((argv[i] = (char *)alloca(c - l + 1)) == 0) {
578 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
579 "alloca() failed: %s", strerror(errno));
580 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
581 telnet->buffer, telnet->buffer_pos, 0, 0);
582 return 0;
583 }
584
585 /* copy data */
586 memcpy(argv[i], l, c - l);
587 argv[i][c - l] = 0;
588
589 /* prepare for next loop */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400590 l = c;
591 }
592
593 /* invoke event with our arguments */
594 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
Sean Middleditch8c111792009-03-22 22:44:38 -0400595 telnet->buffer, telnet->buffer_pos, (const char **)argv, argc);
596 return 0;
597 }
Sean Middleditchdb128162009-10-25 02:02:17 -0700598#endif /* defined(HAVE_ALLOCA) */
Sean Middleditch8c111792009-03-22 22:44:38 -0400599
Sean Middleditchb10946c2009-03-22 18:21:14 -0400600 /* other generic subnegotiation */
601 default:
602 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
603 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400604 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400605 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400606}
607
Sean Middleditch29144852009-03-12 23:14:47 -0400608/* initialize a telnet state tracker */
Sean Middleditchd2466a02009-09-19 14:35:48 -0700609telnet_t *telnet_init(const telnet_telopt_t *telopts,
Sean Middleditch34bb0992009-03-21 00:20:44 -0400610 telnet_event_handler_t eh, unsigned char flags, void *user_data) {
Sean Middleditchd2466a02009-09-19 14:35:48 -0700611 /* allocate structure */
612 struct telnet_t *telnet = (telnet_t*)calloc(1, sizeof(telnet_t));
613 if (telnet == 0)
614 return 0;
615
616 /* initialize data */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400617 telnet->ud = user_data;
Sean Middleditch34bb0992009-03-21 00:20:44 -0400618 telnet->telopts = telopts;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400619 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400620 telnet->flags = flags;
Sean Middleditchd2466a02009-09-19 14:35:48 -0700621
622 return telnet;
Sean Middleditch29144852009-03-12 23:14:47 -0400623}
624
625/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400626void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400627 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400628 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400629 free(telnet->buffer);
630 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400631 telnet->buffer_size = 0;
632 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400633 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400634
Sean Middleditchdb128162009-10-25 02:02:17 -0700635#if defined(HAVE_ZLIB)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400636 /* free zlib box */
637 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400638 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400639 deflateEnd(telnet->z);
640 else
641 inflateEnd(telnet->z);
642 free(telnet->z);
643 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400644 }
Sean Middleditchdb128162009-10-25 02:02:17 -0700645#endif /* defined(HAVE_ZLIB) */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400646
647 /* free RFC1143 queue */
648 if (telnet->q) {
649 free(telnet->q);
650 telnet->q = 0;
651 telnet->q_size = 0;
652 }
Sean Middleditchd2466a02009-09-19 14:35:48 -0700653
654 /* free the telnet structure itself */
655 free(telnet);
Sean Middleditch29144852009-03-12 23:14:47 -0400656}
657
Sean Middleditch51ad6792009-03-13 20:15:59 -0400658/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400659static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400660 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400661 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400662 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400663
Sean Middleditch51ad6792009-03-13 20:15:59 -0400664 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400665 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400666 /* find the next buffer size */
667 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400668 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400669 break;
670 }
671
672 /* overflow -- can't grow any more */
673 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400674 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400675 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400676 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400677 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400678
679 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400680 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400681 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400682 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400683 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400684 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400685 }
686
687 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400688 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400689 }
690
691 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400692 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400693 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400694}
695
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400696static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400697 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400698 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400699 for (i = start = 0; i != size; ++i) {
700 byte = buffer[i];
701 switch (telnet->state) {
702 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400703 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400704 /* on an IAC byte, pass through all pending bytes and
705 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400706 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400707 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400708 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditche5327da2009-03-21 00:54:50 -0400709 i - start, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400710 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400711 }
712 break;
713
714 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400715 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400716 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400717 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400718 case TELNET_SB:
719 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400720 break;
721 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400722 case TELNET_WILL:
723 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400724 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400725 case TELNET_WONT:
726 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400727 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400728 case TELNET_DO:
729 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400730 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400731 case TELNET_DONT:
732 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400733 break;
734 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400735 case TELNET_IAC:
Sean Middleditche5327da2009-03-21 00:54:50 -0400736 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400737 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400738 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400739 break;
740 /* some other command */
741 default:
Sean Middleditche5327da2009-03-21 00:54:50 -0400742 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400743 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400744 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400745 }
746 break;
747
748 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400749 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400750 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400751 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400752 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400753 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400754 start = i + 1;
755 telnet->state = TELNET_STATE_DATA;
756 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400757
Sean Middleditchda0e6952009-03-15 13:28:09 -0400758 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400759 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400760 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400761 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400762 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400763 break;
764
765 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400766 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400767 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400768 if (byte == TELNET_IAC) {
769 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400770 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400771 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400772 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400773 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400774 }
775 break;
776
Sean Middleditch6b372882009-03-14 13:06:47 -0400777 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400778 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400779 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400780 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400781 case TELNET_SE:
Sean Middleditch8c567352009-03-21 01:07:18 -0400782 /* return to default state */
783 start = i + 1;
784 telnet->state = TELNET_STATE_DATA;
785
Sean Middleditche5327da2009-03-21 00:54:50 -0400786 /* process subnegotiation */
787 if (_subnegotiate(telnet) != 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400788 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400789 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400790 * bytes inflated and abort trying to process the
791 * remaining compressed bytes in the current _process
792 * buffer argument
793 */
Sean Middleditch8c567352009-03-21 01:07:18 -0400794 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400795 return;
796 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400797 break;
798 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400799 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400800 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400801 if (_buffer_byte(telnet, TELNET_IAC) !=
802 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400803 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400804 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400805 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400806 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400807 }
808 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400809 /* something else -- protocol error. attempt to process
810 * content in subnegotiation buffer, then evaluate the
811 * given command as an IAC code.
812 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400813 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400814 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400815 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400816 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400817
Sean Middleditchf6daa852009-03-21 01:16:20 -0400818 /* enter IAC state */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400819 start = i + 1;
Sean Middleditchf6daa852009-03-21 01:16:20 -0400820 telnet->state = TELNET_STATE_IAC;
Sean Middleditche5327da2009-03-21 00:54:50 -0400821
822 /* process subnegotiation; see comment in
823 * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
824 */
825 if (_subnegotiate(telnet) != 0) {
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400826 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditche5327da2009-03-21 00:54:50 -0400827 return;
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400828 } else {
829 /* recursive call to get the current input byte processed
830 * as a regular IAC command. we could use a goto, but
831 * that would be gross.
832 */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400833 _process(telnet, (char *)&byte, 1);
Sean Middleditche5327da2009-03-21 00:54:50 -0400834 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400835 break;
836 }
837 break;
838 }
839 }
840
841 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400842 if (telnet->state == TELNET_STATE_DATA && i != start)
Sean Middleditche5327da2009-03-21 00:54:50 -0400843 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start, 0, 0);
Sean Middleditch29144852009-03-12 23:14:47 -0400844}
845
Sean Middleditch9de15982009-03-14 03:35:49 -0400846/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400847void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400848 size_t size) {
Sean Middleditchdb128162009-10-25 02:02:17 -0700849#if defined(HAVE_ZLIB)
Sean Middleditch72cc9642009-03-15 11:50:36 -0400850 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400851 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400852 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400853 int rs;
854
855 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400856 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400857 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400858 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400859 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400860
861 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400862 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400863 /* reset output buffer */
864
865 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400866 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400867
868 /* process the decompressed bytes on success */
869 if (rs == Z_OK || rs == Z_STREAM_END)
870 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400871 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400872 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400873 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400874 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400875
876 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400877 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400878 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400879
880 /* on error (or on end of stream) disable further inflation */
881 if (rs != Z_OK) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400882 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400883
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400884 inflateEnd(telnet->z);
885 free(telnet->z);
886 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400887 break;
888 }
889 }
890
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400891 /* COMPRESS2 is not negotiated, just process */
892 } else
Sean Middleditchdb128162009-10-25 02:02:17 -0700893#endif /* defined(HAVE_ZLIB) */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400894 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400895}
896
Sean Middleditch29144852009-03-12 23:14:47 -0400897/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400898void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditche2634bd2010-01-06 16:32:55 -0800899 const unsigned char bytes[2] = { TELNET_IAC, cmd };
900 _sendu(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400901}
902
903/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400904void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400905 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400906 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400907
908 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400909 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditche2634bd2010-01-06 16:32:55 -0800910 const unsigned char bytes[3] = { TELNET_IAC, cmd, telopt };
911 _sendu(telnet, bytes, 3);
Sean Middleditch8b788962009-03-16 01:06:27 -0400912 return;
913 }
914
915 /* get current option states */
916 q = _get_rfc1143(telnet, telopt);
917
918 switch (cmd) {
919 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400920 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400921 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400922 case Q_NO:
923 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400924 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400925 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400926 case Q_WANTNO:
927 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400928 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400929 case Q_WANTYES_OP:
930 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400931 break;
932 }
933 break;
934
935 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400936 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400937 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400938 case Q_YES:
939 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400940 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400941 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400942 case Q_WANTYES:
943 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400944 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400945 case Q_WANTNO_OP:
946 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400947 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400948 }
949 break;
950
951 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400952 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400953 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400954 case Q_NO:
955 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400956 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400957 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400958 case Q_WANTNO:
959 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400960 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400961 case Q_WANTYES_OP:
962 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400963 break;
964 }
965 break;
966
967 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400968 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400969 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400970 case Q_YES:
971 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400972 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400973 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400974 case Q_WANTYES:
975 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400976 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400977 case Q_WANTNO_OP:
978 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400979 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400980 }
981 break;
982 }
Sean Middleditch29144852009-03-12 23:14:47 -0400983}
984
985/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400986void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400987 size_t size) {
988 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400989
Sean Middleditch29144852009-03-12 23:14:47 -0400990 for (l = i = 0; i != size; ++i) {
991 /* dump prior portion of text, send escaped bytes */
Sean Middleditch0003f052009-04-27 19:26:05 -0400992 if (buffer[i] == (char)TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400993 /* dump prior text if any */
994 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400995 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400996 l = i + 1;
997
998 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400999 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -04001000 }
1001 }
1002
1003 /* send whatever portion of buffer is left */
1004 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -04001005 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -04001006}
1007
Sean Middleditch78943842009-03-19 15:22:06 -04001008/* send subnegotiation header */
Sean Middleditch1443ca42009-03-21 00:21:04 -04001009void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
Sean Middleditche2634bd2010-01-06 16:32:55 -08001010 const unsigned char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
1011 _sendu(telnet, sb, 3);
Sean Middleditch78943842009-03-19 15:22:06 -04001012}
1013
1014
1015/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001016void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -04001017 const char *buffer, size_t size) {
Sean Middleditche2634bd2010-01-06 16:32:55 -08001018 const unsigned char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
1019 const unsigned char se[2] = { TELNET_IAC, TELNET_SE };
Sean Middleditch90e79da2009-03-19 15:17:13 -04001020
Sean Middleditche2634bd2010-01-06 16:32:55 -08001021 _sendu(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001022 telnet_send(telnet, buffer, size);
Sean Middleditche2634bd2010-01-06 16:32:55 -08001023 _sendu(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001024
Sean Middleditchdb128162009-10-25 02:02:17 -07001025#if defined(HAVE_ZLIB)
Sean Middleditch72cc9642009-03-15 11:50:36 -04001026 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
1027 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001028 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001029 if (telnet->flags & TELNET_FLAG_PROXY &&
1030 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -04001031
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001032 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -04001033 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001034
1035 /* notify app that compression was enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -04001036 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -04001037 }
Sean Middleditchdb128162009-10-25 02:02:17 -07001038#endif /* defined(HAVE_ZLIB) */
Sean Middleditch29144852009-03-12 23:14:47 -04001039}
Sean Middleditch124a1c22009-03-15 13:20:03 -04001040
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001041void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditchdb128162009-10-25 02:02:17 -07001042#if defined(HAVE_ZLIB)
Sean Middleditche2634bd2010-01-06 16:32:55 -08001043 static const unsigned char compress2[] = { TELNET_IAC, TELNET_SB,
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001044 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -04001045
Sean Middleditch124a1c22009-03-15 13:20:03 -04001046 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001047 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -04001048 return;
1049
Sean Middleditchfbe93e32009-03-15 23:39:31 -04001050 /* send compression marker. we send directly to the event handler
1051 * instead of passing through _send because _send would result in
1052 * the compress marker itself being compressed.
1053 */
Sean Middleditche2634bd2010-01-06 16:32:55 -08001054 _event(telnet, TELNET_EV_SEND, 0, 0, (const char*)compress2, sizeof(compress2), 0, 0);
Sean Middleditch2b4bfc42009-03-16 01:25:52 -04001055
1056 /* notify app that compression was successfully enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -04001057 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditchdb128162009-10-25 02:02:17 -07001058#endif /* defined(HAVE_ZLIB) */
Sean Middleditch124a1c22009-03-15 13:20:03 -04001059}
Sean Middleditchd58f49f2009-03-16 12:49:35 -04001060
Sean Middleditch4a156042009-03-16 17:10:58 -04001061/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001062int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -04001063 static const char CRLF[] = { '\r', '\n' };
1064 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -04001065 char buffer[4096];
1066 va_list va;
1067 int rs, i, l;
1068
1069 /* format */
1070 va_start(va, fmt);
1071 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1072 va_end(va);
1073
1074 /* send */
1075 for (l = i = 0; i != rs; ++i) {
1076 /* special characters */
Sean Middleditch0003f052009-04-27 19:26:05 -04001077 if (buffer[i] == (char)TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -04001078 buffer[i] == '\n') {
1079 /* dump prior portion of text */
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 l = i + 1;
1083
1084 /* IAC -> IAC IAC */
Sean Middleditch0003f052009-04-27 19:26:05 -04001085 if (buffer[i] == (char)TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001086 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -04001087 /* automatic translation of \r -> CRNUL */
1088 else if (buffer[i] == '\r')
1089 _send(telnet, CRNUL, 2);
1090 /* automatic translation of \n -> CRLF */
1091 else if (buffer[i] == '\n')
1092 _send(telnet, CRLF, 2);
1093 }
1094 }
1095
1096 /* send whatever portion of buffer is left */
1097 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001098 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001099
1100 return rs;
1101}
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001102
Sean Middleditch7491bf42009-03-20 23:52:18 -04001103/* send formatted data through telnet_send */
Sean Middleditch9dcfd4b2009-10-14 16:17:46 -07001104int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001105 char buffer[4096];
1106 va_list va;
1107 int rs;
1108
1109 /* format */
1110 va_start(va, fmt);
1111 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1112 va_end(va);
1113
1114 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001115 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001116
1117 return rs;
1118}
Sean Middleditche22b4772009-03-22 16:44:40 -04001119
Sean Middleditcheb950a82009-03-22 23:04:28 -04001120/* send formatted subnegotiation data for TTYPE/ENVIRON/NEW-ENVIRON/MSSP */
1121void telnet_format_sb(telnet_t *telnet, unsigned char telopt,
1122 size_t count, ...) {
1123 va_list va;
1124 size_t i;
1125
1126 /* subnegotiation header */
1127 telnet_begin_sb(telnet, telopt);
1128
1129 /* iterate over the arguments pulling out integers and strings */
1130 va_start(va, count);
1131 for (i = 0; i != count; ++i) {
1132 char t;
1133 const char* s;
1134 t = va_arg(va, int);
1135 s = va_arg(va, const char *);
1136 telnet_send(telnet, &t, 1);
1137 telnet_send(telnet, s, strlen(s));
1138 }
1139 va_end(va);
1140
1141 /* footer */
1142 telnet_finish_sb(telnet);
1143}
1144
Sean Middleditche22b4772009-03-22 16:44:40 -04001145/* send ZMP data */
1146void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) {
1147 size_t i;
1148
1149 /* ZMP header */
1150 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1151
1152 /* send out each argument, including trailing NUL byte */
1153 for (i = 0; i != argc; ++i)
1154 telnet_send(telnet, argv[i], strlen(argv[i] + 1));
1155
1156 /* ZMP footer */
1157 telnet_finish_sb(telnet);
1158}
1159
1160/* send ZMP data using varargs */
1161void telnet_send_zmpv(telnet_t *telnet, ...) {
1162 va_list va;
1163 const char* arg;
1164
1165 /* ZMP header */
1166 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1167
1168 /* send out each argument, including trailing NUL byte */
1169 va_start(va, telnet);
1170 while ((arg = va_arg(va, const char *)) != NULL)
1171 telnet_send(telnet, arg, strlen(arg) + 1);
1172 va_end(va);
1173
1174 /* ZMP footer */
1175 telnet_finish_sb(telnet);
1176}