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
140 static int sb_opt
, sb_len
;
141 static char *sb_buf
= NULL
;
142 static int sb_size
= 0;
143 #define SB_DELTA 1024
145 static void try_write (void) {
146 while (outbuf_head
!= outbuf_reap
) {
147 int end
= (outbuf_reap
< outbuf_head ? outbuf_head
: OUTBUF_SIZE
);
148 int len
= end
- outbuf_reap
;
151 ret
= send (s
, outbuf
+outbuf_reap
, len
, 0);
153 outbuf_reap
= (outbuf_reap
+ ret
) & OUTBUF_MASK
;
159 static void s_write (void *buf
, int len
) {
160 unsigned char *p
= buf
;
162 int new_head
= (outbuf_head
+ 1) & OUTBUF_MASK
;
163 if (new_head
!= outbuf_reap
) {
164 outbuf
[outbuf_head
] = *p
++;
165 outbuf_head
= new_head
;
171 static void c_write (char *buf
, int len
) {
173 int new_head
= (inbuf_head
+ 1) & INBUF_MASK
;
174 int c
= (unsigned char) *buf
;
175 if (new_head
!= inbuf_reap
) {
176 inbuf
[inbuf_head
] = *buf
++;
177 inbuf_head
= new_head
;
182 static void log_option (char *sender
, int cmd
, int option
) {
184 sprintf(buf
, "%s:\t%s %s", sender
,
185 (cmd
== WILL ?
"WILL" : cmd
== WONT ?
"WONT" :
186 cmd
== DO ?
"DO" : cmd
== DONT ?
"DONT" : "<??>"),
191 static void send_opt (int cmd
, int option
) {
194 b
[0] = IAC
; b
[1] = cmd
; b
[2] = option
;
196 log_option("client", cmd
, option
);
199 static void deactivate_option (struct Opt
*o
) {
200 if (o
->state
== REQUESTED
|| o
->state
== ACTIVE
)
201 send_opt (o
->nsend
, o
->option
);
202 o
->state
= REALLY_INACTIVE
;
205 static void activate_option (struct Opt
*o
) {
206 if (o
->send
== WILL
&& o
->option
== TELOPT_NAWS
)
208 if (o
->send
== WILL
&&
209 (o
->option
== TELOPT_NEW_ENVIRON
||
210 o
->option
== TELOPT_OLD_ENVIRON
)) {
212 * We may only have one kind of ENVIRON going at a time.
213 * This is a hack, but who cares.
215 deactivate_option (o
->option
==TELOPT_NEW_ENVIRON ?
&o_oenv
: &o_nenv
);
219 static void refused_option (struct Opt
*o
) {
220 if (o
->send
== WILL
&& o
->option
== TELOPT_NEW_ENVIRON
&&
221 o_oenv
.state
== INACTIVE
) {
222 send_opt (WILL
, TELOPT_OLD_ENVIRON
);
223 o_oenv
.state
= REQUESTED
;
227 static void proc_rec_opt (int cmd
, int option
) {
230 log_option ("server", cmd
, option
);
231 for (o
= opts
; *o
; o
++) {
232 if ((*o
)->option
== option
&& (*o
)->ack
== cmd
) {
233 switch ((*o
)->state
) {
235 (*o
)->state
= ACTIVE
;
236 activate_option (*o
);
241 (*o
)->state
= ACTIVE
;
242 send_opt ((*o
)->send
, option
);
243 activate_option (*o
);
245 case REALLY_INACTIVE
:
246 send_opt ((*o
)->nsend
, option
);
250 } else if ((*o
)->option
== option
&& (*o
)->nak
== cmd
) {
251 switch ((*o
)->state
) {
253 (*o
)->state
= INACTIVE
;
257 (*o
)->state
= INACTIVE
;
258 send_opt ((*o
)->nsend
, option
);
261 case REALLY_INACTIVE
:
268 * If we reach here, the option was one we weren't prepared to
269 * cope with. So send a negative ack.
271 send_opt ((cmd
== WILL ? DONT
: WONT
), option
);
274 static void process_subneg (void) {
275 unsigned char b
[2048], *p
, *q
;
281 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
282 char logbuf
[sizeof(cfg
.termspeed
)+80];
283 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TSPEED
;
285 strcpy(b
+4, cfg
.termspeed
);
286 n
= 4 + strlen(cfg
.termspeed
);
287 b
[n
] = IAC
; b
[n
+1] = SE
;
289 lognegot("server:\tSB TSPEED SEND");
290 sprintf(logbuf
, "client:\tSB TSPEED IS %s", cfg
.termspeed
);
293 lognegot ("server:\tSB TSPEED <something weird>");
296 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
297 char logbuf
[sizeof(cfg
.termtype
)+80];
298 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TTYPE
;
300 for (n
= 0; cfg
.termtype
[n
]; n
++)
301 b
[n
+4] = (cfg
.termtype
[n
] >= 'a' && cfg
.termtype
[n
] <= 'z' ?
302 cfg
.termtype
[n
] + 'A'-'a' : cfg
.termtype
[n
]);
303 b
[n
+4] = IAC
; b
[n
+5] = SE
;
306 lognegot("server:\tSB TTYPE SEND");
307 sprintf(logbuf
, "client:\tSB TTYPE IS %s", b
+4);
310 lognegot("server:\tSB TTYPE <something weird>\r\n");
312 case TELOPT_OLD_ENVIRON
:
313 case TELOPT_NEW_ENVIRON
:
316 if (p
< q
&& *p
== TELQUAL_SEND
) {
319 sprintf (logbuf
, "server:\tSB %s SEND", telopt(sb_opt
));
321 if (sb_opt
== TELOPT_OLD_ENVIRON
) {
322 if (cfg
.rfc_environ
) {
330 * Try to guess the sense of VAR and VALUE.
336 } else if (*p
== BSD_VAR
) {
344 * With NEW_ENVIRON, the sense of VAR and VALUE
350 b
[0] = IAC
; b
[1] = SB
; b
[2] = sb_opt
;
356 while (*e
&& *e
!= '\t') b
[n
++] = *e
++;
359 while (*e
) b
[n
++] = *e
++;
363 b
[n
++] = var
; b
[n
++] = 'U'; b
[n
++] = 'S';
364 b
[n
++] = 'E'; b
[n
++] = 'R'; b
[n
++] = value
;
366 while (*e
) b
[n
++] = *e
++;
368 b
[n
++] = IAC
; b
[n
++] = SE
;
370 sprintf(logbuf
, "client:\tSB %s IS %s", telopt(sb_opt
),
371 n
==6 ?
"<nothing>" : "<stuff>");
379 TOPLEVEL
, SEENIAC
, SEENWILL
, SEENWONT
, SEENDO
, SEENDONT
,
380 SEENSB
, SUBNEGOT
, SUBNEG_IAC
, SEENCR
381 } telnet_state
= TOPLEVEL
;
383 static void do_telnet_read (char *buf
, int len
) {
387 int c
= (unsigned char) *buf
++;
389 switch (telnet_state
) {
392 if (c
== NUL
&& telnet_state
== SEENCR
)
393 telnet_state
= TOPLEVEL
;
395 telnet_state
= SEENIAC
;
403 telnet_state
= SEENCR
;
405 telnet_state
= TOPLEVEL
;
409 if (c
== DO
) telnet_state
= SEENDO
;
410 else if (c
== DONT
) telnet_state
= SEENDONT
;
411 else if (c
== WILL
) telnet_state
= SEENWILL
;
412 else if (c
== WONT
) telnet_state
= SEENWONT
;
413 else if (c
== SB
) telnet_state
= SEENSB
;
414 else telnet_state
= TOPLEVEL
;/* ignore _everything_ else! */
417 proc_rec_opt (WILL
, c
);
418 telnet_state
= TOPLEVEL
;
421 proc_rec_opt (WONT
, c
);
422 telnet_state
= TOPLEVEL
;
425 proc_rec_opt (DO
, c
);
426 telnet_state
= TOPLEVEL
;
429 proc_rec_opt (DONT
, c
);
430 telnet_state
= TOPLEVEL
;
435 telnet_state
= SUBNEGOT
;
439 telnet_state
= SUBNEG_IAC
;
442 if (sb_len
>= sb_size
) {
446 realloc(sb_buf
, sb_size
) :
453 if (sb_len
< sb_size
)
454 sb_buf
[sb_len
++] = c
;
455 telnet_state
= SUBNEGOT
;/* in case we came here by goto */
460 goto subneg_addchar
; /* yes, it's a hack, I know, but... */
463 telnet_state
= TOPLEVEL
;
471 * Called to set up the Telnet connection. Will arrange for
472 * WM_NETEVENT messages to be passed to the specified window, whose
473 * window procedure should then call telnet_msg().
475 * Returns an error message, or NULL on success.
477 * Also places the canonical host name into `realhost'.
479 static char *telnet_init (HWND hwnd
, char *host
, int port
, char **realhost
) {
487 if ( (a
= inet_addr(host
)) == (unsigned long) INADDR_NONE
) {
488 if ( (h
= gethostbyname(host
)) == NULL
)
489 switch (WSAGetLastError()) {
490 case WSAENETDOWN
: return "Network is down";
491 case WSAHOST_NOT_FOUND
: case WSANO_DATA
:
492 return "Host does not exist";
493 case WSATRY_AGAIN
: return "Host not found";
494 default: return "gethostbyname: unknown error";
496 memcpy (&a
, h
->h_addr
, sizeof(a
));
497 *realhost
= h
->h_name
;
503 port
= 23; /* default telnet port */
508 s
= socket(AF_INET
, SOCK_STREAM
, 0);
509 if (s
== INVALID_SOCKET
)
510 switch (WSAGetLastError()) {
511 case WSAENETDOWN
: return "Network is down";
512 case WSAEAFNOSUPPORT
: return "TCP/IP support not present";
513 default: return "socket(): unknown error";
519 setsockopt (s
, SOL_SOCKET
, SO_OOBINLINE
, (void *)&b
, sizeof(b
));
524 * Bind to local address.
526 addr
.sin_family
= AF_INET
;
527 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
528 addr
.sin_port
= htons(0);
529 if (bind (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
530 switch (WSAGetLastError()) {
531 case WSAENETDOWN
: return "Network is down";
532 default: return "bind(): unknown error";
536 * Connect to remote address.
538 addr
.sin_addr
.s_addr
= htonl(a
);
539 addr
.sin_port
= htons((short)port
);
540 if (connect (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
541 switch (WSAGetLastError()) {
542 case WSAENETDOWN
: return "Network is down";
543 case WSAECONNREFUSED
: return "Connection refused";
544 case WSAENETUNREACH
: return "Network is unreachable";
545 case WSAEHOSTUNREACH
: return "No route to host";
546 default: return "connect(): unknown error";
549 if (WSAAsyncSelect (s
, hwnd
, WM_NETEVENT
, FD_READ
|
550 FD_WRITE
| FD_OOB
| FD_CLOSE
) == SOCKET_ERROR
)
551 switch (WSAGetLastError()) {
552 case WSAENETDOWN
: return "Network is down";
553 default: return "WSAAsyncSelect(): unknown error";
557 * Initialise option states.
562 for (o
= opts
; *o
; o
++)
563 if ((*o
)->state
== REQUESTED
)
564 send_opt ((*o
)->send
, (*o
)->option
);
569 * Set up SYNCH state.
578 * Process a WM_NETEVENT message. Will return 0 if the connection
579 * has closed, or <0 for a socket error.
581 static int telnet_msg (WPARAM wParam
, LPARAM lParam
) {
585 if (s
== INVALID_SOCKET
) /* how the hell did we get here?! */
588 if (WSAGETSELECTERROR(lParam
) != 0)
589 return -WSAGETSELECTERROR(lParam
);
591 switch (WSAGETSELECTEVENT(lParam
)) {
593 ret
= recv(s
, buf
, sizeof(buf
), 0);
594 if (ret
< 0 && WSAGetLastError() == WSAEWOULDBLOCK
)
596 if (ret
< 0) /* any _other_ error */
597 return -10000-WSAGetLastError();
600 return 0; /* can't happen, in theory */
605 if (ioctlsocket (s
, SIOCATMARK
, &i
) < 0) {
606 return -20000-WSAGetLastError();
612 do_telnet_read (buf
, ret
);
616 ret
= recv(s
, buf
, sizeof(buf
), 0);
618 telnet_state
= TOPLEVEL
;
620 ret
= recv(s
, buf
, 1, MSG_OOB
);
622 do_telnet_read (buf
, ret
);
624 if (ret
< 0 && WSAGetLastError() != WSAEWOULDBLOCK
)
625 return -30000-WSAGetLastError();
628 if (outbuf_head
!= outbuf_reap
)
635 return 1; /* shouldn't happen, but WTF */
639 * Called to send data down the Telnet connection.
641 static void telnet_send (char *buf
, int len
) {
643 static unsigned char iac
[2] = { IAC
, IAC
};
644 static unsigned char cr
[2] = { CR
, NUL
};
646 if (s
== INVALID_SOCKET
)
650 while (p
< buf
+len
) {
653 while (iswritable((unsigned char)*p
) && p
< buf
+len
) p
++;
656 while (p
< buf
+len
&& !iswritable((unsigned char)*p
)) {
657 s_write ((unsigned char)*p
== IAC ? iac
: cr
, 2);
664 * Called to set the size of the window from Telnet's POV.
666 static void telnet_size(void) {
670 if (s
== INVALID_SOCKET
|| o_naws
.state
!= ACTIVE
)
672 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_NAWS
;
673 b
[3] = cols
>> 8; b
[4] = cols
& 0xFF;
674 b
[5] = rows
>> 8; b
[6] = rows
& 0xFF;
675 b
[7] = IAC
; b
[8] = SE
;
677 sprintf(logbuf
, "client:\tSB NAWS %d,%d",
678 ((unsigned char)b
[3] << 8) + (unsigned char)b
[4],
679 ((unsigned char)b
[5] << 8) + (unsigned char)b
[6]);
684 * Send Telnet special codes.
686 static void telnet_special (Telnet_Special code
) {
689 if (s
== INVALID_SOCKET
)
694 case TS_AYT
: b
[1] = AYT
; s_write (b
, 2); break;
695 case TS_BRK
: b
[1] = BREAK
; s_write (b
, 2); break;
696 case TS_EC
: b
[1] = EC
; s_write (b
, 2); break;
697 case TS_EL
: b
[1] = EL
; s_write (b
, 2); break;
698 case TS_GA
: b
[1] = GA
; s_write (b
, 2); break;
699 case TS_NOP
: b
[1] = NOP
; s_write (b
, 2); break;
700 case TS_ABORT
: b
[1] = ABORT
; s_write (b
, 2); break;
701 case TS_AO
: b
[1] = AO
; s_write (b
, 2); break;
702 case TS_IP
: b
[1] = IP
; s_write (b
, 2); break;
703 case TS_SUSP
: b
[1] = SUSP
; s_write (b
, 2); break;
704 case TS_EOR
: b
[1] = EOR
; s_write (b
, 2); break;
705 case TS_EOF
: b
[1] = xEOF
; s_write (b
, 2); break;
707 outbuf_head
= outbuf_reap
= 0;
709 send (s
, b
, 1, MSG_OOB
);
714 Backend telnet_backend
= {