Coverage Report

Created: 2025-12-14 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/telnet.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#ifndef CURL_DISABLE_TELNET
28
29
#ifdef HAVE_NETINET_IN_H
30
#include <netinet/in.h>
31
#endif
32
#ifdef HAVE_NETDB_H
33
#include <netdb.h>
34
#endif
35
#ifdef HAVE_ARPA_INET_H
36
#include <arpa/inet.h>
37
#endif
38
#ifdef HAVE_NET_IF_H
39
#include <net/if.h>
40
#endif
41
#ifdef HAVE_SYS_IOCTL_H
42
#include <sys/ioctl.h>
43
#endif
44
45
#ifdef HAVE_SYS_PARAM_H
46
#include <sys/param.h>
47
#endif
48
49
#include "urldata.h"
50
#include "url.h"
51
#include <curl/curl.h>
52
#include "transfer.h"
53
#include "sendf.h"
54
#include "telnet.h"
55
#include "connect.h"
56
#include "progress.h"
57
#include "system_win32.h"
58
#include "arpa_telnet.h"
59
#include "select.h"
60
#include "curlx/warnless.h"
61
#include "curlx/strparse.h"
62
63
#define SUBBUFSIZE 512
64
65
0
#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer
66
#define CURL_SB_TERM(x)        \
67
0
  do {                         \
68
0
    x->subend = x->subpointer; \
69
0
    CURL_SB_CLEAR(x);          \
70
0
  } while(0)
71
#define CURL_SB_ACCUM(x, c)                                   \
72
0
  do {                                                        \
73
0
    if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \
74
0
      *x->subpointer++ = (c);                                 \
75
0
  } while(0)
76
77
0
#define CURL_SB_GET(x) ((*x->subpointer++) & 0xff)
78
0
#define CURL_SB_LEN(x) (x->subend - x->subpointer)
79
80
/* For posterity:
81
#define  CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
82
#define  CURL_SB_EOF(x) (x->subpointer >= x->subend) */
83
84
#ifdef CURL_DISABLE_VERBOSE_STRINGS
85
#define printoption(a, b, c, d) Curl_nop_stmt
86
#endif
87
88
/* For negotiation compliant to RFC 1143 */
89
0
#define CURL_NO          0
90
0
#define CURL_YES         1
91
0
#define CURL_WANTYES     2
92
0
#define CURL_WANTNO      3
93
94
0
#define CURL_EMPTY       0
95
0
#define CURL_OPPOSITE    1
96
97
98
/* meta key for storing protocol meta at easy handle */
99
0
#define CURL_META_TELNET_EASY   "meta:proto:telnet:easy"
100
101
/*
102
 * Telnet receiver states for fsm
103
 */
104
typedef enum {
105
  CURL_TS_DATA = 0,
106
  CURL_TS_IAC,
107
  CURL_TS_WILL,
108
  CURL_TS_WONT,
109
  CURL_TS_DO,
110
  CURL_TS_DONT,
111
  CURL_TS_CR,
112
  CURL_TS_SB,   /* sub-option collection */
113
  CURL_TS_SE    /* looking for sub-option end */
114
} TelnetReceive;
115
116
struct TELNET {
117
  int please_negotiate;
118
  int already_negotiated;
119
  int us[256];
120
  int usq[256];
121
  int us_preferred[256];
122
  int him[256];
123
  int himq[256];
124
  int him_preferred[256];
125
  int subnegotiation[256];
126
  char *subopt_ttype;                /* Set with suboption TTYPE */
127
  char *subopt_xdisploc;             /* Set with suboption XDISPLOC */
128
  unsigned short subopt_wsx;         /* Set with suboption NAWS */
129
  unsigned short subopt_wsy;         /* Set with suboption NAWS */
130
  TelnetReceive telrcv_state;
131
  struct curl_slist *telnet_vars;    /* Environment variables */
132
  struct dynbuf out;                 /* output buffer */
133
134
  /* suboptions */
135
  unsigned char subbuffer[SUBBUFSIZE];
136
  unsigned char *subpointer, *subend;      /* buffer for sub-options */
137
};
138
139
static CURLcode telrcv(struct Curl_easy *data,
140
                       struct TELNET *tn,
141
                       const unsigned char *inbuf, /* Data received from
142
                                                      socket */
143
                       ssize_t count);             /* Number of bytes
144
                                                      received */
145
146
#ifndef CURL_DISABLE_VERBOSE_STRINGS
147
static void printoption(struct Curl_easy *data,
148
                        const char *direction,
149
                        int cmd, int option);
150
#endif
151
152
static void send_negotiation(struct Curl_easy *data, int cmd, int option);
153
static void set_local_option(struct Curl_easy *data, struct TELNET *tn,
154
                             int option, int newstate);
155
static void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
156
                              int option, int newstate);
157
158
static void printsub(struct Curl_easy *data,
159
                     int direction, unsigned char *pointer,
160
                     size_t length);
161
static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn);
162
static void sendsuboption(struct Curl_easy *data,
163
                          struct TELNET *tn, int option);
164
165
static CURLcode telnet_do(struct Curl_easy *data, bool *done);
166
static CURLcode telnet_done(struct Curl_easy *data,
167
                            CURLcode, bool premature);
168
static CURLcode send_telnet_data(struct Curl_easy *data,
169
                                 struct TELNET *tn,
170
                                 char *buffer, ssize_t nread);
171
172
/*
173
 * TELNET protocol handler.
174
 */
175
176
const struct Curl_handler Curl_handler_telnet = {
177
  "telnet",                             /* scheme */
178
  ZERO_NULL,                            /* setup_connection */
179
  telnet_do,                            /* do_it */
180
  telnet_done,                          /* done */
181
  ZERO_NULL,                            /* do_more */
182
  ZERO_NULL,                            /* connect_it */
183
  ZERO_NULL,                            /* connecting */
184
  ZERO_NULL,                            /* doing */
185
  ZERO_NULL,                            /* proto_pollset */
186
  ZERO_NULL,                            /* doing_pollset */
187
  ZERO_NULL,                            /* domore_pollset */
188
  ZERO_NULL,                            /* perform_pollset */
189
  ZERO_NULL,                            /* disconnect */
190
  ZERO_NULL,                            /* write_resp */
191
  ZERO_NULL,                            /* write_resp_hd */
192
  ZERO_NULL,                            /* connection_check */
193
  ZERO_NULL,                            /* attach connection */
194
  ZERO_NULL,                            /* follow */
195
  PORT_TELNET,                          /* defport */
196
  CURLPROTO_TELNET,                     /* protocol */
197
  CURLPROTO_TELNET,                     /* family */
198
  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
199
};
200
201
static void telnet_easy_dtor(void *key, size_t klen, void *entry)
202
0
{
203
0
  struct TELNET *tn = entry;
204
0
  (void)key;
205
0
  (void)klen;
206
0
  curl_slist_free_all(tn->telnet_vars);
207
0
  curlx_dyn_free(&tn->out);
208
0
  curlx_free(tn);
209
0
}
210
211
static CURLcode init_telnet(struct Curl_easy *data)
212
0
{
213
0
  struct TELNET *tn;
214
215
0
  tn = curlx_calloc(1, sizeof(struct TELNET));
216
0
  if(!tn)
217
0
    return CURLE_OUT_OF_MEMORY;
218
219
0
  curlx_dyn_init(&tn->out, 0xffff);
220
221
0
  tn->telrcv_state = CURL_TS_DATA;
222
223
  /* Init suboptions */
224
0
  CURL_SB_CLEAR(tn);
225
226
  /* Set the options we want by default */
227
0
  tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
228
0
  tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
229
230
  /* To be compliant with previous releases of libcurl
231
     we enable this option by default. This behavior
232
         can be changed thanks to the "BINARY" option in
233
         CURLOPT_TELNETOPTIONS
234
  */
235
0
  tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
236
0
  tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
237
238
  /* We must allow the server to echo what we sent
239
         but it is not necessary to request the server
240
         to do so (it might forces the server to close
241
         the connection). Hence, we ignore ECHO in the
242
         negotiate function
243
  */
244
0
  tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
245
246
  /* Set the subnegotiation fields to send information
247
    just after negotiation passed (do/will)
248
249
     Default values are (0,0) initialized by calloc.
250
     According to the RFC1013 it is valid:
251
     A value equal to zero is acceptable for the width (or height),
252
         and means that no character width (or height) is being sent.
253
         In this case, the width (or height) that will be assumed by the
254
         Telnet server is operating system specific (it will probably be
255
         based upon the terminal type information that may have been sent
256
         using the TERMINAL TYPE Telnet option). */
257
0
  tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
258
259
0
  return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor);
260
0
}
261
262
static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn)
263
0
{
264
0
  int i;
265
266
0
  for(i = 0; i < CURL_NTELOPTS; i++) {
267
0
    if(i == CURL_TELOPT_ECHO)
268
0
      continue;
269
270
0
    if(tn->us_preferred[i] == CURL_YES)
271
0
      set_local_option(data, tn, i, CURL_YES);
272
273
0
    if(tn->him_preferred[i] == CURL_YES)
274
0
      set_remote_option(data, tn, i, CURL_YES);
275
0
  }
276
0
}
277
278
#ifndef CURL_DISABLE_VERBOSE_STRINGS
279
static void printoption(struct Curl_easy *data,
280
                        const char *direction, int cmd, int option)
281
0
{
282
0
  if(data->set.verbose) {
283
0
    if(cmd == CURL_IAC) {
284
0
      if(CURL_TELCMD_OK(option))
285
0
        infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
286
0
      else
287
0
        infof(data, "%s IAC %d", direction, option);
288
0
    }
289
0
    else {
290
0
      const char *fmt = (cmd == CURL_WILL) ? "WILL" :
291
0
                        (cmd == CURL_WONT) ? "WONT" :
292
0
                        (cmd == CURL_DO) ? "DO" :
293
0
                        (cmd == CURL_DONT) ? "DONT" : 0;
294
0
      if(fmt) {
295
0
        const char *opt;
296
0
        if(CURL_TELOPT_OK(option))
297
0
          opt = CURL_TELOPT(option);
298
0
        else if(option == CURL_TELOPT_EXOPL)
299
0
          opt = "EXOPL";
300
0
        else
301
0
          opt = NULL;
302
303
0
        if(opt)
304
0
          infof(data, "%s %s %s", direction, fmt, opt);
305
0
        else
306
0
          infof(data, "%s %s %d", direction, fmt, option);
307
0
      }
308
0
      else
309
0
        infof(data, "%s %d %d", direction, cmd, option);
310
0
    }
311
0
  }
312
0
}
313
#endif
314
315
static void send_negotiation(struct Curl_easy *data, int cmd, int option)
316
0
{
317
0
  unsigned char buf[3];
318
0
  ssize_t bytes_written;
319
0
  struct connectdata *conn = data->conn;
320
321
0
  buf[0] = CURL_IAC;
322
0
  buf[1] = (unsigned char)cmd;
323
0
  buf[2] = (unsigned char)option;
324
325
0
  bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
326
0
  if(bytes_written < 0) {
327
0
    int err = SOCKERRNO;
328
0
    failf(data, "Sending data failed (%d)", err);
329
0
  }
330
331
0
  printoption(data, "SENT", cmd, option);
332
0
}
333
334
static void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
335
                              int option, int newstate)
336
0
{
337
0
  if(newstate == CURL_YES) {
338
0
    switch(tn->him[option]) {
339
0
    case CURL_NO:
340
0
      tn->him[option] = CURL_WANTYES;
341
0
      send_negotiation(data, CURL_DO, option);
342
0
      break;
343
344
0
    case CURL_YES:
345
      /* Already enabled */
346
0
      break;
347
348
0
    case CURL_WANTNO:
349
0
      switch(tn->himq[option]) {
350
0
      case CURL_EMPTY:
351
        /* Already negotiating for CURL_YES, queue the request */
352
0
        tn->himq[option] = CURL_OPPOSITE;
353
0
        break;
354
0
      case CURL_OPPOSITE:
355
        /* Error: already queued an enable request */
356
0
        break;
357
0
      }
358
0
      break;
359
360
0
    case CURL_WANTYES:
361
0
      switch(tn->himq[option]) {
362
0
      case CURL_EMPTY:
363
        /* Error: already negotiating for enable */
364
0
        break;
365
0
      case CURL_OPPOSITE:
366
0
        tn->himq[option] = CURL_EMPTY;
367
0
        break;
368
0
      }
369
0
      break;
370
0
    }
371
0
  }
372
0
  else { /* NO */
373
0
    switch(tn->him[option]) {
374
0
    case CURL_NO:
375
      /* Already disabled */
376
0
      break;
377
378
0
    case CURL_YES:
379
0
      tn->him[option] = CURL_WANTNO;
380
0
      send_negotiation(data, CURL_DONT, option);
381
0
      break;
382
383
0
    case CURL_WANTNO:
384
0
      switch(tn->himq[option]) {
385
0
      case CURL_EMPTY:
386
        /* Already negotiating for NO */
387
0
        break;
388
0
      case CURL_OPPOSITE:
389
0
        tn->himq[option] = CURL_EMPTY;
390
0
        break;
391
0
      }
392
0
      break;
393
394
0
    case CURL_WANTYES:
395
0
      switch(tn->himq[option]) {
396
0
      case CURL_EMPTY:
397
0
        tn->himq[option] = CURL_OPPOSITE;
398
0
        break;
399
0
      case CURL_OPPOSITE:
400
0
        break;
401
0
      }
402
0
      break;
403
0
    }
404
0
  }
405
0
}
406
407
static void rec_will(struct Curl_easy *data, struct TELNET *tn, int option)
408
0
{
409
0
  switch(tn->him[option]) {
410
0
  case CURL_NO:
411
0
    if(tn->him_preferred[option] == CURL_YES) {
412
0
      tn->him[option] = CURL_YES;
413
0
      send_negotiation(data, CURL_DO, option);
414
0
    }
415
0
    else
416
0
      send_negotiation(data, CURL_DONT, option);
417
418
0
    break;
419
420
0
  case CURL_YES:
421
    /* Already enabled */
422
0
    break;
423
424
0
  case CURL_WANTNO:
425
0
    switch(tn->himq[option]) {
426
0
    case CURL_EMPTY:
427
      /* Error: DONT answered by WILL */
428
0
      tn->him[option] = CURL_NO;
429
0
      break;
430
0
    case CURL_OPPOSITE:
431
      /* Error: DONT answered by WILL */
432
0
      tn->him[option] = CURL_YES;
433
0
      tn->himq[option] = CURL_EMPTY;
434
0
      break;
435
0
    }
436
0
    break;
437
438
0
  case CURL_WANTYES:
439
0
    switch(tn->himq[option]) {
440
0
    case CURL_EMPTY:
441
0
      tn->him[option] = CURL_YES;
442
0
      break;
443
0
    case CURL_OPPOSITE:
444
0
      tn->him[option] = CURL_WANTNO;
445
0
      tn->himq[option] = CURL_EMPTY;
446
0
      send_negotiation(data, CURL_DONT, option);
447
0
      break;
448
0
    }
449
0
    break;
450
0
  }
451
0
}
452
453
static void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option)
454
0
{
455
0
  switch(tn->him[option]) {
456
0
  case CURL_NO:
457
    /* Already disabled */
458
0
    break;
459
460
0
  case CURL_YES:
461
0
    tn->him[option] = CURL_NO;
462
0
    send_negotiation(data, CURL_DONT, option);
463
0
    break;
464
465
0
  case CURL_WANTNO:
466
0
    switch(tn->himq[option]) {
467
0
    case CURL_EMPTY:
468
0
      tn->him[option] = CURL_NO;
469
0
      break;
470
471
0
    case CURL_OPPOSITE:
472
0
      tn->him[option] = CURL_WANTYES;
473
0
      tn->himq[option] = CURL_EMPTY;
474
0
      send_negotiation(data, CURL_DO, option);
475
0
      break;
476
0
    }
477
0
    break;
478
479
0
  case CURL_WANTYES:
480
0
    switch(tn->himq[option]) {
481
0
    case CURL_EMPTY:
482
0
      tn->him[option] = CURL_NO;
483
0
      break;
484
0
    case CURL_OPPOSITE:
485
0
      tn->him[option] = CURL_NO;
486
0
      tn->himq[option] = CURL_EMPTY;
487
0
      break;
488
0
    }
489
0
    break;
490
0
  }
491
0
}
492
493
static void set_local_option(struct Curl_easy *data, struct TELNET *tn,
494
                             int option, int newstate)
495
0
{
496
0
  if(newstate == CURL_YES) {
497
0
    switch(tn->us[option]) {
498
0
    case CURL_NO:
499
0
      tn->us[option] = CURL_WANTYES;
500
0
      send_negotiation(data, CURL_WILL, option);
501
0
      break;
502
503
0
    case CURL_YES:
504
      /* Already enabled */
505
0
      break;
506
507
0
    case CURL_WANTNO:
508
0
      switch(tn->usq[option]) {
509
0
      case CURL_EMPTY:
510
        /* Already negotiating for CURL_YES, queue the request */
511
0
        tn->usq[option] = CURL_OPPOSITE;
512
0
        break;
513
0
      case CURL_OPPOSITE:
514
        /* Error: already queued an enable request */
515
0
        break;
516
0
      }
517
0
      break;
518
519
0
    case CURL_WANTYES:
520
0
      switch(tn->usq[option]) {
521
0
      case CURL_EMPTY:
522
        /* Error: already negotiating for enable */
523
0
        break;
524
0
      case CURL_OPPOSITE:
525
0
        tn->usq[option] = CURL_EMPTY;
526
0
        break;
527
0
      }
528
0
      break;
529
0
    }
530
0
  }
531
0
  else { /* NO */
532
0
    switch(tn->us[option]) {
533
0
    case CURL_NO:
534
      /* Already disabled */
535
0
      break;
536
537
0
    case CURL_YES:
538
0
      tn->us[option] = CURL_WANTNO;
539
0
      send_negotiation(data, CURL_WONT, option);
540
0
      break;
541
542
0
    case CURL_WANTNO:
543
0
      switch(tn->usq[option]) {
544
0
      case CURL_EMPTY:
545
        /* Already negotiating for NO */
546
0
        break;
547
0
      case CURL_OPPOSITE:
548
0
        tn->usq[option] = CURL_EMPTY;
549
0
        break;
550
0
      }
551
0
      break;
552
553
0
    case CURL_WANTYES:
554
0
      switch(tn->usq[option]) {
555
0
      case CURL_EMPTY:
556
0
        tn->usq[option] = CURL_OPPOSITE;
557
0
        break;
558
0
      case CURL_OPPOSITE:
559
0
        break;
560
0
      }
561
0
      break;
562
0
    }
563
0
  }
564
0
}
565
566
static void rec_do(struct Curl_easy *data, struct TELNET *tn, int option)
567
0
{
568
0
  switch(tn->us[option]) {
569
0
  case CURL_NO:
570
0
    if(tn->us_preferred[option] == CURL_YES) {
571
0
      tn->us[option] = CURL_YES;
572
0
      send_negotiation(data, CURL_WILL, option);
573
0
      if(tn->subnegotiation[option] == CURL_YES)
574
        /* transmission of data option */
575
0
        sendsuboption(data, tn, option);
576
0
    }
577
0
    else if(tn->subnegotiation[option] == CURL_YES) {
578
      /* send information to achieve this option */
579
0
      tn->us[option] = CURL_YES;
580
0
      send_negotiation(data, CURL_WILL, option);
581
0
      sendsuboption(data, tn, option);
582
0
    }
583
0
    else
584
0
      send_negotiation(data, CURL_WONT, option);
585
0
    break;
586
587
0
  case CURL_YES:
588
    /* Already enabled */
589
0
    break;
590
591
0
  case CURL_WANTNO:
592
0
    switch(tn->usq[option]) {
593
0
    case CURL_EMPTY:
594
      /* Error: DONT answered by WILL */
595
0
      tn->us[option] = CURL_NO;
596
0
      break;
597
0
    case CURL_OPPOSITE:
598
      /* Error: DONT answered by WILL */
599
0
      tn->us[option] = CURL_YES;
600
0
      tn->usq[option] = CURL_EMPTY;
601
0
      break;
602
0
    }
603
0
    break;
604
605
0
  case CURL_WANTYES:
606
0
    switch(tn->usq[option]) {
607
0
    case CURL_EMPTY:
608
0
      tn->us[option] = CURL_YES;
609
0
      if(tn->subnegotiation[option] == CURL_YES) {
610
        /* transmission of data option */
611
0
        sendsuboption(data, tn, option);
612
0
      }
613
0
      break;
614
0
    case CURL_OPPOSITE:
615
0
      tn->us[option] = CURL_WANTNO;
616
0
      tn->himq[option] = CURL_EMPTY;
617
0
      send_negotiation(data, CURL_WONT, option);
618
0
      break;
619
0
    }
620
0
    break;
621
0
  }
622
0
}
623
624
static void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option)
625
0
{
626
0
  switch(tn->us[option]) {
627
0
  case CURL_NO:
628
    /* Already disabled */
629
0
    break;
630
631
0
  case CURL_YES:
632
0
    tn->us[option] = CURL_NO;
633
0
    send_negotiation(data, CURL_WONT, option);
634
0
    break;
635
636
0
  case CURL_WANTNO:
637
0
    switch(tn->usq[option]) {
638
0
    case CURL_EMPTY:
639
0
      tn->us[option] = CURL_NO;
640
0
      break;
641
642
0
    case CURL_OPPOSITE:
643
0
      tn->us[option] = CURL_WANTYES;
644
0
      tn->usq[option] = CURL_EMPTY;
645
0
      send_negotiation(data, CURL_WILL, option);
646
0
      break;
647
0
    }
648
0
    break;
649
650
0
  case CURL_WANTYES:
651
0
    switch(tn->usq[option]) {
652
0
    case CURL_EMPTY:
653
0
      tn->us[option] = CURL_NO;
654
0
      break;
655
0
    case CURL_OPPOSITE:
656
0
      tn->us[option] = CURL_NO;
657
0
      tn->usq[option] = CURL_EMPTY;
658
0
      break;
659
0
    }
660
0
    break;
661
0
  }
662
0
}
663
664
static void printsub(struct Curl_easy *data,
665
                     int direction,             /* '<' or '>' */
666
                     unsigned char *pointer,    /* where suboption data is */
667
                     size_t length)             /* length of suboption data */
668
0
{
669
0
  if(data->set.verbose) {
670
0
    unsigned int i = 0;
671
0
    if(direction) {
672
0
      infof(data, "%s IAC SB ", (direction == '<') ? "RCVD" : "SENT");
673
0
      if(length >= 3) {
674
0
        int j;
675
676
0
        i = pointer[length - 2];
677
0
        j = pointer[length - 1];
678
679
0
        if(i != CURL_IAC || j != CURL_SE) {
680
0
          infof(data, "(terminated by ");
681
0
          if(CURL_TELOPT_OK(i))
682
0
            infof(data, "%s ", CURL_TELOPT(i));
683
0
          else if(CURL_TELCMD_OK(i))
684
0
            infof(data, "%s ", CURL_TELCMD(i));
685
0
          else
686
0
            infof(data, "%u ", i);
687
0
          if(CURL_TELOPT_OK(j))
688
0
            infof(data, "%s", CURL_TELOPT(j));
689
0
          else if(CURL_TELCMD_OK(j))
690
0
            infof(data, "%s", CURL_TELCMD(j));
691
0
          else
692
0
            infof(data, "%d", j);
693
0
          infof(data, ", not IAC SE) ");
694
0
        }
695
0
      }
696
0
      if(length >= 2)
697
0
        length -= 2;
698
0
      else /* bad input */
699
0
        return;
700
0
    }
701
0
    if(length <= 1) {
702
0
      infof(data, "(Empty suboption?)");
703
0
      return;
704
0
    }
705
706
0
    if(CURL_TELOPT_OK(pointer[0])) {
707
0
      switch(pointer[0]) {
708
0
      case CURL_TELOPT_TTYPE:
709
0
      case CURL_TELOPT_XDISPLOC:
710
0
      case CURL_TELOPT_NEW_ENVIRON:
711
0
      case CURL_TELOPT_NAWS:
712
0
        infof(data, "%s", CURL_TELOPT(pointer[0]));
713
0
        break;
714
0
      default:
715
0
        infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
716
0
        break;
717
0
      }
718
0
    }
719
0
    else
720
0
      infof(data, "%d (unknown)", pointer[0]);
721
722
0
    switch(pointer[0]) {
723
0
    case CURL_TELOPT_NAWS:
724
0
      if(length > 4)
725
0
        infof(data, "Width: %d ; Height: %d", (pointer[1] << 8) | pointer[2],
726
0
              (pointer[3] << 8) | pointer[4]);
727
0
      break;
728
0
    default:
729
0
      switch(pointer[1]) {
730
0
      case CURL_TELQUAL_IS:
731
0
        infof(data, " IS");
732
0
        break;
733
0
      case CURL_TELQUAL_SEND:
734
0
        infof(data, " SEND");
735
0
        break;
736
0
      case CURL_TELQUAL_INFO:
737
0
        infof(data, " INFO/REPLY");
738
0
        break;
739
0
      case CURL_TELQUAL_NAME:
740
0
        infof(data, " NAME");
741
0
        break;
742
0
      }
743
744
0
      switch(pointer[0]) {
745
0
      case CURL_TELOPT_TTYPE:
746
0
      case CURL_TELOPT_XDISPLOC:
747
0
        infof(data, " \"%.*s\"",
748
0
              (int)((length > 2) ? (length - 2) : 0), &pointer[2]);
749
0
        break;
750
0
      case CURL_TELOPT_NEW_ENVIRON:
751
0
        if(pointer[1] == CURL_TELQUAL_IS) {
752
0
          infof(data, " ");
753
0
          for(i = 3; i < length; i++) {
754
0
            switch(pointer[i]) {
755
0
            case CURL_NEW_ENV_VAR:
756
0
              infof(data, ", ");
757
0
              break;
758
0
            case CURL_NEW_ENV_VALUE:
759
0
              infof(data, " = ");
760
0
              break;
761
0
            default:
762
0
              infof(data, "%c", pointer[i]);
763
0
              break;
764
0
            }
765
0
          }
766
0
        }
767
0
        break;
768
0
      default:
769
0
        for(i = 2; i < length; i++)
770
0
          infof(data, " %.2x", pointer[i]);
771
0
        break;
772
0
      }
773
0
    }
774
0
  }
775
0
}
776
777
static bool str_is_nonascii(const char *str)
778
0
{
779
0
  char c;
780
0
  while((c = *str++) != 0)
781
0
    if(c & 0x80)
782
0
      return TRUE;
783
784
0
  return FALSE;
785
0
}
786
787
static CURLcode check_telnet_options(struct Curl_easy *data,
788
                                     struct TELNET *tn)
789
0
{
790
0
  struct curl_slist *head;
791
0
  struct curl_slist *beg;
792
0
  CURLcode result = CURLE_OK;
793
794
  /* Add the username as an environment variable if it
795
     was given on the command line */
796
0
  if(data->state.aptr.user) {
797
0
    char buffer[256];
798
0
    if(str_is_nonascii(data->conn->user)) {
799
0
      DEBUGF(infof(data, "set a non ASCII username in telnet"));
800
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
801
0
    }
802
0
    curl_msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
803
0
    beg = curl_slist_append(tn->telnet_vars, buffer);
804
0
    if(!beg) {
805
0
      curl_slist_free_all(tn->telnet_vars);
806
0
      tn->telnet_vars = NULL;
807
0
      return CURLE_OUT_OF_MEMORY;
808
0
    }
809
0
    tn->telnet_vars = beg;
810
0
    tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
811
0
  }
812
813
0
  for(head = data->set.telnet_options; head && !result; head = head->next) {
814
0
    size_t olen;
815
0
    char *option = head->data;
816
0
    char *arg;
817
0
    char *sep = strchr(option, '=');
818
0
    if(sep) {
819
0
      olen = sep - option;
820
0
      arg = ++sep;
821
0
      if(str_is_nonascii(arg))
822
0
        continue;
823
0
      switch(olen) {
824
0
      case 5:
825
        /* Terminal type */
826
0
        if(curl_strnequal(option, "TTYPE", 5)) {
827
0
          tn->subopt_ttype = arg;
828
0
          tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
829
0
          break;
830
0
        }
831
0
        result = CURLE_UNKNOWN_OPTION;
832
0
        break;
833
834
0
      case 8:
835
        /* Display variable */
836
0
        if(curl_strnequal(option, "XDISPLOC", 8)) {
837
0
          tn->subopt_xdisploc = arg;
838
0
          tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
839
0
          break;
840
0
        }
841
0
        result = CURLE_UNKNOWN_OPTION;
842
0
        break;
843
844
0
      case 7:
845
        /* Environment variable */
846
0
        if(curl_strnequal(option, "NEW_ENV", 7)) {
847
0
          beg = curl_slist_append(tn->telnet_vars, arg);
848
0
          if(!beg) {
849
0
            result = CURLE_OUT_OF_MEMORY;
850
0
            break;
851
0
          }
852
0
          tn->telnet_vars = beg;
853
0
          tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
854
0
        }
855
0
        else
856
0
          result = CURLE_UNKNOWN_OPTION;
857
0
        break;
858
859
0
      case 2:
860
        /* Window Size */
861
0
        if(curl_strnequal(option, "WS", 2)) {
862
0
          const char *p = arg;
863
0
          curl_off_t x = 0;
864
0
          curl_off_t y = 0;
865
0
          if(curlx_str_number(&p, &x, 0xffff) ||
866
0
             curlx_str_single(&p, 'x') ||
867
0
             curlx_str_number(&p, &y, 0xffff)) {
868
0
            failf(data, "Syntax error in telnet option: %s", head->data);
869
0
            result = CURLE_SETOPT_OPTION_SYNTAX;
870
0
          }
871
0
          else {
872
0
            tn->subopt_wsx = (unsigned short)x;
873
0
            tn->subopt_wsy = (unsigned short)y;
874
0
            tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
875
0
          }
876
0
        }
877
0
        else
878
0
          result = CURLE_UNKNOWN_OPTION;
879
0
        break;
880
881
0
      case 6:
882
        /* To take care or not of the 8th bit in data exchange */
883
0
        if(curl_strnequal(option, "BINARY", 6)) {
884
0
          const char *p = arg;
885
0
          curl_off_t binary_option;
886
0
          if(!curlx_str_number(&p, &binary_option, 1) &&
887
0
             (binary_option != 1)) {
888
0
            tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
889
0
            tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
890
0
          }
891
0
        }
892
0
        else
893
0
          result = CURLE_UNKNOWN_OPTION;
894
0
        break;
895
0
      default:
896
0
        failf(data, "Unknown telnet option %s", head->data);
897
0
        result = CURLE_UNKNOWN_OPTION;
898
0
        break;
899
0
      }
900
0
    }
901
0
    else {
902
0
      failf(data, "Syntax error in telnet option: %s", head->data);
903
0
      result = CURLE_SETOPT_OPTION_SYNTAX;
904
0
    }
905
0
  }
906
907
0
  if(result) {
908
0
    curl_slist_free_all(tn->telnet_vars);
909
0
    tn->telnet_vars = NULL;
910
0
  }
911
912
0
  return result;
913
0
}
914
915
/* if the option contains an IAC code, it should be escaped in the output, but
916
   as we cannot think of any legit way to send that as part of the content we
917
   rather just ban its use instead */
918
static bool bad_option(const char *data)
919
0
{
920
0
  return !data || !!strchr(data, CURL_IAC);
921
0
}
922
923
/*
924
 * suboption()
925
 *
926
 * Look at the sub-option buffer, and try to be helpful to the other
927
 * side.
928
 */
929
static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn)
930
0
{
931
0
  struct curl_slist *v;
932
0
  unsigned char temp[2048];
933
0
  ssize_t bytes_written;
934
0
  size_t len;
935
0
  int err;
936
0
  struct connectdata *conn = data->conn;
937
938
0
  if(!CURL_SB_LEN(tn)) /* ignore empty suboption */
939
0
    return CURLE_OK;
940
941
0
  printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
942
0
  switch(CURL_SB_GET(tn)) {
943
0
  case CURL_TELOPT_TTYPE:
944
0
    if(bad_option(tn->subopt_ttype))
945
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
946
0
    if(strlen(tn->subopt_ttype) > 1000) {
947
0
      failf(data, "Tool long telnet TTYPE");
948
0
      return CURLE_SEND_ERROR;
949
0
    }
950
0
    len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c",
951
0
                         CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
952
0
                         CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC,
953
0
                         CURL_SE);
954
0
    bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
955
956
0
    if(bytes_written < 0) {
957
0
      err = SOCKERRNO;
958
0
      failf(data, "Sending data failed (%d)", err);
959
0
      return CURLE_SEND_ERROR;
960
0
    }
961
0
    printsub(data, '>', &temp[2], len-2);
962
0
    break;
963
0
  case CURL_TELOPT_XDISPLOC:
964
0
    if(bad_option(tn->subopt_xdisploc))
965
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
966
0
    if(strlen(tn->subopt_xdisploc) > 1000) {
967
0
      failf(data, "Tool long telnet XDISPLOC");
968
0
      return CURLE_SEND_ERROR;
969
0
    }
970
0
    len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c",
971
0
                         CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
972
0
                         CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC,
973
0
                         CURL_SE);
974
0
    bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
975
0
    if(bytes_written < 0) {
976
0
      err = SOCKERRNO;
977
0
      failf(data, "Sending data failed (%d)", err);
978
0
      return CURLE_SEND_ERROR;
979
0
    }
980
0
    printsub(data, '>', &temp[2], len - 2);
981
0
    break;
982
0
  case CURL_TELOPT_NEW_ENVIRON:
983
0
    len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c",
984
0
                         CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
985
0
                         CURL_TELQUAL_IS);
986
0
    for(v = tn->telnet_vars; v; v = v->next) {
987
0
      size_t tmplen = (strlen(v->data) + 1);
988
0
      if(bad_option(v->data))
989
0
        return CURLE_BAD_FUNCTION_ARGUMENT;
990
      /* Add the variable if it fits */
991
0
      if(len + tmplen < (int)sizeof(temp) - 6) {
992
0
        char *s = strchr(v->data, ',');
993
0
        if(!s)
994
0
          len += curl_msnprintf((char *)&temp[len], sizeof(temp) - len,
995
0
                                "%c%s", CURL_NEW_ENV_VAR, v->data);
996
0
        else {
997
0
          size_t vlen = s - v->data;
998
0
          len += curl_msnprintf((char *)&temp[len], sizeof(temp) - len,
999
0
                                "%c%.*s%c%s", CURL_NEW_ENV_VAR,
1000
0
                                (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);
1001
0
        }
1002
0
      }
1003
0
    }
1004
0
    curl_msnprintf((char *)&temp[len], sizeof(temp) - len,
1005
0
                   "%c%c", CURL_IAC, CURL_SE);
1006
0
    len += 2;
1007
0
    bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
1008
0
    if(bytes_written < 0) {
1009
0
      err = SOCKERRNO;
1010
0
      failf(data, "Sending data failed (%d)", err);
1011
0
    }
1012
0
    printsub(data, '>', &temp[2], len - 2);
1013
0
    break;
1014
0
  }
1015
0
  return CURLE_OK;
1016
0
}
1017
1018
/*
1019
 * sendsuboption()
1020
 *
1021
 * Send suboption information to the server side.
1022
 */
1023
static void sendsuboption(struct Curl_easy *data,
1024
                          struct TELNET *tn, int option)
1025
0
{
1026
0
  ssize_t bytes_written;
1027
0
  int err;
1028
0
  unsigned short x, y;
1029
0
  unsigned char *uc1, *uc2;
1030
0
  struct connectdata *conn = data->conn;
1031
1032
0
  switch(option) {
1033
0
  case CURL_TELOPT_NAWS:
1034
    /* We prepare data to be sent */
1035
0
    CURL_SB_CLEAR(tn);
1036
0
    CURL_SB_ACCUM(tn, CURL_IAC);
1037
0
    CURL_SB_ACCUM(tn, CURL_SB);
1038
0
    CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
1039
    /* We must deal either with little or big endian processors */
1040
    /* Window size must be sent according to the 'network order' */
1041
0
    x = htons(tn->subopt_wsx);
1042
0
    y = htons(tn->subopt_wsy);
1043
0
    uc1 = (unsigned char *)&x;
1044
0
    uc2 = (unsigned char *)&y;
1045
0
    CURL_SB_ACCUM(tn, uc1[0]);
1046
0
    CURL_SB_ACCUM(tn, uc1[1]);
1047
0
    CURL_SB_ACCUM(tn, uc2[0]);
1048
0
    CURL_SB_ACCUM(tn, uc2[1]);
1049
1050
0
    CURL_SB_ACCUM(tn, CURL_IAC);
1051
0
    CURL_SB_ACCUM(tn, CURL_SE);
1052
0
    CURL_SB_TERM(tn);
1053
    /* data suboption is now ready */
1054
1055
0
    printsub(data, '>', (unsigned char *)tn->subbuffer + 2,
1056
0
             CURL_SB_LEN(tn) - 2);
1057
1058
    /* we send the header of the suboption... */
1059
0
    bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
1060
0
    if(bytes_written < 0) {
1061
0
      err = SOCKERRNO;
1062
0
      failf(data, "Sending data failed (%d)", err);
1063
0
    }
1064
    /* ... then the window size with the send_telnet_data() function
1065
       to deal with 0xFF cases ... */
1066
0
    send_telnet_data(data, tn, (char *)tn->subbuffer + 3, 4);
1067
    /* ... and the footer */
1068
0
    bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
1069
0
    if(bytes_written < 0) {
1070
0
      err = SOCKERRNO;
1071
0
      failf(data, "Sending data failed (%d)", err);
1072
0
    }
1073
0
    break;
1074
0
  }
1075
0
}
1076
1077
static CURLcode telrcv(struct Curl_easy *data,
1078
                       struct TELNET *tn,
1079
                       const unsigned char *inbuf, /* Data received from
1080
                                                      socket */
1081
                       ssize_t count)              /* Number of bytes
1082
                                                      received */
1083
0
{
1084
0
  unsigned char c;
1085
0
  CURLcode result;
1086
0
  int in = 0;
1087
0
  int startwrite = -1;
1088
1089
0
#define startskipping()                                          \
1090
0
  if(startwrite >= 0) {                                          \
1091
0
    result = Curl_client_write(data,                             \
1092
0
                               CLIENTWRITE_BODY,                 \
1093
0
                               (const char *)&inbuf[startwrite], \
1094
0
                               in-startwrite);                   \
1095
0
    if(result)                                                   \
1096
0
      return result;                                             \
1097
0
  }                                                              \
1098
0
  startwrite = -1
1099
1100
0
#define writebyte()  \
1101
0
  if(startwrite < 0) \
1102
0
    startwrite = in
1103
1104
0
#define bufferflush() startskipping()
1105
1106
0
  while(count--) {
1107
0
    c = inbuf[in];
1108
1109
0
    switch(tn->telrcv_state) {
1110
0
    case CURL_TS_CR:
1111
0
      tn->telrcv_state = CURL_TS_DATA;
1112
0
      if(c == '\0') {
1113
0
        startskipping();
1114
0
        break;   /* Ignore \0 after CR */
1115
0
      }
1116
0
      writebyte();
1117
0
      break;
1118
1119
0
    case CURL_TS_DATA:
1120
0
      if(c == CURL_IAC) {
1121
0
        tn->telrcv_state = CURL_TS_IAC;
1122
0
        startskipping();
1123
0
        break;
1124
0
      }
1125
0
      else if(c == '\r')
1126
0
        tn->telrcv_state = CURL_TS_CR;
1127
0
      writebyte();
1128
0
      break;
1129
1130
0
    case CURL_TS_IAC:
1131
0
process_iac:
1132
0
      DEBUGASSERT(startwrite < 0);
1133
0
      switch(c) {
1134
0
      case CURL_WILL:
1135
0
        tn->telrcv_state = CURL_TS_WILL;
1136
0
        break;
1137
0
      case CURL_WONT:
1138
0
        tn->telrcv_state = CURL_TS_WONT;
1139
0
        break;
1140
0
      case CURL_DO:
1141
0
        tn->telrcv_state = CURL_TS_DO;
1142
0
        break;
1143
0
      case CURL_DONT:
1144
0
        tn->telrcv_state = CURL_TS_DONT;
1145
0
        break;
1146
0
      case CURL_SB:
1147
0
        CURL_SB_CLEAR(tn);
1148
0
        tn->telrcv_state = CURL_TS_SB;
1149
0
        break;
1150
0
      case CURL_IAC:
1151
0
        tn->telrcv_state = CURL_TS_DATA;
1152
0
        writebyte();
1153
0
        break;
1154
0
      case CURL_DM:
1155
0
      case CURL_NOP:
1156
0
      case CURL_GA:
1157
0
      default:
1158
0
        tn->telrcv_state = CURL_TS_DATA;
1159
0
        printoption(data, "RCVD", CURL_IAC, c);
1160
0
        break;
1161
0
      }
1162
0
      break;
1163
1164
0
    case CURL_TS_WILL:
1165
0
      printoption(data, "RCVD", CURL_WILL, c);
1166
0
      tn->please_negotiate = 1;
1167
0
      rec_will(data, tn, c);
1168
0
      tn->telrcv_state = CURL_TS_DATA;
1169
0
      break;
1170
1171
0
    case CURL_TS_WONT:
1172
0
      printoption(data, "RCVD", CURL_WONT, c);
1173
0
      tn->please_negotiate = 1;
1174
0
      rec_wont(data, tn, c);
1175
0
      tn->telrcv_state = CURL_TS_DATA;
1176
0
      break;
1177
1178
0
    case CURL_TS_DO:
1179
0
      printoption(data, "RCVD", CURL_DO, c);
1180
0
      tn->please_negotiate = 1;
1181
0
      rec_do(data, tn, c);
1182
0
      tn->telrcv_state = CURL_TS_DATA;
1183
0
      break;
1184
1185
0
    case CURL_TS_DONT:
1186
0
      printoption(data, "RCVD", CURL_DONT, c);
1187
0
      tn->please_negotiate = 1;
1188
0
      rec_dont(data, tn, c);
1189
0
      tn->telrcv_state = CURL_TS_DATA;
1190
0
      break;
1191
1192
0
    case CURL_TS_SB:
1193
0
      if(c == CURL_IAC)
1194
0
        tn->telrcv_state = CURL_TS_SE;
1195
0
      else
1196
0
        CURL_SB_ACCUM(tn, c);
1197
0
      break;
1198
1199
0
    case CURL_TS_SE:
1200
0
      if(c != CURL_SE) {
1201
0
        if(c != CURL_IAC) {
1202
          /*
1203
           * This is an error. We only expect to get "IAC IAC" or "IAC SE".
1204
           * Several things may have happened. An IAC was not doubled, the
1205
           * IAC SE was left off, or another option got inserted into the
1206
           * suboption are all possibilities. If we assume that the IAC was
1207
           * not doubled, and really the IAC SE was left off, we could get
1208
           * into an infinite loop here. So, instead, we terminate the
1209
           * suboption, and process the partial suboption if we can.
1210
           */
1211
0
          CURL_SB_ACCUM(tn, CURL_IAC);
1212
0
          CURL_SB_ACCUM(tn, c);
1213
0
          tn->subpointer -= 2;
1214
0
          CURL_SB_TERM(tn);
1215
1216
0
          printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
1217
0
          result = suboption(data, tn);   /* handle sub-option */
1218
0
          if(result)
1219
0
            return result;
1220
0
          tn->telrcv_state = CURL_TS_IAC;
1221
0
          goto process_iac;
1222
0
        }
1223
0
        CURL_SB_ACCUM(tn, c);
1224
0
        tn->telrcv_state = CURL_TS_SB;
1225
0
      }
1226
0
      else {
1227
0
        CURL_SB_ACCUM(tn, CURL_IAC);
1228
0
        CURL_SB_ACCUM(tn, CURL_SE);
1229
0
        tn->subpointer -= 2;
1230
0
        CURL_SB_TERM(tn);
1231
0
        result = suboption(data, tn);   /* handle sub-option */
1232
0
        if(result)
1233
0
          return result;
1234
0
        tn->telrcv_state = CURL_TS_DATA;
1235
0
      }
1236
0
      break;
1237
0
    }
1238
0
    ++in;
1239
0
  }
1240
0
  bufferflush();
1241
0
  return CURLE_OK;
1242
0
}
1243
1244
/* Escape and send a telnet data block */
1245
static CURLcode send_telnet_data(struct Curl_easy *data,
1246
                                 struct TELNET *tn,
1247
                                 char *buffer, ssize_t nread)
1248
0
{
1249
0
  size_t i, outlen;
1250
0
  unsigned char *outbuf;
1251
0
  CURLcode result = CURLE_OK;
1252
0
  size_t bytes_written;
1253
0
  size_t total_written = 0;
1254
0
  struct connectdata *conn = data->conn;
1255
1256
0
  DEBUGASSERT(tn);
1257
0
  DEBUGASSERT(nread > 0);
1258
0
  if(nread < 0)
1259
0
    return CURLE_TOO_LARGE;
1260
1261
0
  if(memchr(buffer, CURL_IAC, nread)) {
1262
    /* only use the escape buffer when necessary */
1263
0
    curlx_dyn_reset(&tn->out);
1264
1265
0
    for(i = 0; i < (size_t)nread && !result; i++) {
1266
0
      result = curlx_dyn_addn(&tn->out, &buffer[i], 1);
1267
0
      if(!result && ((unsigned char)buffer[i] == CURL_IAC))
1268
        /* IAC is FF in hex */
1269
0
        result = curlx_dyn_addn(&tn->out, "\xff", 1);
1270
0
    }
1271
1272
0
    outlen = curlx_dyn_len(&tn->out);
1273
0
    outbuf = curlx_dyn_uptr(&tn->out);
1274
0
  }
1275
0
  else {
1276
0
    outlen = (size_t)nread;
1277
0
    outbuf = (unsigned char *)buffer;
1278
0
  }
1279
0
  while(!result && total_written < outlen) {
1280
    /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1281
0
    struct pollfd pfd[1];
1282
0
    pfd[0].fd = conn->sock[FIRSTSOCKET];
1283
0
    pfd[0].events = POLLOUT;
1284
0
    switch(Curl_poll(pfd, 1, -1)) {
1285
0
    case -1:                    /* error, abort writing */
1286
0
    case 0:                     /* timeout (will never happen) */
1287
0
      result = CURLE_SEND_ERROR;
1288
0
      break;
1289
0
    default:                    /* write! */
1290
0
      bytes_written = 0;
1291
0
      result = Curl_xfer_send(data, outbuf + total_written,
1292
0
                              outlen - total_written, FALSE, &bytes_written);
1293
0
      total_written += bytes_written;
1294
0
      break;
1295
0
    }
1296
0
  }
1297
1298
0
  return result;
1299
0
}
1300
1301
static CURLcode telnet_done(struct Curl_easy *data,
1302
                            CURLcode status, bool premature)
1303
0
{
1304
0
  (void)status;
1305
0
  (void)premature;
1306
0
  Curl_meta_remove(data, CURL_META_TELNET_EASY);
1307
0
  return CURLE_OK;
1308
0
}
1309
1310
static CURLcode telnet_do(struct Curl_easy *data, bool *done)
1311
0
{
1312
0
  CURLcode result;
1313
0
  struct connectdata *conn = data->conn;
1314
0
  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1315
#ifdef USE_WINSOCK
1316
  WSAEVENT event_handle;
1317
  WSANETWORKEVENTS events;
1318
  HANDLE stdin_handle;
1319
  HANDLE objs[2];
1320
  DWORD obj_count;
1321
  DWORD wait_timeout;
1322
  DWORD readfile_read;
1323
  int err;
1324
#else
1325
0
  timediff_t interval_ms;
1326
0
  struct pollfd pfd[2];
1327
0
  int poll_cnt;
1328
0
  curl_off_t total_dl = 0;
1329
0
  curl_off_t total_ul = 0;
1330
0
  ssize_t snread;
1331
0
#endif
1332
0
  struct curltime now;
1333
0
  bool keepon = TRUE;
1334
0
  char buffer[4 * 1024];
1335
0
  struct TELNET *tn;
1336
1337
0
  *done = TRUE; /* unconditionally */
1338
1339
0
  result = init_telnet(data);
1340
0
  if(result)
1341
0
    return result;
1342
1343
0
  tn = Curl_meta_get(data, CURL_META_TELNET_EASY);
1344
0
  if(!tn)
1345
0
    return CURLE_FAILED_INIT;
1346
1347
0
  result = check_telnet_options(data, tn);
1348
0
  if(result)
1349
0
    return result;
1350
1351
#ifdef USE_WINSOCK
1352
  /* We want to wait for both stdin and the socket. Since
1353
  ** the select() function in Winsock only works on sockets
1354
  ** we have to use the WaitForMultipleObjects() call.
1355
  */
1356
1357
  /* First, create a sockets event object */
1358
  event_handle = WSACreateEvent();
1359
  if(event_handle == WSA_INVALID_EVENT) {
1360
    failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
1361
    return CURLE_FAILED_INIT;
1362
  }
1363
1364
  /* Tell Winsock what events we want to listen to */
1365
  if(WSAEventSelect(sockfd, event_handle, FD_READ | FD_CLOSE) ==
1366
     SOCKET_ERROR) {
1367
    WSACloseEvent(event_handle);
1368
    return CURLE_RECV_ERROR;
1369
  }
1370
1371
  /* The get the Windows file handle for stdin */
1372
  stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1373
1374
  /* Create the list of objects to wait for */
1375
  objs[0] = event_handle;
1376
  objs[1] = stdin_handle;
1377
1378
  /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1379
     else use the old WaitForMultipleObjects() way */
1380
  if(GetFileType(stdin_handle) == FILE_TYPE_PIPE || data->set.is_fread_set) {
1381
    /* Do not wait for stdin_handle, just wait for event_handle */
1382
    obj_count = 1;
1383
    /* Check stdin_handle per 100 milliseconds */
1384
    wait_timeout = 100;
1385
  }
1386
  else {
1387
    obj_count = 2;
1388
    wait_timeout = 1000;
1389
  }
1390
1391
  /* Keep on listening and act on events */
1392
  while(keepon) {
1393
    const DWORD buf_size = (DWORD)sizeof(buffer);
1394
    DWORD waitret = WaitForMultipleObjects(obj_count, objs,
1395
                                           FALSE, wait_timeout);
1396
    switch(waitret) {
1397
1398
    case WAIT_TIMEOUT: {
1399
      for(;;) {
1400
        if(data->set.is_fread_set) {
1401
          size_t n;
1402
          /* read from user-supplied method */
1403
          n = data->state.fread_func(buffer, 1, buf_size, data->state.in);
1404
          if(n == CURL_READFUNC_ABORT) {
1405
            keepon = FALSE;
1406
            result = CURLE_READ_ERROR;
1407
            break;
1408
          }
1409
1410
          if(n == CURL_READFUNC_PAUSE)
1411
            break;
1412
1413
          if(n == 0)                        /* no bytes */
1414
            break;
1415
1416
          /* fall through with number of bytes read */
1417
          readfile_read = (DWORD)n;
1418
        }
1419
        else {
1420
          /* read from stdin */
1421
          if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
1422
                            &readfile_read, NULL)) {
1423
            keepon = FALSE;
1424
            result = CURLE_READ_ERROR;
1425
            break;
1426
          }
1427
1428
          if(!readfile_read)
1429
            break;
1430
1431
          if(!ReadFile(stdin_handle, buffer, buf_size, &readfile_read, NULL)) {
1432
            keepon = FALSE;
1433
            result = CURLE_READ_ERROR;
1434
            break;
1435
          }
1436
        }
1437
1438
        result = send_telnet_data(data, tn, buffer, readfile_read);
1439
        if(result) {
1440
          keepon = FALSE;
1441
          break;
1442
        }
1443
      }
1444
    }
1445
    break;
1446
1447
    case WAIT_OBJECT_0 + 1: {
1448
      if(!ReadFile(stdin_handle, buffer, buf_size, &readfile_read, NULL)) {
1449
        keepon = FALSE;
1450
        result = CURLE_READ_ERROR;
1451
        break;
1452
      }
1453
1454
      result = send_telnet_data(data, tn, buffer, readfile_read);
1455
      if(result) {
1456
        keepon = FALSE;
1457
        break;
1458
      }
1459
    }
1460
    break;
1461
1462
    case WAIT_OBJECT_0: {
1463
      events.lNetworkEvents = 0;
1464
      if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
1465
        err = SOCKERRNO;
1466
        if(err != SOCKEINPROGRESS) {
1467
          infof(data, "WSAEnumNetworkEvents failed (%d)", err);
1468
          keepon = FALSE;
1469
          result = CURLE_READ_ERROR;
1470
        }
1471
        break;
1472
      }
1473
      if(events.lNetworkEvents & FD_READ) {
1474
        /* read data from network */
1475
        size_t nread;
1476
        result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
1477
        /* read would have blocked. Loop again */
1478
        if(result == CURLE_AGAIN)
1479
          break;
1480
        /* returned not-zero, this an error */
1481
        else if(result) {
1482
          keepon = FALSE;
1483
          break;
1484
        }
1485
        /* returned zero but actually received 0 or less here,
1486
           the server closed the connection and we bail out */
1487
        else if(!nread) {
1488
          keepon = FALSE;
1489
          break;
1490
        }
1491
1492
        result = telrcv(data, tn, (unsigned char *)buffer, nread);
1493
        if(result) {
1494
          keepon = FALSE;
1495
          break;
1496
        }
1497
1498
        /* Negotiate if the peer has started negotiating,
1499
           otherwise do not. We do not want to speak telnet with
1500
           non-telnet servers, like POP or SMTP. */
1501
        if(tn->please_negotiate && !tn->already_negotiated) {
1502
          telnet_negotiate(data, tn);
1503
          tn->already_negotiated = 1;
1504
        }
1505
      }
1506
      if(events.lNetworkEvents & FD_CLOSE) {
1507
        keepon = FALSE;
1508
      }
1509
      break;
1510
    }
1511
    } /* switch */
1512
1513
    if(data->set.timeout) {
1514
      now = curlx_now();
1515
      if(curlx_timediff_ms(now, conn->created) >= data->set.timeout) {
1516
        failf(data, "Time-out");
1517
        result = CURLE_OPERATION_TIMEDOUT;
1518
        keepon = FALSE;
1519
      }
1520
    }
1521
  }
1522
1523
  /* We called WSACreateEvent, so call WSACloseEvent */
1524
  if(!WSACloseEvent(event_handle)) {
1525
    infof(data, "WSACloseEvent failed (%d)", SOCKERRNO);
1526
  }
1527
#else
1528
0
  pfd[0].fd = sockfd;
1529
0
  pfd[0].events = POLLIN;
1530
1531
0
  if(data->set.is_fread_set) {
1532
0
    poll_cnt = 1;
1533
0
    interval_ms = 100; /* poll user-supplied read function */
1534
0
  }
1535
0
  else {
1536
    /* really using fread, so infile is a FILE* */
1537
0
    pfd[1].fd = fileno((FILE *)data->state.in);
1538
0
    pfd[1].events = POLLIN;
1539
0
    poll_cnt = 2;
1540
0
    interval_ms = 1 * 1000;
1541
0
    if(pfd[1].fd < 0) {
1542
0
      failf(data, "cannot read input");
1543
0
      result = CURLE_RECV_ERROR;
1544
0
      keepon = FALSE;
1545
0
    }
1546
0
  }
1547
1548
0
  while(keepon) {
1549
0
    DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt));
1550
0
    switch(Curl_poll(pfd, (unsigned int)poll_cnt, interval_ms)) {
1551
0
    case -1:                    /* error, stop reading */
1552
0
      keepon = FALSE;
1553
0
      continue;
1554
0
    case 0:                     /* timeout */
1555
0
      pfd[0].revents = 0;
1556
0
      pfd[1].revents = 0;
1557
0
      FALLTHROUGH();
1558
0
    default:                    /* read! */
1559
0
      if(pfd[0].revents & POLLIN) {
1560
        /* read data from network */
1561
0
        size_t nread;
1562
0
        result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
1563
        /* read would have blocked. Loop again */
1564
0
        if(result == CURLE_AGAIN)
1565
0
          break;
1566
        /* returned not-zero, this an error */
1567
0
        if(result) {
1568
0
          keepon = FALSE;
1569
          /* In test 1452, macOS sees a ECONNRESET sometimes? Is this the
1570
           * telnet test server not shutting down the socket in a clean way?
1571
           * Seems to be timing related, happens more on slow debug build */
1572
0
          if(data->state.os_errno == SOCKECONNRESET) {
1573
0
            DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
1574
0
          }
1575
0
          break;
1576
0
        }
1577
        /* returned zero but actually received 0 or less here,
1578
           the server closed the connection and we bail out */
1579
0
        else if(!nread) {
1580
0
          keepon = FALSE;
1581
0
          break;
1582
0
        }
1583
1584
0
        total_dl += nread;
1585
0
        Curl_pgrsSetDownloadCounter(data, total_dl);
1586
0
        result = telrcv(data, tn, (unsigned char *)buffer, nread);
1587
0
        if(result) {
1588
0
          keepon = FALSE;
1589
0
          break;
1590
0
        }
1591
1592
        /* Negotiate if the peer has started negotiating,
1593
           otherwise do not. We do not want to speak telnet with
1594
           non-telnet servers, like POP or SMTP. */
1595
0
        if(tn->please_negotiate && !tn->already_negotiated) {
1596
0
          telnet_negotiate(data, tn);
1597
0
          tn->already_negotiated = 1;
1598
0
        }
1599
0
      }
1600
1601
0
      snread = 0;
1602
0
      if(poll_cnt == 2) {
1603
0
        if(pfd[1].revents & POLLIN) { /* read from in file */
1604
0
          snread = read(pfd[1].fd, buffer, sizeof(buffer));
1605
0
        }
1606
0
      }
1607
0
      else {
1608
        /* read from user-supplied method */
1609
0
        snread = (int)data->state.fread_func(buffer, 1, sizeof(buffer),
1610
0
                                             data->state.in);
1611
0
        if(snread == CURL_READFUNC_ABORT) {
1612
0
          keepon = FALSE;
1613
0
          break;
1614
0
        }
1615
0
        if(snread == CURL_READFUNC_PAUSE)
1616
0
          break;
1617
0
      }
1618
1619
0
      if(snread > 0) {
1620
0
        result = send_telnet_data(data, tn, buffer, snread);
1621
0
        if(result) {
1622
0
          keepon = FALSE;
1623
0
          break;
1624
0
        }
1625
0
        total_ul += snread;
1626
0
        Curl_pgrsSetUploadCounter(data, total_ul);
1627
0
      }
1628
0
      else if(snread < 0)
1629
0
        keepon = FALSE;
1630
1631
0
      break;
1632
0
    } /* poll switch statement */
1633
1634
0
    if(data->set.timeout) {
1635
0
      now = curlx_now();
1636
0
      if(curlx_timediff_ms(now, conn->created) >= data->set.timeout) {
1637
0
        failf(data, "Time-out");
1638
0
        result = CURLE_OPERATION_TIMEDOUT;
1639
0
        keepon = FALSE;
1640
0
      }
1641
0
    }
1642
1643
0
    if(!result) {
1644
0
      result = Curl_pgrsUpdate(data);
1645
0
      if(result)
1646
0
        keepon = FALSE;
1647
0
    }
1648
0
  }
1649
0
#endif
1650
  /* mark this as "no further transfer wanted" */
1651
0
  Curl_xfer_setup_nop(data);
1652
1653
0
  return result;
1654
0
}
1655
#endif