blob: a674c69a1d387b2bf20495c3dc0eb99348aa9097 [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 Middleditch5b5bc922009-03-15 23:02:10 -040057/* event dispatch helper; return value is value of the accept field of the
58 * event struct after dispatch; used for the funky REQUEST event */
Sean Middleditch9b07e922009-03-19 18:18:44 -040059static INLINE int _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040060 unsigned char command, unsigned char telopt,
Sean Middleditche5327da2009-03-21 00:54:50 -040061 const char *buffer, size_t size, const char **argv, size_t argc) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040062 telnet_event_t ev;
Sean Middleditche5327da2009-03-21 00:54:50 -040063 ev.argv = argv;
64 ev.argc = argc;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040065 ev.buffer = buffer;
66 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040067 ev.type = type;
68 ev.command = command;
69 ev.telopt = telopt;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040070 ev.accept = 0;
Sean Middleditch637df7f2009-03-15 12:57:32 -040071
Sean Middleditch9f79cc52009-03-15 13:39:24 -040072 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch5b5bc922009-03-15 23:02:10 -040073
74 return ev.accept;
Sean Middleditch637df7f2009-03-15 12:57:32 -040075}
76
Sean Middleditchd922c6f2009-03-14 22:35:01 -040077/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040078static telnet_error_t _error(telnet_t *telnet, unsigned line,
79 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040080 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040081 char buffer[512];
82 va_list va;
83
84 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040085 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040086
Sean Middleditchb43c10c2009-03-20 23:21:02 -040087 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -040088 va_start(va, fmt);
89 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
90 fmt, va);
91 va_end(va);
92
Sean Middleditchb43c10c2009-03-20 23:21:02 -040093 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040094 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditche5327da2009-03-21 00:54:50 -040095 0, buffer, strlen(buffer), 0, 0);
Sean Middleditchfbe93e32009-03-15 23:39:31 -040096
97 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040098}
99
Sean Middleditche5b47592009-03-16 17:37:43 -0400100#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400101/* initialize the zlib box for a telnet box; if deflate is non-zero, it
102 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400103 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400104 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -0400105 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400106telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400107 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400108 int rs;
109
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400110 /* if compression is already enabled, fail loudly */
111 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400112 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400113 err_fatal, "cannot initialize compression twice");
114
Sean Middleditch72cc9642009-03-15 11:50:36 -0400115 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400116 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400117 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400118 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400119
120 /* initialize */
121 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400122 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
123 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400124 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400125 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400126 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400127 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400128 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400129 if ((rs = inflateInit(z)) != Z_OK) {
130 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400131 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400132 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400133 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400134 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400135 }
136
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400137 telnet->z = z;
138
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400139 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400140}
Sean Middleditche5b47592009-03-16 17:37:43 -0400141#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400142
Sean Middleditch8b788962009-03-16 01:06:27 -0400143/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400144static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400145 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400146#ifdef HAVE_ZLIB
147 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400148 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400149 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400150 int rs;
151
152 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400153 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400154 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400155 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400156 telnet->z->avail_out = sizeof(deflate_buffer);
157
158 /* deflate until buffer exhausted and all output is produced */
159 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
160 /* compress */
161 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400162 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400163 "deflate() failed: %s", zError(rs));
164 deflateEnd(telnet->z);
165 free(telnet->z);
166 telnet->z = 0;
167 break;
168 }
169
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400170 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditche5327da2009-03-21 00:54:50 -0400171 sizeof(deflate_buffer) - telnet->z->avail_out, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400172
173 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400174 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400175 telnet->z->avail_out = sizeof(deflate_buffer);
176 }
177
178 /* COMPRESS2 is not negotiated, just send */
179 } else
180#endif /* HAVE_ZLIB */
Sean Middleditche5327da2009-03-21 00:54:50 -0400181 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400182}
183
184/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400185static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
186 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400187 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400188 int i;
189
190 /* search for entry */
191 for (i = 0; i != telnet->q_size; ++i)
192 if (telnet->q[i].telopt == telopt)
193 return telnet->q[i];
194
195 /* not found, return empty value */
196 return empty;
197}
198
199/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400200static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
201 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400202 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400203 int i;
204
205 /* search for entry */
206 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400207 if (telnet->q[i].telopt == telopt) {
208 telnet->q[i].us = us;
209 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400210 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;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400224 telnet->q[telnet->q_size].telopt = telopt;
225 telnet->q[telnet->q_size].us = us;
226 telnet->q[telnet->q_size].him = him;
227 ++telnet->q_size;
Sean Middleditch8b788962009-03-16 01:06:27 -0400228}
229
Sean Middleditch90e79da2009-03-19 15:17:13 -0400230/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400231static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400232 unsigned char telopt) {
233 char bytes[3] = { TELNET_IAC, cmd, telopt };
234 _send(telnet, bytes, 3);
235}
236
Sean Middleditch8b788962009-03-16 01:06:27 -0400237/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400238static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400239 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400240
241 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400242 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400243 switch ((int)telnet->state) {
244 case TELNET_STATE_WILL:
Sean Middleditche5327da2009-03-21 00:54:50 -0400245 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400246 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400247 case TELNET_STATE_WONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400248 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400249 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400250 case TELNET_STATE_DO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400251 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400252 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400253 case TELNET_STATE_DONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400254 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400255 break;
256 }
257 return;
258 }
259
260 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400261 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400262
263 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400264 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400265 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400266 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400267 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400268 case Q_NO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400269 if (_event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0) == 1) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400270 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400271 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400272 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400273 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400274 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400275 case Q_WANTNO:
276 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400277 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400278 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400279 "DONT answered by WILL");
280 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400281 case Q_WANTNO_OP:
282 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400283 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 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;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400287 case Q_WANTYES:
288 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400289 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400290 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400291 case Q_WANTYES_OP:
292 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400293 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400294 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400295 break;
296 }
297 break;
298
299 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400300 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400301 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400302 case Q_YES:
303 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400304 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400305 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400306 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400307 case Q_WANTNO:
308 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400309 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400310 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400311 case Q_WANTNO_OP:
312 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400313 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400314 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400315 case Q_WANTYES:
316 case Q_WANTYES_OP:
317 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400318 break;
319 }
320 break;
321
322 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400323 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400324 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400325 case Q_NO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400326 if (_event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0) == 1) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400327 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400328 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400329 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400330 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400331 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400332 case Q_WANTNO:
333 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400334 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400335 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400336 "WONT answered by DO");
337 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400338 case Q_WANTNO_OP:
339 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400340 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400341 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400342 "WONT answered by DO");
343 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400344 case Q_WANTYES:
345 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400346 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400347 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400348 case Q_WANTYES_OP:
349 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400350 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400351 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400352 break;
353 }
354 break;
355
356 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400357 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400358 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400359 case Q_YES:
360 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400361 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400362 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400363 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400364 case Q_WANTNO:
365 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400366 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400367 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400368 case Q_WANTNO_OP:
369 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400370 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400371 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400372 case Q_WANTYES:
373 case Q_WANTYES_OP:
374 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400375 break;
376 }
377 break;
378 }
379}
380
Sean Middleditche5327da2009-03-21 00:54:50 -0400381/* process a subnegotiation buffer; return non-zero if the current buffer
382 * must be aborted and reprocessed due to COMPRESS2 being activated
383 */
384static int _subnegotiate(telnet_t *telnet) {
385 /* invoke callback */
386 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
387 telnet->buffer, telnet->buffer_pos, 0, 0);
388
389 /* received COMPRESS2 begin marker, setup our zlib box and
390 * start handling the compressed stream if it's not already.
391 */
392#ifdef HAVE_ZLIB
393 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
394 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
395 return 0;
396
397 /* notify app that compression was enabled */
398 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
399 return 1;
400 }
401#endif /* HAVE_ZLIB */
402
403 /* parse ZMP args */
404 if (telnet->sb_telopt == TELNET_TELOPT_ZMP) {
405 const char **argv;
406 const char *c = telnet->buffer;
407 size_t i, argc = 0;
408
409 /* make sure this is a valid ZMP buffer */
410 if (telnet->buffer_pos == 0 || telnet->buffer[telnet->buffer_pos - 1]
411 != 0)
412 return 0;
413
414 /* count arguments */
415 while (c != telnet->buffer + telnet->buffer_pos + 1) {
416 ++argc;
417 c += strlen(c) + 1;
418 }
419
420 /* allocate argument array, bail on error */
421 if ((argv = (const char **)malloc(sizeof(char *) * argc)) == 0) {
422 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
423 "malloc() failed: %s", strerror(errno));
424 return 0;
425 }
426
427 /* populate argument array */
428 for (i = 0, c = telnet->buffer; i != argc; ++i) {
429 argv[i] = c;
430 c += strlen(c) + 1;
431 }
432
433 /* invoke ZMP event */
434 _event(telnet, TELNET_EV_ZMP, 0, 0, 0, 0, argv, argc);
435
436 /* free argument array */
437 free(argv);
438 }
439
440 return 0;
441}
442
Sean Middleditch29144852009-03-12 23:14:47 -0400443/* initialize a telnet state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400444void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400445 unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400446 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400447 telnet->ud = user_data;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400448 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400449 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400450}
451
452/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400453void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400454 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400455 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400456 free(telnet->buffer);
457 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400458 telnet->buffer_size = 0;
459 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400460 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400461
Sean Middleditche5b47592009-03-16 17:37:43 -0400462#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400463 /* free zlib box */
464 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400465 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400466 deflateEnd(telnet->z);
467 else
468 inflateEnd(telnet->z);
469 free(telnet->z);
470 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400471 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400472#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400473
474 /* free RFC1143 queue */
475 if (telnet->q) {
476 free(telnet->q);
477 telnet->q = 0;
478 telnet->q_size = 0;
479 }
Sean Middleditch29144852009-03-12 23:14:47 -0400480}
481
Sean Middleditch51ad6792009-03-13 20:15:59 -0400482/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400483static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400484 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400485 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400486 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400487
Sean Middleditch51ad6792009-03-13 20:15:59 -0400488 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400489 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400490 /* find the next buffer size */
491 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400492 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400493 break;
494 }
495
496 /* overflow -- can't grow any more */
497 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400498 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400499 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400500 telnet_free(telnet);
501 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400502 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400503
504 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400505 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400506 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400507 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400508 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400509 telnet_free(telnet);
510 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400511 }
512
513 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400514 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400515 }
516
517 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400518 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400519 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400520}
521
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400522static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400523 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400524 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400525 for (i = start = 0; i != size; ++i) {
526 byte = buffer[i];
527 switch (telnet->state) {
528 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400529 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400530 /* on an IAC byte, pass through all pending bytes and
531 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400532 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400533 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400534 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditche5327da2009-03-21 00:54:50 -0400535 i - start, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400536 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400537 }
538 break;
539
540 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400541 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400542 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400543 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400544 case TELNET_SB:
545 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400546 break;
547 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400548 case TELNET_WILL:
549 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400550 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400551 case TELNET_WONT:
552 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400553 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400554 case TELNET_DO:
555 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400556 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400557 case TELNET_DONT:
558 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400559 break;
560 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400561 case TELNET_IAC:
Sean Middleditche5327da2009-03-21 00:54:50 -0400562 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400563 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400564 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400565 break;
566 /* some other command */
567 default:
Sean Middleditche5327da2009-03-21 00:54:50 -0400568 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400569 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400570 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400571 }
572 break;
573
574 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400575 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400576 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400577 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400578 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400579 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400580 start = i + 1;
581 telnet->state = TELNET_STATE_DATA;
582 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400583
Sean Middleditchda0e6952009-03-15 13:28:09 -0400584 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400585 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400586 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400587 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400588 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400589 break;
590
591 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400592 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400593 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400594 if (byte == TELNET_IAC) {
595 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400596 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400597 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400598 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400599 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400600 }
601 break;
602
Sean Middleditch6b372882009-03-14 13:06:47 -0400603 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400604 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400605 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400606 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400607 case TELNET_SE:
Sean Middleditche5327da2009-03-21 00:54:50 -0400608 /* process subnegotiation */
609 if (_subnegotiate(telnet) != 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400610 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400611 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400612 * bytes inflated and abort trying to process the
613 * remaining compressed bytes in the current _process
614 * buffer argument
615 */
Sean Middleditche5327da2009-03-21 00:54:50 -0400616 telnet_recv(telnet, &buffer[i + 1], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400617 return;
618 }
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400619
Sean Middleditche5327da2009-03-21 00:54:50 -0400620 /* return to default state */
621 start = i + 1;
622 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400623 break;
624 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400625 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400626 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400627 if (_buffer_byte(telnet, TELNET_IAC) !=
628 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400629 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400630 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400631 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400632 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400633 }
634 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400635 /* something else -- protocol error. attempt to process
636 * content in subnegotiation buffer, then evaluate the
637 * given command as an IAC code.
638 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400639 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400640 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400641 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400642 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400643
644 /* process what we've got */
645 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
Sean Middleditche5327da2009-03-21 00:54:50 -0400646 telnet->buffer, telnet->buffer_pos, 0, 0);
647
648 /* process subnegotiation; see comment in
649 * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
650 */
651 if (_subnegotiate(telnet) != 0) {
652 telnet_recv(telnet, &buffer[i + 1], size - start);
653 return;
654 }
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400655
656 /* recursive call to get the current input byte processed
Sean Middleditche5327da2009-03-21 00:54:50 -0400657 * as a regular IAC command. we could use a goto, but
658 * that would be gross.
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400659 */
660 telnet->state = TELNET_STATE_IAC;
661 _process(telnet, (char *)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400662 break;
663 }
664 break;
665 }
666 }
667
668 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400669 if (telnet->state == TELNET_STATE_DATA && i != start)
Sean Middleditche5327da2009-03-21 00:54:50 -0400670 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start, 0, 0);
Sean Middleditch29144852009-03-12 23:14:47 -0400671}
672
Sean Middleditch9de15982009-03-14 03:35:49 -0400673/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400674void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400675 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400676#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400677 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400678 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400679 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400680 int rs;
681
682 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400683 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400684 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400685 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400686 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400687
688 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400689 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400690 /* reset output buffer */
691
692 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400693 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400694
695 /* process the decompressed bytes on success */
696 if (rs == Z_OK || rs == Z_STREAM_END)
697 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400698 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400699 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400700 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400701 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400702
703 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400704 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400705 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400706
707 /* on error (or on end of stream) disable further inflation */
708 if (rs != Z_OK) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400709 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400710
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400711 inflateEnd(telnet->z);
712 free(telnet->z);
713 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400714 break;
715 }
716 }
717
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400718 /* COMPRESS2 is not negotiated, just process */
719 } else
720#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400721 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400722}
723
Sean Middleditch29144852009-03-12 23:14:47 -0400724/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400725void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400726 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400727 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400728}
729
730/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400731void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400732 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400733 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400734
735 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400736 if (telnet->flags & TELNET_FLAG_PROXY) {
737 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400738 _send(telnet, bytes, 3);
739 return;
740 }
741
742 /* get current option states */
743 q = _get_rfc1143(telnet, telopt);
744
745 switch (cmd) {
746 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400747 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400748 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400749 case Q_NO:
750 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400751 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400752 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400753 case Q_WANTNO:
754 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400755 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400756 case Q_WANTYES_OP:
757 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400758 break;
759 }
760 break;
761
762 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400763 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400764 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400765 case Q_YES:
766 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400767 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400768 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400769 case Q_WANTYES:
770 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400771 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400772 case Q_WANTNO_OP:
773 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400774 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400775 }
776 break;
777
778 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400779 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400780 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400781 case Q_NO:
782 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400783 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400784 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400785 case Q_WANTNO:
786 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400787 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400788 case Q_WANTYES_OP:
789 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400790 break;
791 }
792 break;
793
794 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400795 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400796 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400797 case Q_YES:
798 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400799 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400800 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400801 case Q_WANTYES:
802 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400803 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400804 case Q_WANTNO_OP:
805 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400806 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400807 }
808 break;
809 }
Sean Middleditch29144852009-03-12 23:14:47 -0400810}
811
812/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400813void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400814 size_t size) {
815 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400816
Sean Middleditch29144852009-03-12 23:14:47 -0400817 for (l = i = 0; i != size; ++i) {
818 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400819 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400820 /* dump prior text if any */
821 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400822 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400823 l = i + 1;
824
825 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400826 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400827 }
828 }
829
830 /* send whatever portion of buffer is left */
831 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400832 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400833}
834
Sean Middleditch78943842009-03-19 15:22:06 -0400835/* send subnegotiation header */
Sean Middleditch1443ca42009-03-21 00:21:04 -0400836void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch78943842009-03-19 15:22:06 -0400837 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
838 _send(telnet, sb, 3);
839}
840
841
842/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400843void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400844 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400845 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
846 static const char se[2] = { TELNET_IAC, TELNET_SE };
847
848 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400849 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400850 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400851
852#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400853 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
854 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400855 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400856 if (telnet->flags & TELNET_FLAG_PROXY &&
857 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400858
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400859 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400860 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400861
862 /* notify app that compression was enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -0400863 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400864 }
865#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400866}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400867
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400868void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400869#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400870 static const char compress2[] = { TELNET_IAC, TELNET_SB,
871 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400872
Sean Middleditch124a1c22009-03-15 13:20:03 -0400873 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400874 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400875 return;
876
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400877 /* send compression marker. we send directly to the event handler
878 * instead of passing through _send because _send would result in
879 * the compress marker itself being compressed.
880 */
Sean Middleditche5327da2009-03-21 00:54:50 -0400881 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2), 0, 0);
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400882
883 /* notify app that compression was successfully enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -0400884 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -0400885#endif /* HAVE_ZLIB */
886}
Sean Middleditchd58f49f2009-03-16 12:49:35 -0400887
Sean Middleditch4a156042009-03-16 17:10:58 -0400888/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400889int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400890 static const char CRLF[] = { '\r', '\n' };
891 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -0400892 char buffer[4096];
893 va_list va;
894 int rs, i, l;
895
896 /* format */
897 va_start(va, fmt);
898 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
899 va_end(va);
900
901 /* send */
902 for (l = i = 0; i != rs; ++i) {
903 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400904 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -0400905 buffer[i] == '\n') {
906 /* dump prior portion of text */
907 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400908 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400909 l = i + 1;
910
911 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400912 if (buffer[i] == TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400913 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -0400914 /* automatic translation of \r -> CRNUL */
915 else if (buffer[i] == '\r')
916 _send(telnet, CRNUL, 2);
917 /* automatic translation of \n -> CRLF */
918 else if (buffer[i] == '\n')
919 _send(telnet, CRLF, 2);
920 }
921 }
922
923 /* send whatever portion of buffer is left */
924 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400925 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400926
927 return rs;
928}
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400929
Sean Middleditch7491bf42009-03-20 23:52:18 -0400930/* send formatted data through telnet_send */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400931int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
932 char buffer[4096];
933 va_list va;
934 int rs;
935
936 /* format */
937 va_start(va, fmt);
938 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
939 va_end(va);
940
941 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400942 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400943
944 return rs;
945}