blob: 1e9bcf84417cc7978a65136e9e1ea3b45dc869ee [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 Middleditch340a51b2009-03-19 02:08:46 -040061 const char *buffer, size_t size) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040062 telnet_event_t ev;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040063 ev.buffer = buffer;
64 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040065 ev.type = type;
66 ev.command = command;
67 ev.telopt = telopt;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040068 ev.accept = 0;
Sean Middleditch637df7f2009-03-15 12:57:32 -040069
Sean Middleditch9f79cc52009-03-15 13:39:24 -040070 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch5b5bc922009-03-15 23:02:10 -040071
72 return ev.accept;
Sean Middleditch637df7f2009-03-15 12:57:32 -040073}
74
Sean Middleditchd922c6f2009-03-14 22:35:01 -040075/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040076static telnet_error_t _error(telnet_t *telnet, unsigned line,
77 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040078 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040079 char buffer[512];
80 va_list va;
81
82 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040083 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040084
Sean Middleditchb43c10c2009-03-20 23:21:02 -040085 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -040086 va_start(va, fmt);
87 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
88 fmt, va);
89 va_end(va);
90
Sean Middleditchb43c10c2009-03-20 23:21:02 -040091 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040092 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditchb43c10c2009-03-20 23:21:02 -040093 0, buffer, strlen(buffer));
Sean Middleditchfbe93e32009-03-15 23:39:31 -040094
95 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040096}
97
Sean Middleditche5b47592009-03-16 17:37:43 -040098#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -040099/* initialize the zlib box for a telnet box; if deflate is non-zero, it
100 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400101 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400102 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -0400103 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400104telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400105 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400106 int rs;
107
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400108 /* if compression is already enabled, fail loudly */
109 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400110 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400111 err_fatal, "cannot initialize compression twice");
112
Sean Middleditch72cc9642009-03-15 11:50:36 -0400113 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400114 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400115 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400116 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400117
118 /* initialize */
119 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400120 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
121 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400122 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400123 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400124 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400125 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400126 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400127 if ((rs = inflateInit(z)) != Z_OK) {
128 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400129 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400130 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400131 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400132 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400133 }
134
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400135 telnet->z = z;
136
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400137 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400138}
Sean Middleditche5b47592009-03-16 17:37:43 -0400139#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400140
Sean Middleditch8b788962009-03-16 01:06:27 -0400141/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400142static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400143 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400144#ifdef HAVE_ZLIB
145 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400146 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400147 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400148 int rs;
149
150 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400151 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400152 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400153 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400154 telnet->z->avail_out = sizeof(deflate_buffer);
155
156 /* deflate until buffer exhausted and all output is produced */
157 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
158 /* compress */
159 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400160 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400161 "deflate() failed: %s", zError(rs));
162 deflateEnd(telnet->z);
163 free(telnet->z);
164 telnet->z = 0;
165 break;
166 }
167
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400168 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditch8b788962009-03-16 01:06:27 -0400169 sizeof(deflate_buffer) - telnet->z->avail_out);
170
171 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400172 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400173 telnet->z->avail_out = sizeof(deflate_buffer);
174 }
175
176 /* COMPRESS2 is not negotiated, just send */
177 } else
178#endif /* HAVE_ZLIB */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400179 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size);
Sean Middleditch8b788962009-03-16 01:06:27 -0400180}
181
182/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400183static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
184 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400185 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400186 int i;
187
188 /* search for entry */
189 for (i = 0; i != telnet->q_size; ++i)
190 if (telnet->q[i].telopt == telopt)
191 return telnet->q[i];
192
193 /* not found, return empty value */
194 return empty;
195}
196
197/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400198static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
199 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400200 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400201 int i;
202
203 /* search for entry */
204 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400205 if (telnet->q[i].telopt == telopt) {
206 telnet->q[i].us = us;
207 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400208 return;
209 }
210 }
211
212 /* we're going to need to track state for it, so grow the queue
213 * and put the telopt into it; bail on allocation error
214 */
Sean Middleditch5dea1f72009-03-19 12:45:34 -0400215 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400216 telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
217 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400218 "malloc() failed: %s", strerror(errno));
219 return;
220 }
221 telnet->q = qtmp;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400222 telnet->q[telnet->q_size].telopt = telopt;
223 telnet->q[telnet->q_size].us = us;
224 telnet->q[telnet->q_size].him = him;
225 ++telnet->q_size;
Sean Middleditch8b788962009-03-16 01:06:27 -0400226}
227
Sean Middleditch90e79da2009-03-19 15:17:13 -0400228/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400229static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400230 unsigned char telopt) {
231 char bytes[3] = { TELNET_IAC, cmd, telopt };
232 _send(telnet, bytes, 3);
233}
234
Sean Middleditch8b788962009-03-16 01:06:27 -0400235/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400236static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400237 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400238
239 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400240 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400241 switch ((int)telnet->state) {
242 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400243 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400244 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400245 case TELNET_STATE_WONT:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400246 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400247 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400248 case TELNET_STATE_DO:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400249 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400250 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400251 case TELNET_STATE_DONT:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400252 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400253 break;
254 }
255 return;
256 }
257
258 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400259 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400260
261 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400262 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400263 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400264 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400265 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400266 case Q_NO:
Sean Middleditch330c2612009-03-20 23:29:17 -0400267 if (_event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0) == 1) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400268 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400269 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400270 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400271 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400272 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400273 case Q_WANTNO:
274 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch330c2612009-03-20 23:29:17 -0400275 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400276 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400277 "DONT answered by WILL");
278 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400279 case Q_WANTNO_OP:
280 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch330c2612009-03-20 23:29:17 -0400281 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400282 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400283 "DONT answered by WILL");
284 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400285 case Q_WANTYES:
286 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch330c2612009-03-20 23:29:17 -0400287 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400288 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400289 case Q_WANTYES_OP:
290 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400291 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch330c2612009-03-20 23:29:17 -0400292 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400293 break;
294 }
295 break;
296
297 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400298 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400299 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400300 case Q_YES:
301 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400302 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditchf7133022009-03-19 14:57:15 -0400303 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400304 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400305 case Q_WANTNO:
306 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditchf7133022009-03-19 14:57:15 -0400307 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400308 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400309 case Q_WANTNO_OP:
310 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditchf7133022009-03-19 14:57:15 -0400311 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400312 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400313 case Q_WANTYES:
314 case Q_WANTYES_OP:
315 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400316 break;
317 }
318 break;
319
320 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400321 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400322 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400323 case Q_NO:
Sean Middleditch330c2612009-03-20 23:29:17 -0400324 if (_event(telnet, TELNET_EV_DO, 0, telopt, 0, 0) == 1) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400325 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400326 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400327 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400328 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400329 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400330 case Q_WANTNO:
331 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch330c2612009-03-20 23:29:17 -0400332 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400333 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400334 "WONT answered by DO");
335 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400336 case Q_WANTNO_OP:
337 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch330c2612009-03-20 23:29:17 -0400338 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400339 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400340 "WONT answered by DO");
341 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400342 case Q_WANTYES:
343 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch330c2612009-03-20 23:29:17 -0400344 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400345 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400346 case Q_WANTYES_OP:
347 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400348 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch330c2612009-03-20 23:29:17 -0400349 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400350 break;
351 }
352 break;
353
354 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400355 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400356 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400357 case Q_YES:
358 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400359 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400360 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400361 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400362 case Q_WANTNO:
363 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400364 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400365 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400366 case Q_WANTNO_OP:
367 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400368 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400369 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400370 case Q_WANTYES:
371 case Q_WANTYES_OP:
372 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400373 break;
374 }
375 break;
376 }
377}
378
Sean Middleditch29144852009-03-12 23:14:47 -0400379/* initialize a telnet state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400380void telnet_init(telnet_t *telnet, telnet_event_handler_t eh,
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400381 unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400382 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400383 telnet->ud = user_data;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400384 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400385 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400386}
387
388/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400389void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400390 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400391 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400392 free(telnet->buffer);
393 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400394 telnet->buffer_size = 0;
395 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400396 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400397
Sean Middleditche5b47592009-03-16 17:37:43 -0400398#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400399 /* free zlib box */
400 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400401 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400402 deflateEnd(telnet->z);
403 else
404 inflateEnd(telnet->z);
405 free(telnet->z);
406 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400407 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400408#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400409
410 /* free RFC1143 queue */
411 if (telnet->q) {
412 free(telnet->q);
413 telnet->q = 0;
414 telnet->q_size = 0;
415 }
Sean Middleditch29144852009-03-12 23:14:47 -0400416}
417
Sean Middleditch51ad6792009-03-13 20:15:59 -0400418/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400419static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400420 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400421 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400422 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400423
Sean Middleditch51ad6792009-03-13 20:15:59 -0400424 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400425 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400426 /* find the next buffer size */
427 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400428 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400429 break;
430 }
431
432 /* overflow -- can't grow any more */
433 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400434 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400435 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400436 telnet_free(telnet);
437 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400438 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400439
440 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400441 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400442 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400443 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400444 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400445 telnet_free(telnet);
446 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400447 }
448
449 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400450 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400451 }
452
453 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400454 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400455 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400456}
457
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400458static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400459 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400460 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400461 for (i = start = 0; i != size; ++i) {
462 byte = buffer[i];
463 switch (telnet->state) {
464 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400465 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400466 /* on an IAC byte, pass through all pending bytes and
467 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400468 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400469 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400470 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400471 i - start);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400472 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400473 }
474 break;
475
476 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400477 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400478 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400479 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400480 case TELNET_SB:
481 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400482 break;
483 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400484 case TELNET_WILL:
485 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400486 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400487 case TELNET_WONT:
488 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400489 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400490 case TELNET_DO:
491 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400492 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400493 case TELNET_DONT:
494 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400495 break;
496 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400497 case TELNET_IAC:
498 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400499 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400500 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400501 break;
502 /* some other command */
503 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400504 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400505 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400506 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400507 }
508 break;
509
510 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400511 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400512 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400513 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400514 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400515 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400516 start = i + 1;
517 telnet->state = TELNET_STATE_DATA;
518 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400519
Sean Middleditchda0e6952009-03-15 13:28:09 -0400520 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400521 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400522 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400523 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400524 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400525 break;
526
527 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400528 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400529 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400530 if (byte == TELNET_IAC) {
531 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400532 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400533 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400534 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400535 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400536 }
537 break;
538
Sean Middleditch6b372882009-03-14 13:06:47 -0400539 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400540 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400541 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400542 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400543 case TELNET_SE:
Sean Middleditch9de15982009-03-14 03:35:49 -0400544 /* return to default state */
545 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400546 telnet->state = TELNET_STATE_DATA;
Sean Middleditch9de15982009-03-14 03:35:49 -0400547
Sean Middleditch9de15982009-03-14 03:35:49 -0400548 /* invoke callback */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400549 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400550 telnet->sb_telopt, telnet->buffer, telnet->buffer_pos);
Sean Middleditch9de15982009-03-14 03:35:49 -0400551
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400552#ifdef HAVE_ZLIB
Sean Middleditch3df17f92009-03-16 01:12:16 -0400553 /* received COMPRESS2 begin marker, setup our zlib box and
554 * start handling the compressed stream if it's not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400555 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400556 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
557 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch9de15982009-03-14 03:35:49 -0400558 break;
Sean Middleditch9de15982009-03-14 03:35:49 -0400559
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400560 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400561 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400562
Sean Middleditch9de15982009-03-14 03:35:49 -0400563 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400564 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400565 * bytes inflated and abort trying to process the
566 * remaining compressed bytes in the current _process
567 * buffer argument
568 */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400569 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400570 return;
571 }
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400572#endif /* HAVE_ZLIB */
573
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400574 break;
575 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400576 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400577 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400578 if (_buffer_byte(telnet, TELNET_IAC) !=
579 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400580 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400581 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400582 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400583 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400584 }
585 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400586 /* something else -- protocol error. attempt to process
587 * content in subnegotiation buffer, then evaluate the
588 * given command as an IAC code.
589 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400590 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400591 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400592 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400593 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400594
595 /* process what we've got */
596 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
597 telnet->buffer, telnet->buffer_pos);
598
599 /* recursive call to get the current input byte processed
600 * as a regular IAC command
601 */
602 telnet->state = TELNET_STATE_IAC;
603 _process(telnet, (char *)&byte, 1);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400604 break;
605 }
606 break;
607 }
608 }
609
610 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400611 if (telnet->state == TELNET_STATE_DATA && i != start)
612 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start);
Sean Middleditch29144852009-03-12 23:14:47 -0400613}
614
Sean Middleditch9de15982009-03-14 03:35:49 -0400615/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400616void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400617 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400618#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400619 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400620 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400621 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400622 int rs;
623
624 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400625 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400626 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400627 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400628 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400629
630 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400631 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400632 /* reset output buffer */
633
634 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400635 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400636
637 /* process the decompressed bytes on success */
638 if (rs == Z_OK || rs == Z_STREAM_END)
639 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400640 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400641 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400642 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400643 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400644
645 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400646 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400647 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400648
649 /* on error (or on end of stream) disable further inflation */
650 if (rs != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400651 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400652
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400653 inflateEnd(telnet->z);
654 free(telnet->z);
655 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400656 break;
657 }
658 }
659
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400660 /* COMPRESS2 is not negotiated, just process */
661 } else
662#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400663 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400664}
665
Sean Middleditch29144852009-03-12 23:14:47 -0400666/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400667void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400668 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400669 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400670}
671
672/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400673void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400674 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400675 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400676
677 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400678 if (telnet->flags & TELNET_FLAG_PROXY) {
679 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400680 _send(telnet, bytes, 3);
681 return;
682 }
683
684 /* get current option states */
685 q = _get_rfc1143(telnet, telopt);
686
687 switch (cmd) {
688 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400689 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400690 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400691 case Q_NO:
692 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400693 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400694 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400695 case Q_WANTNO:
696 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400697 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400698 case Q_WANTYES_OP:
699 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400700 break;
701 }
702 break;
703
704 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400705 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400706 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400707 case Q_YES:
708 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400709 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400710 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400711 case Q_WANTYES:
712 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400713 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400714 case Q_WANTNO_OP:
715 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400716 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400717 }
718 break;
719
720 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400721 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400722 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400723 case Q_NO:
724 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400725 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400726 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400727 case Q_WANTNO:
728 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400729 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400730 case Q_WANTYES_OP:
731 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400732 break;
733 }
734 break;
735
736 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400737 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400738 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400739 case Q_YES:
740 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400741 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400742 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400743 case Q_WANTYES:
744 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400745 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400746 case Q_WANTNO_OP:
747 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400748 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400749 }
750 break;
751 }
Sean Middleditch29144852009-03-12 23:14:47 -0400752}
753
754/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400755void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400756 size_t size) {
757 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400758
Sean Middleditch29144852009-03-12 23:14:47 -0400759 for (l = i = 0; i != size; ++i) {
760 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400761 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400762 /* dump prior text if any */
763 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400764 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400765 l = i + 1;
766
767 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400768 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400769 }
770 }
771
772 /* send whatever portion of buffer is left */
773 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400774 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400775}
776
Sean Middleditch78943842009-03-19 15:22:06 -0400777/* send subnegotiation header */
778void telnet_begin_subnegotiation(telnet_t *telnet, unsigned char telopt) {
779 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
780 _send(telnet, sb, 3);
781}
782
783
784/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400785void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400786 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400787 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
788 static const char se[2] = { TELNET_IAC, TELNET_SE };
789
790 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400791 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400792 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400793
794#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400795 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
796 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400797 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400798 if (telnet->flags & TELNET_FLAG_PROXY &&
799 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400800
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400801 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400802 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400803
804 /* notify app that compression was enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400805 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400806 }
807#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400808}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400809
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400810void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400811#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400812 static const char compress2[] = { TELNET_IAC, TELNET_SB,
813 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400814
Sean Middleditch124a1c22009-03-15 13:20:03 -0400815 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400816 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400817 return;
818
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400819 /* send compression marker. we send directly to the event handler
820 * instead of passing through _send because _send would result in
821 * the compress marker itself being compressed.
822 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400823 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2));
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400824
825 /* notify app that compression was successfully enabled */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400826 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -0400827#endif /* HAVE_ZLIB */
828}
Sean Middleditchd58f49f2009-03-16 12:49:35 -0400829
Sean Middleditch4a156042009-03-16 17:10:58 -0400830/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400831int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400832 static const char CRLF[] = { '\r', '\n' };
833 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -0400834 char buffer[4096];
835 va_list va;
836 int rs, i, l;
837
838 /* format */
839 va_start(va, fmt);
840 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
841 va_end(va);
842
843 /* send */
844 for (l = i = 0; i != rs; ++i) {
845 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400846 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -0400847 buffer[i] == '\n') {
848 /* dump prior portion of text */
849 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400850 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400851 l = i + 1;
852
853 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400854 if (buffer[i] == TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400855 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -0400856 /* automatic translation of \r -> CRNUL */
857 else if (buffer[i] == '\r')
858 _send(telnet, CRNUL, 2);
859 /* automatic translation of \n -> CRLF */
860 else if (buffer[i] == '\n')
861 _send(telnet, CRLF, 2);
862 }
863 }
864
865 /* send whatever portion of buffer is left */
866 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400867 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -0400868
869 return rs;
870}
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400871
Sean Middleditch7491bf42009-03-20 23:52:18 -0400872/* send formatted data through telnet_send */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400873int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
874 char buffer[4096];
875 va_list va;
876 int rs;
877
878 /* format */
879 va_start(va, fmt);
880 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
881 va_end(va);
882
883 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400884 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400885
886 return rs;
887}