Coverage Report

Created: 2025-10-10 06:09

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