Coverage Report

Created: 2026-07-16 06:10

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