Coverage Report

Created: 2024-02-25 06:14

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