blob: 6ee8e25d354becc77fc1df67efda94f9c4e3b99f [file] [log] [blame]
Sean Middleditch6aef0732009-03-12 23:27:35 -04001/*
Sean Middleditchae39cee2009-03-22 22:47:30 -04002 * libtelnet 0.9
3 *
Sean Middleditch9de15982009-03-14 03:35:49 -04004 * Sean Middleditch
5 * sean@sourcemud.org
6 *
Sean Middleditch6aef0732009-03-12 23:27:35 -04007 * The author or authors of this code dedicate any and all copyright interest
8 * in this code to the public domain. We make this dedication for the benefit
9 * of the public at large and to the detriment of our heirs and successors. We
10 * intend this dedication to be an overt act of relinquishment in perpetuity of
11 * all present and future rights to this code under copyright law.
12 */
13
Sean Middleditch4d9444d2009-03-13 22:48:05 -040014#include <malloc.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040015#include <string.h>
Sean Middleditchd922c6f2009-03-14 22:35:01 -040016#include <stdio.h>
17#include <errno.h>
18#include <string.h>
19#include <stdarg.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040020
Sean Middleditch2d5c4992009-03-22 22:21:42 -040021#ifdef HAVE_ALLOCA
22#include <alloca.h>
23#endif
24
Sean Middleditch9de15982009-03-14 03:35:49 -040025#ifdef HAVE_ZLIB
Sean Middleditch2d5c4992009-03-22 22:21:42 -040026#include <zlib.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040027#endif
28
Sean Middleditch29144852009-03-12 23:14:47 -040029#include "libtelnet.h"
30
Sean Middleditch9b07e922009-03-19 18:18:44 -040031/* inlinable functions */
32#if __GNUC__ || __STDC_VERSION__ >= 199901L
Sean Middleditch53a86612009-03-22 17:43:29 -040033# define INLINE __inline__
Sean Middleditch9b07e922009-03-19 18:18:44 -040034#else
35# define INLINE
36#endif
37
Sean Middleditch90e79da2009-03-19 15:17:13 -040038/* RFC1143 option negotiation state */
39typedef struct telnet_rfc1143_t {
40 unsigned char telopt;
41 char us:4, him:4;
42} telnet_rfc1143_t;
43
Sean Middleditch5b5bc922009-03-15 23:02:10 -040044/* RFC1143 state names */
Sean Middleditch7491bf42009-03-20 23:52:18 -040045#define Q_NO 0
46#define Q_YES 1
47#define Q_WANTNO 2
48#define Q_WANTYES 3
49#define Q_WANTNO_OP 4
50#define Q_WANTYES_OP 5
Sean Middleditch5b5bc922009-03-15 23:02:10 -040051
Sean Middleditch4d9444d2009-03-13 22:48:05 -040052/* buffer sizes */
Sean Middleditch8c111792009-03-22 22:44:38 -040053static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
Sean Middleditch340a51b2009-03-19 02:08:46 -040054static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040055 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040056
Sean Middleditch34bb0992009-03-21 00:20:44 -040057/* event dispatch helper */
58static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040059 unsigned char command, unsigned char telopt,
Sean Middleditche5327da2009-03-21 00:54:50 -040060 const char *buffer, size_t size, const char **argv, size_t argc) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040061 telnet_event_t ev;
Sean Middleditche5327da2009-03-21 00:54:50 -040062 ev.argv = argv;
63 ev.argc = argc;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040064 ev.buffer = buffer;
65 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040066 ev.type = type;
67 ev.command = command;
68 ev.telopt = telopt;
Sean Middleditch637df7f2009-03-15 12:57:32 -040069
Sean Middleditch9f79cc52009-03-15 13:39:24 -040070 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch637df7f2009-03-15 12:57:32 -040071}
72
Sean Middleditchd922c6f2009-03-14 22:35:01 -040073/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040074static telnet_error_t _error(telnet_t *telnet, unsigned line,
75 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040076 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040077 char buffer[512];
78 va_list va;
79
80 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040081 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040082
Sean Middleditchb43c10c2009-03-20 23:21:02 -040083 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -040084 va_start(va, fmt);
85 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
86 fmt, va);
87 va_end(va);
88
Sean Middleditchb43c10c2009-03-20 23:21:02 -040089 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040090 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditche5327da2009-03-21 00:54:50 -040091 0, buffer, strlen(buffer), 0, 0);
Sean Middleditchfbe93e32009-03-15 23:39:31 -040092
93 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040094}
95
Sean Middleditche5b47592009-03-16 17:37:43 -040096#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -040097/* initialize the zlib box for a telnet box; if deflate is non-zero, it
98 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -040099 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400100 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -0400101 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400102telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400103 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400104 int rs;
105
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400106 /* if compression is already enabled, fail loudly */
107 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400108 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400109 err_fatal, "cannot initialize compression twice");
110
Sean Middleditch72cc9642009-03-15 11:50:36 -0400111 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400112 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400113 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400114 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400115
116 /* initialize */
117 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400118 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
119 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400120 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400121 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400122 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400123 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400124 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400125 if ((rs = inflateInit(z)) != Z_OK) {
126 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400127 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400128 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400129 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400130 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400131 }
132
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400133 telnet->z = z;
134
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400135 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400136}
Sean Middleditche5b47592009-03-16 17:37:43 -0400137#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400138
Sean Middleditch8b788962009-03-16 01:06:27 -0400139/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400140static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400141 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400142#ifdef HAVE_ZLIB
143 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400144 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400145 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400146 int rs;
147
148 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400149 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400150 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400151 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400152 telnet->z->avail_out = sizeof(deflate_buffer);
153
154 /* deflate until buffer exhausted and all output is produced */
155 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
156 /* compress */
157 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400158 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400159 "deflate() failed: %s", zError(rs));
160 deflateEnd(telnet->z);
161 free(telnet->z);
162 telnet->z = 0;
163 break;
164 }
165
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400166 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditche5327da2009-03-21 00:54:50 -0400167 sizeof(deflate_buffer) - telnet->z->avail_out, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400168
169 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400170 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400171 telnet->z->avail_out = sizeof(deflate_buffer);
172 }
173
174 /* COMPRESS2 is not negotiated, just send */
175 } else
176#endif /* HAVE_ZLIB */
Sean Middleditche5327da2009-03-21 00:54:50 -0400177 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400178}
179
Sean Middleditch34bb0992009-03-21 00:20:44 -0400180/* check if we support a particular telopt; if us is non-zero, we
181 * check if we (local) supports it, otherwise we check if he (remote)
182 * supports it. return non-zero if supported, zero if not supported.
183 */
184static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
185 int us) {
186 int i;
187
188 /* if we have no telopts table, we obviously don't support it */
189 if (telnet->telopts == 0)
190 return 0;
191
192 /* loop unti found or end marker (us and him both 0) */
Sean Middleditchbfc641e2009-03-22 16:26:06 -0400193 for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
194 if (telnet->telopts[i].telopt == telopt) {
195 if (us && telnet->telopts[i].us == TELNET_WILL)
196 return 1;
197 else if (!us && telnet->telopts[i].him == TELNET_DO)
198 return 1;
199 else
200 return 0;
201 }
202 }
Sean Middleditch34bb0992009-03-21 00:20:44 -0400203
204 /* not found, so not supported */
205 return 0;
206}
207
Sean Middleditch8b788962009-03-16 01:06:27 -0400208/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400209static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
210 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400211 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400212 int i;
213
214 /* search for entry */
215 for (i = 0; i != telnet->q_size; ++i)
216 if (telnet->q[i].telopt == telopt)
217 return telnet->q[i];
218
219 /* not found, return empty value */
220 return empty;
221}
222
223/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400224static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
225 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400226 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400227 int i;
228
229 /* search for entry */
230 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400231 if (telnet->q[i].telopt == telopt) {
232 telnet->q[i].us = us;
233 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400234 return;
235 }
236 }
237
238 /* we're going to need to track state for it, so grow the queue
Sean Middleditch1e80f522009-03-22 18:24:18 -0400239 * by 4 (four) elements and put the telopt into it; bail on allocation
240 * error. we go by four because it seems like a reasonable guess as
241 * to the number of enabled options for most simple code, and it
242 * allows for an acceptable number of reallocations for complex code.
Sean Middleditch8b788962009-03-16 01:06:27 -0400243 */
Sean Middleditcha63ff7f2009-04-03 01:27:06 -0400244 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q,
245 sizeof(telnet_rfc1143_t) * (telnet->q_size + 4))) == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400246 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400247 "malloc() failed: %s", strerror(errno));
248 return;
249 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400250 memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
Sean Middleditch8b788962009-03-16 01:06:27 -0400251 telnet->q = qtmp;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400252 telnet->q[telnet->q_size].telopt = telopt;
253 telnet->q[telnet->q_size].us = us;
254 telnet->q[telnet->q_size].him = him;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400255 telnet->q_size += 4;
Sean Middleditch8b788962009-03-16 01:06:27 -0400256}
257
Sean Middleditch90e79da2009-03-19 15:17:13 -0400258/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400259static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400260 unsigned char telopt) {
261 char bytes[3] = { TELNET_IAC, cmd, telopt };
262 _send(telnet, bytes, 3);
263}
264
Sean Middleditch8b788962009-03-16 01:06:27 -0400265/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400266static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400267 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400268
269 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400270 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400271 switch ((int)telnet->state) {
272 case TELNET_STATE_WILL:
Sean Middleditche5327da2009-03-21 00:54:50 -0400273 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400274 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400275 case TELNET_STATE_WONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400276 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400277 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400278 case TELNET_STATE_DO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400279 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400280 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400281 case TELNET_STATE_DONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400282 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400283 break;
284 }
285 return;
286 }
287
288 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400289 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400290
291 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400292 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400293 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400294 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400295 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400296 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400297 if (_check_telopt(telnet, telopt, 0)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400298 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400299 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400300 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400301 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400302 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400303 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400304 case Q_WANTNO:
305 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400306 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400307 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400308 "DONT answered by WILL");
309 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400310 case Q_WANTNO_OP:
311 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400312 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400313 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400314 "DONT answered by WILL");
315 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400316 case Q_WANTYES:
317 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400318 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400319 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400320 case Q_WANTYES_OP:
321 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400322 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400323 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400324 break;
325 }
326 break;
327
328 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400329 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400330 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400331 case Q_YES:
332 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400333 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400334 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400335 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400336 case Q_WANTNO:
337 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400338 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400339 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400340 case Q_WANTNO_OP:
341 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400342 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400343 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400344 case Q_WANTYES:
345 case Q_WANTYES_OP:
346 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400347 break;
348 }
349 break;
350
351 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400352 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400353 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400354 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400355 if (_check_telopt(telnet, telopt, 1)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400356 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400357 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400358 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400359 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400360 _send_negotiate(telnet, TELNET_WONT, telopt);
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 Middleditche5327da2009-03-21 00:54:50 -0400364 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400365 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400366 "WONT answered by DO");
367 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400368 case Q_WANTNO_OP:
369 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400370 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400371 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400372 "WONT answered by DO");
373 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400374 case Q_WANTYES:
375 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400376 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400377 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400378 case Q_WANTYES_OP:
379 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400380 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400381 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400382 break;
383 }
384 break;
385
386 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400387 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400388 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400389 case Q_YES:
390 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400391 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400392 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400393 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400394 case Q_WANTNO:
395 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400396 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400397 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400398 case Q_WANTNO_OP:
399 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400400 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400401 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400402 case Q_WANTYES:
403 case Q_WANTYES_OP:
404 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400405 break;
406 }
407 break;
408 }
409}
410
Sean Middleditche5327da2009-03-21 00:54:50 -0400411/* process a subnegotiation buffer; return non-zero if the current buffer
412 * must be aborted and reprocessed due to COMPRESS2 being activated
413 */
414static int _subnegotiate(telnet_t *telnet) {
Sean Middleditchb10946c2009-03-22 18:21:14 -0400415 switch (telnet->sb_telopt) {
416#ifdef HAVE_ZLIB
Sean Middleditche5327da2009-03-21 00:54:50 -0400417 /* received COMPRESS2 begin marker, setup our zlib box and
418 * start handling the compressed stream if it's not already.
419 */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400420 case TELNET_TELOPT_COMPRESS2:
421 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
422 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch8c111792009-03-22 22:44:38 -0400423 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400424
Sean Middleditchb10946c2009-03-22 18:21:14 -0400425 /* standard SB notification */
426 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
427 telnet->buffer, telnet->buffer_pos, 0, 0);
428
429 /* notify app that compression was enabled */
430 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
431 return 1;
432 }
Sean Middleditch8c111792009-03-22 22:44:38 -0400433 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400434#endif /* HAVE_ZLIB */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400435#ifdef HAVE_ALLOCA
Sean Middleditch8c111792009-03-22 22:44:38 -0400436
Sean Middleditchb10946c2009-03-22 18:21:14 -0400437 /* ZMP command */
Sean Middleditch8c111792009-03-22 22:44:38 -0400438 case TELNET_TELOPT_ZMP: {
439 const char **argv, *c;
440 size_t i, argc;
Sean Middleditche5327da2009-03-21 00:54:50 -0400441 /* make sure this is a valid ZMP buffer */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400442 if (telnet->buffer_pos == 0 ||
443 telnet->buffer[telnet->buffer_pos - 1] != 0) {
444 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
445 "incomplete ZMP frame");
446 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
447 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400448 return 0;
Sean Middleditchb10946c2009-03-22 18:21:14 -0400449 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400450
451 /* count arguments */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400452 for (argc = 0, c = telnet->buffer; c != telnet->buffer +
453 telnet->buffer_pos; ++argc)
Sean Middleditche5327da2009-03-21 00:54:50 -0400454 c += strlen(c) + 1;
Sean Middleditche5327da2009-03-21 00:54:50 -0400455
456 /* allocate argument array, bail on error */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400457 if ((argv = (const char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400458 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400459 "alloca() failed: %s", strerror(errno));
Sean Middleditchb10946c2009-03-22 18:21:14 -0400460 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
461 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400462 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400463 }
464
465 /* populate argument array */
466 for (i = 0, c = telnet->buffer; i != argc; ++i) {
467 argv[i] = c;
468 c += strlen(c) + 1;
469 }
470
Sean Middleditchb10946c2009-03-22 18:21:14 -0400471 /* invoke event with our arguments */
472 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
473 telnet->buffer, telnet->buffer_pos, argv, argc);
Sean Middleditch8c111792009-03-22 22:44:38 -0400474 return 0;
475 }
476
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400477 /* any of a number of commands that use the form <BYTE>data<BYTE>data,
478 * including TTYPE, ENVIRON, NEW-ENVIRON, and MSSP
479 */
480 case TELNET_TELOPT_TTYPE:
481 case TELNET_TELOPT_ENVIRON:
482 case TELNET_TELOPT_NEW_ENVIRON:
Sean Middleditch8c111792009-03-22 22:44:38 -0400483 case TELNET_TELOPT_MSSP: {
484 char **argv, *c, *l;
485 size_t i, argc;
486
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400487 /* if we have no data, just pass it through */
488 if (telnet->buffer_pos == 0) {
489 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
490 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400491 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400492 }
493
494 /* very first byte must be in range 0-3 */
495 if ((unsigned)telnet->buffer[0] > 3) {
496 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
497 "telopt %d subneg has invalid data", telnet->sb_telopt);
498 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
499 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400500 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400501 }
502
503 /* count arguments; each argument is preceded by a byte in the
504 * range 0-3, so just count those.
505 * NOTE: we don't support the ENVIRON/NEW-ENVIRON ESC handling
506 * properly at all. guess that's a FIXME.
507 */
508 for (argc = 0, i = 0; i != telnet->buffer_pos; ++i)
509 if ((unsigned)telnet->buffer[i] <= 3)
510 ++argc;
511
512 /* allocate argument array, bail on error */
Sean Middleditch8c111792009-03-22 22:44:38 -0400513 if ((argv = (char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400514 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
515 "alloca() failed: %s", strerror(errno));
516 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
517 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400518 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400519 }
520
521 /* allocate strings in argument array */
522 for (i = 0, l = telnet->buffer; i != argc; ++i) {
Sean Middleditch8c111792009-03-22 22:44:38 -0400523 /* search for end marker */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400524 c = l + 1;
525 while (c != telnet->buffer + telnet->buffer_pos &&
526 (unsigned)*c > 3)
527 ++c;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400528
Sean Middleditch8c111792009-03-22 22:44:38 -0400529 /* allocate space; bail on error */
530 if ((argv[i] = (char *)alloca(c - l + 1)) == 0) {
531 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
532 "alloca() failed: %s", strerror(errno));
533 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
534 telnet->buffer, telnet->buffer_pos, 0, 0);
535 return 0;
536 }
537
538 /* copy data */
539 memcpy(argv[i], l, c - l);
540 argv[i][c - l] = 0;
541
542 /* prepare for next loop */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400543 l = c;
544 }
545
546 /* invoke event with our arguments */
547 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
Sean Middleditch8c111792009-03-22 22:44:38 -0400548 telnet->buffer, telnet->buffer_pos, (const char **)argv, argc);
549 return 0;
550 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400551#endif /* HAVE_ALLOCA */
Sean Middleditch8c111792009-03-22 22:44:38 -0400552
Sean Middleditchb10946c2009-03-22 18:21:14 -0400553 /* other generic subnegotiation */
554 default:
555 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
556 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400557 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400558 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400559}
560
Sean Middleditch29144852009-03-12 23:14:47 -0400561/* initialize a telnet state tracker */
Sean Middleditch34bb0992009-03-21 00:20:44 -0400562void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts,
563 telnet_event_handler_t eh, unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400564 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400565 telnet->ud = user_data;
Sean Middleditch34bb0992009-03-21 00:20:44 -0400566 telnet->telopts = telopts;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400567 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400568 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400569}
570
571/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400572void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400573 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400574 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400575 free(telnet->buffer);
576 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400577 telnet->buffer_size = 0;
578 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400579 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400580
Sean Middleditche5b47592009-03-16 17:37:43 -0400581#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400582 /* free zlib box */
583 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400584 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400585 deflateEnd(telnet->z);
586 else
587 inflateEnd(telnet->z);
588 free(telnet->z);
589 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400590 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400591#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400592
593 /* free RFC1143 queue */
594 if (telnet->q) {
595 free(telnet->q);
596 telnet->q = 0;
597 telnet->q_size = 0;
598 }
Sean Middleditch29144852009-03-12 23:14:47 -0400599}
600
Sean Middleditch51ad6792009-03-13 20:15:59 -0400601/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400602static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400603 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400604 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400605 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400606
Sean Middleditch51ad6792009-03-13 20:15:59 -0400607 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400608 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400609 /* find the next buffer size */
610 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400611 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400612 break;
613 }
614
615 /* overflow -- can't grow any more */
616 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400617 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400618 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400619 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400620 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400621
622 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400623 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400624 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400625 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400626 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400627 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400628 }
629
630 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400631 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400632 }
633
634 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400635 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400636 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400637}
638
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400639static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400640 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400641 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400642 for (i = start = 0; i != size; ++i) {
643 byte = buffer[i];
644 switch (telnet->state) {
645 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400646 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400647 /* on an IAC byte, pass through all pending bytes and
648 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400649 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400650 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400651 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditche5327da2009-03-21 00:54:50 -0400652 i - start, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400653 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400654 }
655 break;
656
657 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400658 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400659 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400660 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400661 case TELNET_SB:
662 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400663 break;
664 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400665 case TELNET_WILL:
666 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400667 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400668 case TELNET_WONT:
669 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400670 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400671 case TELNET_DO:
672 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400673 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400674 case TELNET_DONT:
675 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400676 break;
677 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400678 case TELNET_IAC:
Sean Middleditche5327da2009-03-21 00:54:50 -0400679 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400680 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400681 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400682 break;
683 /* some other command */
684 default:
Sean Middleditche5327da2009-03-21 00:54:50 -0400685 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400686 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400687 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400688 }
689 break;
690
691 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400692 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400693 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400694 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400695 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400696 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400697 start = i + 1;
698 telnet->state = TELNET_STATE_DATA;
699 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400700
Sean Middleditchda0e6952009-03-15 13:28:09 -0400701 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400702 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400703 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400704 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400705 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400706 break;
707
708 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400709 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400710 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400711 if (byte == TELNET_IAC) {
712 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400713 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400714 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400715 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400716 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400717 }
718 break;
719
Sean Middleditch6b372882009-03-14 13:06:47 -0400720 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400721 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400722 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400723 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400724 case TELNET_SE:
Sean Middleditch8c567352009-03-21 01:07:18 -0400725 /* return to default state */
726 start = i + 1;
727 telnet->state = TELNET_STATE_DATA;
728
Sean Middleditche5327da2009-03-21 00:54:50 -0400729 /* process subnegotiation */
730 if (_subnegotiate(telnet) != 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400731 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400732 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400733 * bytes inflated and abort trying to process the
734 * remaining compressed bytes in the current _process
735 * buffer argument
736 */
Sean Middleditch8c567352009-03-21 01:07:18 -0400737 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400738 return;
739 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400740 break;
741 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400742 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400743 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400744 if (_buffer_byte(telnet, TELNET_IAC) !=
745 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400746 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400747 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400748 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400749 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400750 }
751 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400752 /* something else -- protocol error. attempt to process
753 * content in subnegotiation buffer, then evaluate the
754 * given command as an IAC code.
755 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400756 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400757 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400758 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400759 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400760
Sean Middleditchf6daa852009-03-21 01:16:20 -0400761 /* enter IAC state */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400762 start = i + 1;
Sean Middleditchf6daa852009-03-21 01:16:20 -0400763 telnet->state = TELNET_STATE_IAC;
Sean Middleditche5327da2009-03-21 00:54:50 -0400764
765 /* process subnegotiation; see comment in
766 * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
767 */
768 if (_subnegotiate(telnet) != 0) {
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400769 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditche5327da2009-03-21 00:54:50 -0400770 return;
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400771 } else {
772 /* recursive call to get the current input byte processed
773 * as a regular IAC command. we could use a goto, but
774 * that would be gross.
775 */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400776 _process(telnet, (char *)&byte, 1);
Sean Middleditche5327da2009-03-21 00:54:50 -0400777 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400778 break;
779 }
780 break;
781 }
782 }
783
784 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400785 if (telnet->state == TELNET_STATE_DATA && i != start)
Sean Middleditche5327da2009-03-21 00:54:50 -0400786 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start, 0, 0);
Sean Middleditch29144852009-03-12 23:14:47 -0400787}
788
Sean Middleditch9de15982009-03-14 03:35:49 -0400789/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400790void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400791 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400792#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400793 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400794 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400795 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400796 int rs;
797
798 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400799 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400800 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400801 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400802 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400803
804 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400805 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400806 /* reset output buffer */
807
808 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400809 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400810
811 /* process the decompressed bytes on success */
812 if (rs == Z_OK || rs == Z_STREAM_END)
813 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400814 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400815 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400816 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400817 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400818
819 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400820 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400821 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400822
823 /* on error (or on end of stream) disable further inflation */
824 if (rs != Z_OK) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400825 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400826
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400827 inflateEnd(telnet->z);
828 free(telnet->z);
829 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400830 break;
831 }
832 }
833
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400834 /* COMPRESS2 is not negotiated, just process */
835 } else
836#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400837 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400838}
839
Sean Middleditch29144852009-03-12 23:14:47 -0400840/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400841void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400842 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400843 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400844}
845
846/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400847void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400848 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400849 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400850
851 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400852 if (telnet->flags & TELNET_FLAG_PROXY) {
853 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400854 _send(telnet, bytes, 3);
855 return;
856 }
857
858 /* get current option states */
859 q = _get_rfc1143(telnet, telopt);
860
861 switch (cmd) {
862 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400863 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400864 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400865 case Q_NO:
866 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400867 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400868 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400869 case Q_WANTNO:
870 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400871 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400872 case Q_WANTYES_OP:
873 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400874 break;
875 }
876 break;
877
878 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400879 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400880 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400881 case Q_YES:
882 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400883 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400884 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400885 case Q_WANTYES:
886 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400887 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400888 case Q_WANTNO_OP:
889 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400890 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400891 }
892 break;
893
894 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400895 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400896 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400897 case Q_NO:
898 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400899 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400900 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400901 case Q_WANTNO:
902 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400903 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400904 case Q_WANTYES_OP:
905 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400906 break;
907 }
908 break;
909
910 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400911 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400912 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400913 case Q_YES:
914 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400915 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400916 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400917 case Q_WANTYES:
918 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400919 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400920 case Q_WANTNO_OP:
921 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400922 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400923 }
924 break;
925 }
Sean Middleditch29144852009-03-12 23:14:47 -0400926}
927
928/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400929void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400930 size_t size) {
931 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400932
Sean Middleditch29144852009-03-12 23:14:47 -0400933 for (l = i = 0; i != size; ++i) {
934 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400935 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400936 /* dump prior text if any */
937 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400938 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400939 l = i + 1;
940
941 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400942 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400943 }
944 }
945
946 /* send whatever portion of buffer is left */
947 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400948 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400949}
950
Sean Middleditch78943842009-03-19 15:22:06 -0400951/* send subnegotiation header */
Sean Middleditch1443ca42009-03-21 00:21:04 -0400952void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch78943842009-03-19 15:22:06 -0400953 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
954 _send(telnet, sb, 3);
955}
956
957
958/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400959void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400960 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400961 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
962 static const char se[2] = { TELNET_IAC, TELNET_SE };
963
964 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400965 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400966 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400967
968#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400969 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
970 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400971 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400972 if (telnet->flags & TELNET_FLAG_PROXY &&
973 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400974
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400975 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400976 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400977
978 /* notify app that compression was enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -0400979 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400980 }
981#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400982}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400983
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400984void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400985#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400986 static const char compress2[] = { TELNET_IAC, TELNET_SB,
987 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400988
Sean Middleditch124a1c22009-03-15 13:20:03 -0400989 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400990 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400991 return;
992
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400993 /* send compression marker. we send directly to the event handler
994 * instead of passing through _send because _send would result in
995 * the compress marker itself being compressed.
996 */
Sean Middleditche5327da2009-03-21 00:54:50 -0400997 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2), 0, 0);
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400998
999 /* notify app that compression was successfully enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -04001000 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -04001001#endif /* HAVE_ZLIB */
1002}
Sean Middleditchd58f49f2009-03-16 12:49:35 -04001003
Sean Middleditch4a156042009-03-16 17:10:58 -04001004/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001005int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -04001006 static const char CRLF[] = { '\r', '\n' };
1007 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -04001008 char buffer[4096];
1009 va_list va;
1010 int rs, i, l;
1011
1012 /* format */
1013 va_start(va, fmt);
1014 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1015 va_end(va);
1016
1017 /* send */
1018 for (l = i = 0; i != rs; ++i) {
1019 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001020 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -04001021 buffer[i] == '\n') {
1022 /* dump prior portion of text */
1023 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001024 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001025 l = i + 1;
1026
1027 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001028 if (buffer[i] == TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001029 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -04001030 /* automatic translation of \r -> CRNUL */
1031 else if (buffer[i] == '\r')
1032 _send(telnet, CRNUL, 2);
1033 /* automatic translation of \n -> CRLF */
1034 else if (buffer[i] == '\n')
1035 _send(telnet, CRLF, 2);
1036 }
1037 }
1038
1039 /* send whatever portion of buffer is left */
1040 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001041 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001042
1043 return rs;
1044}
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001045
Sean Middleditch7491bf42009-03-20 23:52:18 -04001046/* send formatted data through telnet_send */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001047int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
1048 char buffer[4096];
1049 va_list va;
1050 int rs;
1051
1052 /* format */
1053 va_start(va, fmt);
1054 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1055 va_end(va);
1056
1057 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001058 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001059
1060 return rs;
1061}
Sean Middleditche22b4772009-03-22 16:44:40 -04001062
Sean Middleditcheb950a82009-03-22 23:04:28 -04001063/* send formatted subnegotiation data for TTYPE/ENVIRON/NEW-ENVIRON/MSSP */
1064void telnet_format_sb(telnet_t *telnet, unsigned char telopt,
1065 size_t count, ...) {
1066 va_list va;
1067 size_t i;
1068
1069 /* subnegotiation header */
1070 telnet_begin_sb(telnet, telopt);
1071
1072 /* iterate over the arguments pulling out integers and strings */
1073 va_start(va, count);
1074 for (i = 0; i != count; ++i) {
1075 char t;
1076 const char* s;
1077 t = va_arg(va, int);
1078 s = va_arg(va, const char *);
1079 telnet_send(telnet, &t, 1);
1080 telnet_send(telnet, s, strlen(s));
1081 }
1082 va_end(va);
1083
1084 /* footer */
1085 telnet_finish_sb(telnet);
1086}
1087
Sean Middleditche22b4772009-03-22 16:44:40 -04001088/* send ZMP data */
1089void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) {
1090 size_t i;
1091
1092 /* ZMP header */
1093 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1094
1095 /* send out each argument, including trailing NUL byte */
1096 for (i = 0; i != argc; ++i)
1097 telnet_send(telnet, argv[i], strlen(argv[i] + 1));
1098
1099 /* ZMP footer */
1100 telnet_finish_sb(telnet);
1101}
1102
1103/* send ZMP data using varargs */
1104void telnet_send_zmpv(telnet_t *telnet, ...) {
1105 va_list va;
1106 const char* arg;
1107
1108 /* ZMP header */
1109 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1110
1111 /* send out each argument, including trailing NUL byte */
1112 va_start(va, telnet);
1113 while ((arg = va_arg(va, const char *)) != NULL)
1114 telnet_send(telnet, arg, strlen(arg) + 1);
1115 va_end(va);
1116
1117 /* ZMP footer */
1118 telnet_finish_sb(telnet);
1119}