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