blob: 9409f49553fcb06e2089f370f74592a90e390607 [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2010, 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 http://curl.haxx.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 ***************************************************************************/
22
23#include "setup.h"
24
25#ifndef CURL_DISABLE_TELNET
26/* -- WIN32 approved -- */
27#include <stdio.h>
28#include <string.h>
29#include <stdarg.h>
30#include <stdlib.h>
31#include <ctype.h>
32
33#if defined(WIN32)
34#include <time.h>
35#include <io.h>
36#else
37#ifdef HAVE_SYS_SOCKET_H
38#include <sys/socket.h>
39#endif
40#include <netinet/in.h>
41#ifdef HAVE_SYS_TIME_H
42#include <sys/time.h>
43#endif
44#ifdef HAVE_UNISTD_H
45#include <unistd.h>
46#endif
47#include <netdb.h>
48#ifdef HAVE_ARPA_INET_H
49#include <arpa/inet.h>
50#endif
51#ifdef HAVE_NET_IF_H
52#include <net/if.h>
53#endif
54#ifdef HAVE_SYS_IOCTL_H
55#include <sys/ioctl.h>
56#endif
57
58#ifdef HAVE_SYS_PARAM_H
59#include <sys/param.h>
60#endif
61
62#endif /* WIN32 */
63
64#include "urldata.h"
65#include <curl/curl.h>
66#include "transfer.h"
67#include "sendf.h"
68#include "telnet.h"
69#include "connect.h"
70
71#define _MPRINTF_REPLACE /* use our functions only */
72#include <curl/mprintf.h>
73
74#define TELOPTS
75#define TELCMDS
76
77#include "arpa_telnet.h"
78#include "curl_memory.h"
79#include "select.h"
80#include "strequal.h"
81#include "rawstr.h"
82
83/* The last #include file should be: */
84#include "memdebug.h"
85
86#define SUBBUFSIZE 512
87
88#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer;
89#define CURL_SB_TERM(x) { x->subend = x->subpointer; CURL_SB_CLEAR(x); }
90#define CURL_SB_ACCUM(x,c) \
91 if(x->subpointer < (x->subbuffer+sizeof x->subbuffer)) { \
92 *x->subpointer++ = (c); \
93 }
94
95#define CURL_SB_GET(x) ((*x->subpointer++)&0xff)
96#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
97#define CURL_SB_EOF(x) (x->subpointer >= x->subend)
98#define CURL_SB_LEN(x) (x->subend - x->subpointer)
99
100#ifdef CURL_DISABLE_VERBOSE_STRINGS
101#define printoption(a,b,c,d) do { } while(0)
102#endif
103
104#ifdef USE_WINSOCK
105typedef FARPROC WSOCK2_FUNC;
106static CURLcode check_wsock2 ( struct SessionHandle *data );
107#endif
108
109static
110CURLcode telrcv(struct connectdata *,
111 const unsigned char *inbuf, /* Data received from socket */
112 ssize_t count); /* Number of bytes received */
113
114#ifndef CURL_DISABLE_VERBOSE_STRINGS
115static void printoption(struct SessionHandle *data,
116 const char *direction,
117 int cmd, int option);
118#endif
119
120static void negotiate(struct connectdata *);
121static void send_negotiation(struct connectdata *, int cmd, int option);
122static void set_local_option(struct connectdata *, int cmd, int option);
123static void set_remote_option(struct connectdata *, int cmd, int option);
124
125static void printsub(struct SessionHandle *data,
126 int direction, unsigned char *pointer,
127 size_t length);
128static void suboption(struct connectdata *);
129
130static CURLcode telnet_do(struct connectdata *conn, bool *done);
131static CURLcode telnet_done(struct connectdata *conn,
132 CURLcode, bool premature);
133
134/* For negotiation compliant to RFC 1143 */
135#define CURL_NO 0
136#define CURL_YES 1
137#define CURL_WANTYES 2
138#define CURL_WANTNO 3
139
140#define CURL_EMPTY 0
141#define CURL_OPPOSITE 1
142
143/*
144 * Telnet receiver states for fsm
145 */
146typedef enum
147{
148 CURL_TS_DATA = 0,
149 CURL_TS_IAC,
150 CURL_TS_WILL,
151 CURL_TS_WONT,
152 CURL_TS_DO,
153 CURL_TS_DONT,
154 CURL_TS_CR,
155 CURL_TS_SB, /* sub-option collection */
156 CURL_TS_SE /* looking for sub-option end */
157} TelnetReceive;
158
159struct TELNET {
160 int please_negotiate;
161 int already_negotiated;
162 int us[256];
163 int usq[256];
164 int us_preferred[256];
165 int him[256];
166 int himq[256];
167 int him_preferred[256];
168 char subopt_ttype[32]; /* Set with suboption TTYPE */
169 char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */
170 struct curl_slist *telnet_vars; /* Environment variables */
171
172 /* suboptions */
173 unsigned char subbuffer[SUBBUFSIZE];
174 unsigned char *subpointer, *subend; /* buffer for sub-options */
175
176 TelnetReceive telrcv_state;
177};
178
179
180/*
181 * TELNET protocol handler.
182 */
183
184const struct Curl_handler Curl_handler_telnet = {
185 "TELNET", /* scheme */
186 ZERO_NULL, /* setup_connection */
187 telnet_do, /* do_it */
188 telnet_done, /* done */
189 ZERO_NULL, /* do_more */
190 ZERO_NULL, /* connect_it */
191 ZERO_NULL, /* connecting */
192 ZERO_NULL, /* doing */
193 ZERO_NULL, /* proto_getsock */
194 ZERO_NULL, /* doing_getsock */
195 ZERO_NULL, /* perform_getsock */
196 ZERO_NULL, /* disconnect */
197 PORT_TELNET, /* defport */
198 PROT_TELNET /* protocol */
199};
200
201
202#ifdef USE_WINSOCK
203static CURLcode
204check_wsock2 ( struct SessionHandle *data )
205{
206 int err;
207 WORD wVersionRequested;
208 WSADATA wsaData;
209
210 DEBUGASSERT(data);
211
212 /* telnet requires at least WinSock 2.0 so ask for it. */
213 wVersionRequested = MAKEWORD(2, 0);
214
215 err = WSAStartup(wVersionRequested, &wsaData);
216
217 /* We must've called this once already, so this call */
218 /* should always succeed. But, just in case... */
219 if(err != 0) {
220 failf(data,"WSAStartup failed (%d)",err);
221 return CURLE_FAILED_INIT;
222 }
223
224 /* We have to have a WSACleanup call for every successful */
225 /* WSAStartup call. */
226 WSACleanup();
227
228 /* Check that our version is supported */
229 if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
230 HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) {
231 /* Our version isn't supported */
232 failf(data,"insufficient winsock version to support "
233 "telnet");
234 return CURLE_FAILED_INIT;
235 }
236
237 /* Our version is supported */
238 return CURLE_OK;
239}
240#endif
241
242static
243CURLcode init_telnet(struct connectdata *conn)
244{
245 struct TELNET *tn;
246
247 tn = calloc(1, sizeof(struct TELNET));
248 if(!tn)
249 return CURLE_OUT_OF_MEMORY;
250
251 conn->data->state.proto.telnet = (void *)tn; /* make us known */
252
253 tn->telrcv_state = CURL_TS_DATA;
254
255 /* Init suboptions */
256 CURL_SB_CLEAR(tn);
257
258 /* Set the options we want by default */
259 tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
260 tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
261 tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
262 tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
263
264 return CURLE_OK;
265}
266
267static void negotiate(struct connectdata *conn)
268{
269 int i;
270 struct TELNET *tn = (struct TELNET *) conn->data->state.proto.telnet;
271
272 for(i = 0;i < CURL_NTELOPTS;i++)
273 {
274 if(tn->us_preferred[i] == CURL_YES)
275 set_local_option(conn, i, CURL_YES);
276
277 if(tn->him_preferred[i] == CURL_YES)
278 set_remote_option(conn, i, CURL_YES);
279 }
280}
281
282#ifndef CURL_DISABLE_VERBOSE_STRINGS
283static void printoption(struct SessionHandle *data,
284 const char *direction, int cmd, int option)
285{
286 const char *fmt;
287 const char *opt;
288
289 if(data->set.verbose)
290 {
291 if(cmd == CURL_IAC)
292 {
293 if(CURL_TELCMD_OK(option))
294 infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option));
295 else
296 infof(data, "%s IAC %d\n", direction, option);
297 }
298 else
299 {
300 fmt = (cmd == CURL_WILL) ? "WILL" : (cmd == CURL_WONT) ? "WONT" :
301 (cmd == CURL_DO) ? "DO" : (cmd == CURL_DONT) ? "DONT" : 0;
302 if(fmt)
303 {
304 if(CURL_TELOPT_OK(option))
305 opt = CURL_TELOPT(option);
306 else if(option == CURL_TELOPT_EXOPL)
307 opt = "EXOPL";
308 else
309 opt = NULL;
310
311 if(opt)
312 infof(data, "%s %s %s\n", direction, fmt, opt);
313 else
314 infof(data, "%s %s %d\n", direction, fmt, option);
315 }
316 else
317 infof(data, "%s %d %d\n", direction, cmd, option);
318 }
319 }
320}
321#endif
322
323static void send_negotiation(struct connectdata *conn, int cmd, int option)
324{
325 unsigned char buf[3];
326 ssize_t bytes_written;
327 int err;
328 struct SessionHandle *data = conn->data;
329
330 buf[0] = CURL_IAC;
331 buf[1] = (unsigned char)cmd;
332 buf[2] = (unsigned char)option;
333
334 bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
335 if(bytes_written < 0) {
336 err = SOCKERRNO;
337 failf(data,"Sending data failed (%d)",err);
338 }
339
340 printoption(conn->data, "SENT", cmd, option);
341}
342
343static
344void set_remote_option(struct connectdata *conn, int option, int newstate)
345{
346 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
347 if(newstate == CURL_YES)
348 {
349 switch(tn->him[option])
350 {
351 case CURL_NO:
352 tn->him[option] = CURL_WANTYES;
353 send_negotiation(conn, CURL_DO, option);
354 break;
355
356 case CURL_YES:
357 /* Already enabled */
358 break;
359
360 case CURL_WANTNO:
361 switch(tn->himq[option])
362 {
363 case CURL_EMPTY:
364 /* Already negotiating for CURL_YES, queue the request */
365 tn->himq[option] = CURL_OPPOSITE;
366 break;
367 case CURL_OPPOSITE:
368 /* Error: already queued an enable request */
369 break;
370 }
371 break;
372
373 case CURL_WANTYES:
374 switch(tn->himq[option])
375 {
376 case CURL_EMPTY:
377 /* Error: already negotiating for enable */
378 break;
379 case CURL_OPPOSITE:
380 tn->himq[option] = CURL_EMPTY;
381 break;
382 }
383 break;
384 }
385 }
386 else /* NO */
387 {
388 switch(tn->him[option])
389 {
390 case CURL_NO:
391 /* Already disabled */
392 break;
393
394 case CURL_YES:
395 tn->him[option] = CURL_WANTNO;
396 send_negotiation(conn, CURL_DONT, option);
397 break;
398
399 case CURL_WANTNO:
400 switch(tn->himq[option])
401 {
402 case CURL_EMPTY:
403 /* Already negotiating for NO */
404 break;
405 case CURL_OPPOSITE:
406 tn->himq[option] = CURL_EMPTY;
407 break;
408 }
409 break;
410
411 case CURL_WANTYES:
412 switch(tn->himq[option])
413 {
414 case CURL_EMPTY:
415 tn->himq[option] = CURL_OPPOSITE;
416 break;
417 case CURL_OPPOSITE:
418 break;
419 }
420 break;
421 }
422 }
423}
424
425static
426void rec_will(struct connectdata *conn, int option)
427{
428 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
429 switch(tn->him[option])
430 {
431 case CURL_NO:
432 if(tn->him_preferred[option] == CURL_YES)
433 {
434 tn->him[option] = CURL_YES;
435 send_negotiation(conn, CURL_DO, option);
436 }
437 else
438 {
439 send_negotiation(conn, CURL_DONT, option);
440 }
441 break;
442
443 case CURL_YES:
444 /* Already enabled */
445 break;
446
447 case CURL_WANTNO:
448 switch(tn->himq[option])
449 {
450 case CURL_EMPTY:
451 /* Error: DONT answered by WILL */
452 tn->him[option] = CURL_NO;
453 break;
454 case CURL_OPPOSITE:
455 /* Error: DONT answered by WILL */
456 tn->him[option] = CURL_YES;
457 tn->himq[option] = CURL_EMPTY;
458 break;
459 }
460 break;
461
462 case CURL_WANTYES:
463 switch(tn->himq[option])
464 {
465 case CURL_EMPTY:
466 tn->him[option] = CURL_YES;
467 break;
468 case CURL_OPPOSITE:
469 tn->him[option] = CURL_WANTNO;
470 tn->himq[option] = CURL_EMPTY;
471 send_negotiation(conn, CURL_DONT, option);
472 break;
473 }
474 break;
475 }
476}
477
478static
479void rec_wont(struct connectdata *conn, int option)
480{
481 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
482 switch(tn->him[option])
483 {
484 case CURL_NO:
485 /* Already disabled */
486 break;
487
488 case CURL_YES:
489 tn->him[option] = CURL_NO;
490 send_negotiation(conn, CURL_DONT, option);
491 break;
492
493 case CURL_WANTNO:
494 switch(tn->himq[option])
495 {
496 case CURL_EMPTY:
497 tn->him[option] = CURL_NO;
498 break;
499
500 case CURL_OPPOSITE:
501 tn->him[option] = CURL_WANTYES;
502 tn->himq[option] = CURL_EMPTY;
503 send_negotiation(conn, CURL_DO, option);
504 break;
505 }
506 break;
507
508 case CURL_WANTYES:
509 switch(tn->himq[option])
510 {
511 case CURL_EMPTY:
512 tn->him[option] = CURL_NO;
513 break;
514 case CURL_OPPOSITE:
515 tn->him[option] = CURL_NO;
516 tn->himq[option] = CURL_EMPTY;
517 break;
518 }
519 break;
520 }
521}
522
523static void
524set_local_option(struct connectdata *conn, int option, int newstate)
525{
526 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
527 if(newstate == CURL_YES)
528 {
529 switch(tn->us[option])
530 {
531 case CURL_NO:
532 tn->us[option] = CURL_WANTYES;
533 send_negotiation(conn, CURL_WILL, option);
534 break;
535
536 case CURL_YES:
537 /* Already enabled */
538 break;
539
540 case CURL_WANTNO:
541 switch(tn->usq[option])
542 {
543 case CURL_EMPTY:
544 /* Already negotiating for CURL_YES, queue the request */
545 tn->usq[option] = CURL_OPPOSITE;
546 break;
547 case CURL_OPPOSITE:
548 /* Error: already queued an enable request */
549 break;
550 }
551 break;
552
553 case CURL_WANTYES:
554 switch(tn->usq[option])
555 {
556 case CURL_EMPTY:
557 /* Error: already negotiating for enable */
558 break;
559 case CURL_OPPOSITE:
560 tn->usq[option] = CURL_EMPTY;
561 break;
562 }
563 break;
564 }
565 }
566 else /* NO */
567 {
568 switch(tn->us[option])
569 {
570 case CURL_NO:
571 /* Already disabled */
572 break;
573
574 case CURL_YES:
575 tn->us[option] = CURL_WANTNO;
576 send_negotiation(conn, CURL_WONT, option);
577 break;
578
579 case CURL_WANTNO:
580 switch(tn->usq[option])
581 {
582 case CURL_EMPTY:
583 /* Already negotiating for NO */
584 break;
585 case CURL_OPPOSITE:
586 tn->usq[option] = CURL_EMPTY;
587 break;
588 }
589 break;
590
591 case CURL_WANTYES:
592 switch(tn->usq[option])
593 {
594 case CURL_EMPTY:
595 tn->usq[option] = CURL_OPPOSITE;
596 break;
597 case CURL_OPPOSITE:
598 break;
599 }
600 break;
601 }
602 }
603}
604
605static
606void rec_do(struct connectdata *conn, int option)
607{
608 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
609 switch(tn->us[option])
610 {
611 case CURL_NO:
612 if(tn->us_preferred[option] == CURL_YES)
613 {
614 tn->us[option] = CURL_YES;
615 send_negotiation(conn, CURL_WILL, option);
616 }
617 else
618 {
619 send_negotiation(conn, CURL_WONT, option);
620 }
621 break;
622
623 case CURL_YES:
624 /* Already enabled */
625 break;
626
627 case CURL_WANTNO:
628 switch(tn->usq[option])
629 {
630 case CURL_EMPTY:
631 /* Error: DONT answered by WILL */
632 tn->us[option] = CURL_NO;
633 break;
634 case CURL_OPPOSITE:
635 /* Error: DONT answered by WILL */
636 tn->us[option] = CURL_YES;
637 tn->usq[option] = CURL_EMPTY;
638 break;
639 }
640 break;
641
642 case CURL_WANTYES:
643 switch(tn->usq[option])
644 {
645 case CURL_EMPTY:
646 tn->us[option] = CURL_YES;
647 break;
648 case CURL_OPPOSITE:
649 tn->us[option] = CURL_WANTNO;
650 tn->himq[option] = CURL_EMPTY;
651 send_negotiation(conn, CURL_WONT, option);
652 break;
653 }
654 break;
655 }
656}
657
658static
659void rec_dont(struct connectdata *conn, int option)
660{
661 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
662 switch(tn->us[option])
663 {
664 case CURL_NO:
665 /* Already disabled */
666 break;
667
668 case CURL_YES:
669 tn->us[option] = CURL_NO;
670 send_negotiation(conn, CURL_WONT, option);
671 break;
672
673 case CURL_WANTNO:
674 switch(tn->usq[option])
675 {
676 case CURL_EMPTY:
677 tn->us[option] = CURL_NO;
678 break;
679
680 case CURL_OPPOSITE:
681 tn->us[option] = CURL_WANTYES;
682 tn->usq[option] = CURL_EMPTY;
683 send_negotiation(conn, CURL_WILL, option);
684 break;
685 }
686 break;
687
688 case CURL_WANTYES:
689 switch(tn->usq[option])
690 {
691 case CURL_EMPTY:
692 tn->us[option] = CURL_NO;
693 break;
694 case CURL_OPPOSITE:
695 tn->us[option] = CURL_NO;
696 tn->usq[option] = CURL_EMPTY;
697 break;
698 }
699 break;
700 }
701}
702
703
704static void printsub(struct SessionHandle *data,
705 int direction, /* '<' or '>' */
706 unsigned char *pointer, /* where suboption data is */
707 size_t length) /* length of suboption data */
708{
709 unsigned int i = 0;
710
711 if(data->set.verbose)
712 {
713 if(direction)
714 {
715 infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
716 if(length >= 3)
717 {
718 int j;
719
720 i = pointer[length-2];
721 j = pointer[length-1];
722
723 if(i != CURL_IAC || j != CURL_SE)
724 {
725 infof(data, "(terminated by ");
726 if(CURL_TELOPT_OK(i))
727 infof(data, "%s ", CURL_TELOPT(i));
728 else if(CURL_TELCMD_OK(i))
729 infof(data, "%s ", CURL_TELCMD(i));
730 else
731 infof(data, "%u ", i);
732 if(CURL_TELOPT_OK(j))
733 infof(data, "%s", CURL_TELOPT(j));
734 else if(CURL_TELCMD_OK(j))
735 infof(data, "%s", CURL_TELCMD(j));
736 else
737 infof(data, "%d", j);
738 infof(data, ", not IAC SE!) ");
739 }
740 }
741 length -= 2;
742 }
743 if(length < 1)
744 {
745 infof(data, "(Empty suboption?)");
746 return;
747 }
748
749 if(CURL_TELOPT_OK(pointer[0])) {
750 switch(pointer[0]) {
751 case CURL_TELOPT_TTYPE:
752 case CURL_TELOPT_XDISPLOC:
753 case CURL_TELOPT_NEW_ENVIRON:
754 infof(data, "%s", CURL_TELOPT(pointer[0]));
755 break;
756 default:
757 infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
758 break;
759 }
760 }
761 else
762 infof(data, "%d (unknown)", pointer[i]);
763
764 switch(pointer[1]) {
765 case CURL_TELQUAL_IS:
766 infof(data, " IS");
767 break;
768 case CURL_TELQUAL_SEND:
769 infof(data, " SEND");
770 break;
771 case CURL_TELQUAL_INFO:
772 infof(data, " INFO/REPLY");
773 break;
774 case CURL_TELQUAL_NAME:
775 infof(data, " NAME");
776 break;
777 }
778
779 switch(pointer[0]) {
780 case CURL_TELOPT_TTYPE:
781 case CURL_TELOPT_XDISPLOC:
782 pointer[length] = 0;
783 infof(data, " \"%s\"", &pointer[2]);
784 break;
785 case CURL_TELOPT_NEW_ENVIRON:
786 if(pointer[1] == CURL_TELQUAL_IS) {
787 infof(data, " ");
788 for(i = 3;i < length;i++) {
789 switch(pointer[i]) {
790 case CURL_NEW_ENV_VAR:
791 infof(data, ", ");
792 break;
793 case CURL_NEW_ENV_VALUE:
794 infof(data, " = ");
795 break;
796 default:
797 infof(data, "%c", pointer[i]);
798 break;
799 }
800 }
801 }
802 break;
803 default:
804 for (i = 2; i < length; i++)
805 infof(data, " %.2x", pointer[i]);
806 break;
807 }
808
809 if(direction)
810 {
811 infof(data, "\n");
812 }
813 }
814}
815
816static CURLcode check_telnet_options(struct connectdata *conn)
817{
818 struct curl_slist *head;
819 char option_keyword[128];
820 char option_arg[256];
821 char *buf;
822 struct SessionHandle *data = conn->data;
823 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
824
825 /* Add the user name as an environment variable if it
826 was given on the command line */
827 if(conn->bits.user_passwd)
828 {
829 snprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
830 tn->telnet_vars = curl_slist_append(tn->telnet_vars, option_arg);
831
832 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
833 }
834
835 for(head = data->set.telnet_options; head; head=head->next) {
836 if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
837 option_keyword, option_arg) == 2) {
838
839 /* Terminal type */
840 if(Curl_raw_equal(option_keyword, "TTYPE")) {
841 strncpy(tn->subopt_ttype, option_arg, 31);
842 tn->subopt_ttype[31] = 0; /* String termination */
843 tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
844 continue;
845 }
846
847 /* Display variable */
848 if(Curl_raw_equal(option_keyword, "XDISPLOC")) {
849 strncpy(tn->subopt_xdisploc, option_arg, 127);
850 tn->subopt_xdisploc[127] = 0; /* String termination */
851 tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
852 continue;
853 }
854
855 /* Environment variable */
856 if(Curl_raw_equal(option_keyword, "NEW_ENV")) {
857 buf = strdup(option_arg);
858 if(!buf)
859 return CURLE_OUT_OF_MEMORY;
860 tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf);
861 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
862 continue;
863 }
864
865 failf(data, "Unknown telnet option %s", head->data);
866 return CURLE_UNKNOWN_TELNET_OPTION;
867 } else {
868 failf(data, "Syntax error in telnet option: %s", head->data);
869 return CURLE_TELNET_OPTION_SYNTAX;
870 }
871 }
872
873 return CURLE_OK;
874}
875
876/*
877 * suboption()
878 *
879 * Look at the sub-option buffer, and try to be helpful to the other
880 * side.
881 */
882
883static void suboption(struct connectdata *conn)
884{
885 struct curl_slist *v;
886 unsigned char temp[2048];
887 ssize_t bytes_written;
888 size_t len;
889 size_t tmplen;
890 int err;
891 char varname[128];
892 char varval[128];
893 struct SessionHandle *data = conn->data;
894 struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
895
896 printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2);
897 switch (CURL_SB_GET(tn)) {
898 case CURL_TELOPT_TTYPE:
899 len = strlen(tn->subopt_ttype) + 4 + 2;
900 snprintf((char *)temp, sizeof(temp),
901 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
902 CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
903 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
904 if(bytes_written < 0) {
905 err = SOCKERRNO;
906 failf(data,"Sending data failed (%d)",err);
907 }
908 printsub(data, '>', &temp[2], len-2);
909 break;
910 case CURL_TELOPT_XDISPLOC:
911 len = strlen(tn->subopt_xdisploc) + 4 + 2;
912 snprintf((char *)temp, sizeof(temp),
913 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
914 CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
915 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
916 if(bytes_written < 0) {
917 err = SOCKERRNO;
918 failf(data,"Sending data failed (%d)",err);
919 }
920 printsub(data, '>', &temp[2], len-2);
921 break;
922 case CURL_TELOPT_NEW_ENVIRON:
923 snprintf((char *)temp, sizeof(temp),
924 "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
925 CURL_TELQUAL_IS);
926 len = 4;
927
928 for(v = tn->telnet_vars;v;v = v->next) {
929 tmplen = (strlen(v->data) + 1);
930 /* Add the variable only if it fits */
931 if(len + tmplen < (int)sizeof(temp)-6) {
932 sscanf(v->data, "%127[^,],%127s", varname, varval);
933 snprintf((char *)&temp[len], sizeof(temp) - len,
934 "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
935 CURL_NEW_ENV_VALUE, varval);
936 len += tmplen;
937 }
938 }
939 snprintf((char *)&temp[len], sizeof(temp) - len,
940 "%c%c", CURL_IAC, CURL_SE);
941 len += 2;
942 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
943 if(bytes_written < 0) {
944 err = SOCKERRNO;
945 failf(data,"Sending data failed (%d)",err);
946 }
947 printsub(data, '>', &temp[2], len-2);
948 break;
949 }
950 return;
951}
952
953static
954CURLcode telrcv(struct connectdata *conn,
955 const unsigned char *inbuf, /* Data received from socket */
956 ssize_t count) /* Number of bytes received */
957{
958 unsigned char c;
959 CURLcode result;
960 int in = 0;
961 int startwrite=-1;
962 struct SessionHandle *data = conn->data;
963 struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
964
965#define startskipping() \
966 if(startwrite >= 0) { \
967 result = Curl_client_write(conn, \
968 CLIENTWRITE_BODY, \
969 (char *)&inbuf[startwrite], \
970 in-startwrite); \
971 if(result != CURLE_OK) \
972 return result; \
973 } \
974 startwrite = -1
975
976#define writebyte() \
977 if(startwrite < 0) \
978 startwrite = in
979
980#define bufferflush() startskipping()
981
982 while(count--)
983 {
984 c = inbuf[in];
985
986 /*infof(data,"In rcv state %d char %d\n", tn->telrcv_state, c);*/
987 switch (tn->telrcv_state)
988 {
989 case CURL_TS_CR:
990 tn->telrcv_state = CURL_TS_DATA;
991 if(c == '\0')
992 {
993 startskipping();
994 break; /* Ignore \0 after CR */
995 }
996 writebyte();
997 break;
998
999 case CURL_TS_DATA:
1000 if(c == CURL_IAC)
1001 {
1002 tn->telrcv_state = CURL_TS_IAC;
1003 startskipping();
1004 break;
1005 }
1006 else if(c == '\r')
1007 {
1008 tn->telrcv_state = CURL_TS_CR;
1009 }
1010 writebyte();
1011 break;
1012
1013 case CURL_TS_IAC:
1014 process_iac:
1015 DEBUGASSERT(startwrite < 0);
1016 switch (c)
1017 {
1018 case CURL_WILL:
1019 tn->telrcv_state = CURL_TS_WILL;
1020 break;
1021 case CURL_WONT:
1022 tn->telrcv_state = CURL_TS_WONT;
1023 break;
1024 case CURL_DO:
1025 tn->telrcv_state = CURL_TS_DO;
1026 break;
1027 case CURL_DONT:
1028 tn->telrcv_state = CURL_TS_DONT;
1029 break;
1030 case CURL_SB:
1031 CURL_SB_CLEAR(tn);
1032 tn->telrcv_state = CURL_TS_SB;
1033 break;
1034 case CURL_IAC:
1035 tn->telrcv_state = CURL_TS_DATA;
1036 writebyte();
1037 break;
1038 case CURL_DM:
1039 case CURL_NOP:
1040 case CURL_GA:
1041 default:
1042 tn->telrcv_state = CURL_TS_DATA;
1043 printoption(data, "RCVD", CURL_IAC, c);
1044 break;
1045 }
1046 break;
1047
1048 case CURL_TS_WILL:
1049 printoption(data, "RCVD", CURL_WILL, c);
1050 tn->please_negotiate = 1;
1051 rec_will(conn, c);
1052 tn->telrcv_state = CURL_TS_DATA;
1053 break;
1054
1055 case CURL_TS_WONT:
1056 printoption(data, "RCVD", CURL_WONT, c);
1057 tn->please_negotiate = 1;
1058 rec_wont(conn, c);
1059 tn->telrcv_state = CURL_TS_DATA;
1060 break;
1061
1062 case CURL_TS_DO:
1063 printoption(data, "RCVD", CURL_DO, c);
1064 tn->please_negotiate = 1;
1065 rec_do(conn, c);
1066 tn->telrcv_state = CURL_TS_DATA;
1067 break;
1068
1069 case CURL_TS_DONT:
1070 printoption(data, "RCVD", CURL_DONT, c);
1071 tn->please_negotiate = 1;
1072 rec_dont(conn, c);
1073 tn->telrcv_state = CURL_TS_DATA;
1074 break;
1075
1076 case CURL_TS_SB:
1077 if(c == CURL_IAC)
1078 {
1079 tn->telrcv_state = CURL_TS_SE;
1080 }
1081 else
1082 {
1083 CURL_SB_ACCUM(tn,c);
1084 }
1085 break;
1086
1087 case CURL_TS_SE:
1088 if(c != CURL_SE)
1089 {
1090 if(c != CURL_IAC)
1091 {
1092 /*
1093 * This is an error. We only expect to get "IAC IAC" or "IAC SE".
1094 * Several things may have happend. An IAC was not doubled, the
1095 * IAC SE was left off, or another option got inserted into the
1096 * suboption are all possibilities. If we assume that the IAC was
1097 * not doubled, and really the IAC SE was left off, we could get
1098 * into an infinate loop here. So, instead, we terminate the
1099 * suboption, and process the partial suboption if we can.
1100 */
1101 CURL_SB_ACCUM(tn, CURL_IAC);
1102 CURL_SB_ACCUM(tn, c);
1103 tn->subpointer -= 2;
1104 CURL_SB_TERM(tn);
1105
1106 printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
1107 suboption(conn); /* handle sub-option */
1108 tn->telrcv_state = CURL_TS_IAC;
1109 goto process_iac;
1110 }
1111 CURL_SB_ACCUM(tn,c);
1112 tn->telrcv_state = CURL_TS_SB;
1113 }
1114 else
1115 {
1116 CURL_SB_ACCUM(tn, CURL_IAC);
1117 CURL_SB_ACCUM(tn, CURL_SE);
1118 tn->subpointer -= 2;
1119 CURL_SB_TERM(tn);
1120 suboption(conn); /* handle sub-option */
1121 tn->telrcv_state = CURL_TS_DATA;
1122 }
1123 break;
1124 }
1125 ++in;
1126 }
1127 bufferflush();
1128 return CURLE_OK;
1129}
1130
1131/* Escape and send a telnet data block */
1132/* TODO: write large chunks of data instead of one byte at a time */
1133static CURLcode send_telnet_data(struct connectdata *conn,
1134 char *buffer, ssize_t nread)
1135{
1136 unsigned char outbuf[2];
1137 ssize_t bytes_written, total_written;
1138 int out_count;
1139 CURLcode rc = CURLE_OK;
1140
1141 while(rc == CURLE_OK && nread--) {
1142 outbuf[0] = *buffer++;
1143 out_count = 1;
1144 if(outbuf[0] == CURL_IAC)
1145 outbuf[out_count++] = CURL_IAC;
1146
1147 total_written = 0;
1148 do {
1149 /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1150 struct pollfd pfd[1];
1151 pfd[0].fd = conn->sock[FIRSTSOCKET];
1152 pfd[0].events = POLLOUT;
1153 switch (Curl_poll(pfd, 1, -1)) {
1154 case -1: /* error, abort writing */
1155 case 0: /* timeout (will never happen) */
1156 rc = CURLE_SEND_ERROR;
1157 break;
1158 default: /* write! */
1159 bytes_written = 0;
1160 rc = Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf+total_written,
1161 out_count-total_written, &bytes_written);
1162 total_written += bytes_written;
1163 break;
1164 }
1165 /* handle partial write */
1166 } while (rc == CURLE_OK && total_written < out_count);
1167 }
1168 return rc;
1169}
1170
1171static CURLcode telnet_done(struct connectdata *conn,
1172 CURLcode status, bool premature)
1173{
1174 struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
1175 (void)status; /* unused */
1176 (void)premature; /* not used */
1177
1178 curl_slist_free_all(tn->telnet_vars);
1179
1180 free(conn->data->state.proto.telnet);
1181 conn->data->state.proto.telnet = NULL;
1182
1183 return CURLE_OK;
1184}
1185
1186static CURLcode telnet_do(struct connectdata *conn, bool *done)
1187{
1188 CURLcode code;
1189 struct SessionHandle *data = conn->data;
1190 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1191#ifdef USE_WINSOCK
1192 HMODULE wsock2;
1193 WSOCK2_FUNC close_event_func;
1194 WSOCK2_FUNC create_event_func;
1195 WSOCK2_FUNC event_select_func;
1196 WSOCK2_FUNC enum_netevents_func;
1197 WSAEVENT event_handle;
1198 WSANETWORKEVENTS events;
1199 HANDLE stdin_handle;
1200 HANDLE objs[2];
1201 DWORD obj_count;
1202 DWORD wait_timeout;
1203 DWORD waitret;
1204 DWORD readfile_read;
1205 int err;
1206#else
1207 int interval_ms;
1208 struct pollfd pfd[2];
1209#endif
1210 int ret;
1211 ssize_t nread;
1212 struct timeval now;
1213 bool keepon = TRUE;
1214 char *buf = data->state.buffer;
1215 struct TELNET *tn;
1216
1217 *done = TRUE; /* unconditionally */
1218
1219 code = init_telnet(conn);
1220 if(code)
1221 return code;
1222
1223 tn = (struct TELNET *)data->state.proto.telnet;
1224
1225 code = check_telnet_options(conn);
1226 if(code)
1227 return code;
1228
1229#ifdef USE_WINSOCK
1230 /*
1231 ** This functionality only works with WinSock >= 2.0. So,
1232 ** make sure have it.
1233 */
1234 code = check_wsock2(data);
1235 if(code)
1236 return code;
1237
1238 /* OK, so we have WinSock 2.0. We need to dynamically */
1239 /* load ws2_32.dll and get the function pointers we need. */
1240 wsock2 = LoadLibrary("WS2_32.DLL");
1241 if(wsock2 == NULL) {
1242 failf(data,"failed to load WS2_32.DLL (%d)", ERRNO);
1243 return CURLE_FAILED_INIT;
1244 }
1245
1246 /* Grab a pointer to WSACreateEvent */
1247 create_event_func = GetProcAddress(wsock2,"WSACreateEvent");
1248 if(create_event_func == NULL) {
1249 failf(data,"failed to find WSACreateEvent function (%d)",
1250 ERRNO);
1251 FreeLibrary(wsock2);
1252 return CURLE_FAILED_INIT;
1253 }
1254
1255 /* And WSACloseEvent */
1256 close_event_func = GetProcAddress(wsock2,"WSACloseEvent");
1257 if(close_event_func == NULL) {
1258 failf(data,"failed to find WSACloseEvent function (%d)",
1259 ERRNO);
1260 FreeLibrary(wsock2);
1261 return CURLE_FAILED_INIT;
1262 }
1263
1264 /* And WSAEventSelect */
1265 event_select_func = GetProcAddress(wsock2,"WSAEventSelect");
1266 if(event_select_func == NULL) {
1267 failf(data,"failed to find WSAEventSelect function (%d)",
1268 ERRNO);
1269 FreeLibrary(wsock2);
1270 return CURLE_FAILED_INIT;
1271 }
1272
1273 /* And WSAEnumNetworkEvents */
1274 enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents");
1275 if(enum_netevents_func == NULL) {
1276 failf(data,"failed to find WSAEnumNetworkEvents function (%d)",
1277 ERRNO);
1278 FreeLibrary(wsock2);
1279 return CURLE_FAILED_INIT;
1280 }
1281
1282 /* We want to wait for both stdin and the socket. Since
1283 ** the select() function in winsock only works on sockets
1284 ** we have to use the WaitForMultipleObjects() call.
1285 */
1286
1287 /* First, create a sockets event object */
1288 event_handle = (WSAEVENT)create_event_func();
1289 if(event_handle == WSA_INVALID_EVENT) {
1290 failf(data,"WSACreateEvent failed (%d)", SOCKERRNO);
1291 FreeLibrary(wsock2);
1292 return CURLE_FAILED_INIT;
1293 }
1294
1295 /* The get the Windows file handle for stdin */
1296 stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1297
1298 /* Create the list of objects to wait for */
1299 objs[0] = event_handle;
1300 objs[1] = stdin_handle;
1301
1302 /* Tell winsock what events we want to listen to */
1303 if(event_select_func(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
1304 close_event_func(event_handle);
1305 FreeLibrary(wsock2);
1306 return CURLE_OK;
1307 }
1308
1309 /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1310 else use the old WaitForMultipleObjects() way */
1311 if(GetFileType(stdin_handle) == FILE_TYPE_PIPE) {
1312 /* Don't wait for stdin_handle, just wait for event_handle */
1313 obj_count = 1;
1314 /* Check stdin_handle per 100 milliseconds */
1315 wait_timeout = 100;
1316 } else {
1317 obj_count = 2;
1318 wait_timeout = 1000;
1319 }
1320
1321 /* Keep on listening and act on events */
1322 while(keepon) {
1323 waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout);
1324 switch(waitret) {
1325 case WAIT_TIMEOUT:
1326 {
1327 for(;;) {
1328 if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, &readfile_read, NULL)) {
1329 keepon = FALSE;
1330 code = CURLE_READ_ERROR;
1331 break;
1332 }
1333
1334 if(!readfile_read)
1335 break;
1336
1337 if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
1338 &readfile_read, NULL)) {
1339 keepon = FALSE;
1340 code = CURLE_READ_ERROR;
1341 break;
1342 }
1343
1344 code = send_telnet_data(conn, buf, readfile_read);
1345 if(code) {
1346 keepon = FALSE;
1347 break;
1348 }
1349 }
1350 }
1351 break;
1352
1353 case WAIT_OBJECT_0 + 1:
1354 {
1355 if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer),
1356 &readfile_read, NULL)) {
1357 keepon = FALSE;
1358 code = CURLE_READ_ERROR;
1359 break;
1360 }
1361
1362 code = send_telnet_data(conn, buf, readfile_read);
1363 if(code) {
1364 keepon = FALSE;
1365 break;
1366 }
1367 }
1368 break;
1369
1370 case WAIT_OBJECT_0:
1371
1372 if(SOCKET_ERROR == enum_netevents_func(sockfd, event_handle, &events)) {
1373 if((err = SOCKERRNO) != EINPROGRESS) {
1374 infof(data,"WSAEnumNetworkEvents failed (%d)", err);
1375 keepon = FALSE;
1376 code = CURLE_READ_ERROR;
1377 }
1378 break;
1379 }
1380 if(events.lNetworkEvents & FD_READ) {
1381 /* read data from network */
1382 ret = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
1383 /* returned sub-zero, this would've blocked. Loop again */
1384 if(ret < 0)
1385 break;
1386 /* returned not-zero, this an error */
1387 else if(ret) {
1388 keepon = FALSE;
1389 code = (CURLcode)ret;
1390 break;
1391 }
1392 /* returned zero but actually received 0 or less here,
1393 the server closed the connection and we bail out */
1394 else if(nread <= 0) {
1395 keepon = FALSE;
1396 break;
1397 }
1398
1399 code = telrcv(conn, (unsigned char *)buf, nread);
1400 if(code) {
1401 keepon = FALSE;
1402 break;
1403 }
1404
1405 fflush(stdout);
1406
1407 /* Negotiate if the peer has started negotiating,
1408 otherwise don't. We don't want to speak telnet with
1409 non-telnet servers, like POP or SMTP. */
1410 if(tn->please_negotiate && !tn->already_negotiated) {
1411 negotiate(conn);
1412 tn->already_negotiated = 1;
1413 }
1414 }
1415 if(events.lNetworkEvents & FD_CLOSE) {
1416 keepon = FALSE;
1417 }
1418 break;
1419
1420 }
1421
1422 if(data->set.timeout) {
1423 now = Curl_tvnow();
1424 if(Curl_tvdiff(now, conn->created) >= data->set.timeout) {
1425 failf(data, "Time-out");
1426 code = CURLE_OPERATION_TIMEDOUT;
1427 keepon = FALSE;
1428 }
1429 }
1430 }
1431
1432 /* We called WSACreateEvent, so call WSACloseEvent */
1433 if(close_event_func(event_handle) == FALSE) {
1434 infof(data,"WSACloseEvent failed (%d)", SOCKERRNO);
1435 }
1436
1437 /* "Forget" pointers into the library we're about to free */
1438 create_event_func = NULL;
1439 close_event_func = NULL;
1440 event_select_func = NULL;
1441 enum_netevents_func = NULL;
1442
1443 /* We called LoadLibrary, so call FreeLibrary */
1444 if(!FreeLibrary(wsock2))
1445 infof(data,"FreeLibrary(wsock2) failed (%d)", ERRNO);
1446#else
1447 pfd[0].fd = sockfd;
1448 pfd[0].events = POLLIN;
1449 pfd[1].fd = 0;
1450 pfd[1].events = POLLIN;
1451 interval_ms = 1 * 1000;
1452
1453 while(keepon) {
1454 switch (Curl_poll(pfd, 2, interval_ms)) {
1455 case -1: /* error, stop reading */
1456 keepon = FALSE;
1457 continue;
1458 case 0: /* timeout */
1459 break;
1460 default: /* read! */
1461 if(pfd[1].revents & POLLIN) { /* read from stdin */
1462 nread = read(0, buf, 255);
1463 code = send_telnet_data(conn, buf, nread);
1464 if(code) {
1465 keepon = FALSE;
1466 break;
1467 }
1468 }
1469
1470 if(pfd[0].revents & POLLIN) {
1471 /* read data from network */
1472 ret = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread);
1473 /* returned sub-zero, this would've blocked. Loop again */
1474 if(ret < 0)
1475 break;
1476 /* returned not-zero, this an error */
1477 else if(ret) {
1478 keepon = FALSE;
1479 code = (CURLcode)ret;
1480 break;
1481 }
1482 /* returned zero but actually received 0 or less here,
1483 the server closed the connection and we bail out */
1484 else if(nread <= 0) {
1485 keepon = FALSE;
1486 break;
1487 }
1488
1489 code = telrcv(conn, (unsigned char *)buf, nread);
1490 if(code) {
1491 keepon = FALSE;
1492 break;
1493 }
1494
1495 /* Negotiate if the peer has started negotiating,
1496 otherwise don't. We don't want to speak telnet with
1497 non-telnet servers, like POP or SMTP. */
1498 if(tn->please_negotiate && !tn->already_negotiated) {
1499 negotiate(conn);
1500 tn->already_negotiated = 1;
1501 }
1502 }
1503 }
1504 if(data->set.timeout) {
1505 now = Curl_tvnow();
1506 if(Curl_tvdiff(now, conn->created) >= data->set.timeout) {
1507 failf(data, "Time-out");
1508 code = CURLE_OPERATION_TIMEDOUT;
1509 keepon = FALSE;
1510 }
1511 }
1512 }
1513#endif
1514 /* mark this as "no further transfer wanted" */
1515 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1516
1517 return code;
1518}
1519#endif