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
);
219 if (o
->option
== TELOPT_ECHO
)
221 cfg
.ldisc_term
= FALSE
;
222 ldisc
= &ldisc_simple
;
226 static void refused_option (struct Opt
*o
) {
227 if (o
->send
== WILL
&& o
->option
== TELOPT_NEW_ENVIRON
&&
228 o_oenv
.state
== INACTIVE
) {
229 send_opt (WILL
, TELOPT_OLD_ENVIRON
);
230 o_oenv
.state
= REQUESTED
;
232 if (o
->option
== TELOPT_ECHO
)
234 cfg
.ldisc_term
= TRUE
;
239 static void proc_rec_opt (int cmd
, int option
) {
242 log_option ("server", cmd
, option
);
243 for (o
= opts
; *o
; o
++) {
244 if ((*o
)->option
== option
&& (*o
)->ack
== cmd
) {
245 switch ((*o
)->state
) {
247 (*o
)->state
= ACTIVE
;
248 activate_option (*o
);
253 (*o
)->state
= ACTIVE
;
254 send_opt ((*o
)->send
, option
);
255 activate_option (*o
);
257 case REALLY_INACTIVE
:
258 send_opt ((*o
)->nsend
, option
);
262 } else if ((*o
)->option
== option
&& (*o
)->nak
== cmd
) {
263 switch ((*o
)->state
) {
265 (*o
)->state
= INACTIVE
;
269 (*o
)->state
= INACTIVE
;
270 send_opt ((*o
)->nsend
, option
);
273 case REALLY_INACTIVE
:
280 * If we reach here, the option was one we weren't prepared to
281 * cope with. So send a negative ack.
283 send_opt ((cmd
== WILL ? DONT
: WONT
), option
);
286 static void process_subneg (void) {
287 unsigned char b
[2048], *p
, *q
;
293 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
294 char logbuf
[sizeof(cfg
.termspeed
)+80];
295 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TSPEED
;
297 strcpy(b
+4, cfg
.termspeed
);
298 n
= 4 + strlen(cfg
.termspeed
);
299 b
[n
] = IAC
; b
[n
+1] = SE
;
301 logevent("server:\tSB TSPEED SEND");
302 sprintf(logbuf
, "client:\tSB TSPEED IS %s", cfg
.termspeed
);
305 logevent ("server:\tSB TSPEED <something weird>");
308 if (sb_len
== 1 && sb_buf
[0] == TELQUAL_SEND
) {
309 char logbuf
[sizeof(cfg
.termtype
)+80];
310 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_TTYPE
;
312 for (n
= 0; cfg
.termtype
[n
]; n
++)
313 b
[n
+4] = (cfg
.termtype
[n
] >= 'a' && cfg
.termtype
[n
] <= 'z' ?
314 cfg
.termtype
[n
] + 'A'-'a' : cfg
.termtype
[n
]);
315 b
[n
+4] = IAC
; b
[n
+5] = SE
;
318 logevent("server:\tSB TTYPE SEND");
319 sprintf(logbuf
, "client:\tSB TTYPE IS %s", b
+4);
322 logevent("server:\tSB TTYPE <something weird>\r\n");
324 case TELOPT_OLD_ENVIRON
:
325 case TELOPT_NEW_ENVIRON
:
328 if (p
< q
&& *p
== TELQUAL_SEND
) {
331 sprintf (logbuf
, "server:\tSB %s SEND", telopt(sb_opt
));
333 if (sb_opt
== TELOPT_OLD_ENVIRON
) {
334 if (cfg
.rfc_environ
) {
342 * Try to guess the sense of VAR and VALUE.
348 } else if (*p
== BSD_VAR
) {
356 * With NEW_ENVIRON, the sense of VAR and VALUE
362 b
[0] = IAC
; b
[1] = SB
; b
[2] = sb_opt
;
368 while (*e
&& *e
!= '\t') b
[n
++] = *e
++;
371 while (*e
) b
[n
++] = *e
++;
375 b
[n
++] = var
; b
[n
++] = 'U'; b
[n
++] = 'S';
376 b
[n
++] = 'E'; b
[n
++] = 'R'; b
[n
++] = value
;
378 while (*e
) b
[n
++] = *e
++;
380 b
[n
++] = IAC
; b
[n
++] = SE
;
382 sprintf(logbuf
, "client:\tSB %s IS %s", telopt(sb_opt
),
383 n
==6 ?
"<nothing>" : "<stuff>");
391 TOPLEVEL
, SEENIAC
, SEENWILL
, SEENWONT
, SEENDO
, SEENDONT
,
392 SEENSB
, SUBNEGOT
, SUBNEG_IAC
, SEENCR
393 } telnet_state
= TOPLEVEL
;
395 static void do_telnet_read (char *buf
, int len
) {
399 int c
= (unsigned char) *buf
++;
401 switch (telnet_state
) {
404 if (c
== NUL
&& telnet_state
== SEENCR
)
405 telnet_state
= TOPLEVEL
;
407 telnet_state
= SEENIAC
;
415 telnet_state
= SEENCR
;
417 telnet_state
= TOPLEVEL
;
421 if (c
== DO
) telnet_state
= SEENDO
;
422 else if (c
== DONT
) telnet_state
= SEENDONT
;
423 else if (c
== WILL
) telnet_state
= SEENWILL
;
424 else if (c
== WONT
) telnet_state
= SEENWONT
;
425 else if (c
== SB
) telnet_state
= SEENSB
;
427 /* ignore everything else; print it if it's IAC */
432 telnet_state
= TOPLEVEL
;
436 proc_rec_opt (WILL
, c
);
437 telnet_state
= TOPLEVEL
;
440 proc_rec_opt (WONT
, c
);
441 telnet_state
= TOPLEVEL
;
444 proc_rec_opt (DO
, c
);
445 telnet_state
= TOPLEVEL
;
448 proc_rec_opt (DONT
, c
);
449 telnet_state
= TOPLEVEL
;
454 telnet_state
= SUBNEGOT
;
458 telnet_state
= SUBNEG_IAC
;
461 if (sb_len
>= sb_size
) {
465 realloc(sb_buf
, sb_size
) :
472 if (sb_len
< sb_size
)
473 sb_buf
[sb_len
++] = c
;
474 telnet_state
= SUBNEGOT
;/* in case we came here by goto */
479 goto subneg_addchar
; /* yes, it's a hack, I know, but... */
482 telnet_state
= TOPLEVEL
;
490 * Called to set up the Telnet connection. Will arrange for
491 * WM_NETEVENT messages to be passed to the specified window, whose
492 * window procedure should then call telnet_msg().
494 * Returns an error message, or NULL on success.
496 * Also places the canonical host name into `realhost'.
498 static char *telnet_init (HWND hwnd
, char *host
, int port
, char **realhost
) {
506 if ( (a
= inet_addr(host
)) == (unsigned long) INADDR_NONE
) {
507 if ( (h
= gethostbyname(host
)) == NULL
)
508 switch (WSAGetLastError()) {
509 case WSAENETDOWN
: return "Network is down";
510 case WSAHOST_NOT_FOUND
: case WSANO_DATA
:
511 return "Host does not exist";
512 case WSATRY_AGAIN
: return "Host not found";
513 default: return "gethostbyname: unknown error";
515 memcpy (&a
, h
->h_addr
, sizeof(a
));
516 *realhost
= h
->h_name
;
522 port
= 23; /* default telnet port */
527 s
= socket(AF_INET
, SOCK_STREAM
, 0);
528 if (s
== INVALID_SOCKET
)
529 switch (WSAGetLastError()) {
530 case WSAENETDOWN
: return "Network is down";
531 case WSAEAFNOSUPPORT
: return "TCP/IP support not present";
532 default: return "socket(): unknown error";
538 setsockopt (s
, SOL_SOCKET
, SO_OOBINLINE
, (void *)&b
, sizeof(b
));
543 * Bind to local address.
545 addr
.sin_family
= AF_INET
;
546 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
547 addr
.sin_port
= htons(0);
548 if (bind (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
549 switch (WSAGetLastError()) {
550 case WSAENETDOWN
: return "Network is down";
551 default: return "bind(): unknown error";
555 * Connect to remote address.
557 addr
.sin_addr
.s_addr
= htonl(a
);
558 addr
.sin_port
= htons((short)port
);
559 if (connect (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == SOCKET_ERROR
)
560 switch (WSAGetLastError()) {
561 case WSAENETDOWN
: return "Network is down";
562 case WSAECONNREFUSED
: return "Connection refused";
563 case WSAENETUNREACH
: return "Network is unreachable";
564 case WSAEHOSTUNREACH
: return "No route to host";
565 default: return "connect(): unknown error";
568 if (WSAAsyncSelect (s
, hwnd
, WM_NETEVENT
, FD_READ
|
569 FD_WRITE
| FD_OOB
| FD_CLOSE
) == SOCKET_ERROR
)
570 switch (WSAGetLastError()) {
571 case WSAENETDOWN
: return "Network is down";
572 default: return "WSAAsyncSelect(): unknown error";
576 * Initialise option states.
582 for (o
= opts
; *o
; o
++)
583 if ((*o
)->state
== REQUESTED
)
584 (*o
)->state
= INACTIVE
;
590 for (o
= opts
; *o
; o
++)
591 if ((*o
)->state
== REQUESTED
)
592 send_opt ((*o
)->send
, (*o
)->option
);
597 * Set up SYNCH state.
606 * Process a WM_NETEVENT message. Will return 0 if the connection
607 * has closed, or <0 for a socket error.
609 static int telnet_msg (WPARAM wParam
, LPARAM lParam
) {
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)
624 return -WSAGETSELECTERROR(lParam
);
626 switch (WSAGETSELECTEVENT(lParam
)) {
629 ret
= recv(s
, buf
, sizeof(buf
), 0);
630 if (ret
< 0 && WSAGetLastError() == WSAEWOULDBLOCK
)
632 if (ret
< 0) /* any _other_ error */
633 return -10000-WSAGetLastError();
641 if (ioctlsocket (s
, SIOCATMARK
, &i
) < 0) {
642 return -20000-WSAGetLastError();
648 do_telnet_read (buf
, ret
);
652 ret
= recv(s
, buf
, sizeof(buf
), 0);
654 telnet_state
= TOPLEVEL
;
656 ret
= recv(s
, buf
, 1, MSG_OOB
);
658 do_telnet_read (buf
, ret
);
660 if (ret
< 0 && WSAGetLastError() != WSAEWOULDBLOCK
)
661 return -30000-WSAGetLastError();
664 if (outbuf_head
!= outbuf_reap
)
668 return 1; /* shouldn't happen, but WTF */
672 * Called to send data down the Telnet connection.
674 static void telnet_send (char *buf
, int len
) {
676 static unsigned char iac
[2] = { IAC
, IAC
};
677 static unsigned char cr
[2] = { CR
, NUL
};
678 static unsigned char nl
[2] = { CR
, LF
};
680 if (s
== INVALID_SOCKET
)
684 if (cfg
.ldisc_term
) {
685 while (p
< buf
+len
) {
687 unsigned char * cstr
= 0;
688 while (p
< buf
+len
) {
689 if ((unsigned char)*p
== IAC
) {
694 if( p
+1 >= buf
+len
|| ( p
[1] != '\n' && p
[1] != '\0'))
702 if (p
!=q
) s_write (q
, p
-q
);
703 if (cstr
) s_write (cstr
,2), p
++;
705 } else while (p
< buf
+len
) {
708 while (iswritable((unsigned char)*p
) && p
< buf
+len
) p
++;
711 while (p
< buf
+len
&& !iswritable((unsigned char)*p
)) {
712 s_write ((unsigned char)*p
== IAC ? iac
: nl
, 2);
719 * Called to set the size of the window from Telnet's POV.
721 static void telnet_size(void) {
725 if (s
== INVALID_SOCKET
|| o_naws
.state
!= ACTIVE
)
727 b
[0] = IAC
; b
[1] = SB
; b
[2] = TELOPT_NAWS
;
728 b
[3] = cols
>> 8; b
[4] = cols
& 0xFF;
729 b
[5] = rows
>> 8; b
[6] = rows
& 0xFF;
730 b
[7] = IAC
; b
[8] = SE
;
732 sprintf(logbuf
, "client:\tSB NAWS %d,%d",
733 ((unsigned char)b
[3] << 8) + (unsigned char)b
[4],
734 ((unsigned char)b
[5] << 8) + (unsigned char)b
[6]);
739 * Send Telnet special codes.
741 static void telnet_special (Telnet_Special code
) {
744 if (s
== INVALID_SOCKET
)
749 case TS_AYT
: b
[1] = AYT
; s_write (b
, 2); break;
750 case TS_BRK
: b
[1] = BREAK
; s_write (b
, 2); break;
751 case TS_EC
: b
[1] = EC
; s_write (b
, 2); break;
752 case TS_EL
: b
[1] = EL
; s_write (b
, 2); break;
753 case TS_GA
: b
[1] = GA
; s_write (b
, 2); break;
754 case TS_NOP
: b
[1] = NOP
; s_write (b
, 2); break;
755 case TS_ABORT
: b
[1] = ABORT
; s_write (b
, 2); break;
756 case TS_AO
: b
[1] = AO
; s_write (b
, 2); break;
757 case TS_IP
: b
[1] = IP
; s_write (b
, 2); break;
758 case TS_SUSP
: b
[1] = SUSP
; s_write (b
, 2); break;
759 case TS_EOR
: b
[1] = EOR
; s_write (b
, 2); break;
760 case TS_EOF
: b
[1] = xEOF
; s_write (b
, 2); break;
762 outbuf_head
= outbuf_reap
= 0;
764 send (s
, b
, 2, MSG_OOB
);
767 if (o_echo
.state
== INACTIVE
|| o_echo
.state
== REALLY_INACTIVE
) {
768 o_echo
.state
= REQUESTED
;
769 send_opt (o_echo
.send
, o_echo
.option
);
773 if (o_echo
.state
== ACTIVE
) {
774 o_echo
.state
= REQUESTED
;
775 send_opt (o_echo
.nsend
, o_echo
.option
);
781 Backend telnet_backend
= {