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 if (new_head
!= inbuf_reap
) {
175 inbuf
[inbuf_head
] = *buf
++;
176 inbuf_head
= new_head
;
179 if( inbuf_head
== inbuf_reap
) len
++; else break;
184 static void log_option (char *sender
, int cmd
, int option
) {
186 sprintf(buf
, "%s:\t%s %s", sender
,
187 (cmd
== WILL ?
"WILL" : cmd
== WONT ?
"WONT" :
188 cmd
== DO ?
"DO" : cmd
== DONT ?
"DONT" : "<??>"),
193 static void send_opt (int cmd
, int option
) {
196 b
[0] = IAC
; b
[1] = cmd
; b
[2] = option
;
198 log_option("client", cmd
, option
);
201 static void deactivate_option (struct Opt
*o
) {
202 if (o
->state
== REQUESTED
|| o
->state
== ACTIVE
)
203 send_opt (o
->nsend
, o
->option
);
204 o
->state
= REALLY_INACTIVE
;
207 static void activate_option (struct Opt
*o
) {
208 if (o
->send
== WILL
&& o
->option
== TELOPT_NAWS
)
210 if (o
->send
== WILL
&&
211 (o
->option
== TELOPT_NEW_ENVIRON
||
212 o
->option
== TELOPT_OLD_ENVIRON
)) {
214 * We may only have one kind of ENVIRON going at a time.
215 * This is a hack, but who cares.
217 deactivate_option (o
->option
==TELOPT_NEW_ENVIRON ?
&o_oenv
: &o_nenv
);
221 static void refused_option (struct Opt
*o
) {
222 if (o
->send
== WILL
&& o
->option
== TELOPT_NEW_ENVIRON
&&
223 o_oenv
.state
== INACTIVE
) {
224 send_opt (WILL
, TELOPT_OLD_ENVIRON
);
225 o_oenv
.state
= REQUESTED
;
229 static void proc_rec_opt (int cmd
, int option
) {
232 log_option ("server", cmd
, option
);
233 for (o
= opts
; *o
; o
++) {
234 if ((*o
)->option
== option
&& (*o
)->ack
== cmd
) {
235 switch ((*o
)->state
) {
237 (*o
)->state
= ACTIVE
;
238 activate_option (*o
);
243 (*o
)->state
= ACTIVE
;
244 send_opt ((*o
)->send
, option
);
245 activate_option (*o
);
247 case REALLY_INACTIVE
:
248 send_opt ((*o
)->nsend
, option
);
252 } else if ((*o
)->option
== option
&& (*o
)->nak
== cmd
) {
253 switch ((*o
)->state
) {
255 (*o
)->state
= INACTIVE
;
259 (*o
)->state
= INACTIVE
;
260 send_opt ((*o
)->nsend
, option
);
263 case REALLY_INACTIVE
:
270 * If we reach here, the option was one we weren't prepared to
271 * cope with. So send a negative ack.
273 send_opt ((cmd
== WILL ? DONT
: WONT
), option
);
276 static void process_subneg (void) {
277 unsigned char b
[2048], *p
, *q
;
283 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
284 char logbuf
[sizeof(cfg
.termspeed
)+80];
285 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TSPEED
;
287 strcpy(b
+4, cfg
.termspeed
);
288 n
= 4 + strlen(cfg
.termspeed
);
289 b
[n
] = IAC
; b
[n
+1] = SE
;
291 logevent("server:\tSB TSPEED SEND");
292 sprintf(logbuf
, "client:\tSB TSPEED IS %s", cfg
.termspeed
);
295 logevent ("server:\tSB TSPEED <something weird>");
298 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
299 char logbuf
[sizeof(cfg
.termtype
)+80];
300 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TTYPE
;
302 for (n
= 0; cfg
.termtype
[n
]; n
++)
303 b
[n
+4] = (cfg
.termtype
[n
] >= 'a' && cfg
.termtype
[n
] <= 'z' ?
304 cfg
.termtype
[n
] + 'A'-'a' : cfg
.termtype
[n
]);
305 b
[n
+4] = IAC
; b
[n
+5] = SE
;
308 logevent("server:\tSB TTYPE SEND");
309 sprintf(logbuf
, "client:\tSB TTYPE IS %s", b
+4);
312 logevent("server:\tSB TTYPE <something weird>\r\n");
314 case TELOPT_OLD_ENVIRON
:
315 case TELOPT_NEW_ENVIRON
:
318 if (p
< q
&& *p
== TELQUAL_SEND
) {
321 sprintf (logbuf
, "server:\tSB %s SEND", telopt(sb_opt
));
323 if (sb_opt
== TELOPT_OLD_ENVIRON
) {
324 if (cfg
.rfc_environ
) {
332 * Try to guess the sense of VAR and VALUE.
338 } else if (*p
== BSD_VAR
) {
346 * With NEW_ENVIRON, the sense of VAR and VALUE
352 b
[0] = IAC
; b
[1] = SB
; b
[2] = sb_opt
;
358 while (*e
&& *e
!= '\t') b
[n
++] = *e
++;
361 while (*e
) b
[n
++] = *e
++;
365 b
[n
++] = var
; b
[n
++] = 'U'; b
[n
++] = 'S';
366 b
[n
++] = 'E'; b
[n
++] = 'R'; b
[n
++] = value
;
368 while (*e
) b
[n
++] = *e
++;
370 b
[n
++] = IAC
; b
[n
++] = SE
;
372 sprintf(logbuf
, "client:\tSB %s IS %s", telopt(sb_opt
),
373 n
==6 ?
"<nothing>" : "<stuff>");
381 TOPLEVEL
, SEENIAC
, SEENWILL
, SEENWONT
, SEENDO
, SEENDONT
,
382 SEENSB
, SUBNEGOT
, SUBNEG_IAC
, SEENCR
383 } telnet_state
= TOPLEVEL
;
385 static void do_telnet_read (char *buf
, int len
) {
389 int c
= (unsigned char) *buf
++;
391 switch (telnet_state
) {
394 if (c
== NUL
&& telnet_state
== SEENCR
)
395 telnet_state
= TOPLEVEL
;
397 telnet_state
= SEENIAC
;
405 telnet_state
= SEENCR
;
407 telnet_state
= TOPLEVEL
;
411 if (c
== DO
) telnet_state
= SEENDO
;
412 else if (c
== DONT
) telnet_state
= SEENDONT
;
413 else if (c
== WILL
) telnet_state
= SEENWILL
;
414 else if (c
== WONT
) telnet_state
= SEENWONT
;
415 else if (c
== SB
) telnet_state
= SEENSB
;
417 /* ignore everything else; print it if it's IAC */
422 telnet_state
= TOPLEVEL
;
426 proc_rec_opt (WILL
, c
);
427 telnet_state
= TOPLEVEL
;
430 proc_rec_opt (WONT
, c
);
431 telnet_state
= TOPLEVEL
;
434 proc_rec_opt (DO
, c
);
435 telnet_state
= TOPLEVEL
;
438 proc_rec_opt (DONT
, c
);
439 telnet_state
= TOPLEVEL
;
444 telnet_state
= SUBNEGOT
;
448 telnet_state
= SUBNEG_IAC
;
451 if (sb_len
>= sb_size
) {
455 realloc(sb_buf
, sb_size
) :
462 if (sb_len
< sb_size
)
463 sb_buf
[sb_len
++] = c
;
464 telnet_state
= SUBNEGOT
;/* in case we came here by goto */
469 goto subneg_addchar
; /* yes, it's a hack, I know, but... */
472 telnet_state
= TOPLEVEL
;
480 * Called to set up the Telnet connection. Will arrange for
481 * WM_NETEVENT messages to be passed to the specified window, whose
482 * window procedure should then call telnet_msg().
484 * Returns an error message, or NULL on success.
486 * Also places the canonical host name into `realhost'.
488 static char *telnet_init (HWND hwnd
, char *host
, int port
, char **realhost
) {
496 if ( (a
= inet_addr(host
)) == (unsigned long) INADDR_NONE
) {
497 if ( (h
= gethostbyname(host
)) == NULL
)
498 switch (WSAGetLastError()) {
499 case WSAENETDOWN
: return "Network is down";
500 case WSAHOST_NOT_FOUND
: case WSANO_DATA
:
501 return "Host does not exist";
502 case WSATRY_AGAIN
: return "Host not found";
503 default: return "gethostbyname: unknown error";
505 memcpy (&a
, h
->h_addr
, sizeof(a
));
506 *realhost
= h
->h_name
;
512 port
= 23; /* default telnet port */
517 s
= socket(AF_INET
, SOCK_STREAM
, 0);
518 if (s
== INVALID_SOCKET
)
519 switch (WSAGetLastError()) {
520 case WSAENETDOWN
: return "Network is down";
521 case WSAEAFNOSUPPORT
: return "TCP/IP support not present";
522 default: return "socket(): unknown error";
528 setsockopt (s
, SOL_SOCKET
, SO_OOBINLINE
, (void *)&b
, sizeof(b
));
533 * Bind to local address.
535 addr
.sin_family
= AF_INET
;
536 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
537 addr
.sin_port
= htons(0);
538 if (bind (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
539 switch (WSAGetLastError()) {
540 case WSAENETDOWN
: return "Network is down";
541 default: return "bind(): unknown error";
545 * Connect to remote address.
547 addr
.sin_addr
.s_addr
= htonl(a
);
548 addr
.sin_port
= htons((short)port
);
549 if (connect (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
550 switch (WSAGetLastError()) {
551 case WSAENETDOWN
: return "Network is down";
552 case WSAECONNREFUSED
: return "Connection refused";
553 case WSAENETUNREACH
: return "Network is unreachable";
554 case WSAEHOSTUNREACH
: return "No route to host";
555 default: return "connect(): unknown error";
558 if (WSAAsyncSelect (s
, hwnd
, WM_NETEVENT
, FD_READ
|
559 FD_WRITE
| FD_OOB
| FD_CLOSE
) == SOCKET_ERROR
)
560 switch (WSAGetLastError()) {
561 case WSAENETDOWN
: return "Network is down";
562 default: return "WSAAsyncSelect(): unknown error";
566 * Initialise option states.
571 for (o
= opts
; *o
; o
++)
572 if ((*o
)->state
== REQUESTED
)
573 send_opt ((*o
)->send
, (*o
)->option
);
578 * Set up SYNCH state.
587 * Process a WM_NETEVENT message. Will return 0 if the connection
588 * has closed, or <0 for a socket error.
590 static int telnet_msg (WPARAM wParam
, LPARAM lParam
) {
595 * Because reading less than the whole of the available pending
596 * data can generate an FD_READ event, we need to allow for the
597 * possibility that FD_READ may arrive with FD_CLOSE already in
598 * the queue; so it's possible that we can get here even with s
599 * invalid. If so, we return 1 and don't worry about it.
601 if (s
== INVALID_SOCKET
)
604 if (WSAGETSELECTERROR(lParam
) != 0)
605 return -WSAGETSELECTERROR(lParam
);
607 switch (WSAGETSELECTEVENT(lParam
)) {
610 ret
= recv(s
, buf
, sizeof(buf
), 0);
611 if (ret
< 0 && WSAGetLastError() == WSAEWOULDBLOCK
)
613 if (ret
< 0) /* any _other_ error */
614 return -10000-WSAGetLastError();
622 if (ioctlsocket (s
, SIOCATMARK
, &i
) < 0) {
623 return -20000-WSAGetLastError();
629 do_telnet_read (buf
, ret
);
633 ret
= recv(s
, buf
, sizeof(buf
), 0);
635 telnet_state
= TOPLEVEL
;
637 ret
= recv(s
, buf
, 1, MSG_OOB
);
639 do_telnet_read (buf
, ret
);
641 if (ret
< 0 && WSAGetLastError() != WSAEWOULDBLOCK
)
642 return -30000-WSAGetLastError();
645 if (outbuf_head
!= outbuf_reap
)
649 return 1; /* shouldn't happen, but WTF */
653 * Called to send data down the Telnet connection.
655 static void telnet_send (char *buf
, int len
) {
657 static unsigned char iac
[2] = { IAC
, IAC
};
658 static unsigned char cr
[2] = { CR
, NUL
};
660 if (s
== INVALID_SOCKET
)
664 while (p
< buf
+len
) {
667 while (iswritable((unsigned char)*p
) && p
< buf
+len
) p
++;
670 while (p
< buf
+len
&& !iswritable((unsigned char)*p
)) {
671 s_write ((unsigned char)*p
== IAC ? iac
: cr
, 2);
678 * Called to set the size of the window from Telnet's POV.
680 static void telnet_size(void) {
684 if (s
== INVALID_SOCKET
|| o_naws
.state
!= ACTIVE
)
686 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_NAWS
;
687 b
[3] = cols
>> 8; b
[4] = cols
& 0xFF;
688 b
[5] = rows
>> 8; b
[6] = rows
& 0xFF;
689 b
[7] = IAC
; b
[8] = SE
;
691 sprintf(logbuf
, "client:\tSB NAWS %d,%d",
692 ((unsigned char)b
[3] << 8) + (unsigned char)b
[4],
693 ((unsigned char)b
[5] << 8) + (unsigned char)b
[6]);
698 * Send Telnet special codes.
700 static void telnet_special (Telnet_Special code
) {
703 if (s
== INVALID_SOCKET
)
708 case TS_AYT
: b
[1] = AYT
; s_write (b
, 2); break;
709 case TS_BRK
: b
[1] = BREAK
; s_write (b
, 2); break;
710 case TS_EC
: b
[1] = EC
; s_write (b
, 2); break;
711 case TS_EL
: b
[1] = EL
; s_write (b
, 2); break;
712 case TS_GA
: b
[1] = GA
; s_write (b
, 2); break;
713 case TS_NOP
: b
[1] = NOP
; s_write (b
, 2); break;
714 case TS_ABORT
: b
[1] = ABORT
; s_write (b
, 2); break;
715 case TS_AO
: b
[1] = AO
; s_write (b
, 2); break;
716 case TS_IP
: b
[1] = IP
; s_write (b
, 2); break;
717 case TS_SUSP
: b
[1] = SUSP
; s_write (b
, 2); break;
718 case TS_EOR
: b
[1] = EOR
; s_write (b
, 2); break;
719 case TS_EOF
: b
[1] = xEOF
; s_write (b
, 2); break;
721 outbuf_head
= outbuf_reap
= 0;
723 send (s
, b
, 1, MSG_OOB
);
728 Backend telnet_backend
= {