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