blob: cbdb44cc6a7ee535c3f3c8cd7b7f0fc19838de9f [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 Middleditch9b07e922009-03-19 18:18:44 -040025/* inlinable functions */
26#if __GNUC__ || __STDC_VERSION__ >= 199901L
27# define INLINE inline
28#else
29# define INLINE
30#endif
31
Sean Middleditch90e79da2009-03-19 15:17:13 -040032/* RFC1143 option negotiation state */
33typedef struct telnet_rfc1143_t {
34 unsigned char telopt;
35 char us:4, him:4;
36} telnet_rfc1143_t;
37
Sean Middleditch5b5bc922009-03-15 23:02:10 -040038/* RFC1143 state names */
39#define RFC1143_NO 0x00
40#define RFC1143_YES 0x01
41
42#define RFC1143_WANT 0x02
43#define RFC1143_OP 0x04
44
45#define RFC1143_WANTNO (RFC1143_WANT|RFC1143_YES)
46#define RFC1143_WANTYES (RFC1143_WANT|RFC1143_NO)
47#define RFC1143_WANTNO_OP (RFC1143_WANTNO|RFC1143_OP)
48#define RFC1143_WANTYES_OP (RFC1143_WANTYES|RFC1143_OP)
49
Sean Middleditch4d9444d2009-03-13 22:48:05 -040050/* buffer sizes */
Sean Middleditch340a51b2009-03-19 02:08:46 -040051static const size_t _buffer_sizes[] = {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040052 0,
53 512,
54 2048,
55 8192,
56 16384,
57};
Sean Middleditch340a51b2009-03-19 02:08:46 -040058static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040059 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040060
Sean Middleditch5b5bc922009-03-15 23:02:10 -040061/* event dispatch helper; return value is value of the accept field of the
62 * event struct after dispatch; used for the funky REQUEST event */
Sean Middleditch9b07e922009-03-19 18:18:44 -040063static INLINE int _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040064 unsigned char command, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -040065 const char *buffer, size_t size) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040066 telnet_event_t ev;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040067 ev.buffer = buffer;
68 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040069 ev.type = type;
70 ev.command = command;
71 ev.telopt = telopt;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040072 ev.accept = 0;
Sean Middleditch637df7f2009-03-15 12:57:32 -040073
Sean Middleditch9f79cc52009-03-15 13:39:24 -040074 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch5b5bc922009-03-15 23:02:10 -040075
76 return ev.accept;
Sean Middleditch637df7f2009-03-15 12:57:32 -040077}
78
Sean Middleditchd922c6f2009-03-14 22:35:01 -040079/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040080static telnet_error_t _error(telnet_t *telnet, unsigned line,
81 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040082 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040083 char buffer[512];
84 va_list va;
85
86 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040087 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040088
Sean Middleditchb43c10c2009-03-20 23:21:02 -040089 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -040090 va_start(va, fmt);
91 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
92 fmt, va);
93 va_end(va);
94
Sean Middleditchb43c10c2009-03-20 23:21:02 -040095 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040096 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditchb43c10c2009-03-20 23:21:02 -040097 0, buffer, strlen(buffer));
Sean Middleditchfbe93e32009-03-15 23:39:31 -040098
99 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400100}
101
Sean Middleditche5b47592009-03-16 17:37:43 -0400102#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400103/* initialize the zlib box for a telnet box; if deflate is non-zero, it
104 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400105 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400106 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -0400107 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400108telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400109 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400110 int rs;
111
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400112 /* if compression is already enabled, fail loudly */
113 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400114 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400115 err_fatal, "cannot initialize compression twice");
116
Sean Middleditch72cc9642009-03-15 11:50:36 -0400117 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400118 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400119 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400120 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400121
122 /* initialize */
123 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400124 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
125 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400126 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400127 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400128 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400129 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400130 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400131 if ((rs = inflateInit(z)) != Z_OK) {
132 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400133 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400134 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400135 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400136 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400137 }
138
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400139 telnet->z = z;
140
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400141 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400142}
Sean Middleditche5b47592009-03-16 17:37:43 -0400143#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400144
Sean Middleditch8b788962009-03-16 01:06:27 -0400145/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400146static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400147 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400148#ifdef HAVE_ZLIB
149 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400150 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400151 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400152 int rs;
153
154 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400155 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400156 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400157 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400158 telnet->z->avail_out = sizeof(deflate_buffer);
159
160 /* deflate until buffer exhausted and all output is produced */
161 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
162 /* compress */
163 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400164 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400165 "deflate() failed: %s", zError(rs));
166 deflateEnd(telnet->z);
167 free(telnet->z);
168 telnet->z = 0;
169 break;
170 }
171
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400172 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditch8b788962009-03-16 01:06:27 -0400173 sizeof(deflate_buffer) - telnet->z->avail_out);
174
175 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400176 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400177 telnet->z->avail_out = sizeof(deflate_buffer);
178 }
179
180 /* COMPRESS2 is not negotiated, just send */
181 } else
182#endif /* HAVE_ZLIB */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400183 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size);
Sean Middleditch8b788962009-03-16 01:06:27 -0400184}
185
186/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400187static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
188 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400189 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400190 int i;
191
192 /* search for entry */
193 for (i = 0; i != telnet->q_size; ++i)
194 if (telnet->q[i].telopt == telopt)
195 return telnet->q[i];
196
197 /* not found, return empty value */
198 return empty;
199}
200
201/* save RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400202static INLINE void _set_rfc1143(telnet_t *telnet, telnet_rfc1143_t q) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400203 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400204 int i;
205
206 /* search for entry */
207 for (i = 0; i != telnet->q_size; ++i) {
208 if (telnet->q[i].telopt == q.telopt) {
209 telnet->q[i] = q;
210 return;
211 }
212 }
213
214 /* we're going to need to track state for it, so grow the queue
215 * and put the telopt into it; bail on allocation error
216 */
Sean Middleditch5dea1f72009-03-19 12:45:34 -0400217 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400218 telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
219 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400220 "malloc() failed: %s", strerror(errno));
221 return;
222 }
223 telnet->q = qtmp;
224 telnet->q[telnet->q_size++] = q;
225}
226
Sean Middleditch90e79da2009-03-19 15:17:13 -0400227/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400228static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400229 unsigned char telopt) {
230 char bytes[3] = { TELNET_IAC, cmd, telopt };
231 _send(telnet, bytes, 3);
232}
233
Sean Middleditch8b788962009-03-16 01:06:27 -0400234/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400235static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400236 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400237
238 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400239 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400240 switch ((int)telnet->state) {
241 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400242 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400243 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400244 case TELNET_STATE_WONT:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400245 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400246 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400247 case TELNET_STATE_DO:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400248 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400249 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400250 case TELNET_STATE_DONT:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400251 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400252 break;
253 }
254 return;
255 }
256
257 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400258 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400259
260 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400261 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400262 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400263 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400264 switch (q.him) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400265 case RFC1143_NO:
Sean Middleditch330c2612009-03-20 23:29:17 -0400266 if (_event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0) == 1) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400267 q.him = RFC1143_YES;
268 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400269 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400270 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400271 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400272 break;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400273 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400274 q.him = RFC1143_NO;
275 _set_rfc1143(telnet, q);
Sean Middleditch330c2612009-03-20 23:29:17 -0400276 _event(telnet, TELNET_EV_WONT, 0, 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_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400281 q.him = RFC1143_YES;
282 _set_rfc1143(telnet, q);
Sean Middleditch330c2612009-03-20 23:29:17 -0400283 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400284 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400285 "DONT answered by WILL");
286 break;
287 case RFC1143_WANTYES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400288 q.him = RFC1143_YES;
289 _set_rfc1143(telnet, q);
Sean Middleditch330c2612009-03-20 23:29:17 -0400290 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400291 break;
292 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400293 q.him = RFC1143_WANTNO;
294 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400295 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch330c2612009-03-20 23:29:17 -0400296 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400297 break;
298 }
299 break;
300
301 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400302 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400303 switch (q.him) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400304 case RFC1143_YES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400305 q.him = RFC1143_NO;
306 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400307 _send_negotiate(telnet, TELNET_DONT, telopt);
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:
Sean Middleditch8b788962009-03-16 01:06:27 -0400311 q.him = RFC1143_NO;
312 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400313 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400314 break;
315 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400316 q.him = RFC1143_WANTYES;
317 _set_rfc1143(telnet, q);
Sean Middleditchf7133022009-03-19 14:57:15 -0400318 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400319 break;
320 case RFC1143_WANTYES:
321 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400322 q.him = RFC1143_NO;
323 _set_rfc1143(telnet, q);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400324 break;
325 }
326 break;
327
328 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400329 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400330 switch (q.us) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400331 case RFC1143_NO:
Sean Middleditch330c2612009-03-20 23:29:17 -0400332 if (_event(telnet, TELNET_EV_DO, 0, telopt, 0, 0) == 1) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400333 q.us = RFC1143_YES;
334 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400335 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400336 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400337 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400338 break;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400339 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400340 q.us = RFC1143_NO;
341 _set_rfc1143(telnet, q);
Sean Middleditch330c2612009-03-20 23:29:17 -0400342 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400343 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400344 "WONT answered by DO");
345 break;
346 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400347 q.us = RFC1143_YES;
348 _set_rfc1143(telnet, q);
Sean Middleditch330c2612009-03-20 23:29:17 -0400349 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400350 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400351 "WONT answered by DO");
352 break;
353 case RFC1143_WANTYES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400354 q.us = RFC1143_YES;
355 _set_rfc1143(telnet, q);
Sean Middleditch330c2612009-03-20 23:29:17 -0400356 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400357 break;
358 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400359 q.us = RFC1143_WANTNO;
360 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400361 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch330c2612009-03-20 23:29:17 -0400362 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400363 break;
364 }
365 break;
366
367 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400368 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400369 switch (q.us) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400370 case RFC1143_YES:
Sean Middleditch8b788962009-03-16 01:06:27 -0400371 q.us = RFC1143_NO;
372 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400373 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400374 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400375 break;
376 case RFC1143_WANTNO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400377 q.us = RFC1143_NO;
378 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400379 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400380 break;
381 case RFC1143_WANTNO_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400382 q.us = RFC1143_WANTYES;
383 _set_rfc1143(telnet, q);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400384 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400385 break;
386 case RFC1143_WANTYES:
387 case RFC1143_WANTYES_OP:
Sean Middleditch8b788962009-03-16 01:06:27 -0400388 q.us = RFC1143_NO;
389 _set_rfc1143(telnet, q);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400390 break;
391 }
392 break;
393 }
394}
395
Sean Middleditch29144852009-03-12 23:14:47 -0400396/* initialize a telnet state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400397void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400398 unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400399 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400400 telnet->ud = user_data;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400401 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400402 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400403}
404
405/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400406void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400407 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400408 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400409 free(telnet->buffer);
410 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400411 telnet->buffer_size = 0;
412 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400413 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400414
Sean Middleditche5b47592009-03-16 17:37:43 -0400415#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400416 /* free zlib box */
417 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400418 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400419 deflateEnd(telnet->z);
420 else
421 inflateEnd(telnet->z);
422 free(telnet->z);
423 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400424 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400425#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400426
427 /* free RFC1143 queue */
428 if (telnet->q) {
429 free(telnet->q);
430 telnet->q = 0;
431 telnet->q_size = 0;
432 }
Sean Middleditch29144852009-03-12 23:14:47 -0400433}
434
Sean Middleditch51ad6792009-03-13 20:15:59 -0400435/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400436static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400437 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400438 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400439 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400440
Sean Middleditch51ad6792009-03-13 20:15:59 -0400441 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400442 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400443 /* find the next buffer size */
444 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400445 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400446 break;
447 }
448
449 /* overflow -- can't grow any more */
450 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400451 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400452 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400453 telnet_free(telnet);
454 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400455 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400456
457 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400458 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400459 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 Middleditch2d5f4bf2009-03-20 23:32:47 -0400475static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400476 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400477 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400478 for (i = start = 0; i != size; ++i) {
479 byte = buffer[i];
480 switch (telnet->state) {
481 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400482 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400483 /* on an IAC byte, pass through all pending bytes and
484 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400485 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400486 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400487 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400488 i - start);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400489 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400490 }
491 break;
492
493 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400494 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400495 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400496 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400497 case TELNET_SB:
498 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400499 break;
500 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400501 case TELNET_WILL:
502 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400503 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400504 case TELNET_WONT:
505 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400506 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400507 case TELNET_DO:
508 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400509 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400510 case TELNET_DONT:
511 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400512 break;
513 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400514 case TELNET_IAC:
515 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400516 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400517 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400518 break;
519 /* some other command */
520 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400521 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400522 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400523 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400524 }
525 break;
526
527 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400528 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400529 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400530 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400531 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400532 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400533 start = i + 1;
534 telnet->state = TELNET_STATE_DATA;
535 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400536
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 Middleditch4f0c37f2009-03-20 23:08:55 -0400581 * we have to re-invoke telnet_recv 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 Middleditch4f0c37f2009-03-20 23:08:55 -0400586 telnet_recv(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;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400603 /* something else -- protocol error. attempt to process
604 * content in subnegotiation buffer, then evaluate the
605 * given command as an IAC code.
606 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400607 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400608 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400609 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400610 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400611
612 /* process what we've got */
613 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
614 telnet->buffer, telnet->buffer_pos);
615
616 /* recursive call to get the current input byte processed
617 * as a regular IAC command
618 */
619 telnet->state = TELNET_STATE_IAC;
620 _process(telnet, (char *)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400621 break;
622 }
623 break;
624 }
625 }
626
627 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400628 if (telnet->state == TELNET_STATE_DATA && i != start)
629 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start);
Sean Middleditch29144852009-03-12 23:14:47 -0400630}
631
Sean Middleditch9de15982009-03-14 03:35:49 -0400632/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400633void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400634 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400635#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400636 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400637 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400638 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400639 int rs;
640
641 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400642 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400643 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400644 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400645 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400646
647 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400648 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400649 /* reset output buffer */
650
651 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400652 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400653
654 /* process the decompressed bytes on success */
655 if (rs == Z_OK || rs == Z_STREAM_END)
656 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400657 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400658 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400659 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400660 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400661
662 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400663 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400664 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400665
666 /* on error (or on end of stream) disable further inflation */
667 if (rs != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400668 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400669
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400670 inflateEnd(telnet->z);
671 free(telnet->z);
672 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400673 break;
674 }
675 }
676
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400677 /* COMPRESS2 is not negotiated, just process */
678 } else
679#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400680 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400681}
682
Sean Middleditch29144852009-03-12 23:14:47 -0400683/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400684void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400685 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400686 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400687}
688
689/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400690void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400691 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400692 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400693
694 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400695 if (telnet->flags & TELNET_FLAG_PROXY) {
696 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400697 _send(telnet, bytes, 3);
698 return;
699 }
700
701 /* get current option states */
702 q = _get_rfc1143(telnet, telopt);
703
704 switch (cmd) {
705 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400706 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400707 switch (q.us) {
708 case RFC1143_NO:
709 q.us = RFC1143_WANTYES;
710 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400711 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400712 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400713 case RFC1143_WANTNO:
714 q.us = RFC1143_WANTNO_OP;
715 _set_rfc1143(telnet, q);
716 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400717 case RFC1143_WANTYES_OP:
718 q.us = RFC1143_WANTYES;
719 _set_rfc1143(telnet, q);
720 break;
721 }
722 break;
723
724 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400725 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400726 switch (q.us) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400727 case RFC1143_YES:
728 q.us = RFC1143_WANTNO;
729 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400730 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400731 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400732 case RFC1143_WANTYES:
733 q.us = RFC1143_WANTYES_OP;
734 _set_rfc1143(telnet, q);
735 break;
736 case RFC1143_WANTNO_OP:
737 q.us = RFC1143_WANTNO;
738 _set_rfc1143(telnet, q);
739 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400740 }
741 break;
742
743 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400744 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400745 switch (q.him) {
746 case RFC1143_NO:
747 q.him = RFC1143_WANTYES;
748 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400749 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400750 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400751 case RFC1143_WANTNO:
752 q.him = RFC1143_WANTNO_OP;
753 _set_rfc1143(telnet, q);
754 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400755 case RFC1143_WANTYES_OP:
756 q.him = RFC1143_WANTYES;
757 _set_rfc1143(telnet, q);
758 break;
759 }
760 break;
761
762 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400763 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400764 switch (q.him) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400765 case RFC1143_YES:
766 q.him = RFC1143_WANTNO;
767 _set_rfc1143(telnet, q);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400768 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400769 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400770 case RFC1143_WANTYES:
771 q.him = RFC1143_WANTYES_OP;
772 _set_rfc1143(telnet, q);
773 break;
774 case RFC1143_WANTNO_OP:
775 q.him = RFC1143_WANTNO;
776 _set_rfc1143(telnet, q);
777 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400778 }
779 break;
780 }
Sean Middleditch29144852009-03-12 23:14:47 -0400781}
782
783/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400784void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400785 size_t size) {
786 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400787
Sean Middleditch29144852009-03-12 23:14:47 -0400788 for (l = i = 0; i != size; ++i) {
789 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400790 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400791 /* dump prior text if any */
792 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400793 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400794 l = i + 1;
795
796 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400797 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400798 }
799 }
800
801 /* send whatever portion of buffer is left */
802 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400803 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400804}
805
Sean Middleditch78943842009-03-19 15:22:06 -0400806/* send subnegotiation header */
807void telnet_begin_subnegotiation(telnet_t *telnet, unsigned char telopt) {
808 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
809 _send(telnet, sb, 3);
810}
811
812
813/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400814void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400815 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400816 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
817 static const char se[2] = { TELNET_IAC, TELNET_SE };
818
819 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400820 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400821 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400822
823#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400824 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
825 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400826 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400827 if (telnet->flags & TELNET_FLAG_PROXY &&
828 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400829
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400830 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400831 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400832
833 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400834 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400835 }
836#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400837}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400838
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400839void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400840#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400841 static const char compress2[] = { TELNET_IAC, TELNET_SB,
842 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400843
Sean Middleditch124a1c22009-03-15 13:20:03 -0400844 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400845 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400846 return;
847
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400848 /* send compression marker. we send directly to the event handler
849 * instead of passing through _send because _send would result in
850 * the compress marker itself being compressed.
851 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400852 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400853
854 /* notify app that compression was successfully enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400855 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -0400856#endif /* HAVE_ZLIB */
857}
Sean Middleditchd58f49f2009-03-16 12:49:35 -0400858
Sean Middleditch4a156042009-03-16 17:10:58 -0400859/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400860int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400861 static const char CRLF[] = { '\r', '\n' };
862 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -0400863 char buffer[4096];
864 va_list va;
865 int rs, i, l;
866
867 /* format */
868 va_start(va, fmt);
869 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
870 va_end(va);
871
872 /* send */
873 for (l = i = 0; i != rs; ++i) {
874 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400875 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -0400876 buffer[i] == '\n') {
877 /* dump prior portion of text */
878 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400879 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400880 l = i + 1;
881
882 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400883 if (buffer[i] == TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400884 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -0400885 /* automatic translation of \r -> CRNUL */
886 else if (buffer[i] == '\r')
887 _send(telnet, CRNUL, 2);
888 /* automatic translation of \n -> CRLF */
889 else if (buffer[i] == '\n')
890 _send(telnet, CRLF, 2);
891 }
892 }
893
894 /* send whatever portion of buffer is left */
895 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400896 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400897
898 return rs;
899}
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400900
901/* send formatted data through telnet_send_data */
902int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
903 char buffer[4096];
904 va_list va;
905 int rs;
906
907 /* format */
908 va_start(va, fmt);
909 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
910 va_end(va);
911
912 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400913 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400914
915 return rs;
916}