Coverage Report

Created: 2026-01-10 06:51

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