Coverage Report

Created: 2025-06-09 08:44

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