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

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