blob: 212bf0ff717c3040c60b1862f8331a00d8bd31bd [file] [log] [blame]
Sean Middleditch6aef0732009-03-12 23:27:35 -04001/*
Sean Middleditch9de15982009-03-14 03:35:49 -04002 * Sean Middleditch
3 * sean@sourcemud.org
4 *
Sean Middleditch6aef0732009-03-12 23:27:35 -04005 * The author or authors of this code dedicate any and all copyright interest
6 * in this code to the public domain. We make this dedication for the benefit
7 * of the public at large and to the detriment of our heirs and successors. We
8 * intend this dedication to be an overt act of relinquishment in perpetuity of
9 * all present and future rights to this code under copyright law.
10 */
11
Sean Middleditch4d9444d2009-03-13 22:48:05 -040012#include <malloc.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040013#include <string.h>
Sean Middleditchd922c6f2009-03-14 22:35:01 -040014#include <stdio.h>
15#include <errno.h>
16#include <string.h>
17#include <stdarg.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040018
19#ifdef HAVE_ZLIB
20#include "zlib.h"
21#endif
22
Sean Middleditch29144852009-03-12 23:14:47 -040023#include "libtelnet.h"
24
Sean Middleditch5b5bc922009-03-15 23:02:10 -040025/* RFC1143 state names */
26#define RFC1143_NO 0x00
27#define RFC1143_YES 0x01
28
29#define RFC1143_WANT 0x02
30#define RFC1143_OP 0x04
31
32#define RFC1143_WANTNO (RFC1143_WANT|RFC1143_YES)
33#define RFC1143_WANTYES (RFC1143_WANT|RFC1143_NO)
34#define RFC1143_WANTNO_OP (RFC1143_WANTNO|RFC1143_OP)
35#define RFC1143_WANTYES_OP (RFC1143_WANTYES|RFC1143_OP)
36
Sean Middleditch4d9444d2009-03-13 22:48:05 -040037/* buffer sizes */
Sean Middleditch340a51b2009-03-19 02:08:46 -040038static const size_t _buffer_sizes[] = {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040039 0,
40 512,
41 2048,
42 8192,
43 16384,
44};
Sean Middleditch340a51b2009-03-19 02:08:46 -040045static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040046 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040047
Sean Middleditch5b5bc922009-03-15 23:02:10 -040048/* event dispatch helper; return value is value of the accept field of the
49 * event struct after dispatch; used for the funky REQUEST event */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040050static int _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040051 unsigned char command, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -040052 const char *buffer, size_t size) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040053 telnet_event_t ev;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040054 ev.buffer = buffer;
55 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040056 ev.type = type;
57 ev.command = command;
58 ev.telopt = telopt;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040059 ev.accept = 0;
Sean Middleditch637df7f2009-03-15 12:57:32 -040060
Sean Middleditch9f79cc52009-03-15 13:39:24 -040061 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch5b5bc922009-03-15 23:02:10 -040062
63 return ev.accept;
Sean Middleditch637df7f2009-03-15 12:57:32 -040064}
65
Sean Middleditchd922c6f2009-03-14 22:35:01 -040066/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040067static telnet_error_t _error(telnet_t *telnet, unsigned line,
68 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040069 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040070 char buffer[512];
71 va_list va;
72
73 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040074 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040075
76 va_start(va, fmt);
77 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
78 fmt, va);
79 va_end(va);
80
Sean Middleditchf65f27d2009-03-19 02:32:04 -040081 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditch8daf7742009-03-19 02:05:24 -040082 0, (char *)buffer, strlen(buffer));
Sean Middleditchfbe93e32009-03-15 23:39:31 -040083
84 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040085}
86
Sean Middleditche5b47592009-03-16 17:37:43 -040087#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -040088/* initialize the zlib box for a telnet box; if deflate is non-zero, it
89 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -040090 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -040091 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -040092 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040093telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -040094 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -040095 int rs;
96
Sean Middleditchfbe93e32009-03-15 23:39:31 -040097 /* if compression is already enabled, fail loudly */
98 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -040099 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400100 err_fatal, "cannot initialize compression twice");
101
Sean Middleditch72cc9642009-03-15 11:50:36 -0400102 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400103 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400104 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400105 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400106
107 /* initialize */
108 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400109 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
110 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400111 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400112 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400113 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400114 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400115 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400116 if ((rs = inflateInit(z)) != Z_OK) {
117 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400118 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400119 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400120 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400121 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400122 }
123
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400124 telnet->z = z;
125
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400126 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400127}
Sean Middleditche5b47592009-03-16 17:37:43 -0400128#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400129
Sean Middleditch8b788962009-03-16 01:06:27 -0400130/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400131static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400132 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400133#ifdef HAVE_ZLIB
134 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400135 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400136 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400137 int rs;
138
139 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400140 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400141 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400142 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400143 telnet->z->avail_out = sizeof(deflate_buffer);
144
145 /* deflate until buffer exhausted and all output is produced */
146 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
147 /* compress */
148 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400149 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400150 "deflate() failed: %s", zError(rs));
151 deflateEnd(telnet->z);
152 free(telnet->z);
153 telnet->z = 0;
154 break;
155 }
156
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400157 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditch8b788962009-03-16 01:06:27 -0400158 sizeof(deflate_buffer) - telnet->z->avail_out);
159
160 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400161 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400162 telnet->z->avail_out = sizeof(deflate_buffer);
163 }
164
165 /* COMPRESS2 is not negotiated, just send */
166 } else
167#endif /* HAVE_ZLIB */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400168 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size);
Sean Middleditch8b788962009-03-16 01:06:27 -0400169}
170
171/* retrieve RFC1143 option state */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400172telnet_rfc1143_t _get_rfc1143(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400173 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400174 int i;
175
176 /* search for entry */
177 for (i = 0; i != telnet->q_size; ++i)
178 if (telnet->q[i].telopt == telopt)
179 return telnet->q[i];
180
181 /* not found, return empty value */
182 return empty;
183}
184
185/* save RFC1143 option state */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400186void _set_rfc1143(telnet_t *telnet, telnet_rfc1143_t q) {
187 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400188 int i;
189
190 /* search for entry */
191 for (i = 0; i != telnet->q_size; ++i) {
192 if (telnet->q[i].telopt == q.telopt) {
193 telnet->q[i] = q;
194 return;
195 }
196 }
197
198 /* we're going to need to track state for it, so grow the queue
199 * and put the telopt into it; bail on allocation error
200 */
Sean Middleditch5dea1f72009-03-19 12:45:34 -0400201 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400202 telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
203 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400204 "malloc() failed: %s", strerror(errno));
205 return;
206 }
207 telnet->q = qtmp;
208 telnet->q[telnet->q_size++] = q;
209}
210
Sean Middleditch8b788962009-03-16 01:06:27 -0400211/* negotiation handling magic for RFC1143 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400212static void _negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400213 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400214 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400215
216 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400217 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400218 switch (cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400219 case TELNET_WILL:
220 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400221 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400222 case TELNET_WONT:
223 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400224 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400225 case TELNET_DO:
226 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400227 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400228 case TELNET_DONT:
229 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400230 break;
231 }
232 return;
233 }
234
235 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400236 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400237
238 /* start processing... */
239 switch (cmd) {
240 /* request to enable option on remote end or confirm DO */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400241 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400242 switch (q.him) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400243 case RFC1143_NO:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400244 if (_event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0) == 1) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400245 q.him = RFC1143_YES;
246 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400247 telnet_send_telopt(telnet, TELNET_DO, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400248 } else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400249 telnet_send_telopt(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400250 break;
251 case RFC1143_YES:
252 break;
253 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400254 q.him = RFC1143_NO;
255 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400256 _event(telnet, TELNET_EV_WONT, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400257 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400258 "DONT answered by WILL");
259 break;
260 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400261 q.him = RFC1143_YES;
262 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400263 _event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400264 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400265 "DONT answered by WILL");
266 break;
267 case RFC1143_WANTYES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400268 q.him = RFC1143_YES;
269 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400270 _event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400271 break;
272 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400273 q.him = RFC1143_WANTNO;
274 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400275 telnet_send_telopt(telnet, TELNET_DONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400276 _event(telnet, TELNET_EV_WILL, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400277 break;
278 }
279 break;
280
281 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400282 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400283 switch (q.him) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400284 case RFC1143_NO:
285 break;
286 case RFC1143_YES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400287 q.him = RFC1143_NO;
288 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400289 telnet_send_telopt(telnet, TELNET_DONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400290 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400291 break;
292 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400293 q.him = RFC1143_NO;
294 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400295 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400296 break;
297 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400298 q.him = RFC1143_WANTYES;
299 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400300 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400301 break;
302 case RFC1143_WANTYES:
303 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400304 q.him = RFC1143_NO;
305 _set_rfc1143(telnet, q);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400306 break;
307 }
308 break;
309
310 /* request to enable option on local end or confirm WILL */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400311 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400312 switch (q.us) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400313 case RFC1143_NO:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400314 if (_event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0) == 1) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400315 q.us = RFC1143_YES;
316 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400317 telnet_send_telopt(telnet, TELNET_WILL, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400318 } else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400319 telnet_send_telopt(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400320 break;
321 case RFC1143_YES:
322 break;
323 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400324 q.us = RFC1143_NO;
325 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400326 _event(telnet, TELNET_EV_DONT, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400327 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400328 "WONT answered by DO");
329 break;
330 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400331 q.us = RFC1143_YES;
332 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400333 _event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400334 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400335 "WONT answered by DO");
336 break;
337 case RFC1143_WANTYES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400338 q.us = RFC1143_YES;
339 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400340 _event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400341 break;
342 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400343 q.us = RFC1143_WANTNO;
344 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400345 telnet_send_telopt(telnet, TELNET_WONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400346 _event(telnet, TELNET_EV_DO, cmd, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400347 break;
348 }
349 break;
350
351 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400352 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400353 switch (q.us) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400354 case RFC1143_NO:
355 break;
356 case RFC1143_YES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400357 q.us = RFC1143_NO;
358 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400359 telnet_send_telopt(telnet, TELNET_WONT, telopt);
360 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400361 break;
362 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400363 q.us = RFC1143_NO;
364 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400365 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400366 break;
367 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400368 q.us = RFC1143_WANTYES;
369 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400370 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400371 break;
372 case RFC1143_WANTYES:
373 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400374 q.us = RFC1143_NO;
375 _set_rfc1143(telnet, q);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400376 break;
377 }
378 break;
379 }
380}
381
Sean Middleditch29144852009-03-12 23:14:47 -0400382/* initialize a telnet state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400383void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400384 unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400385 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400386 telnet->ud = user_data;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400387 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400388 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400389}
390
391/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400392void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400393 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400394 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400395 free(telnet->buffer);
396 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400397 telnet->buffer_size = 0;
398 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400399 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400400
Sean Middleditche5b47592009-03-16 17:37:43 -0400401#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400402 /* free zlib box */
403 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400404 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400405 deflateEnd(telnet->z);
406 else
407 inflateEnd(telnet->z);
408 free(telnet->z);
409 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400410 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400411#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400412
413 /* free RFC1143 queue */
414 if (telnet->q) {
415 free(telnet->q);
416 telnet->q = 0;
417 telnet->q_size = 0;
418 }
Sean Middleditch29144852009-03-12 23:14:47 -0400419}
420
Sean Middleditch51ad6792009-03-13 20:15:59 -0400421/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400422static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400423 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400424 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400425 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400426
Sean Middleditch51ad6792009-03-13 20:15:59 -0400427 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400428 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400429 /* find the next buffer size */
430 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400431 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400432 break;
433 }
434
435 /* overflow -- can't grow any more */
436 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400437 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400438 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400439 telnet_free(telnet);
440 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400441 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400442
443 /* (re)allocate buffer */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400444 new_buffer = (char *)realloc(telnet->buffer,
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400445 _buffer_sizes[i + 1]);
446 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400447 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400448 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400449 telnet_free(telnet);
450 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400451 }
452
453 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400454 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400455 }
456
457 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400458 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400459 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400460}
461
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400462static void _process(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400463 size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400464 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400465 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400466 for (i = start = 0; i != size; ++i) {
467 byte = buffer[i];
468 switch (telnet->state) {
469 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400470 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400471 /* on an IAC byte, pass through all pending bytes and
472 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400473 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400474 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400475 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400476 i - start);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400477 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400478 }
479 break;
480
481 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400482 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400483 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400484 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400485 case TELNET_SB:
486 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400487 break;
488 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400489 case TELNET_WILL:
490 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400491 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400492 case TELNET_WONT:
493 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400494 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400495 case TELNET_DO:
496 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400497 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400498 case TELNET_DONT:
499 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400500 break;
501 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400502 case TELNET_IAC:
503 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400504 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400505 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400506 break;
507 /* some other command */
508 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400509 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400510 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400511 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400512 }
513 break;
514
515 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400516 case TELNET_STATE_DO:
517 _negotiate(telnet, TELNET_DO, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400518 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400519 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400520 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400521 case TELNET_STATE_DONT:
522 _negotiate(telnet, TELNET_DONT, byte);
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 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400526 case TELNET_STATE_WILL:
527 _negotiate(telnet, TELNET_WILL, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400528 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400529 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400530 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400531 case TELNET_STATE_WONT:
532 _negotiate(telnet, TELNET_WONT, byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400533 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400534 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400535 break;
536
Sean Middleditchda0e6952009-03-15 13:28:09 -0400537 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400538 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400539 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400540 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400541 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400542 break;
543
544 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400545 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400546 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400547 if (byte == TELNET_IAC) {
548 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400549 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400550 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400551 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400552 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400553 }
554 break;
555
Sean Middleditch6b372882009-03-14 13:06:47 -0400556 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400557 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400558 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400559 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400560 case TELNET_SE:
Sean Middleditch9de15982009-03-14 03:35:49 -0400561 /* return to default state */
562 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400563 telnet->state = TELNET_STATE_DATA;
Sean Middleditch9de15982009-03-14 03:35:49 -0400564
Sean Middleditch9de15982009-03-14 03:35:49 -0400565 /* invoke callback */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400566 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400567 telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
Sean Middleditch9de15982009-03-14 03:35:49 -0400568
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400569#ifdef HAVE_ZLIB
Sean Middleditch3df17f92009-03-16 01:12:16 -0400570 /* received COMPRESS2 begin marker, setup our zlib box and
571 * start handling the compressed stream if it's not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400572 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400573 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
574 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch9de15982009-03-14 03:35:49 -0400575 break;
Sean Middleditch9de15982009-03-14 03:35:49 -0400576
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400577 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400578 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400579
Sean Middleditch9de15982009-03-14 03:35:49 -0400580 /* any remaining bytes in the buffer are compressed.
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400581 * we have to re-invoke telnet_push to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400582 * bytes inflated and abort trying to process the
583 * remaining compressed bytes in the current _process
584 * buffer argument
585 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400586 telnet_push(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400587 return;
588 }
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400589#endif /* HAVE_ZLIB */
590
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400591 break;
592 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400593 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400594 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400595 if (_buffer_byte(telnet, TELNET_IAC) !=
596 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400597 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400598 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400599 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400600 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400601 }
602 break;
603 /* something else -- protocol error */
604 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400605 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400606 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400607 byte);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400608 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400609 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400610 break;
611 }
612 break;
613 }
614 }
615
616 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400617 if (telnet->state == TELNET_STATE_DATA && i != start)
618 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start);
Sean Middleditch29144852009-03-12 23:14:47 -0400619}
620
Sean Middleditch9de15982009-03-14 03:35:49 -0400621/* push a bytes into the state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400622void telnet_push(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400623 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400624#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400625 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400626 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400627 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400628 int rs;
629
630 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400631 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400632 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400633 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400634 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400635
636 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400637 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400638 /* reset output buffer */
639
640 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400641 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400642
643 /* process the decompressed bytes on success */
644 if (rs == Z_OK || rs == Z_STREAM_END)
645 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400646 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400647 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400648 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400649 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400650
651 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400652 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400653 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400654
655 /* on error (or on end of stream) disable further inflation */
656 if (rs != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400657 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400658
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400659 inflateEnd(telnet->z);
660 free(telnet->z);
661 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400662 break;
663 }
664 }
665
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400666 /* COMPRESS2 is not negotiated, just process */
667 } else
668#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400669 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400670}
671
Sean Middleditch29144852009-03-12 23:14:47 -0400672/* send an iac command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400673void telnet_send_command(telnet_t *telnet, unsigned char cmd) {
674 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400675 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400676}
677
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400678/* send an iac command with telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400679void telnet_send_telopt(telnet_t *telnet, unsigned char cmd,
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400680 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400681 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400682 _send(telnet, bytes, 3);
683}
684
Sean Middleditch29144852009-03-12 23:14:47 -0400685/* send negotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400686void telnet_send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400687 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400688 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400689
690 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400691 if (telnet->flags & TELNET_FLAG_PROXY) {
692 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400693 _send(telnet, bytes, 3);
694 return;
695 }
696
697 /* get current option states */
698 q = _get_rfc1143(telnet, telopt);
699
700 switch (cmd) {
701 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400702 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400703 switch (q.us) {
704 case RFC1143_NO:
705 q.us = RFC1143_WANTYES;
706 _set_rfc1143(telnet, q);
Sean Middleditch1688da12009-03-19 13:02:09 -0400707 telnet_send_telopt(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400708 break;
709 case RFC1143_YES:
710 break;
711 case RFC1143_WANTNO:
712 q.us = RFC1143_WANTNO_OP;
713 _set_rfc1143(telnet, q);
714 break;
715 case RFC1143_WANTYES:
716 break;
717 case RFC1143_WANTNO_OP:
718 break;
719 case RFC1143_WANTYES_OP:
720 q.us = RFC1143_WANTYES;
721 _set_rfc1143(telnet, q);
722 break;
723 }
724 break;
725
726 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400727 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400728 switch (q.us) {
729 case RFC1143_NO:
730 break;
731 case RFC1143_YES:
732 q.us = RFC1143_WANTNO;
733 _set_rfc1143(telnet, q);
Sean Middleditch1688da12009-03-19 13:02:09 -0400734 telnet_send_telopt(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400735 break;
736 case RFC1143_WANTNO:
737 break;
738 case RFC1143_WANTYES:
739 q.us = RFC1143_WANTYES_OP;
740 _set_rfc1143(telnet, q);
741 break;
742 case RFC1143_WANTNO_OP:
743 q.us = RFC1143_WANTNO;
744 _set_rfc1143(telnet, q);
745 break;
746 case RFC1143_WANTYES_OP:
747 break;
748 }
749 break;
750
751 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400752 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400753 switch (q.him) {
754 case RFC1143_NO:
755 q.him = RFC1143_WANTYES;
756 _set_rfc1143(telnet, q);
Sean Middleditch1688da12009-03-19 13:02:09 -0400757 telnet_send_telopt(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400758 break;
759 case RFC1143_YES:
760 break;
761 case RFC1143_WANTNO:
762 q.him = RFC1143_WANTNO_OP;
763 _set_rfc1143(telnet, q);
764 break;
765 case RFC1143_WANTYES:
766 break;
767 case RFC1143_WANTNO_OP:
768 break;
769 case RFC1143_WANTYES_OP:
770 q.him = RFC1143_WANTYES;
771 _set_rfc1143(telnet, q);
772 break;
773 }
774 break;
775
776 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400777 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400778 switch (q.him) {
779 case RFC1143_NO:
780 break;
781 case RFC1143_YES:
782 q.him = RFC1143_WANTNO;
783 _set_rfc1143(telnet, q);
Sean Middleditch1688da12009-03-19 13:02:09 -0400784 telnet_send_telopt(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400785 break;
786 case RFC1143_WANTNO:
787 break;
788 case RFC1143_WANTYES:
789 q.him = RFC1143_WANTYES_OP;
790 _set_rfc1143(telnet, q);
791 break;
792 case RFC1143_WANTNO_OP:
793 q.him = RFC1143_WANTNO;
794 _set_rfc1143(telnet, q);
795 break;
796 case RFC1143_WANTYES_OP:
797 break;
798 }
799 break;
800 }
Sean Middleditch29144852009-03-12 23:14:47 -0400801}
802
803/* send non-command data (escapes IAC bytes) */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400804void telnet_send_data(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400805 size_t size) {
806 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400807
Sean Middleditch29144852009-03-12 23:14:47 -0400808 for (l = i = 0; i != size; ++i) {
809 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400810 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400811 /* dump prior text if any */
812 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400813 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400814 l = i + 1;
815
816 /* send escape */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400817 telnet_send_command(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400818 }
819 }
820
821 /* send whatever portion of buffer is left */
822 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400823 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400824}
825
826/* send sub-request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400827void telnet_send_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400828 const char *buffer, size_t size) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400829 telnet_send_telopt(telnet, TELNET_SB, telopt);
830 telnet_send_data(telnet, buffer, size);
831 telnet_send_command(telnet, TELNET_SE);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400832
833#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400834 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
835 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400836 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400837 if (telnet->flags & TELNET_FLAG_PROXY &&
838 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400839
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400840 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400841 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400842
843 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400844 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400845 }
846#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400847}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400848
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400849void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400850#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400851 static const char compress2[] = { TELNET_IAC, TELNET_SB,
852 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400853
Sean Middleditch124a1c22009-03-15 13:20:03 -0400854 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400855 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400856 return;
857
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400858 /* send compression marker. we send directly to the event handler
859 * instead of passing through _send because _send would result in
860 * the compress marker itself being compressed.
861 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400862 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400863
864 /* notify app that compression was successfully enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400865 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -0400866#endif /* HAVE_ZLIB */
867}
Sean Middleditchd58f49f2009-03-16 12:49:35 -0400868
Sean Middleditch4a156042009-03-16 17:10:58 -0400869/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400870int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400871 static const char CRLF[] = { '\r', '\n' };
872 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -0400873 char buffer[4096];
874 va_list va;
875 int rs, i, l;
876
877 /* format */
878 va_start(va, fmt);
879 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
880 va_end(va);
881
882 /* send */
883 for (l = i = 0; i != rs; ++i) {
884 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400885 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -0400886 buffer[i] == '\n') {
887 /* dump prior portion of text */
888 if (i != l)
Sean Middleditch8daf7742009-03-19 02:05:24 -0400889 _send(telnet, (char *)buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400890 l = i + 1;
891
892 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400893 if (buffer[i] == TELNET_IAC)
894 telnet_send_command(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -0400895 /* automatic translation of \r -> CRNUL */
896 else if (buffer[i] == '\r')
897 _send(telnet, CRNUL, 2);
898 /* automatic translation of \n -> CRLF */
899 else if (buffer[i] == '\n')
900 _send(telnet, CRLF, 2);
901 }
902 }
903
904 /* send whatever portion of buffer is left */
905 if (i != l)
Sean Middleditch8daf7742009-03-19 02:05:24 -0400906 _send(telnet, (char *)buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400907
908 return rs;
909}
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400910
911/* send formatted data through telnet_send_data */
912int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
913 char buffer[4096];
914 va_list va;
915 int rs;
916
917 /* format */
918 va_start(va, fmt);
919 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
920 va_end(va);
921
922 /* send */
923 telnet_send_data(telnet, (char *)buffer, rs);
924
925 return rs;
926}