root/src/dps8/libtelnet.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _send
  2. _check_telopt
  3. _get_rfc1143
  4. _set_rfc1143
  5. _send_negotiate
  6. _negotiate
  7. _environ_telnet
  8. _ttype_telnet
  9. _subnegotiate
  10. telnet_init
  11. telnet_free
  12. _buffer_byte
  13. _process
  14. telnet_recv
  15. telnet_iac
  16. telnet_negotiate
  17. telnet_send
  18. telnet_send_text
  19. telnet_begin_sb
  20. telnet_vprintf
  21. telnet_printf
  22. telnet_raw_vprintf
  23. telnet_raw_printf

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

/* [previous][next][first][last][top][bottom][index][help] */