1 /* telnet.c - Telnet client.
3 * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5 * Modified by Ashwini Kumar <ak.ashwini1981@gmail.com>
9 USE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN))
15 usage: telnet HOST [PORT]
17 Connect to telnet server
22 #include <arpa/telnet.h>
23 #include <netinet/in.h>
34 struct termios def_term;
35 struct termios raw_term;
43 #define DATABUFSIZE 128
44 #define IACBUFSIZE 256
52 * creates a socket of family INET/INET6 and protocol TCP and connects
54 * if successful then returns SOCK othrwise error
56 static int xconnect_inet_tcp(char *host, int port)
59 struct addrinfo *info, *rp;
62 rp = xzalloc(sizeof(struct addrinfo));
63 rp->ai_family = AF_UNSPEC;
64 rp->ai_socktype = SOCK_STREAM;
65 rp->ai_protocol = IPPROTO_TCP;
66 sprintf(buf, "%d", port);
68 ret = getaddrinfo(host, buf, rp, &info);
69 if(ret || !info) perror_exit("BAD ADDRESS: can't find : %s ", host);
72 for (rp = info; rp; rp = rp->ai_next)
73 if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
75 if (!rp) error_exit("Invalid IP %s", host);
77 ret = xsocket(rp->ai_family, SOCK_STREAM, IPPROTO_TCP);
78 if(connect(ret, rp->ai_addr, rp->ai_addrlen) == -1) perror_exit("connect");
84 // sets terminal mode: LINE or CHARACTER based om internal stat.
85 static char const es[] = "\r\nEscape character is ";
86 static void set_mode(void)
88 if (TT.flags & UF_ECHO) {
89 if (TT.term_mode == CM_TRY) {
91 printf("\r\nEntering character mode%s'^]'.\r\n", es);
92 if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
95 if (TT.term_mode != CM_OFF) {
96 TT.term_mode = CM_OFF;
97 printf("\r\nEntering line mode%s'^C'.\r\n", es);
98 if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
103 // flushes all data in IAC buff to server.
104 static void flush_iac(void)
106 int wlen = write(TT.sfd, TT.iac, TT.piac);
108 if(wlen <= 0) error_msg("IAC : send failed.");
112 // puts DATA in iac buff of length LEN and updates iac buff pointer.
113 static void put_iac(int len, ...)
117 if(TT.piac + len >= IACBUFSIZE) flush_iac();
119 for(;len > 0; TT.iac[TT.piac++] = (uint8_t)va_arg(va, int), len--);
123 // puts string STR in iac buff and updates iac buff pointer.
124 static void str_iac(char *str)
126 int len = strlen(str);
128 if(TT.piac + len + 1 >= IACBUFSIZE) flush_iac();
129 strcpy(&TT.iac[TT.piac], str);
133 static void handle_esc(void)
137 if(toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
138 write(1,"\r\nConsole escape. Commands are:\r\n\n"
139 " l go to line mode\r\n"
140 " c go to character mode\r\n"
141 " z suspend telnet\r\n"
142 " e exit telnet\r\n", 114);
144 if (read(STDIN_FILENO, &input, 1) <= 0) {
145 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
152 TT.term_mode = CM_TRY;
153 TT.flags &= ~(UF_ECHO | UF_SGA);
155 put_iac(6, IAC,DONT,TELOPT_ECHO,IAC,DONT, TELOPT_SGA);
162 TT.term_mode = CM_TRY;
163 TT.flags |= (UF_ECHO | UF_SGA);
165 put_iac(6, IAC,DO,TELOPT_ECHO,IAC,DO,TELOPT_SGA);
171 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
173 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term);
176 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
181 write(1, "continuing...\r\n", 15);
182 if (toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);
189 * handles telnet SUB NEGOTIATIONS
190 * only terminal type is supported.
192 static void handle_negotiations(void)
194 char opt = TT.buff[TT.pbuff++];
198 opt = TT.buff[TT.pbuff++];
199 if(opt == TELQUAL_SEND) {
200 put_iac(4, IAC,SB,TELOPT_TTYPE,TELQUAL_IS);
210 * handles server's DO DONT WILL WONT requests.
211 * supports ECHO, SGA, TTYPE, NAWS
213 static void handle_ddww(char ddww)
215 char opt = TT.buff[TT.pbuff++];
218 case TELOPT_ECHO: /* ECHO */
219 if (ddww == DO) put_iac(3, IAC,WONT,TELOPT_ECHO);
220 if(ddww == DONT) break;
221 if (TT.flags & UF_ECHO) {
222 if (ddww == WILL) return;
223 } else if (ddww == WONT) return;
224 if (TT.term_mode != CM_OFF) TT.flags ^= UF_ECHO;
225 (TT.flags & UF_ECHO)? put_iac(3, IAC,DO,TELOPT_ECHO) :
226 put_iac(3, IAC,DONT,TELOPT_ECHO);
231 case TELOPT_SGA: /* Supress GO Ahead */
232 if (TT.flags & UF_SGA){ if (ddww == WILL) return;
233 } else if (ddww == WONT) return;
236 (TT.flags & UF_SGA)? put_iac(3, IAC,DO,TELOPT_SGA) :
237 put_iac(3, IAC,DONT,TELOPT_SGA);
240 case TELOPT_TTYPE: /* Terminal Type */
241 (TT.ttype)? put_iac(3, IAC,WILL,TELOPT_TTYPE):
242 put_iac(3, IAC,WONT,TELOPT_TTYPE);
245 case TELOPT_NAWS: /* Window Size */
246 put_iac(3, IAC,WILL,TELOPT_NAWS);
247 put_iac(9, IAC,SB,TELOPT_NAWS,(TT.win_width >> 8) & 0xff,
248 TT.win_width & 0xff,(TT.win_height >> 8) & 0xff,
249 TT.win_height & 0xff,IAC,SE);
252 default: /* Default behaviour is to say NO */
253 if(ddww == WILL) put_iac(3, IAC,DONT,opt);
254 if(ddww == DO) put_iac(3, IAC,WONT,opt);
260 * parses data which is read from server of length LEN.
261 * and passes it to console.
263 static int read_server(int len)
270 curr = TT.buff[TT.pbuff++];
272 curr = TT.buff[TT.pbuff++];
274 case DO: /* FALLTHROUGH */
275 case DONT: /* FALLTHROUGH */
276 case WILL: /* FALLTHROUGH */
281 handle_negotiations();
289 if (curr == '\r') { curr = TT.buff[TT.pbuff++];
290 if (curr != '\0') TT.pbuff--;
293 } while (TT.pbuff < len);
295 if (i) write(STDIN_FILENO, toybuf, i);
300 * parses data which is read from console of length LEN
301 * and passes it to server.
303 static void write_server(int len)
305 char *c = (char*)TT.buff;
308 for (; len > 0; len--, c++) {
314 if (*c == IAC) toybuf[i++] = *c; /* IAC -> IAC IAC */
315 else if (*c == '\r') toybuf[i++] = '\0'; /* CR -> CR NUL */
317 if(i) write(TT.sfd, toybuf, i);
320 void telnet_main(void)
323 struct pollfd pfds[2];
325 TT.port = 23; //TELNET_PORT
326 TT.win_width = 80; //columns
327 TT.win_height = 24; //rows
329 if(toys.optc == 2) TT.port = atoi(toys.optargs[1]);
330 if(TT.port <= 0 || TT.port > 65535) error_exit("bad PORT (1-65535)");
332 TT.ttype = getenv("TERM");
333 if(!TT.ttype) TT.ttype = "";
334 if(strlen(TT.ttype) > IACBUFSIZE-1) TT.ttype[IACBUFSIZE - 1] = '\0';
336 if (!tcgetattr(0, &TT.def_term)) {
338 TT.raw_term = TT.def_term;
339 cfmakeraw(&TT.raw_term);
341 terminal_size(&TT.win_width, &TT.win_height);
343 TT.sfd = xconnect_inet_tcp(toys.optargs[0], TT.port);
344 setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
345 setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set));
347 pfds[0].fd = STDIN_FILENO;
348 pfds[0].events = POLLIN;
350 pfds[1].events = POLLIN;
352 signal(SIGINT, generic_signal);
354 if(TT.piac) flush_iac();
355 if(poll(pfds, 2, -1) < 0) {
356 if (toys.signal) handle_esc();
361 if(pfds[0].revents) {
362 len = read(STDIN_FILENO, TT.buff, DATABUFSIZE);
363 if(len > 0) write_server(len);
366 if(pfds[1].revents) {
367 len = read(TT.sfd, TT.buff, DATABUFSIZE);
368 if(len > 0) read_server(len);
370 printf("Connection closed by foreign host\r\n");
371 if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term);