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