blob: 695d2136c80486676c58b2efe7cb156f1f9d55bc [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
Sean Middleditch2d5c4992009-03-22 22:21:42 -040019#ifdef HAVE_ALLOCA
20#include <alloca.h>
21#endif
22
Sean Middleditch9de15982009-03-14 03:35:49 -040023#ifdef HAVE_ZLIB
Sean Middleditch2d5c4992009-03-22 22:21:42 -040024#include <zlib.h>
Sean Middleditch9de15982009-03-14 03:35:49 -040025#endif
26
Sean Middleditch29144852009-03-12 23:14:47 -040027#include "libtelnet.h"
28
Sean Middleditch9b07e922009-03-19 18:18:44 -040029/* inlinable functions */
30#if __GNUC__ || __STDC_VERSION__ >= 199901L
Sean Middleditch53a86612009-03-22 17:43:29 -040031# define INLINE __inline__
Sean Middleditch9b07e922009-03-19 18:18:44 -040032#else
33# define INLINE
34#endif
35
Sean Middleditch90e79da2009-03-19 15:17:13 -040036/* RFC1143 option negotiation state */
37typedef struct telnet_rfc1143_t {
38 unsigned char telopt;
39 char us:4, him:4;
40} telnet_rfc1143_t;
41
Sean Middleditch5b5bc922009-03-15 23:02:10 -040042/* RFC1143 state names */
Sean Middleditch7491bf42009-03-20 23:52:18 -040043#define Q_NO 0
44#define Q_YES 1
45#define Q_WANTNO 2
46#define Q_WANTYES 3
47#define Q_WANTNO_OP 4
48#define Q_WANTYES_OP 5
Sean Middleditch5b5bc922009-03-15 23:02:10 -040049
Sean Middleditch4d9444d2009-03-13 22:48:05 -040050/* buffer sizes */
Sean Middleditch8c111792009-03-22 22:44:38 -040051static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
Sean Middleditch340a51b2009-03-19 02:08:46 -040052static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040053 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040054
Sean Middleditch34bb0992009-03-21 00:20:44 -040055/* event dispatch helper */
56static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040057 unsigned char command, unsigned char telopt,
Sean Middleditche5327da2009-03-21 00:54:50 -040058 const char *buffer, size_t size, const char **argv, size_t argc) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040059 telnet_event_t ev;
Sean Middleditche5327da2009-03-21 00:54:50 -040060 ev.argv = argv;
61 ev.argc = argc;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040062 ev.buffer = buffer;
63 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040064 ev.type = type;
65 ev.command = command;
66 ev.telopt = telopt;
Sean Middleditch637df7f2009-03-15 12:57:32 -040067
Sean Middleditch9f79cc52009-03-15 13:39:24 -040068 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch637df7f2009-03-15 12:57:32 -040069}
70
Sean Middleditchd922c6f2009-03-14 22:35:01 -040071/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040072static telnet_error_t _error(telnet_t *telnet, unsigned line,
73 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040074 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040075 char buffer[512];
76 va_list va;
77
78 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040079 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040080
Sean Middleditchb43c10c2009-03-20 23:21:02 -040081 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -040082 va_start(va, fmt);
83 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
84 fmt, va);
85 va_end(va);
86
Sean Middleditchb43c10c2009-03-20 23:21:02 -040087 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040088 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditche5327da2009-03-21 00:54:50 -040089 0, buffer, strlen(buffer), 0, 0);
Sean Middleditchfbe93e32009-03-15 23:39:31 -040090
91 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040092}
93
Sean Middleditche5b47592009-03-16 17:37:43 -040094#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -040095/* initialize the zlib box for a telnet box; if deflate is non-zero, it
96 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -040097 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -040098 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -040099 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400100telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400101 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400102 int rs;
103
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400104 /* if compression is already enabled, fail loudly */
105 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400106 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400107 err_fatal, "cannot initialize compression twice");
108
Sean Middleditch72cc9642009-03-15 11:50:36 -0400109 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400110 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400111 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400112 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400113
114 /* initialize */
115 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400116 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
117 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400118 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400119 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400120 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400121 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400122 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400123 if ((rs = inflateInit(z)) != Z_OK) {
124 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400125 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400126 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400127 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400128 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400129 }
130
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400131 telnet->z = z;
132
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400133 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400134}
Sean Middleditche5b47592009-03-16 17:37:43 -0400135#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400136
Sean Middleditch8b788962009-03-16 01:06:27 -0400137/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400138static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400139 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400140#ifdef HAVE_ZLIB
141 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400142 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400143 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400144 int rs;
145
146 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400147 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400148 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400149 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400150 telnet->z->avail_out = sizeof(deflate_buffer);
151
152 /* deflate until buffer exhausted and all output is produced */
153 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
154 /* compress */
155 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400156 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400157 "deflate() failed: %s", zError(rs));
158 deflateEnd(telnet->z);
159 free(telnet->z);
160 telnet->z = 0;
161 break;
162 }
163
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400164 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditche5327da2009-03-21 00:54:50 -0400165 sizeof(deflate_buffer) - telnet->z->avail_out, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400166
167 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400168 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400169 telnet->z->avail_out = sizeof(deflate_buffer);
170 }
171
172 /* COMPRESS2 is not negotiated, just send */
173 } else
174#endif /* HAVE_ZLIB */
Sean Middleditche5327da2009-03-21 00:54:50 -0400175 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400176}
177
Sean Middleditch34bb0992009-03-21 00:20:44 -0400178/* check if we support a particular telopt; if us is non-zero, we
179 * check if we (local) supports it, otherwise we check if he (remote)
180 * supports it. return non-zero if supported, zero if not supported.
181 */
182static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
183 int us) {
184 int i;
185
186 /* if we have no telopts table, we obviously don't support it */
187 if (telnet->telopts == 0)
188 return 0;
189
190 /* loop unti found or end marker (us and him both 0) */
Sean Middleditchbfc641e2009-03-22 16:26:06 -0400191 for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
192 if (telnet->telopts[i].telopt == telopt) {
193 if (us && telnet->telopts[i].us == TELNET_WILL)
194 return 1;
195 else if (!us && telnet->telopts[i].him == TELNET_DO)
196 return 1;
197 else
198 return 0;
199 }
200 }
Sean Middleditch34bb0992009-03-21 00:20:44 -0400201
202 /* not found, so not supported */
203 return 0;
204}
205
Sean Middleditch8b788962009-03-16 01:06:27 -0400206/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400207static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
208 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400209 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400210 int i;
211
212 /* search for entry */
213 for (i = 0; i != telnet->q_size; ++i)
214 if (telnet->q[i].telopt == telopt)
215 return telnet->q[i];
216
217 /* not found, return empty value */
218 return empty;
219}
220
221/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400222static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
223 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400224 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400225 int i;
226
227 /* search for entry */
228 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400229 if (telnet->q[i].telopt == telopt) {
230 telnet->q[i].us = us;
231 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400232 return;
233 }
234 }
235
236 /* we're going to need to track state for it, so grow the queue
Sean Middleditch1e80f522009-03-22 18:24:18 -0400237 * by 4 (four) elements and put the telopt into it; bail on allocation
238 * error. we go by four because it seems like a reasonable guess as
239 * to the number of enabled options for most simple code, and it
240 * allows for an acceptable number of reallocations for complex code.
Sean Middleditch8b788962009-03-16 01:06:27 -0400241 */
Sean Middleditch5dea1f72009-03-19 12:45:34 -0400242 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400243 telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
244 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400245 "malloc() failed: %s", strerror(errno));
246 return;
247 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400248 memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
Sean Middleditch8b788962009-03-16 01:06:27 -0400249 telnet->q = qtmp;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400250 telnet->q[telnet->q_size].telopt = telopt;
251 telnet->q[telnet->q_size].us = us;
252 telnet->q[telnet->q_size].him = him;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400253 telnet->q_size += 4;
Sean Middleditch8b788962009-03-16 01:06:27 -0400254}
255
Sean Middleditch90e79da2009-03-19 15:17:13 -0400256/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400257static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400258 unsigned char telopt) {
259 char bytes[3] = { TELNET_IAC, cmd, telopt };
260 _send(telnet, bytes, 3);
261}
262
Sean Middleditch8b788962009-03-16 01:06:27 -0400263/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400264static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400265 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400266
267 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400268 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400269 switch ((int)telnet->state) {
270 case TELNET_STATE_WILL:
Sean Middleditche5327da2009-03-21 00:54:50 -0400271 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400272 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400273 case TELNET_STATE_WONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400274 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400275 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400276 case TELNET_STATE_DO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400277 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400278 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400279 case TELNET_STATE_DONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400280 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400281 break;
282 }
283 return;
284 }
285
286 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400287 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400288
289 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400290 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400291 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400292 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400293 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400294 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400295 if (_check_telopt(telnet, telopt, 0)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400296 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400297 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400298 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400299 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400300 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400301 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400302 case Q_WANTNO:
303 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400304 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400305 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400306 "DONT answered by WILL");
307 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400308 case Q_WANTNO_OP:
309 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400310 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400311 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400312 "DONT answered by WILL");
313 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400314 case Q_WANTYES:
315 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400316 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400317 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400318 case Q_WANTYES_OP:
319 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400320 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400321 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400322 break;
323 }
324 break;
325
326 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400327 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400328 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400329 case Q_YES:
330 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400331 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400332 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400333 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400334 case Q_WANTNO:
335 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400336 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400337 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400338 case Q_WANTNO_OP:
339 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400340 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400341 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400342 case Q_WANTYES:
343 case Q_WANTYES_OP:
344 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400345 break;
346 }
347 break;
348
349 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400350 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400351 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400352 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400353 if (_check_telopt(telnet, telopt, 1)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400354 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400355 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400356 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400357 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400358 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400359 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400360 case Q_WANTNO:
361 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400362 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400363 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400364 "WONT answered by DO");
365 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400366 case Q_WANTNO_OP:
367 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400368 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400369 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400370 "WONT answered by DO");
371 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400372 case Q_WANTYES:
373 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400374 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400375 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400376 case Q_WANTYES_OP:
377 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400378 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400379 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400380 break;
381 }
382 break;
383
384 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400385 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400386 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400387 case Q_YES:
388 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400389 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400390 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400391 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400392 case Q_WANTNO:
393 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400394 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400395 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400396 case Q_WANTNO_OP:
397 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400398 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400399 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400400 case Q_WANTYES:
401 case Q_WANTYES_OP:
402 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400403 break;
404 }
405 break;
406 }
407}
408
Sean Middleditche5327da2009-03-21 00:54:50 -0400409/* process a subnegotiation buffer; return non-zero if the current buffer
410 * must be aborted and reprocessed due to COMPRESS2 being activated
411 */
412static int _subnegotiate(telnet_t *telnet) {
Sean Middleditchb10946c2009-03-22 18:21:14 -0400413 switch (telnet->sb_telopt) {
414#ifdef HAVE_ZLIB
Sean Middleditche5327da2009-03-21 00:54:50 -0400415 /* received COMPRESS2 begin marker, setup our zlib box and
416 * start handling the compressed stream if it's not already.
417 */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400418 case TELNET_TELOPT_COMPRESS2:
419 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
420 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch8c111792009-03-22 22:44:38 -0400421 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400422
Sean Middleditchb10946c2009-03-22 18:21:14 -0400423 /* standard SB notification */
424 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
425 telnet->buffer, telnet->buffer_pos, 0, 0);
426
427 /* notify app that compression was enabled */
428 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
429 return 1;
430 }
Sean Middleditch8c111792009-03-22 22:44:38 -0400431 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400432#endif /* HAVE_ZLIB */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400433#ifdef HAVE_ALLOCA
Sean Middleditch8c111792009-03-22 22:44:38 -0400434
Sean Middleditchb10946c2009-03-22 18:21:14 -0400435 /* ZMP command */
Sean Middleditch8c111792009-03-22 22:44:38 -0400436 case TELNET_TELOPT_ZMP: {
437 const char **argv, *c;
438 size_t i, argc;
Sean Middleditche5327da2009-03-21 00:54:50 -0400439 /* make sure this is a valid ZMP buffer */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400440 if (telnet->buffer_pos == 0 ||
441 telnet->buffer[telnet->buffer_pos - 1] != 0) {
442 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
443 "incomplete ZMP frame");
444 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
445 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400446 return 0;
Sean Middleditchb10946c2009-03-22 18:21:14 -0400447 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400448
449 /* count arguments */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400450 for (argc = 0, c = telnet->buffer; c != telnet->buffer +
451 telnet->buffer_pos; ++argc)
Sean Middleditche5327da2009-03-21 00:54:50 -0400452 c += strlen(c) + 1;
Sean Middleditche5327da2009-03-21 00:54:50 -0400453
454 /* allocate argument array, bail on error */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400455 if ((argv = (const char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400456 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400457 "alloca() failed: %s", strerror(errno));
Sean Middleditchb10946c2009-03-22 18:21:14 -0400458 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
459 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400460 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400461 }
462
463 /* populate argument array */
464 for (i = 0, c = telnet->buffer; i != argc; ++i) {
465 argv[i] = c;
466 c += strlen(c) + 1;
467 }
468
Sean Middleditchb10946c2009-03-22 18:21:14 -0400469 /* invoke event with our arguments */
470 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
471 telnet->buffer, telnet->buffer_pos, argv, argc);
Sean Middleditch8c111792009-03-22 22:44:38 -0400472 return 0;
473 }
474
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400475 /* any of a number of commands that use the form <BYTE>data<BYTE>data,
476 * including TTYPE, ENVIRON, NEW-ENVIRON, and MSSP
477 */
478 case TELNET_TELOPT_TTYPE:
479 case TELNET_TELOPT_ENVIRON:
480 case TELNET_TELOPT_NEW_ENVIRON:
Sean Middleditch8c111792009-03-22 22:44:38 -0400481 case TELNET_TELOPT_MSSP: {
482 char **argv, *c, *l;
483 size_t i, argc;
484
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400485 /* if we have no data, just pass it through */
486 if (telnet->buffer_pos == 0) {
487 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
488 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400489 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400490 }
491
492 /* very first byte must be in range 0-3 */
493 if ((unsigned)telnet->buffer[0] > 3) {
494 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
495 "telopt %d subneg has invalid data", telnet->sb_telopt);
496 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
497 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400498 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400499 }
500
501 /* count arguments; each argument is preceded by a byte in the
502 * range 0-3, so just count those.
503 * NOTE: we don't support the ENVIRON/NEW-ENVIRON ESC handling
504 * properly at all. guess that's a FIXME.
505 */
506 for (argc = 0, i = 0; i != telnet->buffer_pos; ++i)
507 if ((unsigned)telnet->buffer[i] <= 3)
508 ++argc;
509
510 /* allocate argument array, bail on error */
Sean Middleditch8c111792009-03-22 22:44:38 -0400511 if ((argv = (char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400512 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
513 "alloca() failed: %s", strerror(errno));
514 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
515 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400516 return 0;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400517 }
518
519 /* allocate strings in argument array */
520 for (i = 0, l = telnet->buffer; i != argc; ++i) {
Sean Middleditch8c111792009-03-22 22:44:38 -0400521 /* search for end marker */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400522 c = l + 1;
523 while (c != telnet->buffer + telnet->buffer_pos &&
524 (unsigned)*c > 3)
525 ++c;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400526
Sean Middleditch8c111792009-03-22 22:44:38 -0400527 /* allocate space; bail on error */
528 if ((argv[i] = (char *)alloca(c - l + 1)) == 0) {
529 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
530 "alloca() failed: %s", strerror(errno));
531 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
532 telnet->buffer, telnet->buffer_pos, 0, 0);
533 return 0;
534 }
535
536 /* copy data */
537 memcpy(argv[i], l, c - l);
538 argv[i][c - l] = 0;
539
540 /* prepare for next loop */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400541 l = c;
542 }
543
544 /* invoke event with our arguments */
545 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
Sean Middleditch8c111792009-03-22 22:44:38 -0400546 telnet->buffer, telnet->buffer_pos, (const char **)argv, argc);
547 return 0;
548 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400549#endif /* HAVE_ALLOCA */
Sean Middleditch8c111792009-03-22 22:44:38 -0400550
Sean Middleditchb10946c2009-03-22 18:21:14 -0400551 /* other generic subnegotiation */
552 default:
553 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
554 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch8c111792009-03-22 22:44:38 -0400555 return 0;
Sean Middleditche5327da2009-03-21 00:54:50 -0400556 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400557}
558
Sean Middleditch29144852009-03-12 23:14:47 -0400559/* initialize a telnet state tracker */
Sean Middleditch34bb0992009-03-21 00:20:44 -0400560void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts,
561 telnet_event_handler_t eh, unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400562 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400563 telnet->ud = user_data;
Sean Middleditch34bb0992009-03-21 00:20:44 -0400564 telnet->telopts = telopts;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400565 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400566 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400567}
568
569/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400570void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400571 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400572 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400573 free(telnet->buffer);
574 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400575 telnet->buffer_size = 0;
576 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400577 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400578
Sean Middleditche5b47592009-03-16 17:37:43 -0400579#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400580 /* free zlib box */
581 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400582 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400583 deflateEnd(telnet->z);
584 else
585 inflateEnd(telnet->z);
586 free(telnet->z);
587 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400588 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400589#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400590
591 /* free RFC1143 queue */
592 if (telnet->q) {
593 free(telnet->q);
594 telnet->q = 0;
595 telnet->q_size = 0;
596 }
Sean Middleditch29144852009-03-12 23:14:47 -0400597}
598
Sean Middleditch51ad6792009-03-13 20:15:59 -0400599/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400600static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400601 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400602 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400603 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400604
Sean Middleditch51ad6792009-03-13 20:15:59 -0400605 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400606 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400607 /* find the next buffer size */
608 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400609 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400610 break;
611 }
612
613 /* overflow -- can't grow any more */
614 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400615 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400616 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400617 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400618 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400619
620 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400621 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400622 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400623 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400624 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400625 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400626 }
627
628 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400629 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400630 }
631
632 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400633 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400634 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400635}
636
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400637static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400638 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400639 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400640 for (i = start = 0; i != size; ++i) {
641 byte = buffer[i];
642 switch (telnet->state) {
643 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400644 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400645 /* on an IAC byte, pass through all pending bytes and
646 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400647 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400648 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400649 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditche5327da2009-03-21 00:54:50 -0400650 i - start, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400651 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400652 }
653 break;
654
655 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400656 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400657 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400658 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400659 case TELNET_SB:
660 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400661 break;
662 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400663 case TELNET_WILL:
664 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400665 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400666 case TELNET_WONT:
667 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400668 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400669 case TELNET_DO:
670 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400671 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400672 case TELNET_DONT:
673 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400674 break;
675 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400676 case TELNET_IAC:
Sean Middleditche5327da2009-03-21 00:54:50 -0400677 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400678 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400679 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400680 break;
681 /* some other command */
682 default:
Sean Middleditche5327da2009-03-21 00:54:50 -0400683 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400684 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400685 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400686 }
687 break;
688
689 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400690 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400691 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400692 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400693 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400694 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400695 start = i + 1;
696 telnet->state = TELNET_STATE_DATA;
697 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400698
Sean Middleditchda0e6952009-03-15 13:28:09 -0400699 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400700 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400701 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400702 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400703 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400704 break;
705
706 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400707 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400708 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400709 if (byte == TELNET_IAC) {
710 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400711 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400712 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400713 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400714 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400715 }
716 break;
717
Sean Middleditch6b372882009-03-14 13:06:47 -0400718 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400719 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400720 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400721 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400722 case TELNET_SE:
Sean Middleditch8c567352009-03-21 01:07:18 -0400723 /* return to default state */
724 start = i + 1;
725 telnet->state = TELNET_STATE_DATA;
726
Sean Middleditche5327da2009-03-21 00:54:50 -0400727 /* process subnegotiation */
728 if (_subnegotiate(telnet) != 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400729 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400730 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400731 * bytes inflated and abort trying to process the
732 * remaining compressed bytes in the current _process
733 * buffer argument
734 */
Sean Middleditch8c567352009-03-21 01:07:18 -0400735 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400736 return;
737 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400738 break;
739 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400740 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400741 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400742 if (_buffer_byte(telnet, TELNET_IAC) !=
743 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400744 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400745 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400746 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400747 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400748 }
749 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400750 /* something else -- protocol error. attempt to process
751 * content in subnegotiation buffer, then evaluate the
752 * given command as an IAC code.
753 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400754 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400755 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400756 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400757 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400758
Sean Middleditchf6daa852009-03-21 01:16:20 -0400759 /* enter IAC state */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400760 start = i + 1;
Sean Middleditchf6daa852009-03-21 01:16:20 -0400761 telnet->state = TELNET_STATE_IAC;
Sean Middleditche5327da2009-03-21 00:54:50 -0400762
763 /* process subnegotiation; see comment in
764 * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
765 */
766 if (_subnegotiate(telnet) != 0) {
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400767 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditche5327da2009-03-21 00:54:50 -0400768 return;
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400769 } else {
770 /* recursive call to get the current input byte processed
771 * as a regular IAC command. we could use a goto, but
772 * that would be gross.
773 */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400774 _process(telnet, (char *)&byte, 1);
Sean Middleditche5327da2009-03-21 00:54:50 -0400775 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400776 break;
777 }
778 break;
779 }
780 }
781
782 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400783 if (telnet->state == TELNET_STATE_DATA && i != start)
Sean Middleditche5327da2009-03-21 00:54:50 -0400784 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start, 0, 0);
Sean Middleditch29144852009-03-12 23:14:47 -0400785}
786
Sean Middleditch9de15982009-03-14 03:35:49 -0400787/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400788void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400789 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400790#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400791 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400792 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400793 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400794 int rs;
795
796 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400797 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400798 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400799 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400800 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400801
802 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400803 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400804 /* reset output buffer */
805
806 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400807 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400808
809 /* process the decompressed bytes on success */
810 if (rs == Z_OK || rs == Z_STREAM_END)
811 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400812 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400813 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400814 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400815 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400816
817 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400818 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400819 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400820
821 /* on error (or on end of stream) disable further inflation */
822 if (rs != Z_OK) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400823 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400824
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400825 inflateEnd(telnet->z);
826 free(telnet->z);
827 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400828 break;
829 }
830 }
831
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400832 /* COMPRESS2 is not negotiated, just process */
833 } else
834#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400835 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400836}
837
Sean Middleditch29144852009-03-12 23:14:47 -0400838/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400839void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400840 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400841 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400842}
843
844/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400845void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400846 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400847 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400848
849 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400850 if (telnet->flags & TELNET_FLAG_PROXY) {
851 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400852 _send(telnet, bytes, 3);
853 return;
854 }
855
856 /* get current option states */
857 q = _get_rfc1143(telnet, telopt);
858
859 switch (cmd) {
860 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400861 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400862 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400863 case Q_NO:
864 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400865 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400866 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400867 case Q_WANTNO:
868 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400869 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400870 case Q_WANTYES_OP:
871 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400872 break;
873 }
874 break;
875
876 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400877 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400878 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400879 case Q_YES:
880 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400881 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400882 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400883 case Q_WANTYES:
884 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400885 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400886 case Q_WANTNO_OP:
887 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400888 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400889 }
890 break;
891
892 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400893 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400894 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400895 case Q_NO:
896 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400897 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400898 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400899 case Q_WANTNO:
900 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400901 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400902 case Q_WANTYES_OP:
903 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400904 break;
905 }
906 break;
907
908 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400909 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400910 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400911 case Q_YES:
912 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400913 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400914 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400915 case Q_WANTYES:
916 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400917 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400918 case Q_WANTNO_OP:
919 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400920 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400921 }
922 break;
923 }
Sean Middleditch29144852009-03-12 23:14:47 -0400924}
925
926/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400927void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400928 size_t size) {
929 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400930
Sean Middleditch29144852009-03-12 23:14:47 -0400931 for (l = i = 0; i != size; ++i) {
932 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400933 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400934 /* dump prior text if any */
935 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400936 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400937 l = i + 1;
938
939 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400940 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400941 }
942 }
943
944 /* send whatever portion of buffer is left */
945 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400946 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400947}
948
Sean Middleditch78943842009-03-19 15:22:06 -0400949/* send subnegotiation header */
Sean Middleditch1443ca42009-03-21 00:21:04 -0400950void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch78943842009-03-19 15:22:06 -0400951 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
952 _send(telnet, sb, 3);
953}
954
955
956/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400957void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400958 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400959 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
960 static const char se[2] = { TELNET_IAC, TELNET_SE };
961
962 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400963 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400964 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400965
966#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400967 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
968 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400969 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400970 if (telnet->flags & TELNET_FLAG_PROXY &&
971 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400972
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400973 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400974 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400975
976 /* notify app that compression was enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -0400977 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400978 }
979#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400980}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400981
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400982void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400983#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400984 static const char compress2[] = { TELNET_IAC, TELNET_SB,
985 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400986
Sean Middleditch124a1c22009-03-15 13:20:03 -0400987 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400988 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400989 return;
990
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400991 /* send compression marker. we send directly to the event handler
992 * instead of passing through _send because _send would result in
993 * the compress marker itself being compressed.
994 */
Sean Middleditche5327da2009-03-21 00:54:50 -0400995 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2), 0, 0);
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400996
997 /* notify app that compression was successfully enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -0400998 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -0400999#endif /* HAVE_ZLIB */
1000}
Sean Middleditchd58f49f2009-03-16 12:49:35 -04001001
Sean Middleditch4a156042009-03-16 17:10:58 -04001002/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001003int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -04001004 static const char CRLF[] = { '\r', '\n' };
1005 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -04001006 char buffer[4096];
1007 va_list va;
1008 int rs, i, l;
1009
1010 /* format */
1011 va_start(va, fmt);
1012 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1013 va_end(va);
1014
1015 /* send */
1016 for (l = i = 0; i != rs; ++i) {
1017 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001018 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -04001019 buffer[i] == '\n') {
1020 /* dump prior portion of text */
1021 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001022 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001023 l = i + 1;
1024
1025 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001026 if (buffer[i] == TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001027 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -04001028 /* automatic translation of \r -> CRNUL */
1029 else if (buffer[i] == '\r')
1030 _send(telnet, CRNUL, 2);
1031 /* automatic translation of \n -> CRLF */
1032 else if (buffer[i] == '\n')
1033 _send(telnet, CRLF, 2);
1034 }
1035 }
1036
1037 /* send whatever portion of buffer is left */
1038 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001039 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001040
1041 return rs;
1042}
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001043
Sean Middleditch7491bf42009-03-20 23:52:18 -04001044/* send formatted data through telnet_send */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001045int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
1046 char buffer[4096];
1047 va_list va;
1048 int rs;
1049
1050 /* format */
1051 va_start(va, fmt);
1052 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1053 va_end(va);
1054
1055 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001056 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001057
1058 return rs;
1059}
Sean Middleditche22b4772009-03-22 16:44:40 -04001060
Sean Middleditcheb950a82009-03-22 23:04:28 -04001061/* send formatted subnegotiation data for TTYPE/ENVIRON/NEW-ENVIRON/MSSP */
1062void telnet_format_sb(telnet_t *telnet, unsigned char telopt,
1063 size_t count, ...) {
1064 va_list va;
1065 size_t i;
1066
1067 /* subnegotiation header */
1068 telnet_begin_sb(telnet, telopt);
1069
1070 /* iterate over the arguments pulling out integers and strings */
1071 va_start(va, count);
1072 for (i = 0; i != count; ++i) {
1073 char t;
1074 const char* s;
1075 t = va_arg(va, int);
1076 s = va_arg(va, const char *);
1077 telnet_send(telnet, &t, 1);
1078 telnet_send(telnet, s, strlen(s));
1079 }
1080 va_end(va);
1081
1082 /* footer */
1083 telnet_finish_sb(telnet);
1084}
1085
Sean Middleditche22b4772009-03-22 16:44:40 -04001086/* send ZMP data */
1087void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) {
1088 size_t i;
1089
1090 /* ZMP header */
1091 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1092
1093 /* send out each argument, including trailing NUL byte */
1094 for (i = 0; i != argc; ++i)
1095 telnet_send(telnet, argv[i], strlen(argv[i] + 1));
1096
1097 /* ZMP footer */
1098 telnet_finish_sb(telnet);
1099}
1100
1101/* send ZMP data using varargs */
1102void telnet_send_zmpv(telnet_t *telnet, ...) {
1103 va_list va;
1104 const char* arg;
1105
1106 /* ZMP header */
1107 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1108
1109 /* send out each argument, including trailing NUL byte */
1110 va_start(va, telnet);
1111 while ((arg = va_arg(va, const char *)) != NULL)
1112 telnet_send(telnet, arg, strlen(arg) + 1);
1113 va_end(va);
1114
1115 /* ZMP footer */
1116 telnet_finish_sb(telnet);
1117}