Coverage Report

Created: 2025-07-11 06:33

/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 "url.h"
51
#include <curl/curl.h>
52
#include "transfer.h"
53
#include "sendf.h"
54
#include "telnet.h"
55
#include "connect.h"
56
#include "progress.h"
57
#include "system_win32.h"
58
#include "arpa_telnet.h"
59
#include "select.h"
60
#include "curlx/warnless.h"
61
#include "curlx/strparse.h"
62
63
/* The last 3 #include files should be in this order */
64
#include "curl_printf.h"
65
#include "curl_memory.h"
66
#include "memdebug.h"
67
68
#define SUBBUFSIZE 512
69
70
0
#define CURL_SB_CLEAR(x)  x->subpointer = x->subbuffer
71
#define CURL_SB_TERM(x)                                 \
72
0
  do {                                                  \
73
0
    x->subend = x->subpointer;                          \
74
0
    CURL_SB_CLEAR(x);                                   \
75
0
  } while(0)
76
#define CURL_SB_ACCUM(x,c)                                      \
77
0
  do {                                                          \
78
0
    if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer)))   \
79
0
      *x->subpointer++ = (c);                                   \
80
0
  } while(0)
81
82
0
#define  CURL_SB_GET(x) ((*x->subpointer++)&0xff)
83
0
#define  CURL_SB_LEN(x) (x->subend - x->subpointer)
84
85
/* For posterity:
86
#define  CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
87
#define  CURL_SB_EOF(x) (x->subpointer >= x->subend) */
88
89
#ifdef CURL_DISABLE_VERBOSE_STRINGS
90
#define printoption(a,b,c,d)  Curl_nop_stmt
91
#endif
92
93
/* For negotiation compliant to RFC 1143 */
94
0
#define CURL_NO          0
95
0
#define CURL_YES         1
96
0
#define CURL_WANTYES     2
97
0
#define CURL_WANTNO      3
98
99
0
#define CURL_EMPTY       0
100
0
#define CURL_OPPOSITE    1
101
102
103
/* meta key for storing protocol meta at easy handle */
104
0
#define CURL_META_TELNET_EASY   "meta:proto:telnet:easy"
105
106
/*
107
 * Telnet receiver states for fsm
108
 */
109
typedef enum
110
{
111
   CURL_TS_DATA = 0,
112
   CURL_TS_IAC,
113
   CURL_TS_WILL,
114
   CURL_TS_WONT,
115
   CURL_TS_DO,
116
   CURL_TS_DONT,
117
   CURL_TS_CR,
118
   CURL_TS_SB,   /* sub-option collection */
119
   CURL_TS_SE   /* looking for sub-option end */
120
} TelnetReceive;
121
122
struct TELNET {
123
  int please_negotiate;
124
  int already_negotiated;
125
  int us[256];
126
  int usq[256];
127
  int us_preferred[256];
128
  int him[256];
129
  int himq[256];
130
  int him_preferred[256];
131
  int subnegotiation[256];
132
  char *subopt_ttype;                /* Set with suboption TTYPE */
133
  char *subopt_xdisploc;             /* Set with suboption XDISPLOC */
134
  unsigned short subopt_wsx;         /* Set with suboption NAWS */
135
  unsigned short subopt_wsy;         /* Set with suboption NAWS */
136
  TelnetReceive telrcv_state;
137
  struct curl_slist *telnet_vars;    /* Environment variables */
138
  struct dynbuf out;                 /* output buffer */
139
140
  /* suboptions */
141
  unsigned char subbuffer[SUBBUFSIZE];
142
  unsigned char *subpointer, *subend;      /* buffer for sub-options */
143
};
144
145
146
static
147
CURLcode telrcv(struct Curl_easy *data,
148
                struct TELNET *tn,
149
                const unsigned char *inbuf, /* Data received from socket */
150
                ssize_t count);             /* Number of bytes received */
151
152
#ifndef CURL_DISABLE_VERBOSE_STRINGS
153
static void printoption(struct Curl_easy *data,
154
                        const char *direction,
155
                        int cmd, int option);
156
#endif
157
158
static void send_negotiation(struct Curl_easy *data, int cmd, int option);
159
static void set_local_option(struct Curl_easy *data, struct TELNET *tn,
160
                             int option, int newstate);
161
static void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
162
                              int option, int newstate);
163
164
static void printsub(struct Curl_easy *data,
165
                     int direction, unsigned char *pointer,
166
                     size_t length);
167
static void suboption(struct Curl_easy *data, struct TELNET *tn);
168
static void sendsuboption(struct Curl_easy *data,
169
                          struct TELNET *tn, int option);
170
171
static CURLcode telnet_do(struct Curl_easy *data, bool *done);
172
static CURLcode telnet_done(struct Curl_easy *data,
173
                                 CURLcode, bool premature);
174
static CURLcode send_telnet_data(struct Curl_easy *data,
175
                                 struct TELNET *tn,
176
                                 char *buffer, ssize_t nread);
177
178
/*
179
 * TELNET protocol handler.
180
 */
181
182
const struct Curl_handler Curl_handler_telnet = {
183
  "telnet",                             /* scheme */
184
  ZERO_NULL,                            /* setup_connection */
185
  telnet_do,                            /* do_it */
186
  telnet_done,                          /* done */
187
  ZERO_NULL,                            /* do_more */
188
  ZERO_NULL,                            /* connect_it */
189
  ZERO_NULL,                            /* connecting */
190
  ZERO_NULL,                            /* doing */
191
  ZERO_NULL,                            /* proto_getsock */
192
  ZERO_NULL,                            /* doing_getsock */
193
  ZERO_NULL,                            /* domore_getsock */
194
  ZERO_NULL,                            /* perform_getsock */
195
  ZERO_NULL,                            /* disconnect */
196
  ZERO_NULL,                            /* write_resp */
197
  ZERO_NULL,                            /* write_resp_hd */
198
  ZERO_NULL,                            /* connection_check */
199
  ZERO_NULL,                            /* attach connection */
200
  ZERO_NULL,                            /* follow */
201
  PORT_TELNET,                          /* defport */
202
  CURLPROTO_TELNET,                     /* protocol */
203
  CURLPROTO_TELNET,                     /* family */
204
  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
205
};
206
207
208
static void telnet_easy_dtor(void *key, size_t klen, void *entry)
209
0
{
210
0
  struct TELNET *tn = entry;
211
0
  (void)key;
212
0
  (void)klen;
213
0
  curl_slist_free_all(tn->telnet_vars);
214
0
  curlx_dyn_free(&tn->out);
215
0
  free(tn);
216
0
}
217
218
static
219
CURLcode init_telnet(struct Curl_easy *data)
220
0
{
221
0
  struct TELNET *tn;
222
223
0
  tn = calloc(1, sizeof(struct TELNET));
224
0
  if(!tn)
225
0
    return CURLE_OUT_OF_MEMORY;
226
227
0
  curlx_dyn_init(&tn->out, 0xffff);
228
229
0
  tn->telrcv_state = CURL_TS_DATA;
230
231
  /* Init suboptions */
232
0
  CURL_SB_CLEAR(tn);
233
234
  /* Set the options we want by default */
235
0
  tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
236
0
  tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
237
238
  /* To be compliant with previous releases of libcurl
239
     we enable this option by default. This behavior
240
         can be changed thanks to the "BINARY" option in
241
         CURLOPT_TELNETOPTIONS
242
  */
243
0
  tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
244
0
  tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
245
246
  /* We must allow the server to echo what we sent
247
         but it is not necessary to request the server
248
         to do so (it might forces the server to close
249
         the connection). Hence, we ignore ECHO in the
250
         negotiate function
251
  */
252
0
  tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
253
254
  /* Set the subnegotiation fields to send information
255
    just after negotiation passed (do/will)
256
257
     Default values are (0,0) initialized by calloc.
258
     According to the RFC1013 it is valid:
259
     A value equal to zero is acceptable for the width (or height),
260
         and means that no character width (or height) is being sent.
261
         In this case, the width (or height) that will be assumed by the
262
         Telnet server is operating system specific (it will probably be
263
         based upon the terminal type information that may have been sent
264
         using the TERMINAL TYPE Telnet option). */
265
0
  tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
266
267
0
  return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor);
268
0
}
269
270
static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn)
271
0
{
272
0
  int i;
273
274
0
  for(i = 0; i < CURL_NTELOPTS; i++) {
275
0
    if(i == CURL_TELOPT_ECHO)
276
0
      continue;
277
278
0
    if(tn->us_preferred[i] == CURL_YES)
279
0
      set_local_option(data, tn, i, CURL_YES);
280
281
0
    if(tn->him_preferred[i] == CURL_YES)
282
0
      set_remote_option(data, tn, i, CURL_YES);
283
0
  }
284
0
}
285
286
#ifndef CURL_DISABLE_VERBOSE_STRINGS
287
static void printoption(struct Curl_easy *data,
288
                        const char *direction, int cmd, int option)
289
0
{
290
0
  if(data->set.verbose) {
291
0
    if(cmd == CURL_IAC) {
292
0
      if(CURL_TELCMD_OK(option))
293
0
        infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
294
0
      else
295
0
        infof(data, "%s IAC %d", direction, option);
296
0
    }
297
0
    else {
298
0
      const char *fmt = (cmd == CURL_WILL) ? "WILL" :
299
0
                        (cmd == CURL_WONT) ? "WONT" :
300
0
                        (cmd == CURL_DO) ? "DO" :
301
0
                        (cmd == CURL_DONT) ? "DONT" : 0;
302
0
      if(fmt) {
303
0
        const char *opt;
304
0
        if(CURL_TELOPT_OK(option))
305
0
          opt = CURL_TELOPT(option);
306
0
        else if(option == CURL_TELOPT_EXOPL)
307
0
          opt = "EXOPL";
308
0
        else
309
0
          opt = NULL;
310
311
0
        if(opt)
312
0
          infof(data, "%s %s %s", direction, fmt, opt);
313
0
        else
314
0
          infof(data, "%s %s %d", direction, fmt, option);
315
0
      }
316
0
      else
317
0
        infof(data, "%s %d %d", direction, cmd, option);
318
0
    }
319
0
  }
320
0
}
321
#endif
322
323
static void send_negotiation(struct Curl_easy *data, int cmd, int option)
324
0
{
325
0
  unsigned char buf[3];
326
0
  ssize_t bytes_written;
327
0
  struct connectdata *conn = data->conn;
328
329
0
  buf[0] = CURL_IAC;
330
0
  buf[1] = (unsigned char)cmd;
331
0
  buf[2] = (unsigned char)option;
332
333
0
  bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
334
0
  if(bytes_written < 0) {
335
0
    int err = SOCKERRNO;
336
0
    failf(data,"Sending data failed (%d)",err);
337
0
  }
338
339
0
  printoption(data, "SENT", cmd, option);
340
0
}
341
342
static
343
void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
344
                       int option, int newstate)
345
0
{
346
0
  if(newstate == CURL_YES) {
347
0
    switch(tn->him[option]) {
348
0
    case CURL_NO:
349
0
      tn->him[option] = CURL_WANTYES;
350
0
      send_negotiation(data, CURL_DO, option);
351
0
      break;
352
353
0
    case CURL_YES:
354
      /* Already enabled */
355
0
      break;
356
357
0
    case CURL_WANTNO:
358
0
      switch(tn->himq[option]) {
359
0
      case CURL_EMPTY:
360
        /* Already negotiating for CURL_YES, queue the request */
361
0
        tn->himq[option] = CURL_OPPOSITE;
362
0
        break;
363
0
      case CURL_OPPOSITE:
364
        /* Error: already queued an enable request */
365
0
        break;
366
0
      }
367
0
      break;
368
369
0
    case CURL_WANTYES:
370
0
      switch(tn->himq[option]) {
371
0
      case CURL_EMPTY:
372
        /* Error: already negotiating for enable */
373
0
        break;
374
0
      case CURL_OPPOSITE:
375
0
        tn->himq[option] = CURL_EMPTY;
376
0
        break;
377
0
      }
378
0
      break;
379
0
    }
380
0
  }
381
0
  else { /* NO */
382
0
    switch(tn->him[option]) {
383
0
    case CURL_NO:
384
      /* Already disabled */
385
0
      break;
386
387
0
    case CURL_YES:
388
0
      tn->him[option] = CURL_WANTNO;
389
0
      send_negotiation(data, CURL_DONT, option);
390
0
      break;
391
392
0
    case CURL_WANTNO:
393
0
      switch(tn->himq[option]) {
394
0
      case CURL_EMPTY:
395
        /* Already negotiating for NO */
396
0
        break;
397
0
      case CURL_OPPOSITE:
398
0
        tn->himq[option] = CURL_EMPTY;
399
0
        break;
400
0
      }
401
0
      break;
402
403
0
    case CURL_WANTYES:
404
0
      switch(tn->himq[option]) {
405
0
      case CURL_EMPTY:
406
0
        tn->himq[option] = CURL_OPPOSITE;
407
0
        break;
408
0
      case CURL_OPPOSITE:
409
0
        break;
410
0
      }
411
0
      break;
412
0
    }
413
0
  }
414
0
}
415
416
static
417
void rec_will(struct Curl_easy *data, struct TELNET *tn, int option)
418
0
{
419
0
  switch(tn->him[option]) {
420
0
  case CURL_NO:
421
0
    if(tn->him_preferred[option] == CURL_YES) {
422
0
      tn->him[option] = CURL_YES;
423
0
      send_negotiation(data, CURL_DO, option);
424
0
    }
425
0
    else
426
0
      send_negotiation(data, CURL_DONT, option);
427
428
0
    break;
429
430
0
  case CURL_YES:
431
    /* Already enabled */
432
0
    break;
433
434
0
  case CURL_WANTNO:
435
0
    switch(tn->himq[option]) {
436
0
    case CURL_EMPTY:
437
      /* Error: DONT answered by WILL */
438
0
      tn->him[option] = CURL_NO;
439
0
      break;
440
0
    case CURL_OPPOSITE:
441
      /* Error: DONT answered by WILL */
442
0
      tn->him[option] = CURL_YES;
443
0
      tn->himq[option] = CURL_EMPTY;
444
0
      break;
445
0
    }
446
0
    break;
447
448
0
  case CURL_WANTYES:
449
0
    switch(tn->himq[option]) {
450
0
    case CURL_EMPTY:
451
0
      tn->him[option] = CURL_YES;
452
0
      break;
453
0
    case CURL_OPPOSITE:
454
0
      tn->him[option] = CURL_WANTNO;
455
0
      tn->himq[option] = CURL_EMPTY;
456
0
      send_negotiation(data, CURL_DONT, option);
457
0
      break;
458
0
    }
459
0
    break;
460
0
  }
461
0
}
462
463
static
464
void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option)
465
0
{
466
0
  switch(tn->him[option]) {
467
0
  case CURL_NO:
468
    /* Already disabled */
469
0
    break;
470
471
0
  case CURL_YES:
472
0
    tn->him[option] = CURL_NO;
473
0
    send_negotiation(data, CURL_DONT, option);
474
0
    break;
475
476
0
  case CURL_WANTNO:
477
0
    switch(tn->himq[option]) {
478
0
    case CURL_EMPTY:
479
0
      tn->him[option] = CURL_NO;
480
0
      break;
481
482
0
    case CURL_OPPOSITE:
483
0
      tn->him[option] = CURL_WANTYES;
484
0
      tn->himq[option] = CURL_EMPTY;
485
0
      send_negotiation(data, CURL_DO, option);
486
0
      break;
487
0
    }
488
0
    break;
489
490
0
  case CURL_WANTYES:
491
0
    switch(tn->himq[option]) {
492
0
    case CURL_EMPTY:
493
0
      tn->him[option] = CURL_NO;
494
0
      break;
495
0
    case CURL_OPPOSITE:
496
0
      tn->him[option] = CURL_NO;
497
0
      tn->himq[option] = CURL_EMPTY;
498
0
      break;
499
0
    }
500
0
    break;
501
0
  }
502
0
}
503
504
static void
505
set_local_option(struct Curl_easy *data, struct TELNET *tn,
506
                 int option, int newstate)
507
0
{
508
0
  if(newstate == CURL_YES) {
509
0
    switch(tn->us[option]) {
510
0
    case CURL_NO:
511
0
      tn->us[option] = CURL_WANTYES;
512
0
      send_negotiation(data, CURL_WILL, option);
513
0
      break;
514
515
0
    case CURL_YES:
516
      /* Already enabled */
517
0
      break;
518
519
0
    case CURL_WANTNO:
520
0
      switch(tn->usq[option]) {
521
0
      case CURL_EMPTY:
522
        /* Already negotiating for CURL_YES, queue the request */
523
0
        tn->usq[option] = CURL_OPPOSITE;
524
0
        break;
525
0
      case CURL_OPPOSITE:
526
        /* Error: already queued an enable request */
527
0
        break;
528
0
      }
529
0
      break;
530
531
0
    case CURL_WANTYES:
532
0
      switch(tn->usq[option]) {
533
0
      case CURL_EMPTY:
534
        /* Error: already negotiating for enable */
535
0
        break;
536
0
      case CURL_OPPOSITE:
537
0
        tn->usq[option] = CURL_EMPTY;
538
0
        break;
539
0
      }
540
0
      break;
541
0
    }
542
0
  }
543
0
  else { /* NO */
544
0
    switch(tn->us[option]) {
545
0
    case CURL_NO:
546
      /* Already disabled */
547
0
      break;
548
549
0
    case CURL_YES:
550
0
      tn->us[option] = CURL_WANTNO;
551
0
      send_negotiation(data, CURL_WONT, option);
552
0
      break;
553
554
0
    case CURL_WANTNO:
555
0
      switch(tn->usq[option]) {
556
0
      case CURL_EMPTY:
557
        /* Already negotiating for NO */
558
0
        break;
559
0
      case CURL_OPPOSITE:
560
0
        tn->usq[option] = CURL_EMPTY;
561
0
        break;
562
0
      }
563
0
      break;
564
565
0
    case CURL_WANTYES:
566
0
      switch(tn->usq[option]) {
567
0
      case CURL_EMPTY:
568
0
        tn->usq[option] = CURL_OPPOSITE;
569
0
        break;
570
0
      case CURL_OPPOSITE:
571
0
        break;
572
0
      }
573
0
      break;
574
0
    }
575
0
  }
576
0
}
577
578
static
579
void rec_do(struct Curl_easy *data, struct TELNET *tn, int option)
580
0
{
581
0
  switch(tn->us[option]) {
582
0
  case CURL_NO:
583
0
    if(tn->us_preferred[option] == CURL_YES) {
584
0
      tn->us[option] = CURL_YES;
585
0
      send_negotiation(data, CURL_WILL, option);
586
0
      if(tn->subnegotiation[option] == CURL_YES)
587
        /* transmission of data option */
588
0
        sendsuboption(data, tn, option);
589
0
    }
590
0
    else if(tn->subnegotiation[option] == CURL_YES) {
591
      /* send information to achieve this option */
592
0
      tn->us[option] = CURL_YES;
593
0
      send_negotiation(data, CURL_WILL, option);
594
0
      sendsuboption(data, tn, option);
595
0
    }
596
0
    else
597
0
      send_negotiation(data, CURL_WONT, option);
598
0
    break;
599
600
0
  case CURL_YES:
601
    /* Already enabled */
602
0
    break;
603
604
0
  case CURL_WANTNO:
605
0
    switch(tn->usq[option]) {
606
0
    case CURL_EMPTY:
607
      /* Error: DONT answered by WILL */
608
0
      tn->us[option] = CURL_NO;
609
0
      break;
610
0
    case CURL_OPPOSITE:
611
      /* Error: DONT answered by WILL */
612
0
      tn->us[option] = CURL_YES;
613
0
      tn->usq[option] = CURL_EMPTY;
614
0
      break;
615
0
    }
616
0
    break;
617
618
0
  case CURL_WANTYES:
619
0
    switch(tn->usq[option]) {
620
0
    case CURL_EMPTY:
621
0
      tn->us[option] = CURL_YES;
622
0
      if(tn->subnegotiation[option] == CURL_YES) {
623
        /* transmission of data option */
624
0
        sendsuboption(data, tn, option);
625
0
      }
626
0
      break;
627
0
    case CURL_OPPOSITE:
628
0
      tn->us[option] = CURL_WANTNO;
629
0
      tn->himq[option] = CURL_EMPTY;
630
0
      send_negotiation(data, CURL_WONT, option);
631
0
      break;
632
0
    }
633
0
    break;
634
0
  }
635
0
}
636
637
static
638
void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option)
639
0
{
640
0
  switch(tn->us[option]) {
641
0
  case CURL_NO:
642
    /* Already disabled */
643
0
    break;
644
645
0
  case CURL_YES:
646
0
    tn->us[option] = CURL_NO;
647
0
    send_negotiation(data, CURL_WONT, option);
648
0
    break;
649
650
0
  case CURL_WANTNO:
651
0
    switch(tn->usq[option]) {
652
0
    case CURL_EMPTY:
653
0
      tn->us[option] = CURL_NO;
654
0
      break;
655
656
0
    case CURL_OPPOSITE:
657
0
      tn->us[option] = CURL_WANTYES;
658
0
      tn->usq[option] = CURL_EMPTY;
659
0
      send_negotiation(data, CURL_WILL, option);
660
0
      break;
661
0
    }
662
0
    break;
663
664
0
  case CURL_WANTYES:
665
0
    switch(tn->usq[option]) {
666
0
    case CURL_EMPTY:
667
0
      tn->us[option] = CURL_NO;
668
0
      break;
669
0
    case CURL_OPPOSITE:
670
0
      tn->us[option] = CURL_NO;
671
0
      tn->usq[option] = CURL_EMPTY;
672
0
      break;
673
0
    }
674
0
    break;
675
0
  }
676
0
}
677
678
679
static void printsub(struct Curl_easy *data,
680
                     int direction,             /* '<' or '>' */
681
                     unsigned char *pointer,    /* where suboption data is */
682
                     size_t length)             /* length of suboption data */
683
0
{
684
0
  if(data->set.verbose) {
685
0
    unsigned int i = 0;
686
0
    if(direction) {
687
0
      infof(data, "%s IAC SB ", (direction == '<') ? "RCVD" : "SENT");
688
0
      if(length >= 3) {
689
0
        int j;
690
691
0
        i = pointer[length-2];
692
0
        j = pointer[length-1];
693
694
0
        if(i != CURL_IAC || j != CURL_SE) {
695
0
          infof(data, "(terminated by ");
696
0
          if(CURL_TELOPT_OK(i))
697
0
            infof(data, "%s ", CURL_TELOPT(i));
698
0
          else if(CURL_TELCMD_OK(i))
699
0
            infof(data, "%s ", CURL_TELCMD(i));
700
0
          else
701
0
            infof(data, "%u ", i);
702
0
          if(CURL_TELOPT_OK(j))
703
0
            infof(data, "%s", CURL_TELOPT(j));
704
0
          else if(CURL_TELCMD_OK(j))
705
0
            infof(data, "%s", CURL_TELCMD(j));
706
0
          else
707
0
            infof(data, "%d", j);
708
0
          infof(data, ", not IAC SE) ");
709
0
        }
710
0
      }
711
0
      if(length >= 2)
712
0
        length -= 2;
713
0
      else /* bad input */
714
0
        return;
715
0
    }
716
0
    if(length < 1) {
717
0
      infof(data, "(Empty suboption?)");
718
0
      return;
719
0
    }
720
721
0
    if(CURL_TELOPT_OK(pointer[0])) {
722
0
      switch(pointer[0]) {
723
0
      case CURL_TELOPT_TTYPE:
724
0
      case CURL_TELOPT_XDISPLOC:
725
0
      case CURL_TELOPT_NEW_ENVIRON:
726
0
      case CURL_TELOPT_NAWS:
727
0
        infof(data, "%s", CURL_TELOPT(pointer[0]));
728
0
        break;
729
0
      default:
730
0
        infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
731
0
        break;
732
0
      }
733
0
    }
734
0
    else
735
0
      infof(data, "%d (unknown)", pointer[i]);
736
737
0
    switch(pointer[0]) {
738
0
    case CURL_TELOPT_NAWS:
739
0
      if(length > 4)
740
0
        infof(data, "Width: %d ; Height: %d", (pointer[1] << 8) | pointer[2],
741
0
              (pointer[3] << 8) | pointer[4]);
742
0
      break;
743
0
    default:
744
0
      switch(pointer[1]) {
745
0
      case CURL_TELQUAL_IS:
746
0
        infof(data, " IS");
747
0
        break;
748
0
      case CURL_TELQUAL_SEND:
749
0
        infof(data, " SEND");
750
0
        break;
751
0
      case CURL_TELQUAL_INFO:
752
0
        infof(data, " INFO/REPLY");
753
0
        break;
754
0
      case CURL_TELQUAL_NAME:
755
0
        infof(data, " NAME");
756
0
        break;
757
0
      }
758
759
0
      switch(pointer[0]) {
760
0
      case CURL_TELOPT_TTYPE:
761
0
      case CURL_TELOPT_XDISPLOC:
762
0
        pointer[length] = 0;
763
0
        infof(data, " \"%s\"", &pointer[2]);
764
0
        break;
765
0
      case CURL_TELOPT_NEW_ENVIRON:
766
0
        if(pointer[1] == CURL_TELQUAL_IS) {
767
0
          infof(data, " ");
768
0
          for(i = 3; i < length; i++) {
769
0
            switch(pointer[i]) {
770
0
            case CURL_NEW_ENV_VAR:
771
0
              infof(data, ", ");
772
0
              break;
773
0
            case CURL_NEW_ENV_VALUE:
774
0
              infof(data, " = ");
775
0
              break;
776
0
            default:
777
0
              infof(data, "%c", pointer[i]);
778
0
              break;
779
0
            }
780
0
          }
781
0
        }
782
0
        break;
783
0
      default:
784
0
        for(i = 2; i < length; i++)
785
0
          infof(data, " %.2x", pointer[i]);
786
0
        break;
787
0
      }
788
0
    }
789
0
  }
790
0
}
791
792
static bool str_is_nonascii(const char *str)
793
0
{
794
0
  char c;
795
0
  while((c = *str++) != 0)
796
0
    if(c & 0x80)
797
0
      return TRUE;
798
799
0
  return FALSE;
800
0
}
801
802
static CURLcode check_telnet_options(struct Curl_easy *data,
803
                                     struct TELNET *tn)
804
0
{
805
0
  struct curl_slist *head;
806
0
  struct curl_slist *beg;
807
0
  CURLcode result = CURLE_OK;
808
809
  /* Add the username as an environment variable if it
810
     was given on the command line */
811
0
  if(data->state.aptr.user) {
812
0
    char buffer[256];
813
0
    if(str_is_nonascii(data->conn->user)) {
814
0
      DEBUGF(infof(data, "set a non ASCII username in telnet"));
815
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
816
0
    }
817
0
    msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
818
0
    beg = curl_slist_append(tn->telnet_vars, buffer);
819
0
    if(!beg) {
820
0
      curl_slist_free_all(tn->telnet_vars);
821
0
      tn->telnet_vars = NULL;
822
0
      return CURLE_OUT_OF_MEMORY;
823
0
    }
824
0
    tn->telnet_vars = beg;
825
0
    tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
826
0
  }
827
828
0
  for(head = data->set.telnet_options; head && !result; head = head->next) {
829
0
    size_t olen;
830
0
    char *option = head->data;
831
0
    char *arg;
832
0
    char *sep = strchr(option, '=');
833
0
    if(sep) {
834
0
      olen = sep - option;
835
0
      arg = ++sep;
836
0
      if(str_is_nonascii(arg))
837
0
        continue;
838
0
      switch(olen) {
839
0
      case 5:
840
        /* Terminal type */
841
0
        if(curl_strnequal(option, "TTYPE", 5)) {
842
0
          tn->subopt_ttype = arg;
843
0
          tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
844
0
          break;
845
0
        }
846
0
        result = CURLE_UNKNOWN_OPTION;
847
0
        break;
848
849
0
      case 8:
850
        /* Display variable */
851
0
        if(curl_strnequal(option, "XDISPLOC", 8)) {
852
0
          tn->subopt_xdisploc = arg;
853
0
          tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
854
0
          break;
855
0
        }
856
0
        result = CURLE_UNKNOWN_OPTION;
857
0
        break;
858
859
0
      case 7:
860
        /* Environment variable */
861
0
        if(curl_strnequal(option, "NEW_ENV", 7)) {
862
0
          beg = curl_slist_append(tn->telnet_vars, arg);
863
0
          if(!beg) {
864
0
            result = CURLE_OUT_OF_MEMORY;
865
0
            break;
866
0
          }
867
0
          tn->telnet_vars = beg;
868
0
          tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
869
0
        }
870
0
        else
871
0
          result = CURLE_UNKNOWN_OPTION;
872
0
        break;
873
874
0
      case 2:
875
        /* Window Size */
876
0
        if(curl_strnequal(option, "WS", 2)) {
877
0
          const char *p = arg;
878
0
          curl_off_t x = 0;
879
0
          curl_off_t y = 0;
880
0
          if(curlx_str_number(&p, &x, 0xffff) ||
881
0
             curlx_str_single(&p, 'x') ||
882
0
             curlx_str_number(&p, &y, 0xffff)) {
883
0
            failf(data, "Syntax error in telnet option: %s", head->data);
884
0
            result = CURLE_SETOPT_OPTION_SYNTAX;
885
0
          }
886
0
          else {
887
0
            tn->subopt_wsx = (unsigned short)x;
888
0
            tn->subopt_wsy = (unsigned short)y;
889
0
            tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
890
0
          }
891
0
        }
892
0
        else
893
0
          result = CURLE_UNKNOWN_OPTION;
894
0
        break;
895
896
0
      case 6:
897
        /* To take care or not of the 8th bit in data exchange */
898
0
        if(curl_strnequal(option, "BINARY", 6)) {
899
0
          int binary_option = atoi(arg);
900
0
          if(binary_option != 1) {
901
0
            tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
902
0
            tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
903
0
          }
904
0
        }
905
0
        else
906
0
          result = CURLE_UNKNOWN_OPTION;
907
0
        break;
908
0
      default:
909
0
        failf(data, "Unknown telnet option %s", head->data);
910
0
        result = CURLE_UNKNOWN_OPTION;
911
0
        break;
912
0
      }
913
0
    }
914
0
    else {
915
0
      failf(data, "Syntax error in telnet option: %s", head->data);
916
0
      result = CURLE_SETOPT_OPTION_SYNTAX;
917
0
    }
918
0
  }
919
920
0
  if(result) {
921
0
    curl_slist_free_all(tn->telnet_vars);
922
0
    tn->telnet_vars = NULL;
923
0
  }
924
925
0
  return result;
926
0
}
927
928
/*
929
 * suboption()
930
 *
931
 * Look at the sub-option buffer, and try to be helpful to the other
932
 * side.
933
 */
934
935
static void suboption(struct Curl_easy *data, struct TELNET *tn)
936
0
{
937
0
  struct curl_slist *v;
938
0
  unsigned char temp[2048];
939
0
  ssize_t bytes_written;
940
0
  size_t len;
941
0
  int err;
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,
1014
                          struct TELNET *tn, int option)
1015
0
{
1016
0
  ssize_t bytes_written;
1017
0
  int err;
1018
0
  unsigned short x, y;
1019
0
  unsigned char *uc1, *uc2;
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, tn, (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
                struct TELNET *tn,
1071
                const unsigned char *inbuf, /* Data received from socket */
1072
                ssize_t count)              /* Number of bytes received */
1073
0
{
1074
0
  unsigned char c;
1075
0
  CURLcode result;
1076
0
  int in = 0;
1077
0
  int startwrite = -1;
1078
1079
0
#define startskipping()                                          \
1080
0
  if(startwrite >= 0) {                                          \
1081
0
    result = Curl_client_write(data,                             \
1082
0
                               CLIENTWRITE_BODY,                 \
1083
0
                               (const 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, tn, 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, tn, 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, tn, 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, tn, 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, tn);   /* 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, tn);   /* 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
                                 struct TELNET *tn,
1233
                                 char *buffer, ssize_t nread)
1234
0
{
1235
0
  size_t i, outlen;
1236
0
  unsigned char *outbuf;
1237
0
  CURLcode result = CURLE_OK;
1238
0
  size_t bytes_written;
1239
0
  size_t total_written = 0;
1240
0
  struct connectdata *conn = data->conn;
1241
1242
0
  DEBUGASSERT(tn);
1243
0
  DEBUGASSERT(nread > 0);
1244
0
  if(nread < 0)
1245
0
    return CURLE_TOO_LARGE;
1246
1247
0
  if(memchr(buffer, CURL_IAC, nread)) {
1248
    /* only use the escape buffer when necessary */
1249
0
    curlx_dyn_reset(&tn->out);
1250
1251
0
    for(i = 0; i < (size_t)nread && !result; i++) {
1252
0
      result = curlx_dyn_addn(&tn->out, &buffer[i], 1);
1253
0
      if(!result && ((unsigned char)buffer[i] == CURL_IAC))
1254
        /* IAC is FF in hex */
1255
0
        result = curlx_dyn_addn(&tn->out, "\xff", 1);
1256
0
    }
1257
1258
0
    outlen = curlx_dyn_len(&tn->out);
1259
0
    outbuf = curlx_dyn_uptr(&tn->out);
1260
0
  }
1261
0
  else {
1262
0
    outlen = (size_t)nread;
1263
0
    outbuf = (unsigned char *)buffer;
1264
0
  }
1265
0
  while(!result && total_written < outlen) {
1266
    /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1267
0
    struct pollfd pfd[1];
1268
0
    pfd[0].fd = conn->sock[FIRSTSOCKET];
1269
0
    pfd[0].events = POLLOUT;
1270
0
    switch(Curl_poll(pfd, 1, -1)) {
1271
0
      case -1:                    /* error, abort writing */
1272
0
      case 0:                     /* timeout (will never happen) */
1273
0
        result = CURLE_SEND_ERROR;
1274
0
        break;
1275
0
      default:                    /* write! */
1276
0
        bytes_written = 0;
1277
0
        result = Curl_xfer_send(data, outbuf + total_written,
1278
0
                                outlen - total_written, FALSE, &bytes_written);
1279
0
        total_written += bytes_written;
1280
0
        break;
1281
0
    }
1282
0
  }
1283
1284
0
  return result;
1285
0
}
1286
1287
static CURLcode telnet_done(struct Curl_easy *data,
1288
                            CURLcode status, bool premature)
1289
0
{
1290
0
  (void)status; /* unused */
1291
0
  (void)premature; /* not used */
1292
0
  Curl_meta_remove(data, CURL_META_TELNET_EASY);
1293
0
  return CURLE_OK;
1294
0
}
1295
1296
static CURLcode telnet_do(struct Curl_easy *data, bool *done)
1297
0
{
1298
0
  CURLcode result;
1299
0
  struct connectdata *conn = data->conn;
1300
0
  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1301
#ifdef USE_WINSOCK
1302
  WSAEVENT event_handle;
1303
  WSANETWORKEVENTS events;
1304
  HANDLE stdin_handle;
1305
  HANDLE objs[2];
1306
  DWORD  obj_count;
1307
  DWORD  wait_timeout;
1308
  DWORD readfile_read;
1309
  int err;
1310
#else
1311
0
  timediff_t interval_ms;
1312
0
  struct pollfd pfd[2];
1313
0
  int poll_cnt;
1314
0
  curl_off_t total_dl = 0;
1315
0
  curl_off_t total_ul = 0;
1316
0
  ssize_t snread;
1317
0
#endif
1318
0
  struct curltime now;
1319
0
  bool keepon = TRUE;
1320
0
  char buffer[4*1024];
1321
0
  struct TELNET *tn;
1322
1323
0
  *done = TRUE; /* unconditionally */
1324
1325
0
  result = init_telnet(data);
1326
0
  if(result)
1327
0
    return result;
1328
1329
0
  tn = Curl_meta_get(data, CURL_META_TELNET_EASY);
1330
0
  if(!tn)
1331
0
    return CURLE_FAILED_INIT;
1332
1333
0
  result = check_telnet_options(data, tn);
1334
0
  if(result)
1335
0
    return result;
1336
1337
#ifdef USE_WINSOCK
1338
  /* We want to wait for both stdin and the socket. Since
1339
  ** the select() function in Winsock only works on sockets
1340
  ** we have to use the WaitForMultipleObjects() call.
1341
  */
1342
1343
  /* First, create a sockets event object */
1344
  event_handle = WSACreateEvent();
1345
  if(event_handle == WSA_INVALID_EVENT) {
1346
    failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
1347
    return CURLE_FAILED_INIT;
1348
  }
1349
1350
  /* Tell Winsock what events we want to listen to */
1351
  if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
1352
    WSACloseEvent(event_handle);
1353
    return CURLE_OK;
1354
  }
1355
1356
  /* The get the Windows file handle for stdin */
1357
  stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1358
1359
  /* Create the list of objects to wait for */
1360
  objs[0] = event_handle;
1361
  objs[1] = stdin_handle;
1362
1363
  /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1364
     else use the old WaitForMultipleObjects() way */
1365
  if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
1366
     data->set.is_fread_set) {
1367
    /* Do not wait for stdin_handle, just wait for event_handle */
1368
    obj_count = 1;
1369
    /* Check stdin_handle per 100 milliseconds */
1370
    wait_timeout = 100;
1371
  }
1372
  else {
1373
    obj_count = 2;
1374
    wait_timeout = 1000;
1375
  }
1376
1377
  /* Keep on listening and act on events */
1378
  while(keepon) {
1379
    const DWORD buf_size = (DWORD)sizeof(buffer);
1380
    DWORD waitret = WaitForMultipleObjects(obj_count, objs,
1381
                                           FALSE, wait_timeout);
1382
    switch(waitret) {
1383
1384
    case WAIT_TIMEOUT:
1385
    {
1386
      for(;;) {
1387
        if(data->set.is_fread_set) {
1388
          size_t n;
1389
          /* read from user-supplied method */
1390
          n = data->state.fread_func(buffer, 1, buf_size, data->state.in);
1391
          if(n == CURL_READFUNC_ABORT) {
1392
            keepon = FALSE;
1393
            result = CURLE_READ_ERROR;
1394
            break;
1395
          }
1396
1397
          if(n == CURL_READFUNC_PAUSE)
1398
            break;
1399
1400
          if(n == 0)                        /* no bytes */
1401
            break;
1402
1403
          /* fall through with number of bytes read */
1404
          readfile_read = (DWORD)n;
1405
        }
1406
        else {
1407
          /* read from stdin */
1408
          if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
1409
                            &readfile_read, NULL)) {
1410
            keepon = FALSE;
1411
            result = CURLE_READ_ERROR;
1412
            break;
1413
          }
1414
1415
          if(!readfile_read)
1416
            break;
1417
1418
          if(!ReadFile(stdin_handle, buffer, buf_size,
1419
                       &readfile_read, NULL)) {
1420
            keepon = FALSE;
1421
            result = CURLE_READ_ERROR;
1422
            break;
1423
          }
1424
        }
1425
1426
        result = send_telnet_data(data, tn, buffer, readfile_read);
1427
        if(result) {
1428
          keepon = FALSE;
1429
          break;
1430
        }
1431
      }
1432
    }
1433
    break;
1434
1435
    case WAIT_OBJECT_0 + 1:
1436
    {
1437
      if(!ReadFile(stdin_handle, buffer, buf_size,
1438
                   &readfile_read, NULL)) {
1439
        keepon = FALSE;
1440
        result = CURLE_READ_ERROR;
1441
        break;
1442
      }
1443
1444
      result = send_telnet_data(data, tn, buffer, readfile_read);
1445
      if(result) {
1446
        keepon = FALSE;
1447
        break;
1448
      }
1449
    }
1450
    break;
1451
1452
    case WAIT_OBJECT_0:
1453
    {
1454
      events.lNetworkEvents = 0;
1455
      if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
1456
        err = SOCKERRNO;
1457
        if(err != SOCKEINPROGRESS) {
1458
          infof(data, "WSAEnumNetworkEvents failed (%d)", err);
1459
          keepon = FALSE;
1460
          result = CURLE_READ_ERROR;
1461
        }
1462
        break;
1463
      }
1464
      if(events.lNetworkEvents & FD_READ) {
1465
        /* read data from network */
1466
        size_t nread;
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) {
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
        size_t nread;
1554
0
        result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
1555
        /* read would have blocked. Loop again */
1556
0
        if(result == CURLE_AGAIN)
1557
0
          break;
1558
        /* returned not-zero, this an error */
1559
0
        if(result) {
1560
0
          keepon = FALSE;
1561
          /* In test 1452, macOS sees a ECONNRESET sometimes? Is this the
1562
           * telnet test server not shutting down the socket in a clean way?
1563
           * Seems to be timing related, happens more on slow debug build */
1564
0
          if(data->state.os_errno == SOCKECONNRESET) {
1565
0
            DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
1566
0
          }
1567
0
          break;
1568
0
        }
1569
        /* returned zero but actually received 0 or less here,
1570
           the server closed the connection and we bail out */
1571
0
        else if(!nread) {
1572
0
          keepon = FALSE;
1573
0
          break;
1574
0
        }
1575
1576
0
        total_dl += nread;
1577
0
        result = Curl_pgrsSetDownloadCounter(data, total_dl);
1578
0
        if(!result)
1579
0
          result = telrcv(data, tn, (unsigned char *)buffer, nread);
1580
0
        if(result) {
1581
0
          keepon = FALSE;
1582
0
          break;
1583
0
        }
1584
1585
        /* Negotiate if the peer has started negotiating,
1586
           otherwise do not. We do not want to speak telnet with
1587
           non-telnet servers, like POP or SMTP. */
1588
0
        if(tn->please_negotiate && !tn->already_negotiated) {
1589
0
          telnet_negotiate(data, tn);
1590
0
          tn->already_negotiated = 1;
1591
0
        }
1592
0
      }
1593
1594
0
      snread = 0;
1595
0
      if(poll_cnt == 2) {
1596
0
        if(pfd[1].revents & POLLIN) { /* read from in file */
1597
0
          snread = read(pfd[1].fd, buffer, sizeof(buffer));
1598
0
        }
1599
0
      }
1600
0
      else {
1601
        /* read from user-supplied method */
1602
0
        snread = (int)data->state.fread_func(buffer, 1, sizeof(buffer),
1603
0
                                            data->state.in);
1604
0
        if(snread == CURL_READFUNC_ABORT) {
1605
0
          keepon = FALSE;
1606
0
          break;
1607
0
        }
1608
0
        if(snread == CURL_READFUNC_PAUSE)
1609
0
          break;
1610
0
      }
1611
1612
0
      if(snread > 0) {
1613
0
        result = send_telnet_data(data, tn, buffer, snread);
1614
0
        if(result) {
1615
0
          keepon = FALSE;
1616
0
          break;
1617
0
        }
1618
0
        total_ul += snread;
1619
0
        Curl_pgrsSetUploadCounter(data, total_ul);
1620
0
      }
1621
0
      else if(snread < 0)
1622
0
        keepon = FALSE;
1623
1624
0
      break;
1625
0
    } /* poll switch statement */
1626
1627
0
    if(data->set.timeout) {
1628
0
      now = curlx_now();
1629
0
      if(curlx_timediff(now, conn->created) >= data->set.timeout) {
1630
0
        failf(data, "Time-out");
1631
0
        result = CURLE_OPERATION_TIMEDOUT;
1632
0
        keepon = FALSE;
1633
0
      }
1634
0
    }
1635
1636
0
    if(Curl_pgrsUpdate(data)) {
1637
0
      result = CURLE_ABORTED_BY_CALLBACK;
1638
0
      break;
1639
0
    }
1640
0
  }
1641
0
#endif
1642
  /* mark this as "no further transfer wanted" */
1643
0
  Curl_xfer_setup_nop(data);
1644
1645
0
  return result;
1646
0
}
1647
#endif