21 static SOCKET s
= INVALID_SOCKET
;
23 #define IAC 255 /* interpret as command: */
24 #define DONT 254 /* you are not to use option */
25 #define DO 253 /* please, you use option */
26 #define WONT 252 /* I won't use option */
27 #define WILL 251 /* I will use option */
28 #define SB 250 /* interpret as subnegotiation */
29 #define SE 240 /* end sub negotiation */
31 #define GA 249 /* you may reverse the line */
32 #define EL 248 /* erase the current line */
33 #define EC 247 /* erase the current character */
34 #define AYT 246 /* are you there */
35 #define AO 245 /* abort output--but let prog finish */
36 #define IP 244 /* interrupt process--permanently */
37 #define BREAK 243 /* break */
38 #define DM 242 /* data mark--for connect. cleaning */
39 #define NOP 241 /* nop */
40 #define EOR 239 /* end of record (transparent mode) */
41 #define ABORT 238 /* Abort process */
42 #define SUSP 237 /* Suspend process */
43 #define xEOF 236 /* End of file: EOF is already used... */
45 #define TELOPT_BINARY 0 /* 8-bit data path */
46 #define TELOPT_ECHO 1 /* echo */
47 #define TELOPT_RCP 2 /* prepare to reconnect */
48 #define TELOPT_SGA 3 /* suppress go ahead */
49 #define TELOPT_NAMS 4 /* approximate message size */
50 #define TELOPT_STATUS 5 /* give status */
51 #define TELOPT_TM 6 /* timing mark */
52 #define TELOPT_RCTE 7 /* remote controlled transmission and echo */
53 #define TELOPT_NAOL 8 /* negotiate about output line width */
54 #define TELOPT_NAOP 9 /* negotiate about output page size */
55 #define TELOPT_NAOCRD 10 /* negotiate about CR disposition */
56 #define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */
57 #define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */
58 #define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */
59 #define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */
60 #define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */
61 #define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */
62 #define TELOPT_XASCII 17 /* extended ascic character set */
63 #define TELOPT_LOGOUT 18 /* force logout */
64 #define TELOPT_BM 19 /* byte macro */
65 #define TELOPT_DET 20 /* data entry terminal */
66 #define TELOPT_SUPDUP 21 /* supdup protocol */
67 #define TELOPT_SUPDUPOUTPUT 22 /* supdup output */
68 #define TELOPT_SNDLOC 23 /* send location */
69 #define TELOPT_TTYPE 24 /* terminal type */
70 #define TELOPT_EOR 25 /* end or record */
71 #define TELOPT_TUID 26 /* TACACS user identification */
72 #define TELOPT_OUTMRK 27 /* output marking */
73 #define TELOPT_TTYLOC 28 /* terminal location number */
74 #define TELOPT_3270REGIME 29 /* 3270 regime */
75 #define TELOPT_X3PAD 30 /* X.3 PAD */
76 #define TELOPT_NAWS 31 /* window size */
77 #define TELOPT_TSPEED 32 /* terminal speed */
78 #define TELOPT_LFLOW 33 /* remote flow control */
79 #define TELOPT_LINEMODE 34 /* Linemode option */
80 #define TELOPT_XDISPLOC 35 /* X Display Location */
81 #define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables */
82 #define TELOPT_AUTHENTICATION 37/* Authenticate */
83 #define TELOPT_ENCRYPT 38 /* Encryption option */
84 #define TELOPT_NEW_ENVIRON 39 /* New - Environment variables */
85 #define TELOPT_EXOPL 255 /* extended-options-list */
87 #define TELQUAL_IS 0 /* option is... */
88 #define TELQUAL_SEND 1 /* send option */
89 #define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */
99 #define iswritable(x) ( (x) != IAC && (x) != CR )
101 static char *telopt(int opt
) {
102 #define i(x) if (opt == TELOPT_ ## x) return #x;
103 i(BINARY
); i(ECHO
); i(RCP
); i(SGA
); i(NAMS
); i(STATUS
); i(TM
); i(RCTE
);
104 i(NAOL
); i(NAOP
); i(NAOCRD
); i(NAOHTS
); i(NAOHTD
); i(NAOFFD
); i(NAOVTS
);
105 i(NAOVTD
); i(NAOLFD
); i(XASCII
); i(LOGOUT
); i(BM
); i(DET
); i(SUPDUP
);
106 i(SUPDUPOUTPUT
); i(SNDLOC
); i(TTYPE
); i(EOR
); i(TUID
); i(OUTMRK
);
107 i(TTYLOC
); i(X3PAD
); i(NAWS
); i(TSPEED
); i(LFLOW
); i(LINEMODE
);
108 i(XDISPLOC
); i(OLD_ENVIRON
); i(AUTHENTICATION
); i(ENCRYPT
);
109 i(NEW_ENVIRON
); i(EXOPL
);
114 static void telnet_size(void);
117 int send
; /* what we initially send */
118 int nsend
; /* -ve send if requested to stop it */
119 int ack
, nak
; /* +ve and -ve acknowledgements */
120 int option
; /* the option code */
122 REQUESTED
, ACTIVE
, INACTIVE
, REALLY_INACTIVE
126 static struct Opt o_naws
= {WILL
, WONT
, DO
, DONT
, TELOPT_NAWS
, REQUESTED
};
127 static struct Opt o_tspeed
= {WILL
, WONT
, DO
, DONT
, TELOPT_TSPEED
, REQUESTED
};
128 static struct Opt o_ttype
= {WILL
, WONT
, DO
, DONT
, TELOPT_TTYPE
, REQUESTED
};
129 static struct Opt o_oenv
= {WILL
, WONT
, DO
, DONT
, TELOPT_OLD_ENVIRON
,
131 static struct Opt o_nenv
= {WILL
, WONT
, DO
, DONT
, TELOPT_NEW_ENVIRON
,
133 static struct Opt o_echo
= {DO
, DONT
, WILL
, WONT
, TELOPT_ECHO
, REQUESTED
};
134 static struct Opt o_we_sga
= {WILL
, WONT
, DO
, DONT
, TELOPT_SGA
, REQUESTED
};
135 static struct Opt o_they_sga
= {DO
, DONT
, WILL
, WONT
, TELOPT_SGA
, REQUESTED
};
137 static struct Opt
*opts
[] = {
138 &o_naws
, &o_tspeed
, &o_ttype
, &o_oenv
, &o_nenv
, &o_echo
,
139 &o_we_sga
, &o_they_sga
, NULL
143 static int sb_opt
, sb_len
;
144 static char *sb_buf
= NULL
;
145 static int sb_size
= 0;
146 #define SB_DELTA 1024
148 static void try_write (void) {
149 while (outbuf_head
!= outbuf_reap
) {
150 int end
= (outbuf_reap
< outbuf_head ? outbuf_head
: OUTBUF_SIZE
);
151 int len
= end
- outbuf_reap
;
154 ret
= send (s
, outbuf
+outbuf_reap
, len
, 0);
156 outbuf_reap
= (outbuf_reap
+ ret
) & OUTBUF_MASK
;
162 static void s_write (void *buf
, int len
) {
163 unsigned char *p
= buf
;
165 int new_head
= (outbuf_head
+ 1) & OUTBUF_MASK
;
166 if (new_head
!= outbuf_reap
) {
167 outbuf
[outbuf_head
] = *p
++;
168 outbuf_head
= new_head
;
174 static void log_option (char *sender
, int cmd
, int option
) {
176 sprintf(buf
, "%s:\t%s %s", sender
,
177 (cmd
== WILL ?
"WILL" : cmd
== WONT ?
"WONT" :
178 cmd
== DO ?
"DO" : cmd
== DONT ?
"DONT" : "<??>"),
183 static void send_opt (int cmd
, int option
) {
186 b
[0] = IAC
; b
[1] = cmd
; b
[2] = option
;
188 log_option("client", cmd
, option
);
191 static void deactivate_option (struct Opt
*o
) {
192 if (o
->state
== REQUESTED
|| o
->state
== ACTIVE
)
193 send_opt (o
->nsend
, o
->option
);
194 o
->state
= REALLY_INACTIVE
;
197 static void activate_option (struct Opt
*o
) {
198 if (o
->send
== WILL
&& o
->option
== TELOPT_NAWS
)
200 if (o
->send
== WILL
&&
201 (o
->option
== TELOPT_NEW_ENVIRON
||
202 o
->option
== TELOPT_OLD_ENVIRON
)) {
204 * We may only have one kind of ENVIRON going at a time.
205 * This is a hack, but who cares.
207 deactivate_option (o
->option
==TELOPT_NEW_ENVIRON ?
&o_oenv
: &o_nenv
);
209 if (o
->option
== TELOPT_ECHO
&& cfg
.ldisc_term
)
210 ldisc
= &ldisc_simple
;
213 static void refused_option (struct Opt
*o
) {
214 if (o
->send
== WILL
&& o
->option
== TELOPT_NEW_ENVIRON
&&
215 o_oenv
.state
== INACTIVE
) {
216 send_opt (WILL
, TELOPT_OLD_ENVIRON
);
217 o_oenv
.state
= REQUESTED
;
219 if (o
->option
== TELOPT_ECHO
&& cfg
.ldisc_term
)
223 static void proc_rec_opt (int cmd
, int option
) {
226 log_option ("server", cmd
, option
);
227 for (o
= opts
; *o
; o
++) {
228 if ((*o
)->option
== option
&& (*o
)->ack
== cmd
) {
229 switch ((*o
)->state
) {
231 (*o
)->state
= ACTIVE
;
232 activate_option (*o
);
237 (*o
)->state
= ACTIVE
;
238 send_opt ((*o
)->send
, option
);
239 activate_option (*o
);
241 case REALLY_INACTIVE
:
242 send_opt ((*o
)->nsend
, option
);
246 } else if ((*o
)->option
== option
&& (*o
)->nak
== cmd
) {
247 switch ((*o
)->state
) {
249 (*o
)->state
= INACTIVE
;
253 (*o
)->state
= INACTIVE
;
254 send_opt ((*o
)->nsend
, option
);
257 case REALLY_INACTIVE
:
264 * If we reach here, the option was one we weren't prepared to
265 * cope with. So send a negative ack.
267 send_opt ((cmd
== WILL ? DONT
: WONT
), option
);
270 static void process_subneg (void) {
271 unsigned char b
[2048], *p
, *q
;
277 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
278 char logbuf
[sizeof(cfg
.termspeed
)+80];
279 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TSPEED
;
281 strcpy(b
+4, cfg
.termspeed
);
282 n
= 4 + strlen(cfg
.termspeed
);
283 b
[n
] = IAC
; b
[n
+1] = SE
;
285 logevent("server:\tSB TSPEED SEND");
286 sprintf(logbuf
, "client:\tSB TSPEED IS %s", cfg
.termspeed
);
289 logevent ("server:\tSB TSPEED <something weird>");
292 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
293 char logbuf
[sizeof(cfg
.termtype
)+80];
294 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TTYPE
;
296 for (n
= 0; cfg
.termtype
[n
]; n
++)
297 b
[n
+4] = (cfg
.termtype
[n
] >= 'a' && cfg
.termtype
[n
] <= 'z' ?
298 cfg
.termtype
[n
] + 'A'-'a' : cfg
.termtype
[n
]);
299 b
[n
+4] = IAC
; b
[n
+5] = SE
;
302 logevent("server:\tSB TTYPE SEND");
303 sprintf(logbuf
, "client:\tSB TTYPE IS %s", b
+4);
306 logevent("server:\tSB TTYPE <something weird>\r\n");
308 case TELOPT_OLD_ENVIRON
:
309 case TELOPT_NEW_ENVIRON
:
312 if (p
< q
&& *p
== TELQUAL_SEND
) {
315 sprintf (logbuf
, "server:\tSB %s SEND", telopt(sb_opt
));
317 if (sb_opt
== TELOPT_OLD_ENVIRON
) {
318 if (cfg
.rfc_environ
) {
326 * Try to guess the sense of VAR and VALUE.
332 } else if (*p
== BSD_VAR
) {
340 * With NEW_ENVIRON, the sense of VAR and VALUE
346 b
[0] = IAC
; b
[1] = SB
; b
[2] = sb_opt
;
352 while (*e
&& *e
!= '\t') b
[n
++] = *e
++;
355 while (*e
) b
[n
++] = *e
++;
359 b
[n
++] = var
; b
[n
++] = 'U'; b
[n
++] = 'S';
360 b
[n
++] = 'E'; b
[n
++] = 'R'; b
[n
++] = value
;
362 while (*e
) b
[n
++] = *e
++;
364 b
[n
++] = IAC
; b
[n
++] = SE
;
366 sprintf(logbuf
, "client:\tSB %s IS %s", telopt(sb_opt
),
367 n
==6 ?
"<nothing>" : "<stuff>");
375 TOPLEVEL
, SEENIAC
, SEENWILL
, SEENWONT
, SEENDO
, SEENDONT
,
376 SEENSB
, SUBNEGOT
, SUBNEG_IAC
, SEENCR
377 } telnet_state
= TOPLEVEL
;
379 static void do_telnet_read (char *buf
, int len
) {
382 int c
= (unsigned char) *buf
++;
384 switch (telnet_state
) {
387 if (c
== NUL
&& telnet_state
== SEENCR
)
388 telnet_state
= TOPLEVEL
;
390 telnet_state
= SEENIAC
;
396 /* I can't get the F***ing winsock to insert the urgent IAC
397 * into the right position! Even with SO_OOBINLINE it gives
398 * it to recv too soon. And of course the DM byte (that
399 * arrives in the same packet!) appears several K later!!
401 * Oh well, we do get the DM in the right place so I'll
402 * just stop hiding on the next 0xf2 and hope for the best.
404 else if (c
== DM
) in_synch
= 0;
407 telnet_state
= SEENCR
;
409 telnet_state
= TOPLEVEL
;
413 if (c
== DO
) telnet_state
= SEENDO
;
414 else if (c
== DONT
) telnet_state
= SEENDONT
;
415 else if (c
== WILL
) telnet_state
= SEENWILL
;
416 else if (c
== WONT
) telnet_state
= SEENWONT
;
417 else if (c
== SB
) telnet_state
= SEENSB
;
420 telnet_state
= TOPLEVEL
;
423 /* ignore everything else; print it if it's IAC */
427 telnet_state
= TOPLEVEL
;
431 proc_rec_opt (WILL
, c
);
432 telnet_state
= TOPLEVEL
;
435 proc_rec_opt (WONT
, c
);
436 telnet_state
= TOPLEVEL
;
439 proc_rec_opt (DO
, c
);
440 telnet_state
= TOPLEVEL
;
443 proc_rec_opt (DONT
, c
);
444 telnet_state
= TOPLEVEL
;
449 telnet_state
= SUBNEGOT
;
453 telnet_state
= SUBNEG_IAC
;
456 if (sb_len
>= sb_size
) {
460 srealloc(sb_buf
, sb_size
) :
467 if (sb_len
< sb_size
)
468 sb_buf
[sb_len
++] = c
;
469 telnet_state
= SUBNEGOT
;/* in case we came here by goto */
474 goto subneg_addchar
; /* yes, it's a hack, I know, but... */
477 telnet_state
= TOPLEVEL
;
485 * Called to set up the Telnet connection. Will arrange for
486 * WM_NETEVENT messages to be passed to the specified window, whose
487 * window procedure should then call telnet_msg().
489 * Returns an error message, or NULL on success.
491 * Also places the canonical host name into `realhost'.
493 static char *telnet_init (HWND hwnd
, char *host
, int port
, char **realhost
) {
501 if ( (a
= inet_addr(host
)) == (unsigned long) INADDR_NONE
) {
502 if ( (h
= gethostbyname(host
)) == NULL
)
503 switch (WSAGetLastError()) {
504 case WSAENETDOWN
: return "Network is down";
505 case WSAHOST_NOT_FOUND
: case WSANO_DATA
:
506 return "Host does not exist";
507 case WSATRY_AGAIN
: return "Host not found";
508 default: return "gethostbyname: unknown error";
510 memcpy (&a
, h
->h_addr
, sizeof(a
));
511 *realhost
= h
->h_name
;
517 port
= 23; /* default telnet port */
522 s
= socket(AF_INET
, SOCK_STREAM
, 0);
523 if (s
== INVALID_SOCKET
)
524 switch (WSAGetLastError()) {
525 case WSAENETDOWN
: return "Network is down";
526 case WSAEAFNOSUPPORT
: return "TCP/IP support not present";
527 default: return "socket(): unknown error";
532 setsockopt (s
, SOL_SOCKET
, SO_OOBINLINE
, (void *)&b
, sizeof(b
));
536 * Bind to local address.
538 addr
.sin_family
= AF_INET
;
539 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
540 addr
.sin_port
= htons(0);
541 if (bind (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
542 switch (WSAGetLastError()) {
543 case WSAENETDOWN
: return "Network is down";
544 default: return "bind(): unknown error";
548 * Connect to remote address.
550 addr
.sin_addr
.s_addr
= htonl(a
);
551 addr
.sin_port
= htons((short)port
);
552 if (connect (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
553 switch (WSAGetLastError()) {
554 case WSAENETDOWN
: return "Network is down";
555 case WSAECONNREFUSED
: return "Connection refused";
556 case WSAENETUNREACH
: return "Network is unreachable";
557 case WSAEHOSTUNREACH
: return "No route to host";
558 default: return "connect(): unknown error";
561 if (hwnd
&& WSAAsyncSelect (s
, hwnd
, WM_NETEVENT
, FD_READ
|
562 FD_WRITE
| FD_OOB
| FD_CLOSE
) == SOCKET_ERROR
)
563 switch (WSAGetLastError()) {
564 case WSAENETDOWN
: return "Network is down";
565 default: return "WSAAsyncSelect(): unknown error";
569 * Initialise option states.
575 for (o
= opts
; *o
; o
++)
576 if ((*o
)->state
== REQUESTED
)
577 (*o
)->state
= INACTIVE
;
583 for (o
= opts
; *o
; o
++)
584 if ((*o
)->state
== REQUESTED
)
585 send_opt ((*o
)->send
, (*o
)->option
);
589 * Set up SYNCH state.
594 * We have no pre-session phase.
602 * Process a WM_NETEVENT message. Will return 0 if the connection
603 * has closed, or <0 for a socket error.
605 static int telnet_msg (WPARAM wParam
, LPARAM lParam
) {
607 /* This needs to be larger than the packet size now that inbuf
608 * cannot overflow, in fact the fewer calls we make to windows
609 * the faster we will run!
614 * Because reading less than the whole of the available pending
615 * data can generate an FD_READ event, we need to allow for the
616 * possibility that FD_READ may arrive with FD_CLOSE already in
617 * the queue; so it's possible that we can get here even with s
618 * invalid. If so, we return 1 and don't worry about it.
620 if (s
== INVALID_SOCKET
)
623 if (WSAGETSELECTERROR(lParam
) != 0) {
626 return -WSAGETSELECTERROR(lParam
);
629 switch (WSAGETSELECTEVENT(lParam
)) {
633 int clear_of_oob
= 1;
635 /* Don't check for error return; some shims don't support
638 ioctlsocket (s
, SIOCATMARK
, &clear_of_oob
);
640 in_synch
= !clear_of_oob
;
643 ret
= recv(s
, buf
, sizeof(buf
), 0);
644 if (ret
< 0 && WSAGetLastError() == WSAEWOULDBLOCK
)
646 if (ret
< 0) { /* any _other_ error */
649 return -10000-WSAGetLastError();
655 do_telnet_read (buf
, ret
);
660 if (outbuf_head
!= outbuf_reap
)
664 return 1; /* shouldn't happen, but WTF */
668 * Called to send data down the Telnet connection.
670 static void telnet_send (char *buf
, int len
) {
672 static unsigned char iac
[2] = { IAC
, IAC
};
673 static unsigned char cr
[2] = { CR
, NUL
};
674 static unsigned char nl
[2] = { CR
, LF
};
676 if (s
== INVALID_SOCKET
)
680 while (p
< buf
+len
) {
683 while (iswritable((unsigned char)*p
) && p
< buf
+len
) p
++;
686 while (p
< buf
+len
&& !iswritable((unsigned char)*p
)) {
687 s_write ((unsigned char)*p
== IAC ? iac
: nl
, 2);
694 * Called to set the size of the window from Telnet's POV.
696 static void telnet_size(void) {
700 if (s
== INVALID_SOCKET
|| o_naws
.state
!= ACTIVE
)
702 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_NAWS
;
703 b
[3] = cols
>> 8; b
[4] = cols
& 0xFF;
704 b
[5] = rows
>> 8; b
[6] = rows
& 0xFF;
705 b
[7] = IAC
; b
[8] = SE
;
707 sprintf(logbuf
, "client:\tSB NAWS %d,%d",
708 ((unsigned char)b
[3] << 8) + (unsigned char)b
[4],
709 ((unsigned char)b
[5] << 8) + (unsigned char)b
[6]);
714 * Send Telnet special codes.
716 static void telnet_special (Telnet_Special code
) {
719 if (s
== INVALID_SOCKET
)
724 case TS_AYT
: b
[1] = AYT
; s_write (b
, 2); break;
725 case TS_BRK
: b
[1] = BREAK
; s_write (b
, 2); break;
726 case TS_EC
: b
[1] = EC
; s_write (b
, 2); break;
727 case TS_EL
: b
[1] = EL
; s_write (b
, 2); break;
728 case TS_GA
: b
[1] = GA
; s_write (b
, 2); break;
729 case TS_NOP
: b
[1] = NOP
; s_write (b
, 2); break;
730 case TS_ABORT
: b
[1] = ABORT
; s_write (b
, 2); break;
731 case TS_AO
: b
[1] = AO
; s_write (b
, 2); break;
732 case TS_IP
: b
[1] = IP
; s_write (b
, 2); break;
733 case TS_SUSP
: b
[1] = SUSP
; s_write (b
, 2); break;
734 case TS_EOR
: b
[1] = EOR
; s_write (b
, 2); break;
735 case TS_EOF
: b
[1] = xEOF
; s_write (b
, 2); break;
737 outbuf_head
= outbuf_reap
= 0;
739 send (s
, b
, 2, MSG_OOB
);
742 if (o_echo
.state
== INACTIVE
|| o_echo
.state
== REALLY_INACTIVE
) {
743 o_echo
.state
= REQUESTED
;
744 send_opt (o_echo
.send
, o_echo
.option
);
748 if (o_echo
.state
== ACTIVE
) {
749 o_echo
.state
= REQUESTED
;
750 send_opt (o_echo
.nsend
, o_echo
.option
);
754 if (o_they_sga
.state
== ACTIVE
) {
762 static SOCKET
telnet_socket(void) { return s
; }
764 static int telnet_sendok(void) { return 1; }
766 Backend telnet_backend
= {