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