15 static SOCKET s
= INVALID_SOCKET
;
17 #define IAC 255 /* interpret as command: */
18 #define DONT 254 /* you are not to use option */
19 #define DO 253 /* please, you use option */
20 #define WONT 252 /* I won't use option */
21 #define WILL 251 /* I will use option */
22 #define SB 250 /* interpret as subnegotiation */
23 #define SE 240 /* end sub negotiation */
25 #define GA 249 /* you may reverse the line */
26 #define EL 248 /* erase the current line */
27 #define EC 247 /* erase the current character */
28 #define AYT 246 /* are you there */
29 #define AO 245 /* abort output--but let prog finish */
30 #define IP 244 /* interrupt process--permanently */
31 #define BREAK 243 /* break */
32 #define DM 242 /* data mark--for connect. cleaning */
33 #define NOP 241 /* nop */
34 #define EOR 239 /* end of record (transparent mode) */
35 #define ABORT 238 /* Abort process */
36 #define SUSP 237 /* Suspend process */
37 #define xEOF 236 /* End of file: EOF is already used... */
39 #define TELOPT_BINARY 0 /* 8-bit data path */
40 #define TELOPT_ECHO 1 /* echo */
41 #define TELOPT_RCP 2 /* prepare to reconnect */
42 #define TELOPT_SGA 3 /* suppress go ahead */
43 #define TELOPT_NAMS 4 /* approximate message size */
44 #define TELOPT_STATUS 5 /* give status */
45 #define TELOPT_TM 6 /* timing mark */
46 #define TELOPT_RCTE 7 /* remote controlled transmission and echo */
47 #define TELOPT_NAOL 8 /* negotiate about output line width */
48 #define TELOPT_NAOP 9 /* negotiate about output page size */
49 #define TELOPT_NAOCRD 10 /* negotiate about CR disposition */
50 #define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */
51 #define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */
52 #define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */
53 #define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */
54 #define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */
55 #define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */
56 #define TELOPT_XASCII 17 /* extended ascic character set */
57 #define TELOPT_LOGOUT 18 /* force logout */
58 #define TELOPT_BM 19 /* byte macro */
59 #define TELOPT_DET 20 /* data entry terminal */
60 #define TELOPT_SUPDUP 21 /* supdup protocol */
61 #define TELOPT_SUPDUPOUTPUT 22 /* supdup output */
62 #define TELOPT_SNDLOC 23 /* send location */
63 #define TELOPT_TTYPE 24 /* terminal type */
64 #define TELOPT_EOR 25 /* end or record */
65 #define TELOPT_TUID 26 /* TACACS user identification */
66 #define TELOPT_OUTMRK 27 /* output marking */
67 #define TELOPT_TTYLOC 28 /* terminal location number */
68 #define TELOPT_3270REGIME 29 /* 3270 regime */
69 #define TELOPT_X3PAD 30 /* X.3 PAD */
70 #define TELOPT_NAWS 31 /* window size */
71 #define TELOPT_TSPEED 32 /* terminal speed */
72 #define TELOPT_LFLOW 33 /* remote flow control */
73 #define TELOPT_LINEMODE 34 /* Linemode option */
74 #define TELOPT_XDISPLOC 35 /* X Display Location */
75 #define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables */
76 #define TELOPT_AUTHENTICATION 37/* Authenticate */
77 #define TELOPT_ENCRYPT 38 /* Encryption option */
78 #define TELOPT_NEW_ENVIRON 39 /* New - Environment variables */
79 #define TELOPT_EXOPL 255 /* extended-options-list */
81 #define TELQUAL_IS 0 /* option is... */
82 #define TELQUAL_SEND 1 /* send option */
83 #define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */
93 #define iswritable(x) ( (x) != IAC && (x) != CR )
95 static char *telopt(int opt
) {
96 #define i(x) if (opt == TELOPT_ ## x) return #x;
97 i(BINARY
); i(ECHO
); i(RCP
); i(SGA
); i(NAMS
); i(STATUS
); i(TM
); i(RCTE
);
98 i(NAOL
); i(NAOP
); i(NAOCRD
); i(NAOHTS
); i(NAOHTD
); i(NAOFFD
); i(NAOVTS
);
99 i(NAOVTD
); i(NAOLFD
); i(XASCII
); i(LOGOUT
); i(BM
); i(DET
); i(SUPDUP
);
100 i(SUPDUPOUTPUT
); i(SNDLOC
); i(TTYPE
); i(EOR
); i(TUID
); i(OUTMRK
);
101 i(TTYLOC
); i(X3PAD
); i(NAWS
); i(TSPEED
); i(LFLOW
); i(LINEMODE
);
102 i(XDISPLOC
); i(OLD_ENVIRON
); i(AUTHENTICATION
); i(ENCRYPT
);
103 i(NEW_ENVIRON
); i(EXOPL
);
108 static void telnet_size(void);
111 int send
; /* what we initially send */
112 int nsend
; /* -ve send if requested to stop it */
113 int ack
, nak
; /* +ve and -ve acknowledgements */
114 int option
; /* the option code */
116 REQUESTED
, ACTIVE
, INACTIVE
, REALLY_INACTIVE
120 static struct Opt o_naws
= {WILL
, WONT
, DO
, DONT
, TELOPT_NAWS
, REQUESTED
};
121 static struct Opt o_tspeed
= {WILL
, WONT
, DO
, DONT
, TELOPT_TSPEED
, REQUESTED
};
122 static struct Opt o_ttype
= {WILL
, WONT
, DO
, DONT
, TELOPT_TTYPE
, REQUESTED
};
123 static struct Opt o_oenv
= {WILL
, WONT
, DO
, DONT
, TELOPT_OLD_ENVIRON
,
125 static struct Opt o_nenv
= {WILL
, WONT
, DO
, DONT
, TELOPT_NEW_ENVIRON
,
127 static struct Opt o_echo
= {DO
, DONT
, WILL
, WONT
, TELOPT_ECHO
, REQUESTED
};
128 static struct Opt o_we_sga
= {WILL
, WONT
, DO
, DONT
, TELOPT_SGA
, REQUESTED
};
129 static struct Opt o_they_sga
= {DO
, DONT
, WILL
, WONT
, TELOPT_SGA
, REQUESTED
};
131 static struct Opt
*opts
[] = {
132 &o_naws
, &o_tspeed
, &o_ttype
, &o_oenv
, &o_nenv
, &o_echo
,
133 &o_we_sga
, &o_they_sga
, NULL
137 static int sb_opt
, sb_len
;
138 static char *sb_buf
= NULL
;
139 static int sb_size
= 0;
140 #define SB_DELTA 1024
142 static void try_write (void) {
143 while (outbuf_head
!= outbuf_reap
) {
144 int end
= (outbuf_reap
< outbuf_head ? outbuf_head
: OUTBUF_SIZE
);
145 int len
= end
- outbuf_reap
;
148 ret
= send (s
, outbuf
+outbuf_reap
, len
, 0);
150 outbuf_reap
= (outbuf_reap
+ ret
) & OUTBUF_MASK
;
156 static void s_write (void *buf
, int len
) {
157 unsigned char *p
= buf
;
159 int new_head
= (outbuf_head
+ 1) & OUTBUF_MASK
;
160 if (new_head
!= outbuf_reap
) {
161 outbuf
[outbuf_head
] = *p
++;
162 outbuf_head
= new_head
;
168 static void log_option (char *sender
, int cmd
, int option
) {
170 sprintf(buf
, "%s:\t%s %s", sender
,
171 (cmd
== WILL ?
"WILL" : cmd
== WONT ?
"WONT" :
172 cmd
== DO ?
"DO" : cmd
== DONT ?
"DONT" : "<??>"),
177 static void send_opt (int cmd
, int option
) {
180 b
[0] = IAC
; b
[1] = cmd
; b
[2] = option
;
182 log_option("client", cmd
, option
);
185 static void deactivate_option (struct Opt
*o
) {
186 if (o
->state
== REQUESTED
|| o
->state
== ACTIVE
)
187 send_opt (o
->nsend
, o
->option
);
188 o
->state
= REALLY_INACTIVE
;
191 static void activate_option (struct Opt
*o
) {
192 if (o
->send
== WILL
&& o
->option
== TELOPT_NAWS
)
194 if (o
->send
== WILL
&&
195 (o
->option
== TELOPT_NEW_ENVIRON
||
196 o
->option
== TELOPT_OLD_ENVIRON
)) {
198 * We may only have one kind of ENVIRON going at a time.
199 * This is a hack, but who cares.
201 deactivate_option (o
->option
==TELOPT_NEW_ENVIRON ?
&o_oenv
: &o_nenv
);
203 if (o
->option
== TELOPT_ECHO
&& cfg
.ldisc_term
)
204 ldisc
= &ldisc_simple
;
207 static void refused_option (struct Opt
*o
) {
208 if (o
->send
== WILL
&& o
->option
== TELOPT_NEW_ENVIRON
&&
209 o_oenv
.state
== INACTIVE
) {
210 send_opt (WILL
, TELOPT_OLD_ENVIRON
);
211 o_oenv
.state
= REQUESTED
;
213 if (o
->option
== TELOPT_ECHO
&& cfg
.ldisc_term
)
217 static void proc_rec_opt (int cmd
, int option
) {
220 log_option ("server", cmd
, option
);
221 for (o
= opts
; *o
; o
++) {
222 if ((*o
)->option
== option
&& (*o
)->ack
== cmd
) {
223 switch ((*o
)->state
) {
225 (*o
)->state
= ACTIVE
;
226 activate_option (*o
);
231 (*o
)->state
= ACTIVE
;
232 send_opt ((*o
)->send
, option
);
233 activate_option (*o
);
235 case REALLY_INACTIVE
:
236 send_opt ((*o
)->nsend
, option
);
240 } else if ((*o
)->option
== option
&& (*o
)->nak
== cmd
) {
241 switch ((*o
)->state
) {
243 (*o
)->state
= INACTIVE
;
247 (*o
)->state
= INACTIVE
;
248 send_opt ((*o
)->nsend
, option
);
251 case REALLY_INACTIVE
:
258 * If we reach here, the option was one we weren't prepared to
259 * cope with. So send a negative ack.
261 send_opt ((cmd
== WILL ? DONT
: WONT
), option
);
264 static void process_subneg (void) {
265 unsigned char b
[2048], *p
, *q
;
271 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
272 char logbuf
[sizeof(cfg
.termspeed
)+80];
273 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TSPEED
;
275 strcpy(b
+4, cfg
.termspeed
);
276 n
= 4 + strlen(cfg
.termspeed
);
277 b
[n
] = IAC
; b
[n
+1] = SE
;
279 logevent("server:\tSB TSPEED SEND");
280 sprintf(logbuf
, "client:\tSB TSPEED IS %s", cfg
.termspeed
);
283 logevent ("server:\tSB TSPEED <something weird>");
286 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
287 char logbuf
[sizeof(cfg
.termtype
)+80];
288 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TTYPE
;
290 for (n
= 0; cfg
.termtype
[n
]; n
++)
291 b
[n
+4] = (cfg
.termtype
[n
] >= 'a' && cfg
.termtype
[n
] <= 'z' ?
292 cfg
.termtype
[n
] + 'A'-'a' : cfg
.termtype
[n
]);
293 b
[n
+4] = IAC
; b
[n
+5] = SE
;
296 logevent("server:\tSB TTYPE SEND");
297 sprintf(logbuf
, "client:\tSB TTYPE IS %s", b
+4);
300 logevent("server:\tSB TTYPE <something weird>\r\n");
302 case TELOPT_OLD_ENVIRON
:
303 case TELOPT_NEW_ENVIRON
:
306 if (p
< q
&& *p
== TELQUAL_SEND
) {
309 sprintf (logbuf
, "server:\tSB %s SEND", telopt(sb_opt
));
311 if (sb_opt
== TELOPT_OLD_ENVIRON
) {
312 if (cfg
.rfc_environ
) {
320 * Try to guess the sense of VAR and VALUE.
326 } else if (*p
== BSD_VAR
) {
334 * With NEW_ENVIRON, the sense of VAR and VALUE
340 b
[0] = IAC
; b
[1] = SB
; b
[2] = sb_opt
;
346 while (*e
&& *e
!= '\t') b
[n
++] = *e
++;
349 while (*e
) b
[n
++] = *e
++;
353 b
[n
++] = var
; b
[n
++] = 'U'; b
[n
++] = 'S';
354 b
[n
++] = 'E'; b
[n
++] = 'R'; b
[n
++] = value
;
356 while (*e
) b
[n
++] = *e
++;
358 b
[n
++] = IAC
; b
[n
++] = SE
;
360 sprintf(logbuf
, "client:\tSB %s IS %s", telopt(sb_opt
),
361 n
==6 ?
"<nothing>" : "<stuff>");
369 TOPLEVEL
, SEENIAC
, SEENWILL
, SEENWONT
, SEENDO
, SEENDONT
,
370 SEENSB
, SUBNEGOT
, SUBNEG_IAC
, SEENCR
371 } telnet_state
= TOPLEVEL
;
373 static void do_telnet_read (char *buf
, int len
) {
376 int c
= (unsigned char) *buf
++;
378 switch (telnet_state
) {
381 if (c
== NUL
&& telnet_state
== SEENCR
)
382 telnet_state
= TOPLEVEL
;
384 telnet_state
= SEENIAC
;
390 /* I can't get the F***ing winsock to insert the urgent IAC
391 * into the right position! Even with SO_OOBINLINE it gives
392 * it to recv too soon. And of course the DM byte (that
393 * arrives in the same packet!) appears several K later!!
395 * Oh well, we do get the DM in the right place so I'll
396 * just stop hiding on the next 0xf2 and hope for the best.
398 else if (c
== DM
) in_synch
= 0;
401 telnet_state
= SEENCR
;
403 telnet_state
= TOPLEVEL
;
407 if (c
== DO
) telnet_state
= SEENDO
;
408 else if (c
== DONT
) telnet_state
= SEENDONT
;
409 else if (c
== WILL
) telnet_state
= SEENWILL
;
410 else if (c
== WONT
) telnet_state
= SEENWONT
;
411 else if (c
== SB
) telnet_state
= SEENSB
;
414 telnet_state
= TOPLEVEL
;
417 /* ignore everything else; print it if it's IAC */
421 telnet_state
= TOPLEVEL
;
425 proc_rec_opt (WILL
, c
);
426 telnet_state
= TOPLEVEL
;
429 proc_rec_opt (WONT
, c
);
430 telnet_state
= TOPLEVEL
;
433 proc_rec_opt (DO
, c
);
434 telnet_state
= TOPLEVEL
;
437 proc_rec_opt (DONT
, c
);
438 telnet_state
= TOPLEVEL
;
443 telnet_state
= SUBNEGOT
;
447 telnet_state
= SUBNEG_IAC
;
450 if (sb_len
>= sb_size
) {
454 srealloc(sb_buf
, sb_size
) :
461 if (sb_len
< sb_size
)
462 sb_buf
[sb_len
++] = c
;
463 telnet_state
= SUBNEGOT
;/* in case we came here by goto */
468 goto subneg_addchar
; /* yes, it's a hack, I know, but... */
471 telnet_state
= TOPLEVEL
;
479 * Called to set up the Telnet connection. Will arrange for
480 * WM_NETEVENT messages to be passed to the specified window, whose
481 * window procedure should then call telnet_msg().
483 * Returns an error message, or NULL on success.
485 * Also places the canonical host name into `realhost'.
487 static char *telnet_init (HWND hwnd
, char *host
, int port
, char **realhost
) {
495 if ( (a
= inet_addr(host
)) == (unsigned long) INADDR_NONE
) {
496 if ( (h
= gethostbyname(host
)) == NULL
)
497 switch (WSAGetLastError()) {
498 case WSAENETDOWN
: return "Network is down";
499 case WSAHOST_NOT_FOUND
: case WSANO_DATA
:
500 return "Host does not exist";
501 case WSATRY_AGAIN
: return "Host not found";
502 default: return "gethostbyname: unknown error";
504 memcpy (&a
, h
->h_addr
, sizeof(a
));
505 *realhost
= h
->h_name
;
511 port
= 23; /* default telnet port */
516 s
= socket(AF_INET
, SOCK_STREAM
, 0);
517 if (s
== INVALID_SOCKET
)
518 switch (WSAGetLastError()) {
519 case WSAENETDOWN
: return "Network is down";
520 case WSAEAFNOSUPPORT
: return "TCP/IP support not present";
521 default: return "socket(): unknown error";
526 setsockopt (s
, SOL_SOCKET
, SO_OOBINLINE
, (void *)&b
, sizeof(b
));
530 * Bind to local address.
532 addr
.sin_family
= AF_INET
;
533 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
534 addr
.sin_port
= htons(0);
535 if (bind (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
536 switch (WSAGetLastError()) {
537 case WSAENETDOWN
: return "Network is down";
538 default: return "bind(): unknown error";
542 * Connect to remote address.
544 addr
.sin_addr
.s_addr
= htonl(a
);
545 addr
.sin_port
= htons((short)port
);
546 if (connect (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
547 switch (WSAGetLastError()) {
548 case WSAENETDOWN
: return "Network is down";
549 case WSAECONNREFUSED
: return "Connection refused";
550 case WSAENETUNREACH
: return "Network is unreachable";
551 case WSAEHOSTUNREACH
: return "No route to host";
552 default: return "connect(): unknown error";
555 if (hwnd
&& WSAAsyncSelect (s
, hwnd
, WM_NETEVENT
, FD_READ
|
556 FD_WRITE
| FD_OOB
| FD_CLOSE
) == SOCKET_ERROR
)
557 switch (WSAGetLastError()) {
558 case WSAENETDOWN
: return "Network is down";
559 default: return "WSAAsyncSelect(): unknown error";
563 * Initialise option states.
569 for (o
= opts
; *o
; o
++)
570 if ((*o
)->state
== REQUESTED
)
571 (*o
)->state
= INACTIVE
;
577 for (o
= opts
; *o
; o
++)
578 if ((*o
)->state
== REQUESTED
)
579 send_opt ((*o
)->send
, (*o
)->option
);
583 * Set up SYNCH state.
588 * We have no pre-session phase.
596 * Process a WM_NETEVENT message. Will return 0 if the connection
597 * has closed, or <0 for a socket error.
599 static int telnet_msg (WPARAM wParam
, LPARAM lParam
) {
601 /* This needs to be larger than the packet size now that inbuf
602 * cannot overflow, in fact the fewer calls we make to windows
603 * the faster we will run!
608 * Because reading less than the whole of the available pending
609 * data can generate an FD_READ event, we need to allow for the
610 * possibility that FD_READ may arrive with FD_CLOSE already in
611 * the queue; so it's possible that we can get here even with s
612 * invalid. If so, we return 1 and don't worry about it.
614 if (s
== INVALID_SOCKET
)
617 if (WSAGETSELECTERROR(lParam
) != 0) {
620 return -WSAGETSELECTERROR(lParam
);
623 switch (WSAGETSELECTEVENT(lParam
)) {
627 int clear_of_oob
= 1;
628 if (ioctlsocket (s
, SIOCATMARK
, &clear_of_oob
) < 0 )
629 return -20000-WSAGetLastError();
631 in_synch
= !clear_of_oob
;
634 ret
= recv(s
, buf
, sizeof(buf
), 0);
635 if (ret
< 0 && WSAGetLastError() == WSAEWOULDBLOCK
)
637 if (ret
< 0) { /* any _other_ error */
640 return -10000-WSAGetLastError();
646 do_telnet_read (buf
, ret
);
651 if (outbuf_head
!= outbuf_reap
)
655 return 1; /* shouldn't happen, but WTF */
659 * Called to send data down the Telnet connection.
661 static void telnet_send (char *buf
, int len
) {
663 static unsigned char iac
[2] = { IAC
, IAC
};
664 static unsigned char cr
[2] = { CR
, NUL
};
665 static unsigned char nl
[2] = { CR
, LF
};
667 if (s
== INVALID_SOCKET
)
671 while (p
< buf
+len
) {
674 while (iswritable((unsigned char)*p
) && p
< buf
+len
) p
++;
677 while (p
< buf
+len
&& !iswritable((unsigned char)*p
)) {
678 s_write ((unsigned char)*p
== IAC ? iac
: nl
, 2);
685 * Called to set the size of the window from Telnet's POV.
687 static void telnet_size(void) {
691 if (s
== INVALID_SOCKET
|| o_naws
.state
!= ACTIVE
)
693 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_NAWS
;
694 b
[3] = cols
>> 8; b
[4] = cols
& 0xFF;
695 b
[5] = rows
>> 8; b
[6] = rows
& 0xFF;
696 b
[7] = IAC
; b
[8] = SE
;
698 sprintf(logbuf
, "client:\tSB NAWS %d,%d",
699 ((unsigned char)b
[3] << 8) + (unsigned char)b
[4],
700 ((unsigned char)b
[5] << 8) + (unsigned char)b
[6]);
705 * Send Telnet special codes.
707 static void telnet_special (Telnet_Special code
) {
710 if (s
== INVALID_SOCKET
)
715 case TS_AYT
: b
[1] = AYT
; s_write (b
, 2); break;
716 case TS_BRK
: b
[1] = BREAK
; s_write (b
, 2); break;
717 case TS_EC
: b
[1] = EC
; s_write (b
, 2); break;
718 case TS_EL
: b
[1] = EL
; s_write (b
, 2); break;
719 case TS_GA
: b
[1] = GA
; s_write (b
, 2); break;
720 case TS_NOP
: b
[1] = NOP
; s_write (b
, 2); break;
721 case TS_ABORT
: b
[1] = ABORT
; s_write (b
, 2); break;
722 case TS_AO
: b
[1] = AO
; s_write (b
, 2); break;
723 case TS_IP
: b
[1] = IP
; s_write (b
, 2); break;
724 case TS_SUSP
: b
[1] = SUSP
; s_write (b
, 2); break;
725 case TS_EOR
: b
[1] = EOR
; s_write (b
, 2); break;
726 case TS_EOF
: b
[1] = xEOF
; s_write (b
, 2); break;
728 outbuf_head
= outbuf_reap
= 0;
730 send (s
, b
, 2, MSG_OOB
);
733 if (o_echo
.state
== INACTIVE
|| o_echo
.state
== REALLY_INACTIVE
) {
734 o_echo
.state
= REQUESTED
;
735 send_opt (o_echo
.send
, o_echo
.option
);
739 if (o_echo
.state
== ACTIVE
) {
740 o_echo
.state
= REQUESTED
;
741 send_opt (o_echo
.nsend
, o_echo
.option
);
747 static SOCKET
telnet_socket(void) { return s
; }
749 static int telnet_sendok(void) { return 1; }
751 Backend telnet_backend
= {