1 /*
2 * vim: filetype=c:tabstop=4:ai:expandtab
3 * SPDX-License-Identifier: CC-PDDC or MIT-0
4 * SPDX-FileCopyrightText: Public domain or The DPS8M Development Team
5 * scspell-id: 845a8edb-f62f-11ec-a0d8-80ee73e9b8e7
6 *
7 * ---------------------------------------------------------------------------
8 *
9 * libTELNET - TELNET protocol handling library
10 *
11 * Sean Middleditch <sean@sourcemud.org>
12 *
13 * The author or authors of this code dedicate any and all copyright
14 * interest in this code to the public domain. We make this dedication
15 * for the benefit of the public at large and to the detriment of our heirs
16 * and successors. We intend this dedication to be an overt act of
17 * relinquishment in perpetuity of all present and future rights to this
18 * code under copyright law.
19 *
20 * ---------------------------------------------------------------------------
21 */
22
23 /*
24 * The person or persons who have associated work with this document
25 * (the "Dedicator" or "Certifier") hereby either (a) certifies that,
26 * to the best of his knowledge, the work of authorship identified
27 * is in the public domain of the country from which the work is
28 * published, or (b) hereby dedicates whatever copyright the dedicators
29 * holds in the work of authorship identified below (the "Work") to the
30 * public domain. A certifier, moreover, dedicates any copyright
31 * interest he may have in the associated work, and for these purposes,
32 * is described as a "dedicator" below.
33 *
34 * A certifier has taken reasonable steps to verify the copyright
35 * status of this work. Certifier recognizes that his good faith
36 * efforts may not shield him from liability if in fact the work
37 * certified is not in the public domain.
38 *
39 * Dedicator makes this dedication for the benefit of the public at
40 * large and to the detriment of the Dedicator's heirs and successors.
41 * Dedicator intends this dedication to be an overt act of
42 * relinquishment in perpetuity of all present and future rights under
43 * copyright law, whether vested or contingent, in the Work. Dedicator
44 * understands that such relinquishment of all rights includes the
45 * relinquishment of all rights to enforce (by lawsuit or otherwise)
46 * those copyrights in the Work.
47 *
48 * Dedicator recognizes that, once placed in the public domain, the
49 * Work may be freely reproduced, distributed, transmitted, used,
50 * modified, built upon, or otherwise exploited by anyone for any
51 * purpose, commercial or non-commercial, and in any way, including by
52 * methods that have not yet been invented or conceived.
53 */
54
55 //-V::595
56
57 #include <stdlib.h>
58 #include <string.h>
59 #include <stdio.h>
60 #include <errno.h>
61 #include <string.h>
62 #include <stdarg.h>
63
64 /* Win32 compatibility */
65 #if defined(_WIN32)
66 # define __func__ __FUNCTION__
67 # if defined(_MSC_VER)
68 # if _MSC_VER <= 1700
69 # define va_copy(dest, src) (dest = src)
70 # endif
71 # endif
72 #endif
73
74 #include "libtelnet.h"
75
76 #if defined(NO_LOCALE)
77 # define xstrerror_l strerror
78 #endif
79
80 #if defined(FREE)
81 # undef FREE
82 #endif /* if defined(FREE) */
83 #define FREE(p) do \
84 { \
85 free((p)); \
86 (p) = NULL; \
87 } while(0)
88
89 /* helper for Q-method option tracking */
90 #define Q_US(q) ((q).state & 0x0F)
91 #define Q_HIM(q) (((q).state & 0xF0) >> 4)
92 #define Q_MAKE(us,him) ((us) | ((him) << 4))
93
94 /* helper for the negotiation routines */
95 #define NEGOTIATE_EVENT(telnet,cmd,opt) \
96 ev.type = (cmd); \
97 ev.neg.telopt = (opt); \
98 (telnet)->eh((telnet), &ev, (telnet)->ud);
99
100 /* telnet state codes */
101 enum telnet_state_t {
102 TELNET_STATE_DATA = 0,
103 TELNET_STATE_EOL,
104 TELNET_STATE_IAC,
105 TELNET_STATE_WILL,
106 TELNET_STATE_WONT,
107 TELNET_STATE_DO,
108 TELNET_STATE_DONT,
109 TELNET_STATE_SB,
110 TELNET_STATE_SB_DATA,
111 TELNET_STATE_SB_DATA_IAC
112 };
113 typedef enum telnet_state_t telnet_state_t;
114
115 /* telnet state tracker */
116 struct telnet_t {
117 /* user data */
118 void *ud;
119 /* telopt support table */
120 const telnet_telopt_t *telopts;
121 /* event handler */
122 telnet_event_handler_t eh;
123 /* RFC-1143 option negotiation states */
124 struct telnet_rfc1143_t *q;
125 /* sub-request buffer */
126 char *buffer;
127 /* current size of the buffer */
128 size_t buffer_size;
129 /* current buffer write position (also length of buffer data) */
130 size_t buffer_pos;
131 /* current state */
132 enum telnet_state_t state;
133 /* option flags */
134 unsigned char flags;
135 /* current subnegotiation telopt */
136 unsigned char sb_telopt;
137 /* length of RFC-1143 queue */
138 unsigned int q_size;
139 /* number of entries in RFC-1143 queue */
140 unsigned int q_cnt;
141 };
142
143 /* RFC-1143 option negotiation state */
144 typedef struct telnet_rfc1143_t {
145 unsigned char telopt;
146 unsigned char state;
147 } telnet_rfc1143_t;
148
149 /* RFC-1143 state names */
150 #define Q_NO 0
151 #define Q_YES 1
152 #define Q_WANTNO 2
153 #define Q_WANTYES 3
154 #define Q_WANTNO_OP 4
155 #define Q_WANTYES_OP 5
156
157 /* telnet NVT EOL sequences */
158 static const char CRLF[] = { '\r', '\n' };
159 static const char CRNUL[] = { '\r', '\0' };
160
161 /* buffer sizes */
162 static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
163 static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
164 sizeof(_buffer_sizes[0]);
165
166 /* error generation function */
167 static telnet_error_t _error(telnet_t *telnet, unsigned line,
168 const char* func, telnet_error_t err,
169 int fatal, const char *fmt, ...) {
170 telnet_event_t ev;
171 char buffer[512];
172 va_list va;
173
174 /* format informational text */
175 va_start(va, fmt);
176 (void)vsnprintf(buffer, sizeof(buffer), fmt, va);
177 va_end(va);
178
179 /* send error event to the user */
180 ev.type = fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING;
181 ev.error.file = __FILE__;
182 ev.error.func = func;
183 ev.error.line = (int) line;
184 ev.error.msg = buffer;
185 telnet->eh(telnet, &ev, telnet->ud);
186
187 return err;
188 }
189
190 /* push bytes out */
191 static void _send(telnet_t *telnet, const char *buffer,
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
192 size_t size) {
193 telnet_event_t ev;
194
195 ev.type = TELNET_EV_SEND;
196 ev.data.buffer = buffer;
197 ev.data.size = size;
198 telnet->eh(telnet, &ev, telnet->ud);
199 }
200
201 /* to send bags of unsigned chars */
202 #define _sendu(t, d, s) _send((t), (const char*)(d), (s))
203
204 /*
205 * check if we support a particular telopt; if us is non-zero, we
206 * check if we (local) supports it, otherwise we check if he (remote)
207 * supports it. return non-zero if supported, zero if not supported.
208 */
209
210 static __inline__ int _check_telopt(telnet_t *telnet, unsigned char telopt,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
211 int us) {
212 int i;
213
214 /* if we have no telopts table, we obviously don't support it */
215 if (telnet->telopts == 0)
216 return 0;
217
218 /* loop until found or end marker (us and him both 0) */
219 for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
220 if (telnet->telopts[i].telopt == telopt) {
221 if (us && telnet->telopts[i].us == TELNET_WILL)
222 return 1;
223 else if (!us && telnet->telopts[i].him == TELNET_DO)
224 return 1;
225 else
226 return 0;
227 }
228 }
229
230 /* not found, so not supported */
231 return 0;
232 }
233
234 /* retrieve RFC-1143 option state */
235 static __inline__ telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
236 unsigned char telopt) {
237 telnet_rfc1143_t empty;
238 unsigned int i;
239
240 /* search for entry */
241 for (i = 0; i != telnet->q_cnt; ++i) {
242 if (telnet->q[i].telopt == telopt) {
243 return telnet->q[i];
244 }
245 }
246
247 /* not found, return empty value */
248 empty.telopt = telopt;
249 empty.state = 0;
250 return empty;
251 }
252
253 /* save RFC-1143 option state */
254 static __inline__ void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
255 unsigned char us, unsigned char him) {
256 telnet_rfc1143_t *qtmp;
257 unsigned int i;
258
259 /* search for entry */
260 for (i = 0; i != telnet->q_cnt; ++i) {
261 if (telnet->q[i].telopt == telopt) {
262 telnet->q[i].state = (unsigned char) Q_MAKE(us,him);
263 if (telopt != TELNET_TELOPT_BINARY)
264 return;
265 telnet->flags &=
266 (unsigned char)~(TELNET_FLAG_TRANSMIT_BINARY |
267 TELNET_FLAG_RECEIVE_BINARY);
268 if (us == Q_YES)
269 telnet->flags |= TELNET_FLAG_TRANSMIT_BINARY;
270 if (him == Q_YES)
271 telnet->flags |= TELNET_FLAG_RECEIVE_BINARY;
272 return;
273 }
274 }
275
276 /*
277 * we're going to need to track state for it, so grow the queue
278 * by 4 (four) elements and put the telopt into it; bail on allocation
279 * error. we go by four because it seems like a reasonable guess as
280 * to the number of enabled options for most simple code, and it
281 * allows for an acceptable number of reallocations for complex code.
282 */
283
284 #define QUANTUM 4
285 /* Did we reach the end of the table? */
286 if (i >= telnet->q_size) {
287 /* Expand the size */
288 if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q,
289 sizeof(telnet_rfc1143_t) * (telnet->q_size + QUANTUM))) == 0) {
290 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
291 "realloc() failed: %s", xstrerror_l(errno));
292 return;
293 }
294 (void)memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * QUANTUM);
295 telnet->q = qtmp;
296 telnet->q_size += QUANTUM;
297 }
298 /* Add entry to end of table */
299 telnet->q[telnet->q_cnt].telopt = telopt;
300 telnet->q[telnet->q_cnt].state = (unsigned char) Q_MAKE(us, him);
301 telnet->q_cnt ++;
302 }
303
304 /* send negotiation bytes */
305 static __inline__ void _send_negotiate(telnet_t *telnet, unsigned char cmd,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
306 unsigned char telopt) {
307 unsigned char bytes[3];
308 bytes[0] = TELNET_IAC;
309 bytes[1] = cmd;
310 bytes[2] = telopt;
311 _sendu(telnet, bytes, 3);
312 }
313
314 /* negotiation handling magic for RFC-1143 */
315 static void _negotiate(telnet_t *telnet, unsigned char telopt) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
316 telnet_event_t ev;
317 telnet_rfc1143_t q;
318
319 /* in PROXY mode, just pass it thru and do nothing */
320 if (telnet->flags & TELNET_FLAG_PROXY) {
321 switch ((int)telnet->state) {
322 case TELNET_STATE_WILL:
323 NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
324 break;
325 case TELNET_STATE_WONT:
326 NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
327 break;
328 case TELNET_STATE_DO:
329 NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
330 break;
331 case TELNET_STATE_DONT:
332 NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
333 break;
334 }
335 return;
336 }
337
338 /* lookup the current state of the option */
339 q = _get_rfc1143(telnet, telopt);
340
341 /* start processing... */
342 switch ((int)telnet->state) {
343 /* request to enable option on remote end or confirm DO */
344 case TELNET_STATE_WILL:
345 switch (Q_HIM(q)) {
346 case Q_NO:
347 if (_check_telopt(telnet, telopt, 0)) {
348 _set_rfc1143(telnet, telopt, Q_US(q), Q_YES);
349 _send_negotiate(telnet, TELNET_DO, telopt);
350 NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
351 } else
352 _send_negotiate(telnet, TELNET_DONT, telopt);
353 break;
354 case Q_WANTNO:
355 _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
356 NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
357 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
358 "DONT answered by WILL");
359 break;
360 case Q_WANTNO_OP:
361 _set_rfc1143(telnet, telopt, Q_US(q), Q_YES);
362 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
363 "DONT answered by WILL");
364 break;
365 case Q_WANTYES:
366 _set_rfc1143(telnet, telopt, Q_US(q), Q_YES);
367 NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
368 break;
369 case Q_WANTYES_OP:
370 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO);
371 _send_negotiate(telnet, TELNET_DONT, telopt);
372 NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
373 break;
374 }
375 break;
376
377 /* request to disable option on remote end, confirm DONT, reject DO */
378 case TELNET_STATE_WONT:
379 switch (Q_HIM(q)) {
380 case Q_YES:
381 _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
382 _send_negotiate(telnet, TELNET_DONT, telopt);
383 NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
384 break;
385 case Q_WANTNO:
386 _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
387 NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
388 break;
389 case Q_WANTNO_OP:
390 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES);
391 _send_negotiate(telnet, TELNET_DO, telopt);
392 NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
393 break;
394 case Q_WANTYES:
395 case Q_WANTYES_OP:
396 _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
397 break;
398 }
399 break;
400
401 /* request to enable option on local end or confirm WILL */
402 case TELNET_STATE_DO:
403 switch (Q_US(q)) {
404 case Q_NO:
405 if (_check_telopt(telnet, telopt, 1)) {
406 _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q));
407 _send_negotiate(telnet, TELNET_WILL, telopt);
408 NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
409 } else
410 _send_negotiate(telnet, TELNET_WONT, telopt);
411 break;
412 case Q_WANTNO:
413 _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
414 NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
415 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
416 "WONT answered by DO");
417 break;
418 case Q_WANTNO_OP:
419 _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q));
420 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
421 "WONT answered by DO");
422 break;
423 case Q_WANTYES:
424 _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q));
425 NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
426 break;
427 case Q_WANTYES_OP:
428 _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q));
429 _send_negotiate(telnet, TELNET_WONT, telopt);
430 NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
431 break;
432 }
433 break;
434
435 /* request to disable option on local end, confirm WONT, reject WILL */
436 case TELNET_STATE_DONT:
437 switch (Q_US(q)) {
438 case Q_YES:
439 _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
440 _send_negotiate(telnet, TELNET_WONT, telopt);
441 NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
442 break;
443 case Q_WANTNO:
444 _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
445 NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
446 break;
447 case Q_WANTNO_OP:
448 _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q));
449 _send_negotiate(telnet, TELNET_WILL, telopt);
450 NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
451 break;
452 case Q_WANTYES:
453 case Q_WANTYES_OP:
454 _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
455 break;
456 }
457 break;
458 }
459 }
460
461 /*
462 * process an ENVIRON/NEW-ENVIRON subnegotiation buffer
463 *
464 * the algorithm and approach used here is kind of a hack,
465 * but it reduces the number of memory allocations we have
466 * to make.
467 *
468 * we copy the bytes back into the buffer, starting at the very
469 * beginning, which makes it easy to handle the ENVIRON ESC
470 * escape mechanism as well as ensure the variable name and
471 * value strings are NUL-terminated, all while fitting inside
472 * of the original buffer.
473 */
474
475 static int _environ_telnet(telnet_t *telnet, unsigned char type,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
476 char* buffer, size_t size) {
477 telnet_event_t ev;
478 struct telnet_environ_t *values = 0;
479 char *c, *last, *out;
480 size_t eindex, count;
481
482 /* if we have no data, just pass it through */
483 if (size == 0) {
484 return 0;
485 }
486
487 /* first byte must be a valid command */
488 if ((unsigned)buffer[0] != TELNET_ENVIRON_SEND &&
489 (unsigned)buffer[0] != TELNET_ENVIRON_IS &&
490 (unsigned)buffer[0] != TELNET_ENVIRON_INFO) {
491 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
492 "telopt %ld subneg has invalid command", (long) type);
493 return 0;
494 }
495
496 /* store ENVIRON command */
497 ev.environ.cmd = (unsigned char) buffer[0];
498
499 /* if we have no arguments, send an event with no data end return */
500 if (size == 1) {
501 /* no list of variables given */
502 ev.environ.values = 0;
503 ev.environ.size = 0;
504
505 /* invoke event with our arguments */
506 ev.type = TELNET_EV_ENVIRON;
507 telnet->eh(telnet, &ev, telnet->ud);
508
509 return 0;
510 }
511
512 /* very second byte must be VAR or USERVAR, if present */
513 if ((unsigned)buffer[1] != TELNET_ENVIRON_VAR &&
514 (unsigned)buffer[1] != TELNET_ENVIRON_USERVAR) {
515 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
516 "telopt %d subneg missing variable type", type);
517 return 0;
518 }
519
520 /* ensure last byte is not an escape byte (makes parsing later easier) */
521 if ((unsigned)buffer[size - 1] == TELNET_ENVIRON_ESC) {
522 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
523 "telopt %d subneg ends with ESC", type);
524 return 0;
525 }
526
527 /* count arguments; each valid entry starts with VAR or USERVAR */
528 count = 0;
529 for (c = buffer + 1; c < buffer + size; ++c) {
530 if (*c == TELNET_ENVIRON_VAR || *c == TELNET_ENVIRON_USERVAR) {
531 ++count;
532 } else if (*c == TELNET_ENVIRON_ESC) {
533 /* skip the next byte */
534 ++c;
535 }
536 }
537
538 /* allocate argument array, bail on error */
539 if ((values = (struct telnet_environ_t *)calloc(count,
540 sizeof(struct telnet_environ_t))) == 0) {
541 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
542 "calloc() failed: %s", xstrerror_l(errno));
543 return 0;
544 }
545
546 /* parse argument array strings */
547 out = buffer;
548 c = buffer + 1;
549 for (eindex = 0; eindex != count; ++eindex) {
550 /* remember the variable type (will be VAR or USERVAR) */
551 values[eindex].type = (unsigned char) (*c++);
552
553 /* scan until we find an end-marker, and buffer up unescaped
554 * bytes into our buffer */
555 last = out;
556 while (c < buffer + size) {
557 /* stop at the next variable or at the value */
558 if ((unsigned)*c == TELNET_ENVIRON_VAR ||
559 (unsigned)*c == TELNET_ENVIRON_VALUE ||
560 (unsigned)*c == TELNET_ENVIRON_USERVAR) {
561 break;
562 }
563
564 /* buffer next byte (taking into account ESC) */
565 if (*c == TELNET_ENVIRON_ESC) {
566 ++c;
567 }
568
569 *out++ = *c++;
570 }
571 *out++ = '\0';
572
573 /* store the variable name we have just received */
574 values[eindex].var = last;
575 values[eindex].value = "";
576
577 /* if we got a value, find the next end marker and
578 * store the value; otherwise, store empty string */
579 if (c < buffer + size && *c == TELNET_ENVIRON_VALUE) {
580 ++c;
581 last = out;
582 while (c < buffer + size) {
583 /* stop when we find the start of the next variable */
584 if ((unsigned)*c == TELNET_ENVIRON_VAR ||
585 (unsigned)*c == TELNET_ENVIRON_USERVAR) {
586 break;
587 }
588
589 /* buffer next byte (taking into account ESC) */
590 if (*c == TELNET_ENVIRON_ESC) {
591 ++c;
592 }
593
594 *out++ = *c++;
595 }
596 *out++ = '\0';
597
598 /* store the variable value */
599 values[eindex].value = last;
600 }
601 }
602
603 /* pass values array and count to event */
604 ev.environ.values = values;
605 ev.environ.size = count;
606
607 /* invoke event with our arguments */
608 ev.type = TELNET_EV_ENVIRON;
609 telnet->eh(telnet, &ev, telnet->ud);
610
611 /* clean up */
612 FREE(values);
613 return 0;
614 }
615
616 /* parse TERMINAL-TYPE command subnegotiation buffers */
617 static int _ttype_telnet(telnet_t *telnet, const char* buffer, size_t size) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
618 telnet_event_t ev;
619
620 /* make sure request is not empty */
621 if (size == 0) {
622 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
623 "incomplete TERMINAL-TYPE request");
624 return 0;
625 }
626
627 /* make sure request has valid command type */
628 if (buffer[0] != TELNET_TTYPE_IS &&
629 buffer[0] != TELNET_TTYPE_SEND) {
630 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
631 "TERMINAL-TYPE request has invalid type");
632 return 0;
633 }
634
635 /* send proper event */
636 if (buffer[0] == TELNET_TTYPE_IS) {
637 char *name;
638
639 /* allocate space for name */
640 if ((name = (char *)malloc(size)) == 0) {
641 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
642 "malloc() failed: %s", xstrerror_l(errno));
643 return 0;
644 }
645 memcpy(name, buffer + 1, size - 1);
646 name[size - 1] = '\0';
647
648 ev.type = TELNET_EV_TTYPE;
649 ev.ttype.cmd = TELNET_TTYPE_IS;
650 ev.ttype.name = name;
651 telnet->eh(telnet, &ev, telnet->ud);
652
653 /* clean up */
654 FREE(name);
655 } else {
656 ev.type = TELNET_EV_TTYPE;
657 ev.ttype.cmd = TELNET_TTYPE_SEND;
658 ev.ttype.name = 0;
659 telnet->eh(telnet, &ev, telnet->ud);
660 }
661
662 return 0;
663 }
664
665 /*
666 * process a subnegotiation buffer; return non-zero if the current buffer
667 * must be aborted and reprocessed.
668 */
669
670 static int _subnegotiate(telnet_t *telnet) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
671 telnet_event_t ev;
672
673 /* standard subnegotiation event */
674 ev.type = TELNET_EV_SUBNEGOTIATION;
675 ev.sub.telopt = telnet->sb_telopt;
676 ev.sub.buffer = telnet->buffer;
677 ev.sub.size = telnet->buffer_pos;
678 telnet->eh(telnet, &ev, telnet->ud);
679
680 switch (telnet->sb_telopt) {
681 /* specially handled subnegotiation telopt types */
682 case TELNET_TELOPT_TTYPE:
683 return _ttype_telnet(telnet, telnet->buffer, telnet->buffer_pos);
684 case TELNET_TELOPT_ENVIRON:
685 case TELNET_TELOPT_NEW_ENVIRON:
686 return _environ_telnet(telnet, telnet->sb_telopt, telnet->buffer,
687 telnet->buffer_pos);
688 default:
689 return 0;
690 }
691 }
692
693 /* initialize a telnet state tracker */
694 telnet_t *telnet_init(const telnet_telopt_t *telopts,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
695 telnet_event_handler_t eh, unsigned char flags, void *user_data) {
696 /* allocate structure */
697 struct telnet_t *telnet = (telnet_t*)calloc(1, sizeof(telnet_t));
698 if (telnet == 0)
699 return 0;
700
701 /* initialize data */
702 telnet->ud = user_data;
703 telnet->telopts = telopts;
704 telnet->eh = eh;
705 telnet->flags = flags;
706
707 return telnet;
708 }
709
710 /* free up any memory allocated by a state tracker */
711 void telnet_free(telnet_t *telnet) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
712 /* free sub-request buffer */
713 if (telnet->buffer != 0) {
714 FREE(telnet->buffer);
715 telnet->buffer = 0; //-V1048
716 telnet->buffer_size = 0;
717 telnet->buffer_pos = 0;
718 }
719
720 /* free RFC-1143 queue */
721 if (telnet->q) {
722 FREE(telnet->q);
723 telnet->q = NULL;
724 telnet->q_size = 0;
725 telnet->q_cnt = 0;
726 }
727
728 /* free the telnet structure itself */
729 free(telnet); /* X-LINTED: FREE */
730 }
731
732 /* push a byte into the telnet buffer */
733 static telnet_error_t _buffer_byte(telnet_t *telnet,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
734 unsigned char byte) {
735 char *new_buffer;
736
737 /* check if we're out of room */
738 if (telnet->buffer_pos == telnet->buffer_size) {
739 size_t i;
740 /* find the next buffer size */
741 for (i = 0; i != _buffer_sizes_count; ++i) {
742 if (_buffer_sizes[i] == telnet->buffer_size) {
743 break;
744 }
745 }
746
747 /* overflow -- can't grow any more */
748 if (i >= _buffer_sizes_count - 1) {
749 _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
750 "subnegotiation buffer size limit reached");
751 return TELNET_EOVERFLOW;
752 }
753
754 /* (re)allocate buffer */
755 new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
756 if (new_buffer == 0) {
757 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
758 "realloc() failed");
759 return TELNET_ENOMEM;
760 }
761
762 telnet->buffer = new_buffer;
763 telnet->buffer_size = _buffer_sizes[i + 1];
764 }
765
766 /* push the byte, all set */
767 telnet->buffer[telnet->buffer_pos++] = (char) byte;
768 return TELNET_EOK;
769 }
770
771 static void _process(telnet_t *telnet, const char *buffer, size_t size) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
772 telnet_event_t ev;
773 unsigned char byte;
774 size_t i, start;
775 for (i = start = 0; i != size; ++i) {
776 byte = (unsigned char) buffer[i];
777 switch (telnet->state) {
778 /* regular data */
779 case TELNET_STATE_DATA:
780 /* on an IAC byte, pass through all pending bytes and
781 * switch states */
782 if (byte == TELNET_IAC) {
783 if (i != start) {
784 ev.type = TELNET_EV_DATA;
785 ev.data.buffer = buffer + start;
786 ev.data.size = i - start;
787 telnet->eh(telnet, &ev, telnet->ud);
788 }
789 telnet->state = TELNET_STATE_IAC;
790 } else if (byte == '\r' &&
791 (telnet->flags & TELNET_FLAG_NVT_EOL) &&
792 !(telnet->flags & TELNET_FLAG_RECEIVE_BINARY)) {
793 if (i != start) {
794 ev.type = TELNET_EV_DATA;
795 ev.data.buffer = buffer + start;
796 ev.data.size = i - start;
797 telnet->eh(telnet, &ev, telnet->ud);
798 }
799 telnet->state = TELNET_STATE_EOL;
800 }
801 break;
802
803 /* NVT EOL to be translated */
804 case TELNET_STATE_EOL:
805 if (byte != '\n') {
806 byte = '\r';
807 ev.type = TELNET_EV_DATA;
808 ev.data.buffer = (char*)&byte;
809 ev.data.size = 1;
810 telnet->eh(telnet, &ev, telnet->ud);
811 byte = (unsigned char) buffer[i];
812 }
813 /* any byte following '\r' other than '\n' or '\0' is invalid, */
814 /* so pass both \r and the byte */
815 start = i;
816 if (byte == '\0')
817 ++start;
818 /* state update */
819 telnet->state = TELNET_STATE_DATA;
820 break;
821
822 /* IAC command */
823 case TELNET_STATE_IAC:
824 switch (byte) {
825 /* subnegotiation */
826 case TELNET_SB:
827 telnet->state = TELNET_STATE_SB;
828 break;
829 /* negotiation commands */
830 case TELNET_WILL:
831 telnet->state = TELNET_STATE_WILL;
832 break;
833 case TELNET_WONT:
834 telnet->state = TELNET_STATE_WONT;
835 break;
836 case TELNET_DO:
837 telnet->state = TELNET_STATE_DO;
838 break;
839 case TELNET_DONT:
840 telnet->state = TELNET_STATE_DONT;
841 break;
842 /* IAC escaping */
843 case TELNET_IAC:
844 /* event */
845 ev.type = TELNET_EV_DATA;
846 ev.data.buffer = (char*)&byte;
847 ev.data.size = 1;
848 telnet->eh(telnet, &ev, telnet->ud);
849
850 /* state update */
851 start = i + 1;
852 telnet->state = TELNET_STATE_DATA;
853 break;
854 /* some other command */
855 default:
856 /* event */
857 ev.type = TELNET_EV_IAC;
858 ev.iac.cmd = byte;
859 telnet->eh(telnet, &ev, telnet->ud);
860
861 /* state update */
862 start = i + 1;
863 telnet->state = TELNET_STATE_DATA;
864 }
865 break;
866
867 /* negotiation commands */
868 case TELNET_STATE_WILL:
869 case TELNET_STATE_WONT:
870 case TELNET_STATE_DO:
871 case TELNET_STATE_DONT:
872 _negotiate(telnet, byte);
873 start = i + 1;
874 telnet->state = TELNET_STATE_DATA;
875 break;
876
877 /* subnegotiation -- determine subnegotiation telopt */
878 case TELNET_STATE_SB:
879 telnet->sb_telopt = byte;
880 telnet->buffer_pos = 0;
881 telnet->state = TELNET_STATE_SB_DATA;
882 break;
883
884 /* subnegotiation -- buffer bytes until end request */
885 case TELNET_STATE_SB_DATA:
886 /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
887 if (byte == TELNET_IAC) {
888 telnet->state = TELNET_STATE_SB_DATA_IAC;
889 } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
890 start = i + 1;
891 telnet->state = TELNET_STATE_DATA;
892 }
893 break;
894
895 /* IAC escaping inside a subnegotiation */
896 case TELNET_STATE_SB_DATA_IAC:
897 switch (byte) {
898 /* end subnegotiation */
899 case TELNET_SE:
900 /* return to default state */
901 start = i + 1;
902 telnet->state = TELNET_STATE_DATA;
903
904 /* process subnegotiation */
905 if (_subnegotiate(telnet) != 0) {
906 telnet_recv(telnet, &buffer[start], size - start);
907 return;
908 }
909 break;
910 /* escaped IAC byte */
911 case TELNET_IAC:
912 /* push IAC into buffer */
913 if (_buffer_byte(telnet, TELNET_IAC) !=
914 TELNET_EOK) {
915 start = i + 1;
916 telnet->state = TELNET_STATE_DATA;
917 } else {
918 telnet->state = TELNET_STATE_SB_DATA;
919 }
920 break;
921 /*
922 * Something else -- protocol error. attempt to process
923 * content in subnegotiation buffer, then evaluate the
924 * given command as an IAC code.
925 */
926
927 default:
928 _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
929 "unexpected byte after IAC inside SB: %d",
930 byte);
931
932 /* enter IAC state */
933 start = i + 1;
934 telnet->state = TELNET_STATE_IAC;
935
936 /*
937 * Process subnegotiation; see comment in
938 * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
939 */
940
941 if (_subnegotiate(telnet) != 0) {
942 telnet_recv(telnet, &buffer[start], size - start);
943 return;
944 } else {
945 /*
946 * Recursive call to get the current input byte processed
947 * as a regular IAC command. we could use a goto, but
948 * that would be gross.
949 */
950
951 _process(telnet, (char *)&byte, 1);
952 }
953 break;
954 }
955 break;
956 }
957 }
958
959 /* pass through any remaining bytes */
960 if (telnet->state == TELNET_STATE_DATA && i != start) {
961 ev.type = TELNET_EV_DATA;
962 ev.data.buffer = buffer + start;
963 ev.data.size = i - start;
964 telnet->eh(telnet, &ev, telnet->ud);
965 }
966 }
967
968 /* push a bytes into the state tracker */
969 void telnet_recv(telnet_t *telnet, const char *buffer,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
970 size_t size) {
971 _process(telnet, buffer, size);
972 }
973
974 /* send an iac command */
975 void telnet_iac(telnet_t *telnet, unsigned char cmd) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
976 unsigned char bytes[2];
977 bytes[0] = TELNET_IAC;
978 bytes[1] = cmd;
979 _sendu(telnet, bytes, 2);
980 }
981
982 /* send negotiation */
983 void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
984 unsigned char telopt) {
985 telnet_rfc1143_t q;
986
987 /* if we're in proxy mode, just send it now */
988 if (telnet->flags & TELNET_FLAG_PROXY) {
989 unsigned char bytes[3];
990 bytes[0] = TELNET_IAC;
991 bytes[1] = cmd;
992 bytes[2] = telopt;
993 _sendu(telnet, bytes, 3);
994 return;
995 }
996
997 /* get current option states */
998 q = _get_rfc1143(telnet, telopt);
999
1000 switch (cmd) {
1001 /* advertise willingness to support an option */
1002 case TELNET_WILL:
1003 switch (Q_US(q)) {
1004 case Q_NO:
1005 _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q));
1006 _send_negotiate(telnet, TELNET_WILL, telopt);
1007 break;
1008 case Q_WANTNO:
1009 _set_rfc1143(telnet, telopt, Q_WANTNO_OP, Q_HIM(q));
1010 break;
1011 case Q_WANTYES_OP:
1012 _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q));
1013 break;
1014 }
1015 break;
1016
1017 /* force turn-off of locally enabled option */
1018 case TELNET_WONT:
1019 switch (Q_US(q)) {
1020 case Q_YES:
1021 _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q));
1022 _send_negotiate(telnet, TELNET_WONT, telopt);
1023 break;
1024 case Q_WANTYES:
1025 _set_rfc1143(telnet, telopt, Q_WANTYES_OP, Q_HIM(q));
1026 break;
1027 case Q_WANTNO_OP:
1028 _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q));
1029 break;
1030 }
1031 break;
1032
1033 /* ask remote end to enable an option */
1034 case TELNET_DO:
1035 switch (Q_HIM(q)) {
1036 case Q_NO:
1037 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES);
1038 _send_negotiate(telnet, TELNET_DO, telopt);
1039 break;
1040 case Q_WANTNO:
1041 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO_OP);
1042 break;
1043 case Q_WANTYES_OP:
1044 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES);
1045 break;
1046 }
1047 break;
1048
1049 /* demand remote end disable an option */
1050 case TELNET_DONT:
1051 switch (Q_HIM(q)) {
1052 case Q_YES:
1053 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO);
1054 _send_negotiate(telnet, TELNET_DONT, telopt);
1055 break;
1056 case Q_WANTYES:
1057 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES_OP);
1058 break;
1059 case Q_WANTNO_OP:
1060 _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO);
1061 break;
1062 }
1063 break;
1064 }
1065 }
1066
1067 /* send non-command data (escapes IAC bytes) */
1068 void telnet_send(telnet_t *telnet, const char *buffer,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1069 size_t size) {
1070 size_t i, l;
1071
1072 for (l = i = 0; i != size; ++i) {
1073 /* dump prior portion of text, send escaped bytes */
1074 if (buffer[i] == (char)TELNET_IAC) {
1075 /* dump prior text if any */
1076 if (i != l) {
1077 _send(telnet, buffer + l, i - l);
1078 }
1079 l = i + 1;
1080
1081 /* send escape */
1082 telnet_iac(telnet, TELNET_IAC);
1083 }
1084 }
1085
1086 /* send whatever portion of buffer is left */
1087 if (i != l) {
1088 _send(telnet, buffer + l, i - l);
1089 }
1090 }
1091
1092 /* send non-command text (escapes IAC bytes and does NVT translation) */
1093 void telnet_send_text(telnet_t *telnet, const char *buffer,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1094 size_t size) {
1095 size_t i, l;
1096
1097 for (l = i = 0; i != size; ++i) {
1098 /* dump prior portion of text, send escaped bytes */
1099 if (buffer[i] == (char)TELNET_IAC) {
1100 /* dump prior text if any */
1101 if (i != l) {
1102 _send(telnet, buffer + l, i - l);
1103 }
1104 l = i + 1;
1105
1106 /* send escape */
1107 telnet_iac(telnet, TELNET_IAC);
1108 }
1109 /* special characters if not in BINARY mode */
1110 else if (!(telnet->flags & TELNET_FLAG_TRANSMIT_BINARY) &&
1111 (buffer[i] == '\r' || buffer[i] == '\n')) {
1112 /* dump prior portion of text */
1113 if (i != l) {
1114 _send(telnet, buffer + l, i - l);
1115 }
1116 l = i + 1;
1117
1118 /* automatic translation of \r -> CRNUL */
1119 if (buffer[i] == '\r') {
1120 _send(telnet, CRNUL, 2);
1121 }
1122 /* automatic translation of \n -> CRLF */
1123 else {
1124 _send(telnet, CRLF, 2);
1125 }
1126 }
1127 }
1128
1129 /* send whatever portion of buffer is left */
1130 if (i != l) {
1131 _send(telnet, buffer + l, i - l);
1132 }
1133 }
1134
1135 /* send subnegotiation header */
1136 void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1137 unsigned char sb[3];
1138 sb[0] = TELNET_IAC;
1139 sb[1] = TELNET_SB;
1140 sb[2] = telopt;
1141 _sendu(telnet, sb, 3);
1142 }
1143
1144 /* send formatted data with \r and \n translation in addition to IAC IAC */
1145 int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1146 char buffer[1024];
1147 char *output = buffer;
1148 int rs, i, l;
1149
1150 /* format */
1151 va_list va2;
1152 va_copy(va2, va);
1153 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1154 if ((unsigned long) rs >= sizeof(buffer)) {
1155 output = (char*)malloc((unsigned long) ((unsigned long)rs + 1L));
1156 if (output == 0) {
1157 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
1158 "malloc() failed: %s", xstrerror_l(errno));
1159 va_end(va2);
1160 return -1;
1161 }
1162 rs = vsnprintf(output, rs + 1, fmt, va2);
1163 }
1164 va_end(va2);
1165 va_end(va);
1166
1167 /* send */
1168 for (l = i = 0; i != rs; ++i) {
1169 /* special characters */
1170 if (output[i] == (char)TELNET_IAC || output[i] == '\r' ||
1171 output[i] == '\n') {
1172 /* dump prior portion of text */
1173 if (i != l)
1174 _send(telnet, output + l, (size_t) (i - l));
1175 l = i + 1;
1176
1177 /* IAC -> IAC IAC */
1178 if (output[i] == (char)TELNET_IAC)
1179 telnet_iac(telnet, TELNET_IAC);
1180 /* automatic translation of \r -> CRNUL */
1181 else if (output[i] == '\r')
1182 _send(telnet, CRNUL, 2);
1183 /* automatic translation of \n -> CRLF */
1184 else if (output[i] == '\n')
1185 _send(telnet, CRLF, 2);
1186 }
1187 }
1188
1189 /* send whatever portion of output is left */
1190 if (i != l) {
1191 _send(telnet, output + l, (size_t) (i - l));
1192 }
1193
1194 /* free allocated memory, if any */
1195 if (output != buffer) {
1196 FREE(output);
1197 }
1198
1199 return rs;
1200 }
1201
1202 /* see telnet_vprintf */
1203 int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1204 va_list va;
1205 int rs;
1206
1207 va_start(va, fmt);
1208 rs = telnet_vprintf(telnet, fmt, va);
1209 va_end(va);
1210
1211 return rs;
1212 }
1213
1214 /* send formatted data through telnet_send */
1215 int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1216 char buffer[1024];
1217 char *output = buffer;
1218 int rs;
1219
1220 /* format; allocate more space if necessary */
1221 va_list va2;
1222 va_copy(va2, va);
1223 rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
1224 if ((unsigned long) rs >= sizeof(buffer)) {
1225 output = (char*)malloc((unsigned long) rs + 1);
1226 if (output == 0) {
1227 _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
1228 "malloc() failed: %s", xstrerror_l(errno));
1229 va_end(va2);
1230 return -1;
1231 }
1232 rs = vsnprintf(output, (int)((unsigned int) rs + 1), fmt, va2);
1233 }
1234 va_end(va2);
1235 va_end(va);
1236
1237 /* send out the formatted data */
1238 telnet_send(telnet, output, (size_t) rs);
1239
1240 /* release allocated memory, if any */
1241 if (output != buffer) {
1242 FREE(output);
1243 }
1244
1245 return rs;
1246 }
1247
1248 /* see telnet_raw_vprintf */
1249 int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) {
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1250 va_list va;
1251 int rs;
1252
1253 va_start(va, fmt);
1254 rs = telnet_raw_vprintf(telnet, fmt, va);
1255 va_end(va);
1256
1257 return rs;
1258 }