Coverage Report

Created: 2023-12-08 06:48

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