blob: aee0bc543698a330fd41c540f893257980ff9c36 [file] [log] [blame]
Sean Middleditch6aef0732009-03-12 23:27:35 -04001/*
Sean Middleditch9de15982009-03-14 03:35:49 -04002 * Sean Middleditch
3 * sean@sourcemud.org
4 *
Sean Middleditch6aef0732009-03-12 23:27:35 -04005 * The author or authors of this code dedicate any and all copyright interest
6 * in this code to the public domain. We make this dedication for the benefit
7 * of the public at large and to the detriment of our heirs and successors. We
8 * intend this dedication to be an overt act of relinquishment in perpetuity of
9 * all present and future rights to this code under copyright law.
10 */
11
Sean Middleditch4d9444d2009-03-13 22:48:05 -040012#include <malloc.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040013#include <string.h>
Sean Middleditchd922c6f2009-03-14 22:35:01 -040014#include <stdio.h>
15#include <errno.h>
16#include <string.h>
17#include <stdarg.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040018
19#ifdef HAVE_ZLIB
20#include "zlib.h"
21#endif
22
Sean Middleditch29144852009-03-12 23:14:47 -040023#include "libtelnet.h"
24
Sean Middleditch90e79da2009-03-19 15:17:13 -040025/* RFC1143 option negotiation state */
26typedef struct telnet_rfc1143_t {
27 unsigned char telopt;
28 char us:4, him:4;
29} telnet_rfc1143_t;
30
Sean Middleditch5b5bc922009-03-15 23:02:10 -040031/* RFC1143 state names */
32#define RFC1143_NO 0x00
33#define RFC1143_YES 0x01
34
35#define RFC1143_WANT 0x02
36#define RFC1143_OP 0x04
37
38#define RFC1143_WANTNO (RFC1143_WANT|RFC1143_YES)
39#define RFC1143_WANTYES (RFC1143_WANT|RFC1143_NO)
40#define RFC1143_WANTNO_OP (RFC1143_WANTNO|RFC1143_OP)
41#define RFC1143_WANTYES_OP (RFC1143_WANTYES|RFC1143_OP)
42
Sean Middleditch4d9444d2009-03-13 22:48:05 -040043/* buffer sizes */
Sean Middleditch340a51b2009-03-19 02:08:46 -040044static const size_t _buffer_sizes[] = {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040045 0,
46 512,
47 2048,
48 8192,
49 16384,
50};
Sean Middleditch340a51b2009-03-19 02:08:46 -040051static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040052 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040053
Sean Middleditch5b5bc922009-03-15 23:02:10 -040054/* event dispatch helper; return value is value of the accept field of the
55 * event struct after dispatch; used for the funky REQUEST event */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040056static int _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040057 unsigned char command, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -040058 const char *buffer, size_t size) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040059 telnet_event_t ev;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040060 ev.buffer = buffer;
61 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040062 ev.type = type;
63 ev.command = command;
64 ev.telopt = telopt;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040065 ev.accept = 0;
Sean Middleditch637df7f2009-03-15 12:57:32 -040066
Sean Middleditch9f79cc52009-03-15 13:39:24 -040067 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch5b5bc922009-03-15 23:02:10 -040068
69 return ev.accept;
Sean Middleditch637df7f2009-03-15 12:57:32 -040070}
71
Sean Middleditchd922c6f2009-03-14 22:35:01 -040072/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040073static telnet_error_t _error(telnet_t *telnet, unsigned line,
74 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040075 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040076 char buffer[512];
77 va_list va;
78
79 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040080 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040081
82 va_start(va, fmt);
83 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
84 fmt, va);
85 va_end(va);
86
Sean Middleditchf65f27d2009-03-19 02:32:04 -040087 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditch8daf7742009-03-19 02:05:24 -040088 0, (char *)buffer, strlen(buffer));
Sean Middleditchfbe93e32009-03-15 23:39:31 -040089
90 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040091}
92
Sean Middleditche5b47592009-03-16 17:37:43 -040093#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -040094/* initialize the zlib box for a telnet box; if deflate is non-zero, it
95 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -040096 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -040097 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -040098 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040099telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400100 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400101 int rs;
102
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400103 /* if compression is already enabled, fail loudly */
104 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400105 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400106 err_fatal, "cannot initialize compression twice");
107
Sean Middleditch72cc9642009-03-15 11:50:36 -0400108 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400109 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400110 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400111 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400112
113 /* initialize */
114 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400115 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
116 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400117 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400118 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400119 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400120 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400121 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400122 if ((rs = inflateInit(z)) != Z_OK) {
123 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400124 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400125 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400126 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400127 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400128 }
129
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400130 telnet->z = z;
131
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400132 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400133}
Sean Middleditche5b47592009-03-16 17:37:43 -0400134#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400135
Sean Middleditch8b788962009-03-16 01:06:27 -0400136/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400137static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400138 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400139#ifdef HAVE_ZLIB
140 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400141 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400142 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400143 int rs;
144
145 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400146 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400147 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400148 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400149 telnet->z->avail_out = sizeof(deflate_buffer);
150
151 /* deflate until buffer exhausted and all output is produced */
152 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
153 /* compress */
154 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400155 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400156 "deflate() failed: %s", zError(rs));
157 deflateEnd(telnet->z);
158 free(telnet->z);
159 telnet->z = 0;
160 break;
161 }
162
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400163 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditch8b788962009-03-16 01:06:27 -0400164 sizeof(deflate_buffer) - telnet->z->avail_out);
165
166 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400167 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400168 telnet->z->avail_out = sizeof(deflate_buffer);
169 }
170
171 /* COMPRESS2 is not negotiated, just send */
172 } else
173#endif /* HAVE_ZLIB */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400174 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size);
Sean Middleditch8b788962009-03-16 01:06:27 -0400175}
176
177/* retrieve RFC1143 option state */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400178telnet_rfc1143_t _get_rfc1143(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400179 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400180 int i;
181
182 /* search for entry */
183 for (i = 0; i != telnet->q_size; ++i)
184 if (telnet->q[i].telopt == telopt)
185 return telnet->q[i];
186
187 /* not found, return empty value */
188 return empty;
189}
190
191/* save RFC1143 option state */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400192void _set_rfc1143(telnet_t *telnet, telnet_rfc1143_t q) {
193 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400194 int i;
195
196 /* search for entry */
197 for (i = 0; i != telnet->q_size; ++i) {
198 if (telnet->q[i].telopt == q.telopt) {
199 telnet->q[i] = q;
200 return;
201 }
202 }
203
204 /* we're going to need to track state for it, so grow the queue
205 * and put the telopt into it; bail on allocation error
206 */
Sean Middleditch5dea1f72009-03-19 12:45:34 -0400207 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400208 telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
209 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400210 "malloc() failed: %s", strerror(errno));
211 return;
212 }
213 telnet->q = qtmp;
214 telnet->q[telnet->q_size++] = q;
215}
216
Sean Middleditch90e79da2009-03-19 15:17:13 -0400217/* send negotiation bytes */
218static void _send_negotiate(telnet_t *telnet, unsigned char cmd,
219 unsigned char telopt) {
220 char bytes[3] = { TELNET_IAC, cmd, telopt };
221 _send(telnet, bytes, 3);
222}
223
Sean Middleditch8b788962009-03-16 01:06:27 -0400224/* negotiation handling magic for RFC1143 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400225static void _negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400226 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400227 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400228
229 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400230 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400231 switch (cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400232 case TELNET_WILL:
233 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400234 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400235 case TELNET_WONT:
236 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400237 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400238 case TELNET_DO:
239 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400240 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400241 case TELNET_DONT:
242 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400243 break;
244 }
245 return;
246 }
247
248 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400249 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400250
251 /* start processing... */
252 switch (cmd) {
253 /* request to enable option on remote end or confirm DO */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400254 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400255 switch (q.him) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400256 case RFC1143_NO:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400257 if (_event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0) == 1) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400258 q.him = RFC1143_YES;
259 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400260 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400261 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400262 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400263 break;
264 case RFC1143_YES:
265 break;
266 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400267 q.him = RFC1143_NO;
268 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400269 _event(telnet, TELNET_EV_WONT, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400270 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400271 "DONT answered by WILL");
272 break;
273 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400274 q.him = RFC1143_YES;
275 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400276 _event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400277 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400278 "DONT answered by WILL");
279 break;
280 case RFC1143_WANTYES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400281 q.him = RFC1143_YES;
282 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400283 _event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400284 break;
285 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400286 q.him = RFC1143_WANTNO;
287 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400288 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400289 _event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400290 break;
291 }
292 break;
293
294 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400295 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400296 switch (q.him) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400297 case RFC1143_NO:
298 break;
299 case RFC1143_YES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400300 q.him = RFC1143_NO;
301 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400302 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400303 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400304 break;
305 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400306 q.him = RFC1143_NO;
307 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400308 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400309 break;
310 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400311 q.him = RFC1143_WANTYES;
312 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400313 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400314 break;
315 case RFC1143_WANTYES:
316 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400317 q.him = RFC1143_NO;
318 _set_rfc1143(telnet, q);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400319 break;
320 }
321 break;
322
323 /* request to enable option on local end or confirm WILL */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400324 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400325 switch (q.us) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400326 case RFC1143_NO:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400327 if (_event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0) == 1) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400328 q.us = RFC1143_YES;
329 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400330 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400331 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400332 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400333 break;
334 case RFC1143_YES:
335 break;
336 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400337 q.us = RFC1143_NO;
338 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400339 _event(telnet, TELNET_EV_DONT, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400340 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400341 "WONT answered by DO");
342 break;
343 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400344 q.us = RFC1143_YES;
345 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400346 _event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400347 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400348 "WONT answered by DO");
349 break;
350 case RFC1143_WANTYES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400351 q.us = RFC1143_YES;
352 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400353 _event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400354 break;
355 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400356 q.us = RFC1143_WANTNO;
357 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400358 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400359 _event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400360 break;
361 }
362 break;
363
364 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400365 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400366 switch (q.us) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400367 case RFC1143_NO:
368 break;
369 case RFC1143_YES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400370 q.us = RFC1143_NO;
371 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400372 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400373 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400374 break;
375 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400376 q.us = RFC1143_NO;
377 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400378 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400379 break;
380 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400381 q.us = RFC1143_WANTYES;
382 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400383 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400384 break;
385 case RFC1143_WANTYES:
386 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400387 q.us = RFC1143_NO;
388 _set_rfc1143(telnet, q);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400389 break;
390 }
391 break;
392 }
393}
394
Sean Middleditch29144852009-03-12 23:14:47 -0400395/* initialize a telnet state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400396void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400397 unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400398 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400399 telnet->ud = user_data;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400400 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400401 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400402}
403
404/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400405void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400406 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400407 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400408 free(telnet->buffer);
409 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400410 telnet->buffer_size = 0;
411 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400412 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400413
Sean Middleditche5b47592009-03-16 17:37:43 -0400414#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400415 /* free zlib box */
416 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400417 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400418 deflateEnd(telnet->z);
419 else
420 inflateEnd(telnet->z);
421 free(telnet->z);
422 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400423 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400424#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400425
426 /* free RFC1143 queue */
427 if (telnet->q) {
428 free(telnet->q);
429 telnet->q = 0;
430 telnet->q_size = 0;
431 }
Sean Middleditch29144852009-03-12 23:14:47 -0400432}
433
Sean Middleditch51ad6792009-03-13 20:15:59 -0400434/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400435static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400436 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400437 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400438 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400439
Sean Middleditch51ad6792009-03-13 20:15:59 -0400440 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400441 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400442 /* find the next buffer size */
443 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400444 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400445 break;
446 }
447
448 /* overflow -- can't grow any more */
449 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400450 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400451 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400452 telnet_free(telnet);
453 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400454 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400455
456 /* (re)allocate buffer */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400457 new_buffer = (char *)realloc(telnet->buffer,
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400458 _buffer_sizes[i + 1]);
459 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400460 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400461 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400462 telnet_free(telnet);
463 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400464 }
465
466 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400467 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400468 }
469
470 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400471 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400472 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400473}
474
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400475static void _process(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400476 size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400477 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400478 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400479 for (i = start = 0; i != size; ++i) {
480 byte = buffer[i];
481 switch (telnet->state) {
482 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400483 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400484 /* on an IAC byte, pass through all pending bytes and
485 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400486 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400487 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400488 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400489 i - start);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400490 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400491 }
492 break;
493
494 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400495 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400496 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400497 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400498 case TELNET_SB:
499 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400500 break;
501 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400502 case TELNET_WILL:
503 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400504 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400505 case TELNET_WONT:
506 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400507 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400508 case TELNET_DO:
509 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400510 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400511 case TELNET_DONT:
512 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400513 break;
514 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400515 case TELNET_IAC:
516 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400517 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400518 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400519 break;
520 /* some other command */
521 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400522 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400523 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400524 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400525 }
526 break;
527
528 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400529 case TELNET_STATE_DO:
530 _negotiate(telnet, TELNET_DO, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400531 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400532 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400533 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400534 case TELNET_STATE_DONT:
535 _negotiate(telnet, TELNET_DONT, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400536 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400537 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400538 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400539 case TELNET_STATE_WILL:
540 _negotiate(telnet, TELNET_WILL, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400541 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400542 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400543 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400544 case TELNET_STATE_WONT:
545 _negotiate(telnet, TELNET_WONT, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400546 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400547 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400548 break;
549
Sean Middleditchda0e6952009-03-15 13:28:09 -0400550 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400551 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400552 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400553 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400554 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400555 break;
556
557 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400558 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400559 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400560 if (byte == TELNET_IAC) {
561 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400562 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400563 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400564 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400565 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400566 }
567 break;
568
Sean Middleditch6b372882009-03-14 13:06:47 -0400569 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400570 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400571 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400572 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400573 case TELNET_SE:
Sean Middleditch9de15982009-03-14 03:35:49 -0400574 /* return to default state */
575 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400576 telnet->state = TELNET_STATE_DATA;
Sean Middleditch9de15982009-03-14 03:35:49 -0400577
Sean Middleditch9de15982009-03-14 03:35:49 -0400578 /* invoke callback */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400579 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400580 telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
Sean Middleditch9de15982009-03-14 03:35:49 -0400581
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400582#ifdef HAVE_ZLIB
Sean Middleditch3df17f92009-03-16 01:12:16 -0400583 /* received COMPRESS2 begin marker, setup our zlib box and
584 * start handling the compressed stream if it's not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400585 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400586 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
587 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch9de15982009-03-14 03:35:49 -0400588 break;
Sean Middleditch9de15982009-03-14 03:35:49 -0400589
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400590 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400591 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400592
Sean Middleditch9de15982009-03-14 03:35:49 -0400593 /* any remaining bytes in the buffer are compressed.
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400594 * we have to re-invoke telnet_push to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400595 * bytes inflated and abort trying to process the
596 * remaining compressed bytes in the current _process
597 * buffer argument
598 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400599 telnet_push(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400600 return;
601 }
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400602#endif /* HAVE_ZLIB */
603
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400604 break;
605 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400606 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400607 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400608 if (_buffer_byte(telnet, TELNET_IAC) !=
609 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400610 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400611 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400612 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400613 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400614 }
615 break;
616 /* something else -- protocol error */
617 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400618 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400619 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400620 byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400621 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400622 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400623 break;
624 }
625 break;
626 }
627 }
628
629 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400630 if (telnet->state == TELNET_STATE_DATA && i != start)
631 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start);
Sean Middleditch29144852009-03-12 23:14:47 -0400632}
633
Sean Middleditch9de15982009-03-14 03:35:49 -0400634/* push a bytes into the state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400635void telnet_push(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400636 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400637#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400638 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400639 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400640 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400641 int rs;
642
643 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400644 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400645 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400646 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400647 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400648
649 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400650 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400651 /* reset output buffer */
652
653 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400654 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400655
656 /* process the decompressed bytes on success */
657 if (rs == Z_OK || rs == Z_STREAM_END)
658 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400659 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400660 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400661 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400662 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400663
664 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400665 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400666 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400667
668 /* on error (or on end of stream) disable further inflation */
669 if (rs != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400670 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400671
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400672 inflateEnd(telnet->z);
673 free(telnet->z);
674 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400675 break;
676 }
677 }
678
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400679 /* COMPRESS2 is not negotiated, just process */
680 } else
681#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400682 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400683}
684
Sean Middleditch29144852009-03-12 23:14:47 -0400685/* send an iac command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400686void telnet_send_command(telnet_t *telnet, unsigned char cmd) {
687 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400688 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400689}
690
691/* send negotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400692void telnet_send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400693 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400694 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400695
696 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400697 if (telnet->flags & TELNET_FLAG_PROXY) {
698 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400699 _send(telnet, bytes, 3);
700 return;
701 }
702
703 /* get current option states */
704 q = _get_rfc1143(telnet, telopt);
705
706 switch (cmd) {
707 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400708 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400709 switch (q.us) {
710 case RFC1143_NO:
711 q.us = RFC1143_WANTYES;
712 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400713 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400714 break;
715 case RFC1143_YES:
716 break;
717 case RFC1143_WANTNO:
718 q.us = RFC1143_WANTNO_OP;
719 _set_rfc1143(telnet, q);
720 break;
721 case RFC1143_WANTYES:
722 break;
723 case RFC1143_WANTNO_OP:
724 break;
725 case RFC1143_WANTYES_OP:
726 q.us = RFC1143_WANTYES;
727 _set_rfc1143(telnet, q);
728 break;
729 }
730 break;
731
732 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400733 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400734 switch (q.us) {
735 case RFC1143_NO:
736 break;
737 case RFC1143_YES:
738 q.us = RFC1143_WANTNO;
739 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400740 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400741 break;
742 case RFC1143_WANTNO:
743 break;
744 case RFC1143_WANTYES:
745 q.us = RFC1143_WANTYES_OP;
746 _set_rfc1143(telnet, q);
747 break;
748 case RFC1143_WANTNO_OP:
749 q.us = RFC1143_WANTNO;
750 _set_rfc1143(telnet, q);
751 break;
752 case RFC1143_WANTYES_OP:
753 break;
754 }
755 break;
756
757 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400758 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400759 switch (q.him) {
760 case RFC1143_NO:
761 q.him = RFC1143_WANTYES;
762 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400763 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400764 break;
765 case RFC1143_YES:
766 break;
767 case RFC1143_WANTNO:
768 q.him = RFC1143_WANTNO_OP;
769 _set_rfc1143(telnet, q);
770 break;
771 case RFC1143_WANTYES:
772 break;
773 case RFC1143_WANTNO_OP:
774 break;
775 case RFC1143_WANTYES_OP:
776 q.him = RFC1143_WANTYES;
777 _set_rfc1143(telnet, q);
778 break;
779 }
780 break;
781
782 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400783 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400784 switch (q.him) {
785 case RFC1143_NO:
786 break;
787 case RFC1143_YES:
788 q.him = RFC1143_WANTNO;
789 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400790 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400791 break;
792 case RFC1143_WANTNO:
793 break;
794 case RFC1143_WANTYES:
795 q.him = RFC1143_WANTYES_OP;
796 _set_rfc1143(telnet, q);
797 break;
798 case RFC1143_WANTNO_OP:
799 q.him = RFC1143_WANTNO;
800 _set_rfc1143(telnet, q);
801 break;
802 case RFC1143_WANTYES_OP:
803 break;
804 }
805 break;
806 }
Sean Middleditch29144852009-03-12 23:14:47 -0400807}
808
809/* send non-command data (escapes IAC bytes) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400810void telnet_send_data(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400811 size_t size) {
812 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400813
Sean Middleditch29144852009-03-12 23:14:47 -0400814 for (l = i = 0; i != size; ++i) {
815 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400816 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400817 /* dump prior text if any */
818 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400819 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400820 l = i + 1;
821
822 /* send escape */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400823 telnet_send_command(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400824 }
825 }
826
827 /* send whatever portion of buffer is left */
828 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400829 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400830}
831
832/* send sub-request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400833void telnet_send_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400834 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400835 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
836 static const char se[2] = { TELNET_IAC, TELNET_SE };
837
838 _send(telnet, sb, 3);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400839 telnet_send_data(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400840 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400841
842#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400843 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
844 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400845 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400846 if (telnet->flags & TELNET_FLAG_PROXY &&
847 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400848
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400849 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400850 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400851
852 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400853 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400854 }
855#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400856}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400857
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400858void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400859#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400860 static const char compress2[] = { TELNET_IAC, TELNET_SB,
861 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400862
Sean Middleditch124a1c22009-03-15 13:20:03 -0400863 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400864 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400865 return;
866
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400867 /* send compression marker. we send directly to the event handler
868 * instead of passing through _send because _send would result in
869 * the compress marker itself being compressed.
870 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400871 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400872
873 /* notify app that compression was successfully enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400874 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -0400875#endif /* HAVE_ZLIB */
876}
Sean Middleditchd58f49f2009-03-16 12:49:35 -0400877
Sean Middleditch4a156042009-03-16 17:10:58 -0400878/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400879int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400880 static const char CRLF[] = { '\r', '\n' };
881 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -0400882 char buffer[4096];
883 va_list va;
884 int rs, i, l;
885
886 /* format */
887 va_start(va, fmt);
888 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
889 va_end(va);
890
891 /* send */
892 for (l = i = 0; i != rs; ++i) {
893 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400894 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -0400895 buffer[i] == '\n') {
896 /* dump prior portion of text */
897 if (i != l)
Sean Middleditch8daf7742009-03-19 02:05:24 -0400898 _send(telnet, (char *)buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400899 l = i + 1;
900
901 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400902 if (buffer[i] == TELNET_IAC)
903 telnet_send_command(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -0400904 /* automatic translation of \r -> CRNUL */
905 else if (buffer[i] == '\r')
906 _send(telnet, CRNUL, 2);
907 /* automatic translation of \n -> CRLF */
908 else if (buffer[i] == '\n')
909 _send(telnet, CRLF, 2);
910 }
911 }
912
913 /* send whatever portion of buffer is left */
914 if (i != l)
Sean Middleditch8daf7742009-03-19 02:05:24 -0400915 _send(telnet, (char *)buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400916
917 return rs;
918}
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400919
920/* send formatted data through telnet_send_data */
921int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
922 char buffer[4096];
923 va_list va;
924 int rs;
925
926 /* format */
927 va_start(va, fmt);
928 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
929 va_end(va);
930
931 /* send */
932 telnet_send_data(telnet, (char *)buffer, rs);
933
934 return rs;
935}