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
92static
93CURLcode 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
98static void printoption(struct Curl_easy *data,
99 const char *direction,
100 int cmd, int option);
101#endif
102
103static void negotiate(struct Curl_easy *data);
104static void send_negotiation(struct Curl_easy *data, int cmd, int option);
105static void set_local_option(struct Curl_easy *data,
106 int option, int newstate);
107static void set_remote_option(struct Curl_easy *data,
108 int option, int newstate);
109
110static void printsub(struct Curl_easy *data,
111 int direction, unsigned char *pointer,
112 size_t length);
113static void suboption(struct Curl_easy *data);
114static void sendsuboption(struct Curl_easy *data, int option);
115
116static CURLcode telnet_do(struct Curl_easy *data, bool *done);
117static CURLcode telnet_done(struct Curl_easy *data,
118 CURLcode, bool premature);
119static 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 */
134typedef 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
147struct 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
174const 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
198static
199CURLcode 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
249static 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
267static 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
303static 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
322static
323void 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
396static
397void 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
444static
445void 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
486static void
487set_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
560static
561void 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
620static
621void 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
663static 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
773#ifdef _MSC_VER
774#pragma warning(push)
775/* warning C4706: assignment within conditional expression */
776#pragma warning(disable:4706)
777#endif
778static bool str_is_nonascii(const char *str)
779{
780 char c;
781 while((c = *str++))
782 if(c & 0x80)
783 return TRUE;
784
785 return FALSE;
786}
787#ifdef _MSC_VER
788#pragma warning(pop)
789#endif
790
791static CURLcode check_telnet_options(struct Curl_easy *data)
792{
793 struct curl_slist *head;
794 struct curl_slist *beg;
795 struct TELNET *tn = data->req.p.telnet;
796 CURLcode result = CURLE_OK;
797
798 /* Add the user name as an environment variable if it
799 was given on the command line */
800 if(data->state.aptr.user) {
801 char buffer[256];
802 if(str_is_nonascii(data->conn->user))
803 return CURLE_BAD_FUNCTION_ARGUMENT;
804 msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
805 beg = curl_slist_append(tn->telnet_vars, buffer);
806 if(!beg) {
807 curl_slist_free_all(tn->telnet_vars);
808 tn->telnet_vars = NULL;
809 return CURLE_OUT_OF_MEMORY;
810 }
811 tn->telnet_vars = beg;
812 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
813 }
814
815 for(head = data->set.telnet_options; head && !result; head = head->next) {
816 size_t olen;
817 char *option = head->data;
818 char *arg;
819 char *sep = strchr(option, '=');
820 if(sep) {
821 olen = sep - option;
822 arg = ++sep;
823 if(str_is_nonascii(arg))
824 continue;
825 switch(olen) {
826 case 5:
827 /* Terminal type */
828 if(strncasecompare(option, "TTYPE", 5)) {
829 strncpy(tn->subopt_ttype, arg, 31);
830 tn->subopt_ttype[31] = 0; /* String termination */
831 tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
832 }
833 else
834 result = CURLE_UNKNOWN_OPTION;
835 break;
836
837 case 8:
838 /* Display variable */
839 if(strncasecompare(option, "XDISPLOC", 8)) {
840 strncpy(tn->subopt_xdisploc, arg, 127);
841 tn->subopt_xdisploc[127] = 0; /* String termination */
842 tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
843 }
844 else
845 result = CURLE_UNKNOWN_OPTION;
846 break;
847
848 case 7:
849 /* Environment variable */
850 if(strncasecompare(option, "NEW_ENV", 7)) {
851 beg = curl_slist_append(tn->telnet_vars, arg);
852 if(!beg) {
853 result = CURLE_OUT_OF_MEMORY;
854 break;
855 }
856 tn->telnet_vars = beg;
857 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
858 }
859 else
860 result = CURLE_UNKNOWN_OPTION;
861 break;
862
863 case 2:
864 /* Window Size */
865 if(strncasecompare(option, "WS", 2)) {
866 char *p;
867 unsigned long x = strtoul(arg, &p, 10);
868 unsigned long y = 0;
869 if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') {
870 p++;
871 y = strtoul(p, NULL, 10);
872 if(y && (y <= 0xffff)) {
873 tn->subopt_wsx = (unsigned short)x;
874 tn->subopt_wsy = (unsigned short)y;
875 tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
876 }
877 }
878 if(!y) {
879 failf(data, "Syntax error in telnet option: %s", head->data);
880 result = CURLE_SETOPT_OPTION_SYNTAX;
881 }
882 }
883 else
884 result = CURLE_UNKNOWN_OPTION;
885 break;
886
887 case 6:
888 /* To take care or not of the 8th bit in data exchange */
889 if(strncasecompare(option, "BINARY", 6)) {
890 int binary_option = atoi(arg);
891 if(binary_option != 1) {
892 tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
893 tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
894 }
895 }
896 else
897 result = CURLE_UNKNOWN_OPTION;
898 break;
899 default:
900 failf(data, "Unknown telnet option %s", head->data);
901 result = CURLE_UNKNOWN_OPTION;
902 break;
903 }
904 }
905 else {
906 failf(data, "Syntax error in telnet option: %s", head->data);
907 result = CURLE_SETOPT_OPTION_SYNTAX;
908 }
909 }
910
911 if(result) {
912 curl_slist_free_all(tn->telnet_vars);
913 tn->telnet_vars = NULL;
914 }
915
916 return result;
917}
918
919/*
920 * suboption()
921 *
922 * Look at the sub-option buffer, and try to be helpful to the other
923 * side.
924 */
925
926static void suboption(struct Curl_easy *data)
927{
928 struct curl_slist *v;
929 unsigned char temp[2048];
930 ssize_t bytes_written;
931 size_t len;
932 int err;
933 struct TELNET *tn = data->req.p.telnet;
934 struct connectdata *conn = data->conn;
935
936 printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
937 switch(CURL_SB_GET(tn)) {
938 case CURL_TELOPT_TTYPE:
939 len = strlen(tn->subopt_ttype) + 4 + 2;
940 msnprintf((char *)temp, sizeof(temp),
941 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
942 CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
943 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
944 if(bytes_written < 0) {
945 err = SOCKERRNO;
946 failf(data,"Sending data failed (%d)",err);
947 }
948 printsub(data, '>', &temp[2], len-2);
949 break;
950 case CURL_TELOPT_XDISPLOC:
951 len = strlen(tn->subopt_xdisploc) + 4 + 2;
952 msnprintf((char *)temp, sizeof(temp),
953 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
954 CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
955 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
956 if(bytes_written < 0) {
957 err = SOCKERRNO;
958 failf(data,"Sending data failed (%d)",err);
959 }
960 printsub(data, '>', &temp[2], len-2);
961 break;
962 case CURL_TELOPT_NEW_ENVIRON:
963 msnprintf((char *)temp, sizeof(temp),
964 "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
965 CURL_TELQUAL_IS);
966 len = 4;
967
968 for(v = tn->telnet_vars; v; v = v->next) {
969 size_t tmplen = (strlen(v->data) + 1);
970 /* Add the variable if it fits */
971 if(len + tmplen < (int)sizeof(temp)-6) {
972 char *s = strchr(v->data, ',');
973 if(!s)
974 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
975 "%c%s", CURL_NEW_ENV_VAR, v->data);
976 else {
977 size_t vlen = s - v->data;
978 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
979 "%c%.*s%c%s", CURL_NEW_ENV_VAR,
980 (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);
981 }
982 }
983 }
984 msnprintf((char *)&temp[len], sizeof(temp) - len,
985 "%c%c", CURL_IAC, CURL_SE);
986 len += 2;
987 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
988 if(bytes_written < 0) {
989 err = SOCKERRNO;
990 failf(data,"Sending data failed (%d)",err);
991 }
992 printsub(data, '>', &temp[2], len-2);
993 break;
994 }
995 return;
996}
997
998
999/*
1000 * sendsuboption()
1001 *
1002 * Send suboption information to the server side.
1003 */
1004
1005static void sendsuboption(struct Curl_easy *data, int option)
1006{
1007 ssize_t bytes_written;
1008 int err;
1009 unsigned short x, y;
1010 unsigned char *uc1, *uc2;
1011 struct TELNET *tn = data->req.p.telnet;
1012 struct connectdata *conn = data->conn;
1013
1014 switch(option) {
1015 case CURL_TELOPT_NAWS:
1016 /* We prepare data to be sent */
1017 CURL_SB_CLEAR(tn);
1018 CURL_SB_ACCUM(tn, CURL_IAC);
1019 CURL_SB_ACCUM(tn, CURL_SB);
1020 CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
1021 /* We must deal either with little or big endian processors */
1022 /* Window size must be sent according to the 'network order' */
1023 x = htons(tn->subopt_wsx);
1024 y = htons(tn->subopt_wsy);
1025 uc1 = (unsigned char *)&x;
1026 uc2 = (unsigned char *)&y;
1027 CURL_SB_ACCUM(tn, uc1[0]);
1028 CURL_SB_ACCUM(tn, uc1[1]);
1029 CURL_SB_ACCUM(tn, uc2[0]);
1030 CURL_SB_ACCUM(tn, uc2[1]);
1031
1032 CURL_SB_ACCUM(tn, CURL_IAC);
1033 CURL_SB_ACCUM(tn, CURL_SE);
1034 CURL_SB_TERM(tn);
1035 /* data suboption is now ready */
1036
1037 printsub(data, '>', (unsigned char *)tn->subbuffer + 2,
1038 CURL_SB_LEN(tn)-2);
1039
1040 /* we send the header of the suboption... */
1041 bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
1042 if(bytes_written < 0) {
1043 err = SOCKERRNO;
1044 failf(data, "Sending data failed (%d)", err);
1045 }
1046 /* ... then the window size with the send_telnet_data() function
1047 to deal with 0xFF cases ... */
1048 send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
1049 /* ... and the footer */
1050 bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
1051 if(bytes_written < 0) {
1052 err = SOCKERRNO;
1053 failf(data, "Sending data failed (%d)", err);
1054 }
1055 break;
1056 }
1057}
1058
1059
1060static
1061CURLcode telrcv(struct Curl_easy *data,
1062 const unsigned char *inbuf, /* Data received from socket */
1063 ssize_t count) /* Number of bytes received */
1064{
1065 unsigned char c;
1066 CURLcode result;
1067 int in = 0;
1068 int startwrite = -1;
1069 struct TELNET *tn = data->req.p.telnet;
1070
1071#define startskipping() \
1072 if(startwrite >= 0) { \
1073 result = Curl_client_write(data, \
1074 CLIENTWRITE_BODY, \
1075 (char *)&inbuf[startwrite], \
1076 in-startwrite); \
1077 if(result) \
1078 return result; \
1079 } \
1080 startwrite = -1
1081
1082#define writebyte() \
1083 if(startwrite < 0) \
1084 startwrite = in
1085
1086#define bufferflush() startskipping()
1087
1088 while(count--) {
1089 c = inbuf[in];
1090
1091 switch(tn->telrcv_state) {
1092 case CURL_TS_CR:
1093 tn->telrcv_state = CURL_TS_DATA;
1094 if(c == '\0') {
1095 startskipping();
1096 break; /* Ignore \0 after CR */
1097 }
1098 writebyte();
1099 break;
1100
1101 case CURL_TS_DATA:
1102 if(c == CURL_IAC) {
1103 tn->telrcv_state = CURL_TS_IAC;
1104 startskipping();
1105 break;
1106 }
1107 else if(c == '\r')
1108 tn->telrcv_state = CURL_TS_CR;
1109 writebyte();
1110 break;
1111
1112 case CURL_TS_IAC:
1113process_iac:
1114 DEBUGASSERT(startwrite < 0);
1115 switch(c) {
1116 case CURL_WILL:
1117 tn->telrcv_state = CURL_TS_WILL;
1118 break;
1119 case CURL_WONT:
1120 tn->telrcv_state = CURL_TS_WONT;
1121 break;
1122 case CURL_DO:
1123 tn->telrcv_state = CURL_TS_DO;
1124 break;
1125 case CURL_DONT:
1126 tn->telrcv_state = CURL_TS_DONT;
1127 break;
1128 case CURL_SB:
1129 CURL_SB_CLEAR(tn);
1130 tn->telrcv_state = CURL_TS_SB;
1131 break;
1132 case CURL_IAC:
1133 tn->telrcv_state = CURL_TS_DATA;
1134 writebyte();
1135 break;
1136 case CURL_DM:
1137 case CURL_NOP:
1138 case CURL_GA:
1139 default:
1140 tn->telrcv_state = CURL_TS_DATA;
1141 printoption(data, "RCVD", CURL_IAC, c);
1142 break;
1143 }
1144 break;
1145
1146 case CURL_TS_WILL:
1147 printoption(data, "RCVD", CURL_WILL, c);
1148 tn->please_negotiate = 1;
1149 rec_will(data, c);
1150 tn->telrcv_state = CURL_TS_DATA;
1151 break;
1152
1153 case CURL_TS_WONT:
1154 printoption(data, "RCVD", CURL_WONT, c);
1155 tn->please_negotiate = 1;
1156 rec_wont(data, c);
1157 tn->telrcv_state = CURL_TS_DATA;
1158 break;
1159
1160 case CURL_TS_DO:
1161 printoption(data, "RCVD", CURL_DO, c);
1162 tn->please_negotiate = 1;
1163 rec_do(data, c);
1164 tn->telrcv_state = CURL_TS_DATA;
1165 break;
1166
1167 case CURL_TS_DONT:
1168 printoption(data, "RCVD", CURL_DONT, c);
1169 tn->please_negotiate = 1;
1170 rec_dont(data, c);
1171 tn->telrcv_state = CURL_TS_DATA;
1172 break;
1173
1174 case CURL_TS_SB:
1175 if(c == CURL_IAC)
1176 tn->telrcv_state = CURL_TS_SE;
1177 else
1178 CURL_SB_ACCUM(tn, c);
1179 break;
1180
1181 case CURL_TS_SE:
1182 if(c != CURL_SE) {
1183 if(c != CURL_IAC) {
1184 /*
1185 * This is an error. We only expect to get "IAC IAC" or "IAC SE".
1186 * Several things may have happened. An IAC was not doubled, the
1187 * IAC SE was left off, or another option got inserted into the
1188 * suboption are all possibilities. If we assume that the IAC was
1189 * not doubled, and really the IAC SE was left off, we could get
1190 * into an infinite loop here. So, instead, we terminate the
1191 * suboption, and process the partial suboption if we can.
1192 */
1193 CURL_SB_ACCUM(tn, CURL_IAC);
1194 CURL_SB_ACCUM(tn, c);
1195 tn->subpointer -= 2;
1196 CURL_SB_TERM(tn);
1197
1198 printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
1199 suboption(data); /* handle sub-option */
1200 tn->telrcv_state = CURL_TS_IAC;
1201 goto process_iac;
1202 }
1203 CURL_SB_ACCUM(tn, c);
1204 tn->telrcv_state = CURL_TS_SB;
1205 }
1206 else {
1207 CURL_SB_ACCUM(tn, CURL_IAC);
1208 CURL_SB_ACCUM(tn, CURL_SE);
1209 tn->subpointer -= 2;
1210 CURL_SB_TERM(tn);
1211 suboption(data); /* handle sub-option */
1212 tn->telrcv_state = CURL_TS_DATA;
1213 }
1214 break;
1215 }
1216 ++in;
1217 }
1218 bufferflush();
1219 return CURLE_OK;
1220}
1221
1222/* Escape and send a telnet data block */
1223static CURLcode send_telnet_data(struct Curl_easy *data,
1224 char *buffer, ssize_t nread)
1225{
1226 ssize_t escapes, i, outlen;
1227 unsigned char *outbuf = NULL;
1228 CURLcode result = CURLE_OK;
1229 ssize_t bytes_written, total_written;
1230 struct connectdata *conn = data->conn;
1231
1232 /* Determine size of new buffer after escaping */
1233 escapes = 0;
1234 for(i = 0; i < nread; i++)
1235 if((unsigned char)buffer[i] == CURL_IAC)
1236 escapes++;
1237 outlen = nread + escapes;
1238
1239 if(outlen == nread)
1240 outbuf = (unsigned char *)buffer;
1241 else {
1242 ssize_t j;
1243 outbuf = malloc(nread + escapes + 1);
1244 if(!outbuf)
1245 return CURLE_OUT_OF_MEMORY;
1246
1247 j = 0;
1248 for(i = 0; i < nread; i++) {
1249 outbuf[j++] = (unsigned char)buffer[i];
1250 if((unsigned char)buffer[i] == CURL_IAC)
1251 outbuf[j++] = CURL_IAC;
1252 }
1253 outbuf[j] = '\0';
1254 }
1255
1256 total_written = 0;
1257 while(!result && total_written < outlen) {
1258 /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1259 struct pollfd pfd[1];
1260 pfd[0].fd = conn->sock[FIRSTSOCKET];
1261 pfd[0].events = POLLOUT;
1262 switch(Curl_poll(pfd, 1, -1)) {
1263 case -1: /* error, abort writing */
1264 case 0: /* timeout (will never happen) */
1265 result = CURLE_SEND_ERROR;
1266 break;
1267 default: /* write! */
1268 bytes_written = 0;
1269 result = Curl_nwrite(data, FIRSTSOCKET,
1270 outbuf + total_written,
1271 outlen - total_written,
1272 &bytes_written);
1273 total_written += bytes_written;
1274 break;
1275 }
1276 }
1277
1278 /* Free malloc copy if escaped */
1279 if(outbuf != (unsigned char *)buffer)
1280 free(outbuf);
1281
1282 return result;
1283}
1284
1285static 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 return CURLE_OK;
1298}
1299
1300static CURLcode telnet_do(struct Curl_easy *data, bool *done)
1301{
1302 CURLcode result;
1303 struct connectdata *conn = data->conn;
1304 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1305#ifdef USE_WINSOCK
1306 WSAEVENT event_handle;
1307 WSANETWORKEVENTS events;
1308 HANDLE stdin_handle;
1309 HANDLE objs[2];
1310 DWORD obj_count;
1311 DWORD wait_timeout;
1312 DWORD readfile_read;
1313 int err;
1314#else
1315 timediff_t interval_ms;
1316 struct pollfd pfd[2];
1317 int poll_cnt;
1318 curl_off_t total_dl = 0;
1319 curl_off_t total_ul = 0;
1320#endif
1321 ssize_t nread;
1322 struct curltime now;
1323 bool keepon = TRUE;
1324 char *buf = data->state.buffer;
1325 struct TELNET *tn;
1326
1327 *done = TRUE; /* unconditionally */
1328
1329 result = init_telnet(data);
1330 if(result)
1331 return result;
1332
1333 tn = data->req.p.telnet;
1334
1335 result = check_telnet_options(data);
1336 if(result)
1337 return result;
1338
1339#ifdef USE_WINSOCK
1340 /* We want to wait for both stdin and the socket. Since
1341 ** the select() function in winsock only works on sockets
1342 ** we have to use the WaitForMultipleObjects() call.
1343 */
1344
1345 /* First, create a sockets event object */
1346 event_handle = WSACreateEvent();
1347 if(event_handle == WSA_INVALID_EVENT) {
1348 failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
1349 return CURLE_FAILED_INIT;
1350 }
1351
1352 /* Tell winsock what events we want to listen to */
1353 if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
1354 WSACloseEvent(event_handle);
1355 return CURLE_OK;
1356 }
1357
1358 /* The get the Windows file handle for stdin */
1359 stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1360
1361 /* Create the list of objects to wait for */
1362 objs[0] = event_handle;
1363 objs[1] = stdin_handle;
1364
1365 /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1366 else use the old WaitForMultipleObjects() way */
1367 if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
1368 data->set.is_fread_set) {
1369 /* Don't wait for stdin_handle, just wait for event_handle */
1370 obj_count = 1;
1371 /* Check stdin_handle per 100 milliseconds */
1372 wait_timeout = 100;
1373 }
1374 else {
1375 obj_count = 2;
1376 wait_timeout = 1000;
1377 }
1378
1379 /* Keep on listening and act on events */
1380 while(keepon) {
1381 const DWORD buf_size = (DWORD)data->set.buffer_size;
1382 DWORD waitret = WaitForMultipleObjects(obj_count, objs,
1383 FALSE, wait_timeout);
1384 switch(waitret) {
1385
1386 case WAIT_TIMEOUT:
1387 {
1388 for(;;) {
1389 if(data->set.is_fread_set) {
1390 size_t n;
1391 /* read from user-supplied method */
1392 n = data->state.fread_func(buf, 1, buf_size, data->state.in);
1393 if(n == CURL_READFUNC_ABORT) {
1394 keepon = FALSE;
1395 result = CURLE_READ_ERROR;
1396 break;
1397 }
1398
1399 if(n == CURL_READFUNC_PAUSE)
1400 break;
1401
1402 if(n == 0) /* no bytes */
1403 break;
1404
1405 /* fall through with number of bytes read */
1406 readfile_read = (DWORD)n;
1407 }
1408 else {
1409 /* read from stdin */
1410 if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
1411 &readfile_read, NULL)) {
1412 keepon = FALSE;
1413 result = CURLE_READ_ERROR;
1414 break;
1415 }
1416
1417 if(!readfile_read)
1418 break;
1419
1420 if(!ReadFile(stdin_handle, buf, buf_size,
1421 &readfile_read, NULL)) {
1422 keepon = FALSE;
1423 result = CURLE_READ_ERROR;
1424 break;
1425 }
1426 }
1427
1428 result = send_telnet_data(data, buf, readfile_read);
1429 if(result) {
1430 keepon = FALSE;
1431 break;
1432 }
1433 }
1434 }
1435 break;
1436
1437 case WAIT_OBJECT_0 + 1:
1438 {
1439 if(!ReadFile(stdin_handle, buf, buf_size,
1440 &readfile_read, NULL)) {
1441 keepon = FALSE;
1442 result = CURLE_READ_ERROR;
1443 break;
1444 }
1445
1446 result = send_telnet_data(data, buf, readfile_read);
1447 if(result) {
1448 keepon = FALSE;
1449 break;
1450 }
1451 }
1452 break;
1453
1454 case WAIT_OBJECT_0:
1455 {
1456 events.lNetworkEvents = 0;
1457 if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
1458 err = SOCKERRNO;
1459 if(err != EINPROGRESS) {
1460 infof(data, "WSAEnumNetworkEvents failed (%d)", err);
1461 keepon = FALSE;
1462 result = CURLE_READ_ERROR;
1463 }
1464 break;
1465 }
1466 if(events.lNetworkEvents & FD_READ) {
1467 /* read data from network */
1468 result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
1469 /* read would've blocked. Loop again */
1470 if(result == CURLE_AGAIN)
1471 break;
1472 /* returned not-zero, this an error */
1473 else if(result) {
1474 keepon = FALSE;
1475 break;
1476 }
1477 /* returned zero but actually received 0 or less here,
1478 the server closed the connection and we bail out */
1479 else if(nread <= 0) {
1480 keepon = FALSE;
1481 break;
1482 }
1483
1484 result = telrcv(data, (unsigned char *) buf, nread);
1485 if(result) {
1486 keepon = FALSE;
1487 break;
1488 }
1489
1490 /* Negotiate if the peer has started negotiating,
1491 otherwise don't. We don't want to speak telnet with
1492 non-telnet servers, like POP or SMTP. */
1493 if(tn->please_negotiate && !tn->already_negotiated) {
1494 negotiate(data);
1495 tn->already_negotiated = 1;
1496 }
1497 }
1498 if(events.lNetworkEvents & FD_CLOSE) {
1499 keepon = FALSE;
1500 }
1501 }
1502 break;
1503
1504 }
1505
1506 if(data->set.timeout) {
1507 now = Curl_now();
1508 if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1509 failf(data, "Time-out");
1510 result = CURLE_OPERATION_TIMEDOUT;
1511 keepon = FALSE;
1512 }
1513 }
1514 }
1515
1516 /* We called WSACreateEvent, so call WSACloseEvent */
1517 if(!WSACloseEvent(event_handle)) {
1518 infof(data, "WSACloseEvent failed (%d)", SOCKERRNO);
1519 }
1520#else
1521 pfd[0].fd = sockfd;
1522 pfd[0].events = POLLIN;
1523
1524 if(data->set.is_fread_set) {
1525 poll_cnt = 1;
1526 interval_ms = 100; /* poll user-supplied read function */
1527 }
1528 else {
1529 /* really using fread, so infile is a FILE* */
1530 pfd[1].fd = fileno((FILE *)data->state.in);
1531 pfd[1].events = POLLIN;
1532 poll_cnt = 2;
1533 interval_ms = 1 * 1000;
1534 }
1535
1536 while(keepon) {
1537 DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt));
1538 switch(Curl_poll(pfd, poll_cnt, interval_ms)) {
1539 case -1: /* error, stop reading */
1540 keepon = FALSE;
1541 continue;
1542 case 0: /* timeout */
1543 pfd[0].revents = 0;
1544 pfd[1].revents = 0;
1545 /* FALLTHROUGH */
1546 default: /* read! */
1547 if(pfd[0].revents & POLLIN) {
1548 /* read data from network */
1549 result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
1550 /* read would've blocked. Loop again */
1551 if(result == CURLE_AGAIN)
1552 break;
1553 /* returned not-zero, this an error */
1554 if(result) {
1555 keepon = FALSE;
1556 /* TODO: in test 1452, macOS sees a ECONNRESET sometimes?
1557 * Is this the telnet test server not shutting down the socket
1558 * in a clean way? Seems to be timing related, happens more
1559 * on slow debug build */
1560 if(data->state.os_errno == ECONNRESET) {
1561 DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
1562 }
1563 break;
1564 }
1565 /* returned zero but actually received 0 or less here,
1566 the server closed the connection and we bail out */
1567 else if(nread <= 0) {
1568 keepon = FALSE;
1569 break;
1570 }
1571
1572 total_dl += nread;
1573 result = Curl_pgrsSetDownloadCounter(data, total_dl);
1574 if(!result)
1575 result = telrcv(data, (unsigned char *)buf, nread);
1576 if(result) {
1577 keepon = FALSE;
1578 break;
1579 }
1580
1581 /* Negotiate if the peer has started negotiating,
1582 otherwise don't. We don't want to speak telnet with
1583 non-telnet servers, like POP or SMTP. */
1584 if(tn->please_negotiate && !tn->already_negotiated) {
1585 negotiate(data);
1586 tn->already_negotiated = 1;
1587 }
1588 }
1589
1590 nread = 0;
1591 if(poll_cnt == 2) {
1592 if(pfd[1].revents & POLLIN) { /* read from in file */
1593 nread = read(pfd[1].fd, buf, data->set.buffer_size);
1594 }
1595 }
1596 else {
1597 /* read from user-supplied method */
1598 nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size,
1599 data->state.in);
1600 if(nread == CURL_READFUNC_ABORT) {
1601 keepon = FALSE;
1602 break;
1603 }
1604 if(nread == CURL_READFUNC_PAUSE)
1605 break;
1606 }
1607
1608 if(nread > 0) {
1609 result = send_telnet_data(data, buf, nread);
1610 if(result) {
1611 keepon = FALSE;
1612 break;
1613 }
1614 total_ul += nread;
1615 Curl_pgrsSetUploadCounter(data, total_ul);
1616 }
1617 else if(nread < 0)
1618 keepon = FALSE;
1619
1620 break;
1621 } /* poll switch statement */
1622
1623 if(data->set.timeout) {
1624 now = Curl_now();
1625 if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1626 failf(data, "Time-out");
1627 result = CURLE_OPERATION_TIMEDOUT;
1628 keepon = FALSE;
1629 }
1630 }
1631
1632 if(Curl_pgrsUpdate(data)) {
1633 result = CURLE_ABORTED_BY_CALLBACK;
1634 break;
1635 }
1636 }
1637#endif
1638 /* mark this as "no further transfer wanted" */
1639 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1640
1641 return result;
1642}
1643#endif
1644