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
  24. libtelnet_is_valid
  25. libtelnet_set_invalid

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

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