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