root/src/dps8/libtelnet.c

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

DEFINITIONS

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

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

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