Coverage Report

Created: 2026-05-30 06:06

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