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