blob: 45593852cf368cbc6b15d228d16557bf835a46e4 [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 */
Sean Middleditch7491bf42009-03-20 23:52:18 -040039#define Q_NO 0
40#define Q_YES 1
41#define Q_WANTNO 2
42#define Q_WANTYES 3
43#define Q_WANTNO_OP 4
44#define Q_WANTYES_OP 5
Sean Middleditch5b5bc922009-03-15 23:02:10 -040045
Sean Middleditch4d9444d2009-03-13 22:48:05 -040046/* buffer sizes */
Sean Middleditch340a51b2009-03-19 02:08:46 -040047static const size_t _buffer_sizes[] = {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040048 0,
49 512,
50 2048,
51 8192,
52 16384,
53};
Sean Middleditch340a51b2009-03-19 02:08:46 -040054static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040055 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040056
Sean Middleditch34bb0992009-03-21 00:20:44 -040057/* event dispatch helper */
58static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040059 unsigned char command, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -040060 const char *buffer, size_t size) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040061 telnet_event_t ev;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040062 ev.buffer = buffer;
63 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040064 ev.type = type;
65 ev.command = command;
66 ev.telopt = telopt;
Sean Middleditch637df7f2009-03-15 12:57:32 -040067
Sean Middleditch9f79cc52009-03-15 13:39:24 -040068 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch637df7f2009-03-15 12:57:32 -040069}
70
Sean Middleditchd922c6f2009-03-14 22:35:01 -040071/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040072static telnet_error_t _error(telnet_t *telnet, unsigned line,
73 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040074 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040075 char buffer[512];
76 va_list va;
77
78 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040079 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040080
Sean Middleditchb43c10c2009-03-20 23:21:02 -040081 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -040082 va_start(va, fmt);
83 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
84 fmt, va);
85 va_end(va);
86
Sean Middleditchb43c10c2009-03-20 23:21:02 -040087 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040088 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditchb43c10c2009-03-20 23:21:02 -040089 0, buffer, strlen(buffer));
Sean Middleditchfbe93e32009-03-15 23:39:31 -040090
91 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040092}
93
Sean Middleditche5b47592009-03-16 17:37:43 -040094#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -040095/* initialize the zlib box for a telnet box; if deflate is non-zero, it
96 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -040097 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -040098 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -040099 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400100telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400101 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400102 int rs;
103
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400104 /* if compression is already enabled, fail loudly */
105 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400106 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400107 err_fatal, "cannot initialize compression twice");
108
Sean Middleditch72cc9642009-03-15 11:50:36 -0400109 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400110 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400111 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400112 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400113
114 /* initialize */
115 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400116 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != 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, "deflateInit() 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 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400123 if ((rs = inflateInit(z)) != Z_OK) {
124 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400125 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400126 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400127 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400128 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400129 }
130
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400131 telnet->z = z;
132
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400133 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400134}
Sean Middleditche5b47592009-03-16 17:37:43 -0400135#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400136
Sean Middleditch8b788962009-03-16 01:06:27 -0400137/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400138static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400139 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400140#ifdef HAVE_ZLIB
141 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400142 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400143 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400144 int rs;
145
146 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400147 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400148 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400149 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400150 telnet->z->avail_out = sizeof(deflate_buffer);
151
152 /* deflate until buffer exhausted and all output is produced */
153 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
154 /* compress */
155 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400156 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400157 "deflate() failed: %s", zError(rs));
158 deflateEnd(telnet->z);
159 free(telnet->z);
160 telnet->z = 0;
161 break;
162 }
163
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400164 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditch8b788962009-03-16 01:06:27 -0400165 sizeof(deflate_buffer) - telnet->z->avail_out);
166
167 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400168 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400169 telnet->z->avail_out = sizeof(deflate_buffer);
170 }
171
172 /* COMPRESS2 is not negotiated, just send */
173 } else
174#endif /* HAVE_ZLIB */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400175 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size);
Sean Middleditch8b788962009-03-16 01:06:27 -0400176}
177
Sean Middleditch34bb0992009-03-21 00:20:44 -0400178/* check if we support a particular telopt; if us is non-zero, we
179 * check if we (local) supports it, otherwise we check if he (remote)
180 * supports it. return non-zero if supported, zero if not supported.
181 */
182static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
183 int us) {
184 int i;
185
186 /* if we have no telopts table, we obviously don't support it */
187 if (telnet->telopts == 0)
188 return 0;
189
190 /* loop unti found or end marker (us and him both 0) */
191 for (i = 0; telnet->telopts[i].telopt != -1; ++i)
192 if (telnet->telopts[i].telopt == telopt)
193 return us ? telnet->telopts[i].us : telnet->telopts[i].him;
194
195 /* not found, so not supported */
196 return 0;
197}
198
Sean Middleditch8b788962009-03-16 01:06:27 -0400199/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400200static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
201 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400202 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400203 int i;
204
205 /* search for entry */
206 for (i = 0; i != telnet->q_size; ++i)
207 if (telnet->q[i].telopt == telopt)
208 return telnet->q[i];
209
210 /* not found, return empty value */
211 return empty;
212}
213
214/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400215static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
216 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400217 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400218 int i;
219
220 /* search for entry */
221 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400222 if (telnet->q[i].telopt == telopt) {
223 telnet->q[i].us = us;
224 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400225 return;
226 }
227 }
228
229 /* we're going to need to track state for it, so grow the queue
230 * and put the telopt into it; bail on allocation error
231 */
Sean Middleditch5dea1f72009-03-19 12:45:34 -0400232 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400233 telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
234 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400235 "malloc() failed: %s", strerror(errno));
236 return;
237 }
238 telnet->q = qtmp;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400239 telnet->q[telnet->q_size].telopt = telopt;
240 telnet->q[telnet->q_size].us = us;
241 telnet->q[telnet->q_size].him = him;
242 ++telnet->q_size;
Sean Middleditch8b788962009-03-16 01:06:27 -0400243}
244
Sean Middleditch90e79da2009-03-19 15:17:13 -0400245/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400246static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400247 unsigned char telopt) {
248 char bytes[3] = { TELNET_IAC, cmd, telopt };
249 _send(telnet, bytes, 3);
250}
251
Sean Middleditch8b788962009-03-16 01:06:27 -0400252/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400253static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400254 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400255
256 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400257 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400258 switch ((int)telnet->state) {
259 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400260 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400261 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400262 case TELNET_STATE_WONT:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400263 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400264 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400265 case TELNET_STATE_DO:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400266 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400267 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400268 case TELNET_STATE_DONT:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400269 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400270 break;
271 }
272 return;
273 }
274
275 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400276 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400277
278 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400279 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400280 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400281 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400282 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400283 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400284 if (_check_telopt(telnet, telopt, 0)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400285 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400286 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch34bb0992009-03-21 00:20:44 -0400287 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400288 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400289 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400290 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400291 case Q_WANTNO:
292 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch330c2612009-03-20 23:29:17 -0400293 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400294 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400295 "DONT answered by WILL");
296 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400297 case Q_WANTNO_OP:
298 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch330c2612009-03-20 23:29:17 -0400299 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400300 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400301 "DONT answered by WILL");
302 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400303 case Q_WANTYES:
304 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch330c2612009-03-20 23:29:17 -0400305 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400306 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400307 case Q_WANTYES_OP:
308 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400309 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch330c2612009-03-20 23:29:17 -0400310 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400311 break;
312 }
313 break;
314
315 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400316 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400317 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400318 case Q_YES:
319 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400320 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400321 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400322 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400323 case Q_WANTNO:
324 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditchf7133022009-03-19 14:57:15 -0400325 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400326 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400327 case Q_WANTNO_OP:
328 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditchf7133022009-03-19 14:57:15 -0400329 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400330 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400331 case Q_WANTYES:
332 case Q_WANTYES_OP:
333 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400334 break;
335 }
336 break;
337
338 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400339 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400340 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400341 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400342 if (_check_telopt(telnet, telopt, 1)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400343 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400344 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch34bb0992009-03-21 00:20:44 -0400345 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400346 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400347 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400348 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400349 case Q_WANTNO:
350 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch330c2612009-03-20 23:29:17 -0400351 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400352 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400353 "WONT answered by DO");
354 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400355 case Q_WANTNO_OP:
356 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch330c2612009-03-20 23:29:17 -0400357 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400358 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400359 "WONT answered by DO");
360 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400361 case Q_WANTYES:
362 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch330c2612009-03-20 23:29:17 -0400363 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400364 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400365 case Q_WANTYES_OP:
366 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400367 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch330c2612009-03-20 23:29:17 -0400368 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400369 break;
370 }
371 break;
372
373 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400374 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400375 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400376 case Q_YES:
377 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400378 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400379 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400380 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400381 case Q_WANTNO:
382 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400383 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400384 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400385 case Q_WANTNO_OP:
386 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400387 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400388 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400389 case Q_WANTYES:
390 case Q_WANTYES_OP:
391 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400392 break;
393 }
394 break;
395 }
396}
397
Sean Middleditch29144852009-03-12 23:14:47 -0400398/* initialize a telnet state tracker */
Sean Middleditch34bb0992009-03-21 00:20:44 -0400399void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts,
400 telnet_event_handler_t eh, unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400401 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400402 telnet->ud = user_data;
Sean Middleditch34bb0992009-03-21 00:20:44 -0400403 telnet->telopts = telopts;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400404 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400405 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400406}
407
408/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400409void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400410 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400411 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400412 free(telnet->buffer);
413 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400414 telnet->buffer_size = 0;
415 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400416 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400417
Sean Middleditche5b47592009-03-16 17:37:43 -0400418#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400419 /* free zlib box */
420 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400421 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400422 deflateEnd(telnet->z);
423 else
424 inflateEnd(telnet->z);
425 free(telnet->z);
426 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400427 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400428#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400429
430 /* free RFC1143 queue */
431 if (telnet->q) {
432 free(telnet->q);
433 telnet->q = 0;
434 telnet->q_size = 0;
435 }
Sean Middleditch29144852009-03-12 23:14:47 -0400436}
437
Sean Middleditch51ad6792009-03-13 20:15:59 -0400438/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400439static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400440 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400441 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400442 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400443
Sean Middleditch51ad6792009-03-13 20:15:59 -0400444 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400445 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400446 /* find the next buffer size */
447 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400448 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400449 break;
450 }
451
452 /* overflow -- can't grow any more */
453 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400454 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400455 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400456 telnet_free(telnet);
457 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400458 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400459
460 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400461 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400462 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400463 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400464 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400465 telnet_free(telnet);
466 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400467 }
468
469 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400470 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400471 }
472
473 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400474 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400475 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400476}
477
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400478static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400479 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400480 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400481 for (i = start = 0; i != size; ++i) {
482 byte = buffer[i];
483 switch (telnet->state) {
484 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400485 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400486 /* on an IAC byte, pass through all pending bytes and
487 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400488 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400489 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400490 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400491 i - start);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400492 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400493 }
494 break;
495
496 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400497 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400498 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400499 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400500 case TELNET_SB:
501 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400502 break;
503 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400504 case TELNET_WILL:
505 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400506 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400507 case TELNET_WONT:
508 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400509 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400510 case TELNET_DO:
511 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400512 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400513 case TELNET_DONT:
514 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400515 break;
516 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400517 case TELNET_IAC:
518 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400519 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400520 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400521 break;
522 /* some other command */
523 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400524 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400525 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400526 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400527 }
528 break;
529
530 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400531 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400532 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400533 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400534 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400535 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400536 start = i + 1;
537 telnet->state = TELNET_STATE_DATA;
538 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400539
Sean Middleditchda0e6952009-03-15 13:28:09 -0400540 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400541 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400542 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400543 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400544 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400545 break;
546
547 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400548 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400549 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400550 if (byte == TELNET_IAC) {
551 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400552 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400553 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400554 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400555 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400556 }
557 break;
558
Sean Middleditch6b372882009-03-14 13:06:47 -0400559 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400560 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400561 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400562 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400563 case TELNET_SE:
Sean Middleditch9de15982009-03-14 03:35:49 -0400564 /* return to default state */
565 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400566 telnet->state = TELNET_STATE_DATA;
Sean Middleditch9de15982009-03-14 03:35:49 -0400567
Sean Middleditch9de15982009-03-14 03:35:49 -0400568 /* invoke callback */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400569 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400570 telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
Sean Middleditch9de15982009-03-14 03:35:49 -0400571
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400572#ifdef HAVE_ZLIB
Sean Middleditch3df17f92009-03-16 01:12:16 -0400573 /* received COMPRESS2 begin marker, setup our zlib box and
574 * start handling the compressed stream if it's not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400575 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400576 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
577 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch9de15982009-03-14 03:35:49 -0400578 break;
Sean Middleditch9de15982009-03-14 03:35:49 -0400579
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400580 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400581 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400582
Sean Middleditch9de15982009-03-14 03:35:49 -0400583 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400584 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400585 * bytes inflated and abort trying to process the
586 * remaining compressed bytes in the current _process
587 * buffer argument
588 */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400589 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400590 return;
591 }
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400592#endif /* HAVE_ZLIB */
593
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400594 break;
595 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400596 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400597 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400598 if (_buffer_byte(telnet, TELNET_IAC) !=
599 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400600 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400601 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400602 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400603 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400604 }
605 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400606 /* something else -- protocol error. attempt to process
607 * content in subnegotiation buffer, then evaluate the
608 * given command as an IAC code.
609 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400610 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400611 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400612 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400613 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400614
615 /* process what we've got */
616 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
617 telnet->buffer, telnet->buffer_pos);
618
619 /* recursive call to get the current input byte processed
620 * as a regular IAC command
621 */
622 telnet->state = TELNET_STATE_IAC;
623 _process(telnet, (char *)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400624 break;
625 }
626 break;
627 }
628 }
629
630 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400631 if (telnet->state == TELNET_STATE_DATA && i != start)
632 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start);
Sean Middleditch29144852009-03-12 23:14:47 -0400633}
634
Sean Middleditch9de15982009-03-14 03:35:49 -0400635/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400636void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400637 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400638#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400639 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400640 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400641 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400642 int rs;
643
644 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400645 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400646 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400647 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400648 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400649
650 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400651 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400652 /* reset output buffer */
653
654 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400655 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400656
657 /* process the decompressed bytes on success */
658 if (rs == Z_OK || rs == Z_STREAM_END)
659 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400660 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400661 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400662 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400663 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400664
665 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400666 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400667 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400668
669 /* on error (or on end of stream) disable further inflation */
670 if (rs != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400671 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400672
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400673 inflateEnd(telnet->z);
674 free(telnet->z);
675 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400676 break;
677 }
678 }
679
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400680 /* COMPRESS2 is not negotiated, just process */
681 } else
682#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400683 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400684}
685
Sean Middleditch29144852009-03-12 23:14:47 -0400686/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400687void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400688 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400689 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400690}
691
692/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400693void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400694 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400695 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400696
697 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400698 if (telnet->flags & TELNET_FLAG_PROXY) {
699 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400700 _send(telnet, bytes, 3);
701 return;
702 }
703
704 /* get current option states */
705 q = _get_rfc1143(telnet, telopt);
706
707 switch (cmd) {
708 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400709 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400710 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400711 case Q_NO:
712 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400713 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400714 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400715 case Q_WANTNO:
716 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400717 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400718 case Q_WANTYES_OP:
719 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400720 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 Middleditch7491bf42009-03-20 23:52:18 -0400727 case Q_YES:
728 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400729 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400730 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400731 case Q_WANTYES:
732 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400733 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400734 case Q_WANTNO_OP:
735 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400736 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400737 }
738 break;
739
740 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400741 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400742 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400743 case Q_NO:
744 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400745 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400746 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400747 case Q_WANTNO:
748 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400749 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400750 case Q_WANTYES_OP:
751 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400752 break;
753 }
754 break;
755
756 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400757 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400758 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400759 case Q_YES:
760 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400761 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400762 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400763 case Q_WANTYES:
764 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400765 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400766 case Q_WANTNO_OP:
767 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400768 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400769 }
770 break;
771 }
Sean Middleditch29144852009-03-12 23:14:47 -0400772}
773
774/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400775void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400776 size_t size) {
777 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400778
Sean Middleditch29144852009-03-12 23:14:47 -0400779 for (l = i = 0; i != size; ++i) {
780 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400781 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400782 /* dump prior text if any */
783 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400784 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400785 l = i + 1;
786
787 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400788 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400789 }
790 }
791
792 /* send whatever portion of buffer is left */
793 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400794 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400795}
796
Sean Middleditch78943842009-03-19 15:22:06 -0400797/* send subnegotiation header */
Sean Middleditch34bb0992009-03-21 00:20:44 -0400798void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch78943842009-03-19 15:22:06 -0400799 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
800 _send(telnet, sb, 3);
801}
802
803
804/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400805void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400806 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400807 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
808 static const char se[2] = { TELNET_IAC, TELNET_SE };
809
810 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400811 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400812 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400813
814#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400815 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
816 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400817 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400818 if (telnet->flags & TELNET_FLAG_PROXY &&
819 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400820
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400821 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400822 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400823
824 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400825 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400826 }
827#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400828}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400829
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400830void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400831#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400832 static const char compress2[] = { TELNET_IAC, TELNET_SB,
833 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400834
Sean Middleditch124a1c22009-03-15 13:20:03 -0400835 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400836 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400837 return;
838
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400839 /* send compression marker. we send directly to the event handler
840 * instead of passing through _send because _send would result in
841 * the compress marker itself being compressed.
842 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400843 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400844
845 /* notify app that compression was successfully enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400846 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -0400847#endif /* HAVE_ZLIB */
848}
Sean Middleditchd58f49f2009-03-16 12:49:35 -0400849
Sean Middleditch4a156042009-03-16 17:10:58 -0400850/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400851int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400852 static const char CRLF[] = { '\r', '\n' };
853 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -0400854 char buffer[4096];
855 va_list va;
856 int rs, i, l;
857
858 /* format */
859 va_start(va, fmt);
860 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
861 va_end(va);
862
863 /* send */
864 for (l = i = 0; i != rs; ++i) {
865 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400866 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -0400867 buffer[i] == '\n') {
868 /* dump prior portion of text */
869 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400870 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400871 l = i + 1;
872
873 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400874 if (buffer[i] == TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400875 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -0400876 /* automatic translation of \r -> CRNUL */
877 else if (buffer[i] == '\r')
878 _send(telnet, CRNUL, 2);
879 /* automatic translation of \n -> CRLF */
880 else if (buffer[i] == '\n')
881 _send(telnet, CRLF, 2);
882 }
883 }
884
885 /* send whatever portion of buffer is left */
886 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400887 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400888
889 return rs;
890}
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400891
Sean Middleditch7491bf42009-03-20 23:52:18 -0400892/* send formatted data through telnet_send */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400893int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
894 char buffer[4096];
895 va_list va;
896 int rs;
897
898 /* format */
899 va_start(va, fmt);
900 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
901 va_end(va);
902
903 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400904 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400905
906 return rs;
907}