blob: 95b40dc74ae60e370271f95b941b3422b25a4de7 [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 Middleditch340a51b2009-03-19 02:08:46 -040051static const size_t _buffer_sizes[] = {
Sean Middleditch4d9444d2009-03-13 22:48:05 -040052 0,
53 512,
54 2048,
55 8192,
56 16384,
57};
Sean Middleditch340a51b2009-03-19 02:08:46 -040058static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
Sean Middleditch812358d2009-03-15 23:24:03 -040059 sizeof(_buffer_sizes[0]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -040060
Sean Middleditch34bb0992009-03-21 00:20:44 -040061/* event dispatch helper */
62static INLINE void _event(telnet_t *telnet, telnet_event_type_t type,
Sean Middleditch97a8cb22009-03-16 16:51:41 -040063 unsigned char command, unsigned char telopt,
Sean Middleditche5327da2009-03-21 00:54:50 -040064 const char *buffer, size_t size, const char **argv, size_t argc) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -040065 telnet_event_t ev;
Sean Middleditche5327da2009-03-21 00:54:50 -040066 ev.argv = argv;
67 ev.argc = argc;
Sean Middleditch5b5bc922009-03-15 23:02:10 -040068 ev.buffer = buffer;
69 ev.size = size;
Sean Middleditch637df7f2009-03-15 12:57:32 -040070 ev.type = type;
71 ev.command = command;
72 ev.telopt = telopt;
Sean Middleditch637df7f2009-03-15 12:57:32 -040073
Sean Middleditch9f79cc52009-03-15 13:39:24 -040074 telnet->eh(telnet, &ev, telnet->ud);
Sean Middleditch637df7f2009-03-15 12:57:32 -040075}
76
Sean Middleditchd922c6f2009-03-14 22:35:01 -040077/* error generation function */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040078static telnet_error_t _error(telnet_t *telnet, unsigned line,
79 const char* func, telnet_error_t err, int fatal, const char *fmt,
Sean Middleditchfbe93e32009-03-15 23:39:31 -040080 ...) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -040081 char buffer[512];
82 va_list va;
83
84 /* format error intro */
Sean Middleditch812358d2009-03-15 23:24:03 -040085 snprintf(buffer, sizeof(buffer), "%s:%u in %s: ", __FILE__, line, func);
Sean Middleditchd922c6f2009-03-14 22:35:01 -040086
Sean Middleditchb43c10c2009-03-20 23:21:02 -040087 /* format informational text */
Sean Middleditchd922c6f2009-03-14 22:35:01 -040088 va_start(va, fmt);
89 vsnprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
90 fmt, va);
91 va_end(va);
92
Sean Middleditchb43c10c2009-03-20 23:21:02 -040093 /* send error event to the user */
Sean Middleditchf65f27d2009-03-19 02:32:04 -040094 _event(telnet, fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING, err,
Sean Middleditche5327da2009-03-21 00:54:50 -040095 0, buffer, strlen(buffer), 0, 0);
Sean Middleditchfbe93e32009-03-15 23:39:31 -040096
97 return err;
Sean Middleditchd922c6f2009-03-14 22:35:01 -040098}
99
Sean Middleditche5b47592009-03-16 17:37:43 -0400100#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400101/* initialize the zlib box for a telnet box; if deflate is non-zero, it
102 * initializes zlib for delating (compression), otherwise for inflating
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400103 * (decompression). returns TELNET_EOK on success, something else on
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400104 * failure.
Sean Middleditch72cc9642009-03-15 11:50:36 -0400105 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400106telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400107 z_stream *z;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400108 int rs;
109
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400110 /* if compression is already enabled, fail loudly */
111 if (telnet->z != 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400112 return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400113 err_fatal, "cannot initialize compression twice");
114
Sean Middleditch72cc9642009-03-15 11:50:36 -0400115 /* allocate zstream box */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400116 if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400117 return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
Sean Middleditch16992272009-03-15 19:42:03 -0400118 "malloc() failed: %s", strerror(errno));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400119
120 /* initialize */
121 if (deflate) {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400122 if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
123 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400124 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400125 err_fatal, "deflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400126 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400127 telnet->flags |= TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400128 } else {
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400129 if ((rs = inflateInit(z)) != Z_OK) {
130 free(z);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400131 return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400132 err_fatal, "inflateInit() failed: %s", zError(rs));
Sean Middleditch72cc9642009-03-15 11:50:36 -0400133 }
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400134 telnet->flags &= ~TELNET_PFLAG_DEFLATE;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400135 }
136
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400137 telnet->z = z;
138
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400139 return TELNET_EOK;
Sean Middleditch72cc9642009-03-15 11:50:36 -0400140}
Sean Middleditche5b47592009-03-16 17:37:43 -0400141#endif
Sean Middleditch72cc9642009-03-15 11:50:36 -0400142
Sean Middleditch8b788962009-03-16 01:06:27 -0400143/* push bytes out, compressing them first if need be */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400144static void _send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400145 size_t size) {
Sean Middleditch8b788962009-03-16 01:06:27 -0400146#ifdef HAVE_ZLIB
147 /* if we have a deflate (compression) zlib box, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400148 if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400149 char deflate_buffer[1024];
Sean Middleditch8b788962009-03-16 01:06:27 -0400150 int rs;
151
152 /* initialize z state */
Sean Middleditch97a8cb22009-03-16 16:51:41 -0400153 telnet->z->next_in = (unsigned char *)buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400154 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400155 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400156 telnet->z->avail_out = sizeof(deflate_buffer);
157
158 /* deflate until buffer exhausted and all output is produced */
159 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
160 /* compress */
161 if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400162 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch8b788962009-03-16 01:06:27 -0400163 "deflate() failed: %s", zError(rs));
164 deflateEnd(telnet->z);
165 free(telnet->z);
166 telnet->z = 0;
167 break;
168 }
169
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400170 _event(telnet, TELNET_EV_SEND, 0, 0, deflate_buffer,
Sean Middleditche5327da2009-03-21 00:54:50 -0400171 sizeof(deflate_buffer) - telnet->z->avail_out, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400172
173 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400174 telnet->z->next_out = (unsigned char *)deflate_buffer;
Sean Middleditch8b788962009-03-16 01:06:27 -0400175 telnet->z->avail_out = sizeof(deflate_buffer);
176 }
177
178 /* COMPRESS2 is not negotiated, just send */
179 } else
180#endif /* HAVE_ZLIB */
Sean Middleditche5327da2009-03-21 00:54:50 -0400181 _event(telnet, TELNET_EV_SEND, 0, 0, buffer, size, 0, 0);
Sean Middleditch8b788962009-03-16 01:06:27 -0400182}
183
Sean Middleditch34bb0992009-03-21 00:20:44 -0400184/* check if we support a particular telopt; if us is non-zero, we
185 * check if we (local) supports it, otherwise we check if he (remote)
186 * supports it. return non-zero if supported, zero if not supported.
187 */
188static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
189 int us) {
190 int i;
191
192 /* if we have no telopts table, we obviously don't support it */
193 if (telnet->telopts == 0)
194 return 0;
195
196 /* loop unti found or end marker (us and him both 0) */
Sean Middleditchbfc641e2009-03-22 16:26:06 -0400197 for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
198 if (telnet->telopts[i].telopt == telopt) {
199 if (us && telnet->telopts[i].us == TELNET_WILL)
200 return 1;
201 else if (!us && telnet->telopts[i].him == TELNET_DO)
202 return 1;
203 else
204 return 0;
205 }
206 }
Sean Middleditch34bb0992009-03-21 00:20:44 -0400207
208 /* not found, so not supported */
209 return 0;
210}
211
Sean Middleditch8b788962009-03-16 01:06:27 -0400212/* retrieve RFC1143 option state */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400213static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
214 unsigned char telopt) {
Sean Middleditch4987f9f2009-03-19 12:51:40 -0400215 const telnet_rfc1143_t empty = { telopt, 0, 0};
Sean Middleditch8b788962009-03-16 01:06:27 -0400216 int i;
217
218 /* search for entry */
219 for (i = 0; i != telnet->q_size; ++i)
220 if (telnet->q[i].telopt == telopt)
221 return telnet->q[i];
222
223 /* not found, return empty value */
224 return empty;
225}
226
227/* save RFC1143 option state */
Sean Middleditch7491bf42009-03-20 23:52:18 -0400228static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
229 char us, char him) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400230 telnet_rfc1143_t *qtmp;
Sean Middleditch8b788962009-03-16 01:06:27 -0400231 int i;
232
233 /* search for entry */
234 for (i = 0; i != telnet->q_size; ++i) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400235 if (telnet->q[i].telopt == telopt) {
236 telnet->q[i].us = us;
237 telnet->q[i].him = him;
Sean Middleditch8b788962009-03-16 01:06:27 -0400238 return;
239 }
240 }
241
242 /* we're going to need to track state for it, so grow the queue
Sean Middleditch1e80f522009-03-22 18:24:18 -0400243 * by 4 (four) elements and put the telopt into it; bail on allocation
244 * error. we go by four because it seems like a reasonable guess as
245 * to the number of enabled options for most simple code, and it
246 * allows for an acceptable number of reallocations for complex code.
Sean Middleditch8b788962009-03-16 01:06:27 -0400247 */
Sean Middleditch5dea1f72009-03-19 12:45:34 -0400248 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q, sizeof(
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400249 telnet_rfc1143_t) * (telnet->q_size + 1))) == 0) {
250 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch8b788962009-03-16 01:06:27 -0400251 "malloc() failed: %s", strerror(errno));
252 return;
253 }
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400254 memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
Sean Middleditch8b788962009-03-16 01:06:27 -0400255 telnet->q = qtmp;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400256 telnet->q[telnet->q_size].telopt = telopt;
257 telnet->q[telnet->q_size].us = us;
258 telnet->q[telnet->q_size].him = him;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400259 telnet->q_size += 4;
Sean Middleditch8b788962009-03-16 01:06:27 -0400260}
261
Sean Middleditch90e79da2009-03-19 15:17:13 -0400262/* send negotiation bytes */
Sean Middleditch9b07e922009-03-19 18:18:44 -0400263static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch90e79da2009-03-19 15:17:13 -0400264 unsigned char telopt) {
265 char bytes[3] = { TELNET_IAC, cmd, telopt };
266 _send(telnet, bytes, 3);
267}
268
Sean Middleditch8b788962009-03-16 01:06:27 -0400269/* negotiation handling magic for RFC1143 */
Sean Middleditch330c2612009-03-20 23:29:17 -0400270static void _negotiate(telnet_t *telnet, unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400271 telnet_rfc1143_t q;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400272
273 /* in PROXY mode, just pass it thru and do nothing */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400274 if (telnet->flags & TELNET_FLAG_PROXY) {
Sean Middleditch330c2612009-03-20 23:29:17 -0400275 switch ((int)telnet->state) {
276 case TELNET_STATE_WILL:
Sean Middleditche5327da2009-03-21 00:54:50 -0400277 _event(telnet, TELNET_EV_WILL, 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_WONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400280 _event(telnet, TELNET_EV_WONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400281 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400282 case TELNET_STATE_DO:
Sean Middleditche5327da2009-03-21 00:54:50 -0400283 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400284 break;
Sean Middleditch330c2612009-03-20 23:29:17 -0400285 case TELNET_STATE_DONT:
Sean Middleditche5327da2009-03-21 00:54:50 -0400286 _event(telnet, TELNET_EV_DONT, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400287 break;
288 }
289 return;
290 }
291
292 /* lookup the current state of the option */
Sean Middleditch8b788962009-03-16 01:06:27 -0400293 q = _get_rfc1143(telnet, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400294
295 /* start processing... */
Sean Middleditch330c2612009-03-20 23:29:17 -0400296 switch ((int)telnet->state) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400297 /* request to enable option on remote end or confirm DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400298 case TELNET_STATE_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400299 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400300 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400301 if (_check_telopt(telnet, telopt, 0)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400302 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400303 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400304 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400305 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400306 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400307 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400308 case Q_WANTNO:
309 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400310 _event(telnet, TELNET_EV_WONT, 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_WANTNO_OP:
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 Middleditchf65f27d2009-03-19 02:32:04 -0400317 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400318 "DONT answered by WILL");
319 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400320 case Q_WANTYES:
321 _set_rfc1143(telnet, telopt, q.us, Q_YES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400322 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400323 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400324 case Q_WANTYES_OP:
325 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400326 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400327 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400328 break;
329 }
330 break;
331
332 /* request to disable option on remote end, confirm DONT, reject DO */
Sean Middleditch330c2612009-03-20 23:29:17 -0400333 case TELNET_STATE_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400334 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400335 case Q_YES:
336 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400337 _send_negotiate(telnet, TELNET_DONT, telopt);
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:
341 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditche5327da2009-03-21 00:54:50 -0400342 _event(telnet, TELNET_EV_WONT, 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_WANTNO_OP:
345 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditche5327da2009-03-21 00:54:50 -0400346 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400347 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400348 case Q_WANTYES:
349 case Q_WANTYES_OP:
350 _set_rfc1143(telnet, telopt, q.us, Q_NO);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400351 break;
352 }
353 break;
354
355 /* request to enable option on local end or confirm WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400356 case TELNET_STATE_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400357 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400358 case Q_NO:
Sean Middleditch34bb0992009-03-21 00:20:44 -0400359 if (_check_telopt(telnet, telopt, 1)) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400360 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400361 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch94738a42009-03-22 16:33:22 -0400362 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400363 } else
Sean Middleditch90e79da2009-03-19 15:17:13 -0400364 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400365 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400366 case Q_WANTNO:
367 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400368 _event(telnet, TELNET_EV_DONT, 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_WANTNO_OP:
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 Middleditchf65f27d2009-03-19 02:32:04 -0400375 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400376 "WONT answered by DO");
377 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400378 case Q_WANTYES:
379 _set_rfc1143(telnet, telopt, Q_YES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400380 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400381 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400382 case Q_WANTYES_OP:
383 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400384 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400385 _event(telnet, TELNET_EV_DO, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400386 break;
387 }
388 break;
389
390 /* request to disable option on local end, confirm WONT, reject WILL */
Sean Middleditch330c2612009-03-20 23:29:17 -0400391 case TELNET_STATE_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400392 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400393 case Q_YES:
394 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400395 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditche5327da2009-03-21 00:54:50 -0400396 _event(telnet, TELNET_EV_DONT, 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:
399 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400400 _event(telnet, TELNET_EV_WONT, 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_WANTNO_OP:
403 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditche5327da2009-03-21 00:54:50 -0400404 _event(telnet, TELNET_EV_WILL, 0, telopt, 0, 0, 0, 0);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400405 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400406 case Q_WANTYES:
407 case Q_WANTYES_OP:
408 _set_rfc1143(telnet, telopt, Q_NO, q.him);
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400409 break;
410 }
411 break;
412 }
413}
414
Sean Middleditche5327da2009-03-21 00:54:50 -0400415/* process a subnegotiation buffer; return non-zero if the current buffer
416 * must be aborted and reprocessed due to COMPRESS2 being activated
417 */
418static int _subnegotiate(telnet_t *telnet) {
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400419 const char **argv; /* for ZMP */
420 char **argv2; /* for everything else */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400421 const char *c;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400422 const char *l;
Sean Middleditchb10946c2009-03-22 18:21:14 -0400423 size_t i, argc;
Sean Middleditche5327da2009-03-21 00:54:50 -0400424
Sean Middleditchb10946c2009-03-22 18:21:14 -0400425 switch (telnet->sb_telopt) {
426#ifdef HAVE_ZLIB
Sean Middleditche5327da2009-03-21 00:54:50 -0400427 /* received COMPRESS2 begin marker, setup our zlib box and
428 * start handling the compressed stream if it's not already.
429 */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400430 case TELNET_TELOPT_COMPRESS2:
431 if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
432 if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400433 break;
Sean Middleditche5327da2009-03-21 00:54:50 -0400434
Sean Middleditchb10946c2009-03-22 18:21:14 -0400435 /* standard SB notification */
436 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
437 telnet->buffer, telnet->buffer_pos, 0, 0);
438
439 /* notify app that compression was enabled */
440 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
441 return 1;
442 }
443 break;
Sean Middleditche5327da2009-03-21 00:54:50 -0400444#endif /* HAVE_ZLIB */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400445#ifdef HAVE_ALLOCA
Sean Middleditchb10946c2009-03-22 18:21:14 -0400446 /* ZMP command */
447 case TELNET_TELOPT_ZMP:
Sean Middleditche5327da2009-03-21 00:54:50 -0400448 /* make sure this is a valid ZMP buffer */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400449 if (telnet->buffer_pos == 0 ||
450 telnet->buffer[telnet->buffer_pos - 1] != 0) {
451 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
452 "incomplete ZMP frame");
453 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
454 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400455 break;
Sean Middleditchb10946c2009-03-22 18:21:14 -0400456 }
Sean Middleditche5327da2009-03-21 00:54:50 -0400457
458 /* count arguments */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400459 for (argc = 0, c = telnet->buffer; c != telnet->buffer +
460 telnet->buffer_pos; ++argc)
Sean Middleditche5327da2009-03-21 00:54:50 -0400461 c += strlen(c) + 1;
Sean Middleditche5327da2009-03-21 00:54:50 -0400462
463 /* allocate argument array, bail on error */
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400464 if ((argv = (const char **)alloca(sizeof(char *) * argc)) == 0) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400465 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400466 "alloca() failed: %s", strerror(errno));
Sean Middleditchb10946c2009-03-22 18:21:14 -0400467 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
468 telnet->buffer, telnet->buffer_pos, 0, 0);
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400469 break;
Sean Middleditche5327da2009-03-21 00:54:50 -0400470 }
471
472 /* populate argument array */
473 for (i = 0, c = telnet->buffer; i != argc; ++i) {
474 argv[i] = c;
475 c += strlen(c) + 1;
476 }
477
Sean Middleditchb10946c2009-03-22 18:21:14 -0400478 /* invoke event with our arguments */
479 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
480 telnet->buffer, telnet->buffer_pos, argv, argc);
Sean Middleditchb10946c2009-03-22 18:21:14 -0400481 break;
Sean Middleditch2d5c4992009-03-22 22:21:42 -0400482 /* any of a number of commands that use the form <BYTE>data<BYTE>data,
483 * including TTYPE, ENVIRON, NEW-ENVIRON, and MSSP
484 */
485 case TELNET_TELOPT_TTYPE:
486 case TELNET_TELOPT_ENVIRON:
487 case TELNET_TELOPT_NEW_ENVIRON:
488 case TELNET_TELOPT_MSSP:
489 /* if we have no data, just pass it through */
490 if (telnet->buffer_pos == 0) {
491 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
492 telnet->buffer, telnet->buffer_pos, 0, 0);
493 break;
494 }
495
496 /* very first byte must be in range 0-3 */
497 if ((unsigned)telnet->buffer[0] > 3) {
498 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
499 "telopt %d subneg has invalid data", telnet->sb_telopt);
500 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
501 telnet->buffer, telnet->buffer_pos, 0, 0);
502 break;
503 }
504
505 /* count arguments; each argument is preceded by a byte in the
506 * range 0-3, so just count those.
507 * NOTE: we don't support the ENVIRON/NEW-ENVIRON ESC handling
508 * properly at all. guess that's a FIXME.
509 */
510 for (argc = 0, i = 0; i != telnet->buffer_pos; ++i)
511 if ((unsigned)telnet->buffer[i] <= 3)
512 ++argc;
513
514 /* allocate argument array, bail on error */
515 if ((argv2 = (char **)alloca(sizeof(char *) * argc)) == 0) {
516 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
517 "alloca() failed: %s", strerror(errno));
518 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
519 telnet->buffer, telnet->buffer_pos, 0, 0);
520 break;
521 }
522
523 /* allocate strings in argument array */
524 for (i = 0, l = telnet->buffer; i != argc; ++i) {
525 c = l + 1;
526 while (c != telnet->buffer + telnet->buffer_pos &&
527 (unsigned)*c > 3)
528 ++c;
529 argv2[i] = (char *)alloca(c - l + 1);
530 l = c;
531 /* FIXME: check failure */
532 }
533
534 /* populate argument array */
535 for (i = 0, l = telnet->buffer; i != argc; ++i) {
536 c = l + 1;
537 while (c != telnet->buffer + telnet->buffer_pos &&
538 (unsigned)*c > 3)
539 ++c;
540 memcpy(argv2[i], l, c - l);
541 argv2[i][c - l] = 0;
542 l = c;
543 }
544
545 /* invoke event with our arguments */
546 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
547 telnet->buffer, telnet->buffer_pos, (const char **)argv2, argc);
548 break;
549#endif /* HAVE_ALLOCA */
Sean Middleditchb10946c2009-03-22 18:21:14 -0400550 /* other generic subnegotiation */
551 default:
552 _event(telnet, TELNET_EV_SUBNEGOTIATION, 0, telnet->sb_telopt,
553 telnet->buffer, telnet->buffer_pos, 0, 0);
554 break;
Sean Middleditche5327da2009-03-21 00:54:50 -0400555 }
556
557 return 0;
558}
559
Sean Middleditch29144852009-03-12 23:14:47 -0400560/* initialize a telnet state tracker */
Sean Middleditch34bb0992009-03-21 00:20:44 -0400561void telnet_init(telnet_t *telnet, const telnet_telopt_t *telopts,
562 telnet_event_handler_t eh, unsigned char flags, void *user_data) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400563 memset(telnet, 0, sizeof(telnet_t));
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400564 telnet->ud = user_data;
Sean Middleditch34bb0992009-03-21 00:20:44 -0400565 telnet->telopts = telopts;
Sean Middleditch637df7f2009-03-15 12:57:32 -0400566 telnet->eh = eh;
Sean Middleditch08bb05f2009-03-15 23:29:46 -0400567 telnet->flags = flags;
Sean Middleditch29144852009-03-12 23:14:47 -0400568}
569
570/* free up any memory allocated by a state tracker */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400571void telnet_free(telnet_t *telnet) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400572 /* free sub-request buffer */
Sean Middleditch51ad6792009-03-13 20:15:59 -0400573 if (telnet->buffer != 0) {
Sean Middleditch29144852009-03-12 23:14:47 -0400574 free(telnet->buffer);
575 telnet->buffer = 0;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400576 telnet->buffer_size = 0;
577 telnet->buffer_pos = 0;
Sean Middleditch29144852009-03-12 23:14:47 -0400578 }
Sean Middleditch9de15982009-03-14 03:35:49 -0400579
Sean Middleditche5b47592009-03-16 17:37:43 -0400580#ifdef HAVE_ZLIB
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400581 /* free zlib box */
582 if (telnet->z != 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400583 if (telnet->flags & TELNET_PFLAG_DEFLATE)
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400584 deflateEnd(telnet->z);
585 else
586 inflateEnd(telnet->z);
587 free(telnet->z);
588 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400589 }
Sean Middleditche5b47592009-03-16 17:37:43 -0400590#endif
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400591
592 /* free RFC1143 queue */
593 if (telnet->q) {
594 free(telnet->q);
595 telnet->q = 0;
596 telnet->q_size = 0;
597 }
Sean Middleditch29144852009-03-12 23:14:47 -0400598}
599
Sean Middleditch51ad6792009-03-13 20:15:59 -0400600/* push a byte into the telnet buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400601static telnet_error_t _buffer_byte(telnet_t *telnet,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400602 unsigned char byte) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400603 char *new_buffer;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400604 size_t i;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400605
Sean Middleditch51ad6792009-03-13 20:15:59 -0400606 /* check if we're out of room */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400607 if (telnet->buffer_pos == telnet->buffer_size) {
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400608 /* find the next buffer size */
609 for (i = 0; i != _buffer_sizes_count; ++i) {
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400610 if (_buffer_sizes[i] == telnet->buffer_size)
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400611 break;
612 }
613
614 /* overflow -- can't grow any more */
615 if (i >= _buffer_sizes_count - 1) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400616 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400617 "subnegotiation buffer size limit reached");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400618 return TELNET_EOVERFLOW;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400619 }
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400620
621 /* (re)allocate buffer */
Sean Middleditchb43c10c2009-03-20 23:21:02 -0400622 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400623 if (new_buffer == 0) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400624 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
Sean Middleditch16992272009-03-15 19:42:03 -0400625 "realloc() failed");
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400626 return TELNET_ENOMEM;
Sean Middleditch4d9444d2009-03-13 22:48:05 -0400627 }
628
629 telnet->buffer = new_buffer;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400630 telnet->buffer_size = _buffer_sizes[i + 1];
Sean Middleditch51ad6792009-03-13 20:15:59 -0400631 }
632
633 /* push the byte, all set */
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400634 telnet->buffer[telnet->buffer_pos++] = byte;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400635 return TELNET_EOK;
Sean Middleditch51ad6792009-03-13 20:15:59 -0400636}
637
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400638static void _process(telnet_t *telnet, const char *buffer, size_t size) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400639 unsigned char byte;
Sean Middleditch340a51b2009-03-19 02:08:46 -0400640 size_t i, start;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400641 for (i = start = 0; i != size; ++i) {
642 byte = buffer[i];
643 switch (telnet->state) {
644 /* regular data */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400645 case TELNET_STATE_DATA:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400646 /* on an IAC byte, pass through all pending bytes and
647 * switch states */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400648 if (byte == TELNET_IAC) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400649 if (i != start)
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400650 _event(telnet, TELNET_EV_DATA, 0, 0, &buffer[start],
Sean Middleditche5327da2009-03-21 00:54:50 -0400651 i - start, 0, 0);
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400652 telnet->state = TELNET_STATE_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400653 }
654 break;
655
656 /* IAC command */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400657 case TELNET_STATE_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400658 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400659 /* subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400660 case TELNET_SB:
661 telnet->state = TELNET_STATE_SB;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400662 break;
663 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400664 case TELNET_WILL:
665 telnet->state = TELNET_STATE_WILL;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400666 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400667 case TELNET_WONT:
668 telnet->state = TELNET_STATE_WONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400669 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400670 case TELNET_DO:
671 telnet->state = TELNET_STATE_DO;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400672 break;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400673 case TELNET_DONT:
674 telnet->state = TELNET_STATE_DONT;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400675 break;
676 /* IAC escaping */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400677 case TELNET_IAC:
Sean Middleditche5327da2009-03-21 00:54:50 -0400678 _event(telnet, TELNET_EV_DATA, 0, 0, (char*)&byte, 1, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400679 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400680 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400681 break;
682 /* some other command */
683 default:
Sean Middleditche5327da2009-03-21 00:54:50 -0400684 _event(telnet, TELNET_EV_IAC, byte, 0, 0, 0, 0, 0);
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400685 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400686 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400687 }
688 break;
689
690 /* negotiation commands */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400691 case TELNET_STATE_WILL:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400692 case TELNET_STATE_WONT:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400693 case TELNET_STATE_DO:
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400694 case TELNET_STATE_DONT:
Sean Middleditch330c2612009-03-20 23:29:17 -0400695 _negotiate(telnet, byte);
Sean Middleditch447d3ad2009-03-20 23:22:37 -0400696 start = i + 1;
697 telnet->state = TELNET_STATE_DATA;
698 break;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400699
Sean Middleditchda0e6952009-03-15 13:28:09 -0400700 /* subnegotiation -- determine subnegotiation telopt */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400701 case TELNET_STATE_SB:
Sean Middleditchda0e6952009-03-15 13:28:09 -0400702 telnet->sb_telopt = byte;
Sean Middleditch5b5bc922009-03-15 23:02:10 -0400703 telnet->buffer_pos = 0;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400704 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditchda0e6952009-03-15 13:28:09 -0400705 break;
706
707 /* subnegotiation -- buffer bytes until end request */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400708 case TELNET_STATE_SB_DATA:
Sean Middleditch6b372882009-03-14 13:06:47 -0400709 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400710 if (byte == TELNET_IAC) {
711 telnet->state = TELNET_STATE_SB_DATA_IAC;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400712 /* buffer the byte, or bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400713 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400714 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400715 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400716 }
717 break;
718
Sean Middleditch6b372882009-03-14 13:06:47 -0400719 /* IAC escaping inside a subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400720 case TELNET_STATE_SB_DATA_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400721 switch (byte) {
Sean Middleditch6b372882009-03-14 13:06:47 -0400722 /* end subnegotiation */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400723 case TELNET_SE:
Sean Middleditch8c567352009-03-21 01:07:18 -0400724 /* return to default state */
725 start = i + 1;
726 telnet->state = TELNET_STATE_DATA;
727
Sean Middleditche5327da2009-03-21 00:54:50 -0400728 /* process subnegotiation */
729 if (_subnegotiate(telnet) != 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400730 /* any remaining bytes in the buffer are compressed.
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400731 * we have to re-invoke telnet_recv to get those
Sean Middleditch9de15982009-03-14 03:35:49 -0400732 * bytes inflated and abort trying to process the
733 * remaining compressed bytes in the current _process
734 * buffer argument
735 */
Sean Middleditch8c567352009-03-21 01:07:18 -0400736 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditch9de15982009-03-14 03:35:49 -0400737 return;
738 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400739 break;
740 /* escaped IAC byte */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400741 case TELNET_IAC:
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400742 /* push IAC into buffer */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400743 if (_buffer_byte(telnet, TELNET_IAC) !=
744 TELNET_EOK) {
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400745 start = i + 1;
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400746 telnet->state = TELNET_STATE_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400747 } else {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400748 telnet->state = TELNET_STATE_SB_DATA;
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400749 }
750 break;
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400751 /* something else -- protocol error. attempt to process
752 * content in subnegotiation buffer, then evaluate the
753 * given command as an IAC code.
754 */
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400755 default:
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400756 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400757 "unexpected byte after IAC inside SB: %d",
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400758 byte);
Sean Middleditch2d5f4bf2009-03-20 23:32:47 -0400759
Sean Middleditchf6daa852009-03-21 01:16:20 -0400760 /* enter IAC state */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400761 start = i + 1;
Sean Middleditchf6daa852009-03-21 01:16:20 -0400762 telnet->state = TELNET_STATE_IAC;
Sean Middleditche5327da2009-03-21 00:54:50 -0400763
764 /* process subnegotiation; see comment in
765 * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
766 */
767 if (_subnegotiate(telnet) != 0) {
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400768 telnet_recv(telnet, &buffer[start], size - start);
Sean Middleditche5327da2009-03-21 00:54:50 -0400769 return;
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400770 } else {
771 /* recursive call to get the current input byte processed
772 * as a regular IAC command. we could use a goto, but
773 * that would be gross.
774 */
Sean Middleditch3f96c5c2009-03-21 01:11:46 -0400775 _process(telnet, (char *)&byte, 1);
Sean Middleditche5327da2009-03-21 00:54:50 -0400776 }
Sean Middleditch8b5e2b12009-03-13 23:39:18 -0400777 break;
778 }
779 break;
780 }
781 }
782
783 /* pass through any remaining bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400784 if (telnet->state == TELNET_STATE_DATA && i != start)
Sean Middleditche5327da2009-03-21 00:54:50 -0400785 _event(telnet, TELNET_EV_DATA, 0, 0, buffer + start, i - start, 0, 0);
Sean Middleditch29144852009-03-12 23:14:47 -0400786}
787
Sean Middleditch9de15982009-03-14 03:35:49 -0400788/* push a bytes into the state tracker */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400789void telnet_recv(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400790 size_t size) {
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400791#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400792 /* if we have an inflate (decompression) zlib stream, use it */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400793 if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
Sean Middleditch8daf7742009-03-19 02:05:24 -0400794 char inflate_buffer[4096];
Sean Middleditch9de15982009-03-14 03:35:49 -0400795 int rs;
796
797 /* initialize zlib state */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400798 telnet->z->next_in = (unsigned char*)buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400799 telnet->z->avail_in = size;
Sean Middleditch8daf7742009-03-19 02:05:24 -0400800 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400801 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400802
803 /* inflate until buffer exhausted and all output is produced */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400804 while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
Sean Middleditch9de15982009-03-14 03:35:49 -0400805 /* reset output buffer */
806
807 /* decompress */
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400808 rs = inflate(telnet->z, Z_SYNC_FLUSH);
Sean Middleditch9de15982009-03-14 03:35:49 -0400809
810 /* process the decompressed bytes on success */
811 if (rs == Z_OK || rs == Z_STREAM_END)
812 _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400813 telnet->z->avail_out);
Sean Middleditch9de15982009-03-14 03:35:49 -0400814 else
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400815 _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
Sean Middleditch16992272009-03-15 19:42:03 -0400816 "inflate() failed: %s", zError(rs));
Sean Middleditch9de15982009-03-14 03:35:49 -0400817
818 /* prepare output buffer for next run */
Sean Middleditch8daf7742009-03-19 02:05:24 -0400819 telnet->z->next_out = (unsigned char *)inflate_buffer;
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400820 telnet->z->avail_out = sizeof(inflate_buffer);
Sean Middleditch9de15982009-03-14 03:35:49 -0400821
822 /* on error (or on end of stream) disable further inflation */
823 if (rs != Z_OK) {
Sean Middleditche5327da2009-03-21 00:54:50 -0400824 _event(telnet, TELNET_EV_COMPRESS, 0, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400825
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400826 inflateEnd(telnet->z);
827 free(telnet->z);
828 telnet->z = 0;
Sean Middleditch9de15982009-03-14 03:35:49 -0400829 break;
830 }
831 }
832
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400833 /* COMPRESS2 is not negotiated, just process */
834 } else
835#endif /* HAVE_ZLIB */
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400836 _process(telnet, buffer, size);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400837}
838
Sean Middleditch29144852009-03-12 23:14:47 -0400839/* send an iac command */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400840void telnet_iac(telnet_t *telnet, unsigned char cmd) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400841 char bytes[2] = { TELNET_IAC, cmd };
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400842 _send(telnet, bytes, 2);
Sean Middleditch29144852009-03-12 23:14:47 -0400843}
844
845/* send negotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400846void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
Sean Middleditch8b788962009-03-16 01:06:27 -0400847 unsigned char telopt) {
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400848 telnet_rfc1143_t q;
Sean Middleditch8b788962009-03-16 01:06:27 -0400849
850 /* if we're in proxy mode, just send it now */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400851 if (telnet->flags & TELNET_FLAG_PROXY) {
852 char bytes[3] = { TELNET_IAC, cmd, telopt };
Sean Middleditch8b788962009-03-16 01:06:27 -0400853 _send(telnet, bytes, 3);
854 return;
855 }
856
857 /* get current option states */
858 q = _get_rfc1143(telnet, telopt);
859
860 switch (cmd) {
861 /* advertise willingess to support an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400862 case TELNET_WILL:
Sean Middleditch8b788962009-03-16 01:06:27 -0400863 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400864 case Q_NO:
865 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400866 _send_negotiate(telnet, TELNET_WILL, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400867 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400868 case Q_WANTNO:
869 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400870 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400871 case Q_WANTYES_OP:
872 _set_rfc1143(telnet, telopt, Q_WANTYES, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400873 break;
874 }
875 break;
876
877 /* force turn-off of locally enabled option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400878 case TELNET_WONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400879 switch (q.us) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400880 case Q_YES:
881 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400882 _send_negotiate(telnet, TELNET_WONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400883 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400884 case Q_WANTYES:
885 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400886 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400887 case Q_WANTNO_OP:
888 _set_rfc1143(telnet, telopt, Q_WANTNO, q.him);
Sean Middleditch8b788962009-03-16 01:06:27 -0400889 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400890 }
891 break;
892
893 /* ask remote end to enable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400894 case TELNET_DO:
Sean Middleditch8b788962009-03-16 01:06:27 -0400895 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400896 case Q_NO:
897 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400898 _send_negotiate(telnet, TELNET_DO, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400899 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400900 case Q_WANTNO:
901 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400902 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400903 case Q_WANTYES_OP:
904 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES);
Sean Middleditch8b788962009-03-16 01:06:27 -0400905 break;
906 }
907 break;
908
909 /* demand remote end disable an option */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400910 case TELNET_DONT:
Sean Middleditch8b788962009-03-16 01:06:27 -0400911 switch (q.him) {
Sean Middleditch7491bf42009-03-20 23:52:18 -0400912 case Q_YES:
913 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400914 _send_negotiate(telnet, TELNET_DONT, telopt);
Sean Middleditch8b788962009-03-16 01:06:27 -0400915 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400916 case Q_WANTYES:
917 _set_rfc1143(telnet, telopt, q.us, Q_WANTYES_OP);
Sean Middleditch8b788962009-03-16 01:06:27 -0400918 break;
Sean Middleditch7491bf42009-03-20 23:52:18 -0400919 case Q_WANTNO_OP:
920 _set_rfc1143(telnet, telopt, q.us, Q_WANTNO);
Sean Middleditch8b788962009-03-16 01:06:27 -0400921 break;
Sean Middleditch8b788962009-03-16 01:06:27 -0400922 }
923 break;
924 }
Sean Middleditch29144852009-03-12 23:14:47 -0400925}
926
927/* send non-command data (escapes IAC bytes) */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400928void telnet_send(telnet_t *telnet, const char *buffer,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400929 size_t size) {
930 size_t i, l;
Sean Middleditchc337ba62009-03-16 16:47:27 -0400931
Sean Middleditch29144852009-03-12 23:14:47 -0400932 for (l = i = 0; i != size; ++i) {
933 /* dump prior portion of text, send escaped bytes */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400934 if (buffer[i] == TELNET_IAC) {
Sean Middleditch29144852009-03-12 23:14:47 -0400935 /* dump prior text if any */
936 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400937 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400938 l = i + 1;
939
940 /* send escape */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400941 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch29144852009-03-12 23:14:47 -0400942 }
943 }
944
945 /* send whatever portion of buffer is left */
946 if (i != l)
Sean Middleditch9f79cc52009-03-15 13:39:24 -0400947 _send(telnet, buffer + l, i - l);
Sean Middleditch29144852009-03-12 23:14:47 -0400948}
949
Sean Middleditch78943842009-03-19 15:22:06 -0400950/* send subnegotiation header */
Sean Middleditch1443ca42009-03-21 00:21:04 -0400951void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
Sean Middleditch78943842009-03-19 15:22:06 -0400952 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
953 _send(telnet, sb, 3);
954}
955
956
957/* send complete subnegotiation */
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400958void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
Sean Middleditch340a51b2009-03-19 02:08:46 -0400959 const char *buffer, size_t size) {
Sean Middleditch90e79da2009-03-19 15:17:13 -0400960 const char sb[3] = { TELNET_IAC, TELNET_SB, telopt };
961 static const char se[2] = { TELNET_IAC, TELNET_SE };
962
963 _send(telnet, sb, 3);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400964 telnet_send(telnet, buffer, size);
Sean Middleditch90e79da2009-03-19 15:17:13 -0400965 _send(telnet, se, 2);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400966
967#ifdef HAVE_ZLIB
Sean Middleditch72cc9642009-03-15 11:50:36 -0400968 /* if we're a proxy and we just sent the COMPRESS2 marker, we must
969 * make sure all further data is compressed if not already.
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400970 */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400971 if (telnet->flags & TELNET_FLAG_PROXY &&
972 telopt == TELNET_TELOPT_COMPRESS2) {
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400973
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400974 if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
Sean Middleditchd922c6f2009-03-14 22:35:01 -0400975 return;
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400976
977 /* notify app that compression was enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -0400978 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch61f8eb62009-03-14 04:57:27 -0400979 }
980#endif /* HAVE_ZLIB */
Sean Middleditch29144852009-03-12 23:14:47 -0400981}
Sean Middleditch124a1c22009-03-15 13:20:03 -0400982
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400983void telnet_begin_compress2(telnet_t *telnet) {
Sean Middleditch124a1c22009-03-15 13:20:03 -0400984#ifdef HAVE_ZLIB
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400985 static const char compress2[] = { TELNET_IAC, TELNET_SB,
986 TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
Sean Middleditch124a1c22009-03-15 13:20:03 -0400987
Sean Middleditch124a1c22009-03-15 13:20:03 -0400988 /* attempt to create output stream first, bail if we can't */
Sean Middleditchf65f27d2009-03-19 02:32:04 -0400989 if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
Sean Middleditch124a1c22009-03-15 13:20:03 -0400990 return;
991
Sean Middleditchfbe93e32009-03-15 23:39:31 -0400992 /* send compression marker. we send directly to the event handler
993 * instead of passing through _send because _send would result in
994 * the compress marker itself being compressed.
995 */
Sean Middleditche5327da2009-03-21 00:54:50 -0400996 _event(telnet, TELNET_EV_SEND, 0, 0, compress2, sizeof(compress2), 0, 0);
Sean Middleditch2b4bfc42009-03-16 01:25:52 -0400997
998 /* notify app that compression was successfully enabled */
Sean Middleditche5327da2009-03-21 00:54:50 -0400999 _event(telnet, TELNET_EV_COMPRESS, 1, 0, 0, 0, 0, 0);
Sean Middleditch124a1c22009-03-15 13:20:03 -04001000#endif /* HAVE_ZLIB */
1001}
Sean Middleditchd58f49f2009-03-16 12:49:35 -04001002
Sean Middleditch4a156042009-03-16 17:10:58 -04001003/* send formatted data with \r and \n translation in addition to IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001004int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
Sean Middleditch8daf7742009-03-19 02:05:24 -04001005 static const char CRLF[] = { '\r', '\n' };
1006 static const char CRNUL[] = { '\r', '\0' };
Sean Middleditch4a156042009-03-16 17:10:58 -04001007 char buffer[4096];
1008 va_list va;
1009 int rs, i, l;
1010
1011 /* format */
1012 va_start(va, fmt);
1013 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1014 va_end(va);
1015
1016 /* send */
1017 for (l = i = 0; i != rs; ++i) {
1018 /* special characters */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001019 if (buffer[i] == TELNET_IAC || buffer[i] == '\r' ||
Sean Middleditch4a156042009-03-16 17:10:58 -04001020 buffer[i] == '\n') {
1021 /* dump prior portion of text */
1022 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001023 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001024 l = i + 1;
1025
1026 /* IAC -> IAC IAC */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001027 if (buffer[i] == TELNET_IAC)
Sean Middleditch4f0c37f2009-03-20 23:08:55 -04001028 telnet_iac(telnet, TELNET_IAC);
Sean Middleditch4a156042009-03-16 17:10:58 -04001029 /* automatic translation of \r -> CRNUL */
1030 else if (buffer[i] == '\r')
1031 _send(telnet, CRNUL, 2);
1032 /* automatic translation of \n -> CRLF */
1033 else if (buffer[i] == '\n')
1034 _send(telnet, CRLF, 2);
1035 }
1036 }
1037
1038 /* send whatever portion of buffer is left */
1039 if (i != l)
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001040 _send(telnet, buffer + l, i - l);
Sean Middleditch4a156042009-03-16 17:10:58 -04001041
1042 return rs;
1043}
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001044
Sean Middleditch7491bf42009-03-20 23:52:18 -04001045/* send formatted data through telnet_send */
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001046int telnet_printf2(telnet_t *telnet, const char *fmt, ...) {
1047 char buffer[4096];
1048 va_list va;
1049 int rs;
1050
1051 /* format */
1052 va_start(va, fmt);
1053 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1054 va_end(va);
1055
1056 /* send */
Sean Middleditchb43c10c2009-03-20 23:21:02 -04001057 telnet_send(telnet, buffer, rs);
Sean Middleditchf65f27d2009-03-19 02:32:04 -04001058
1059 return rs;
1060}
Sean Middleditche22b4772009-03-22 16:44:40 -04001061
1062/* send ZMP data */
1063void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) {
1064 size_t i;
1065
1066 /* ZMP header */
1067 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1068
1069 /* send out each argument, including trailing NUL byte */
1070 for (i = 0; i != argc; ++i)
1071 telnet_send(telnet, argv[i], strlen(argv[i] + 1));
1072
1073 /* ZMP footer */
1074 telnet_finish_sb(telnet);
1075}
1076
1077/* send ZMP data using varargs */
1078void telnet_send_zmpv(telnet_t *telnet, ...) {
1079 va_list va;
1080 const char* arg;
1081
1082 /* ZMP header */
1083 telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
1084
1085 /* send out each argument, including trailing NUL byte */
1086 va_start(va, telnet);
1087 while ((arg = va_arg(va, const char *)) != NULL)
1088 telnet_send(telnet, arg, strlen(arg) + 1);
1089 va_end(va);
1090
1091 /* ZMP footer */
1092 telnet_finish_sb(telnet);
1093}