1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#define FOR_telnet
21#include "toys.h"
22#include <arpa/telnet.h>
23#include <netinet/in.h>
24#include <sys/poll.h>
25
26GLOBALS(
27 int port;
28 int sfd;
29 char buff[128];
30 int pbuff;
31 char iac[256];
32 int piac;
33 char *ttype;
34 struct termios def_term;
35 struct termios raw_term;
36 uint8_t term_ok;
37 uint8_t term_mode;
38 uint8_t flags;
39 unsigned win_width;
40 unsigned win_height;
41)
42
43#define DATABUFSIZE 128
44#define IACBUFSIZE 256
45#define CM_TRY 0
46#define CM_ON 1
47#define CM_OFF 2
48#define UF_ECHO 0x01
49#define UF_SGA 0x02
50
51
52static char const es[] = "\r\nEscape character is ";
53static void set_mode(void)
54{
55 if (TT.flags & UF_ECHO) {
56 if (TT.term_mode == CM_TRY) {
57 TT.term_mode = CM_ON;
58 printf("\r\nEntering character mode%s'^]'.\r\n", es);
59 if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
60 }
61 } else {
62 if (TT.term_mode != CM_OFF) {
63 TT.term_mode = CM_OFF;
64 printf("\r\nEntering line mode%s'^C'.\r\n", es);
65 if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
66 }
67 }
68}
69
70
71static void flush_iac(void)
72{
73 int wlen = write(TT.sfd, TT.iac, TT.piac);
74
75 if(wlen <= 0) error_msg("IAC : send failed.");
76 TT.piac = 0;
77}
78
79
80static void put_iac(int len, ...)
81{
82 va_list va;
83
84 if(TT.piac + len >= IACBUFSIZE) flush_iac();
85 va_start(va, len);
86 for(;len > 0; TT.iac[TT.piac++] = (uint8_t)va_arg(va, int), len--);
87 va_end(va);
88}
89
90
91static void str_iac(char *str)
92{
93 int len = strlen(str);
94
95 if(TT.piac + len + 1 >= IACBUFSIZE) flush_iac();
96 strcpy(&TT.iac[TT.piac], str);
97 TT.piac += len+1;
98}
99
100static void handle_esc(void)
101{
102 char input;
103
104 if(toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
105 xwrite(1,"\r\nConsole escape. Commands are:\r\n\n"
106 " l go to line mode\r\n"
107 " c go to character mode\r\n"
108 " z suspend telnet\r\n"
109 " e exit telnet\r\n", 114);
110
111 if (read(STDIN_FILENO, &input, 1) <= 0) {
112 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
113 exit(0);
114 }
115
116 switch (input) {
117 case 'l':
118 if (!toys.signal) {
119 TT.term_mode = CM_TRY;
120 TT.flags &= ~(UF_ECHO | UF_SGA);
121 set_mode();
122 put_iac(6, IAC,DONT,TELOPT_ECHO,IAC,DONT, TELOPT_SGA);
123 flush_iac();
124 goto ret;
125 }
126 break;
127 case 'c':
128 if (toys.signal) {
129 TT.term_mode = CM_TRY;
130 TT.flags |= (UF_ECHO | UF_SGA);
131 set_mode();
132 put_iac(6, IAC,DO,TELOPT_ECHO,IAC,DO,TELOPT_SGA);
133 flush_iac();
134 goto ret;
135 }
136 break;
137 case 'z':
138 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
139 kill(0, SIGTSTP);
140 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
141 break;
142 case 'e':
143 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
144 exit(0);
145 default: break;
146 }
147
148 xwrite(1, "continuing...\r\n", 15);
149 if (toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
150
151ret:
152 toys.signal = 0;
153}
154
155
156
157
158
159static void handle_negotiations(void)
160{
161 char opt = TT.buff[TT.pbuff++];
162
163 switch(opt) {
164 case TELOPT_TTYPE:
165 opt = TT.buff[TT.pbuff++];
166 if(opt == TELQUAL_SEND) {
167 put_iac(4, IAC,SB,TELOPT_TTYPE,TELQUAL_IS);
168 str_iac(TT.ttype);
169 put_iac(2, IAC,SE);
170 }
171 break;
172 default: break;
173 }
174}
175
176
177
178
179
180static void handle_ddww(char ddww)
181{
182 char opt = TT.buff[TT.pbuff++];
183
184 switch (opt) {
185 case TELOPT_ECHO:
186 if (ddww == DO) put_iac(3, IAC,WONT,TELOPT_ECHO);
187 if(ddww == DONT) break;
188 if (TT.flags & UF_ECHO) {
189 if (ddww == WILL) return;
190 } else if (ddww == WONT) return;
191 if (TT.term_mode != CM_OFF) TT.flags ^= UF_ECHO;
192 (TT.flags & UF_ECHO)? put_iac(3, IAC,DO,TELOPT_ECHO) :
193 put_iac(3, IAC,DONT,TELOPT_ECHO);
194 set_mode();
195 printf("\r\n");
196 break;
197
198 case TELOPT_SGA:
199 if (TT.flags & UF_SGA){ if (ddww == WILL) return;
200 } else if (ddww == WONT) return;
201
202 TT.flags ^= UF_SGA;
203 (TT.flags & UF_SGA)? put_iac(3, IAC,DO,TELOPT_SGA) :
204 put_iac(3, IAC,DONT,TELOPT_SGA);
205 break;
206
207 case TELOPT_TTYPE:
208 (TT.ttype)? put_iac(3, IAC,WILL,TELOPT_TTYPE):
209 put_iac(3, IAC,WONT,TELOPT_TTYPE);
210 break;
211
212 case TELOPT_NAWS:
213 put_iac(3, IAC,WILL,TELOPT_NAWS);
214 put_iac(9, IAC,SB,TELOPT_NAWS,(TT.win_width >> 8) & 0xff,
215 TT.win_width & 0xff,(TT.win_height >> 8) & 0xff,
216 TT.win_height & 0xff,IAC,SE);
217 break;
218
219 default:
220 if(ddww == WILL) put_iac(3, IAC,DONT,opt);
221 if(ddww == DO) put_iac(3, IAC,WONT,opt);
222 break;
223 }
224}
225
226
227
228
229
230static int read_server(int len)
231{
232 int i = 0;
233 char curr;
234 TT.pbuff = 0;
235
236 do {
237 curr = TT.buff[TT.pbuff++];
238 if (curr == IAC) {
239 curr = TT.buff[TT.pbuff++];
240 switch (curr) {
241 case DO:
242 case DONT:
243 case WILL:
244 case WONT:
245 handle_ddww(curr);
246 break;
247 case SB:
248 handle_negotiations();
249 break;
250 case SE:
251 break;
252 default: break;
253 }
254 } else {
255 toybuf[i++] = curr;
256 if (curr == '\r') { curr = TT.buff[TT.pbuff++];
257 if (curr != '\0') TT.pbuff--;
258 }
259 }
260 } while (TT.pbuff < len);
261
262 if (i) xwrite(STDIN_FILENO, toybuf, i);
263 return 0;
264}
265
266
267
268
269
270static void write_server(int len)
271{
272 char *c = (char*)TT.buff;
273 int i = 0;
274
275 for (; len > 0; len--, c++) {
276 if (*c == 0x1d) {
277 handle_esc();
278 return;
279 }
280 toybuf[i++] = *c;
281 if (*c == IAC) toybuf[i++] = *c;
282 else if (*c == '\r') toybuf[i++] = '\0';
283 }
284 if(i) xwrite(TT.sfd, toybuf, i);
285}
286
287void telnet_main(void)
288{
289 char *port = "23";
290 int set = 1, len;
291 struct pollfd pfds[2];
292
293 TT.win_width = 80;
294 TT.win_height = 24;
295
296 if (toys.optc == 2) port = toys.optargs[1];
297
298 TT.ttype = getenv("TERM");
299 if(!TT.ttype) TT.ttype = "";
300 if(strlen(TT.ttype) > IACBUFSIZE-1) TT.ttype[IACBUFSIZE - 1] = '\0';
301
302 if (!tcgetattr(0, &TT.def_term)) {
303 TT.term_ok = 1;
304 TT.raw_term = TT.def_term;
305 cfmakeraw(&TT.raw_term);
306 }
307 terminal_size(&TT.win_width, &TT.win_height);
308
309 TT.sfd = xconnect(xgetaddrinfo(*toys.optargs, port, 0, SOCK_STREAM,
310 IPPROTO_TCP, 0));
311 setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
312 setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set));
313
314 pfds[0].fd = STDIN_FILENO;
315 pfds[0].events = POLLIN;
316 pfds[1].fd = TT.sfd;
317 pfds[1].events = POLLIN;
318
319 signal(SIGINT, generic_signal);
320 while(1) {
321 if(TT.piac) flush_iac();
322 if(poll(pfds, 2, -1) < 0) {
323 if (toys.signal) handle_esc();
324 else sleep(1);
325
326 continue;
327 }
328 if(pfds[0].revents) {
329 len = read(STDIN_FILENO, TT.buff, DATABUFSIZE);
330 if(len > 0) write_server(len);
331 else return;
332 }
333 if(pfds[1].revents) {
334 len = read(TT.sfd, TT.buff, DATABUFSIZE);
335 if(len > 0) read_server(len);
336 else {
337 printf("Connection closed by foreign host\r\n");
338 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
339 exit(1);
340 }
341 }
342 }
343}
344