1 /* telnet.c - Telnet client. 2 * 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> 6 * 7 * Not in SUSv4. 8 9 USE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN)) 10 11 config TELNET 12 bool "telnet" 13 default n 14 help 15 usage: telnet HOST [PORT] 16 17 Connect to telnet server 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 26 GLOBALS( 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 // sets terminal mode: LINE or CHARACTER based om internal stat. 52 static char const es[] = "\r\nEscape character is "; 53 static 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 // flushes all data in IAC buff to server. 71 static 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 // puts DATA in iac buff of length LEN and updates iac buff pointer. 80 static 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 // puts string STR in iac buff and updates iac buff pointer. 91 static 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 100 static 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 151 ret: 152 toys.signal = 0; 153 } 154 155 /* 156 * handles telnet SUB NEGOTIATIONS 157 * only terminal type is supported. 158 */ 159 static 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 * handles server's DO DONT WILL WONT requests. 178 * supports ECHO, SGA, TTYPE, NAWS 179 */ 180 static void handle_ddww(char ddww) 181 { 182 char opt = TT.buff[TT.pbuff++]; 183 184 switch (opt) { 185 case TELOPT_ECHO: /* 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: /* Supress GO Ahead */ 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: /* Terminal Type */ 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: /* Window Size */ 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: /* Default behaviour is to say NO */ 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 * parses data which is read from server of length LEN. 228 * and passes it to console. 229 */ 230 static 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: /* FALLTHROUGH */ 242 case DONT: /* FALLTHROUGH */ 243 case WILL: /* FALLTHROUGH */ 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 * parses data which is read from console of length LEN 268 * and passes it to server. 269 */ 270 static 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; /* IAC -> IAC IAC */ 282 else if (*c == '\r') toybuf[i++] = '\0'; /* CR -> CR NUL */ 283 } 284 if(i) xwrite(TT.sfd, toybuf, i); 285 } 286 287 void telnet_main(void) 288 { 289 char *port = "23"; 290 int set = 1, len; 291 struct pollfd pfds[2]; 292 293 TT.win_width = 80; //columns 294 TT.win_height = 24; //rows 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 = xconnectany(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