10f66f451Sopenharmony_ci/* telnet.c - Telnet client. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2012 Madhur Verma <mad.flexi@gmail.com> 40f66f451Sopenharmony_ci * Copyright 2013 Kyungwan Han <asura321@gmail.com> 50f66f451Sopenharmony_ci * Modified by Ashwini Kumar <ak.ashwini1981@gmail.com> 60f66f451Sopenharmony_ci * 70f66f451Sopenharmony_ci * Not in SUSv4. 80f66f451Sopenharmony_ci 90f66f451Sopenharmony_ciUSE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN)) 100f66f451Sopenharmony_ci 110f66f451Sopenharmony_ciconfig TELNET 120f66f451Sopenharmony_ci bool "telnet" 130f66f451Sopenharmony_ci default n 140f66f451Sopenharmony_ci help 150f66f451Sopenharmony_ci usage: telnet HOST [PORT] 160f66f451Sopenharmony_ci 170f66f451Sopenharmony_ci Connect to telnet server. 180f66f451Sopenharmony_ci*/ 190f66f451Sopenharmony_ci 200f66f451Sopenharmony_ci#define FOR_telnet 210f66f451Sopenharmony_ci#include "toys.h" 220f66f451Sopenharmony_ci#include <arpa/telnet.h> 230f66f451Sopenharmony_ci#include <netinet/in.h> 240f66f451Sopenharmony_ci#include <sys/poll.h> 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_ciGLOBALS( 270f66f451Sopenharmony_ci int port; 280f66f451Sopenharmony_ci int sfd; 290f66f451Sopenharmony_ci char buff[128]; 300f66f451Sopenharmony_ci int pbuff; 310f66f451Sopenharmony_ci char iac[256]; 320f66f451Sopenharmony_ci int piac; 330f66f451Sopenharmony_ci char *ttype; 340f66f451Sopenharmony_ci struct termios def_term; 350f66f451Sopenharmony_ci struct termios raw_term; 360f66f451Sopenharmony_ci uint8_t term_ok; 370f66f451Sopenharmony_ci uint8_t term_mode; 380f66f451Sopenharmony_ci uint8_t flags; 390f66f451Sopenharmony_ci unsigned win_width; 400f66f451Sopenharmony_ci unsigned win_height; 410f66f451Sopenharmony_ci) 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ci#define DATABUFSIZE 128 440f66f451Sopenharmony_ci#define IACBUFSIZE 256 450f66f451Sopenharmony_ci#define CM_TRY 0 460f66f451Sopenharmony_ci#define CM_ON 1 470f66f451Sopenharmony_ci#define CM_OFF 2 480f66f451Sopenharmony_ci#define UF_ECHO 0x01 490f66f451Sopenharmony_ci#define UF_SGA 0x02 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ci// sets terminal mode: LINE or CHARACTER based om internal stat. 520f66f451Sopenharmony_cistatic char const es[] = "\r\nEscape character is "; 530f66f451Sopenharmony_cistatic void set_mode(void) 540f66f451Sopenharmony_ci{ 550f66f451Sopenharmony_ci if (TT.flags & UF_ECHO) { 560f66f451Sopenharmony_ci if (TT.term_mode == CM_TRY) { 570f66f451Sopenharmony_ci TT.term_mode = CM_ON; 580f66f451Sopenharmony_ci printf("\r\nEntering character mode%s'^]'.\r\n", es); 590f66f451Sopenharmony_ci if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term); 600f66f451Sopenharmony_ci } 610f66f451Sopenharmony_ci } else { 620f66f451Sopenharmony_ci if (TT.term_mode != CM_OFF) { 630f66f451Sopenharmony_ci TT.term_mode = CM_OFF; 640f66f451Sopenharmony_ci printf("\r\nEntering line mode%s'^C'.\r\n", es); 650f66f451Sopenharmony_ci if (TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); 660f66f451Sopenharmony_ci } 670f66f451Sopenharmony_ci } 680f66f451Sopenharmony_ci} 690f66f451Sopenharmony_ci 700f66f451Sopenharmony_ci// flushes all data in IAC buff to server. 710f66f451Sopenharmony_cistatic void flush_iac(void) 720f66f451Sopenharmony_ci{ 730f66f451Sopenharmony_ci int wlen = write(TT.sfd, TT.iac, TT.piac); 740f66f451Sopenharmony_ci 750f66f451Sopenharmony_ci if(wlen <= 0) error_msg("IAC : send failed."); 760f66f451Sopenharmony_ci TT.piac = 0; 770f66f451Sopenharmony_ci} 780f66f451Sopenharmony_ci 790f66f451Sopenharmony_ci// puts DATA in iac buff of length LEN and updates iac buff pointer. 800f66f451Sopenharmony_cistatic void put_iac(int len, ...) 810f66f451Sopenharmony_ci{ 820f66f451Sopenharmony_ci va_list va; 830f66f451Sopenharmony_ci 840f66f451Sopenharmony_ci if(TT.piac + len >= IACBUFSIZE) flush_iac(); 850f66f451Sopenharmony_ci va_start(va, len); 860f66f451Sopenharmony_ci for(;len > 0; TT.iac[TT.piac++] = (uint8_t)va_arg(va, int), len--); 870f66f451Sopenharmony_ci va_end(va); 880f66f451Sopenharmony_ci} 890f66f451Sopenharmony_ci 900f66f451Sopenharmony_ci// puts string STR in iac buff and updates iac buff pointer. 910f66f451Sopenharmony_cistatic void str_iac(char *str) 920f66f451Sopenharmony_ci{ 930f66f451Sopenharmony_ci int len = strlen(str); 940f66f451Sopenharmony_ci 950f66f451Sopenharmony_ci if(TT.piac + len + 1 >= IACBUFSIZE) flush_iac(); 960f66f451Sopenharmony_ci strcpy(&TT.iac[TT.piac], str); 970f66f451Sopenharmony_ci TT.piac += len+1; 980f66f451Sopenharmony_ci} 990f66f451Sopenharmony_ci 1000f66f451Sopenharmony_cistatic void handle_esc(void) 1010f66f451Sopenharmony_ci{ 1020f66f451Sopenharmony_ci char input; 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci if(toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term); 1050f66f451Sopenharmony_ci xwrite(1,"\r\nConsole escape. Commands are:\r\n\n" 1060f66f451Sopenharmony_ci " l go to line mode\r\n" 1070f66f451Sopenharmony_ci " c go to character mode\r\n" 1080f66f451Sopenharmony_ci " z suspend telnet\r\n" 1090f66f451Sopenharmony_ci " e exit telnet\r\n", 114); 1100f66f451Sopenharmony_ci 1110f66f451Sopenharmony_ci if (read(STDIN_FILENO, &input, 1) <= 0) { 1120f66f451Sopenharmony_ci if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); 1130f66f451Sopenharmony_ci exit(0); 1140f66f451Sopenharmony_ci } 1150f66f451Sopenharmony_ci 1160f66f451Sopenharmony_ci switch (input) { 1170f66f451Sopenharmony_ci case 'l': 1180f66f451Sopenharmony_ci if (!toys.signal) { 1190f66f451Sopenharmony_ci TT.term_mode = CM_TRY; 1200f66f451Sopenharmony_ci TT.flags &= ~(UF_ECHO | UF_SGA); 1210f66f451Sopenharmony_ci set_mode(); 1220f66f451Sopenharmony_ci put_iac(6, IAC,DONT,TELOPT_ECHO,IAC,DONT, TELOPT_SGA); 1230f66f451Sopenharmony_ci flush_iac(); 1240f66f451Sopenharmony_ci goto ret; 1250f66f451Sopenharmony_ci } 1260f66f451Sopenharmony_ci break; 1270f66f451Sopenharmony_ci case 'c': 1280f66f451Sopenharmony_ci if (toys.signal) { 1290f66f451Sopenharmony_ci TT.term_mode = CM_TRY; 1300f66f451Sopenharmony_ci TT.flags |= (UF_ECHO | UF_SGA); 1310f66f451Sopenharmony_ci set_mode(); 1320f66f451Sopenharmony_ci put_iac(6, IAC,DO,TELOPT_ECHO,IAC,DO,TELOPT_SGA); 1330f66f451Sopenharmony_ci flush_iac(); 1340f66f451Sopenharmony_ci goto ret; 1350f66f451Sopenharmony_ci } 1360f66f451Sopenharmony_ci break; 1370f66f451Sopenharmony_ci case 'z': 1380f66f451Sopenharmony_ci if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); 1390f66f451Sopenharmony_ci kill(0, SIGTSTP); 1400f66f451Sopenharmony_ci if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.raw_term); 1410f66f451Sopenharmony_ci break; 1420f66f451Sopenharmony_ci case 'e': 1430f66f451Sopenharmony_ci if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); 1440f66f451Sopenharmony_ci exit(0); 1450f66f451Sopenharmony_ci default: break; 1460f66f451Sopenharmony_ci } 1470f66f451Sopenharmony_ci 1480f66f451Sopenharmony_ci xwrite(1, "continuing...\r\n", 15); 1490f66f451Sopenharmony_ci if (toys.signal && TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); 1500f66f451Sopenharmony_ci 1510f66f451Sopenharmony_ciret: 1520f66f451Sopenharmony_ci toys.signal = 0; 1530f66f451Sopenharmony_ci} 1540f66f451Sopenharmony_ci 1550f66f451Sopenharmony_ci/* 1560f66f451Sopenharmony_ci * handles telnet SUB NEGOTIATIONS 1570f66f451Sopenharmony_ci * only terminal type is supported. 1580f66f451Sopenharmony_ci */ 1590f66f451Sopenharmony_cistatic void handle_negotiations(void) 1600f66f451Sopenharmony_ci{ 1610f66f451Sopenharmony_ci char opt = TT.buff[TT.pbuff++]; 1620f66f451Sopenharmony_ci 1630f66f451Sopenharmony_ci switch(opt) { 1640f66f451Sopenharmony_ci case TELOPT_TTYPE: 1650f66f451Sopenharmony_ci opt = TT.buff[TT.pbuff++]; 1660f66f451Sopenharmony_ci if(opt == TELQUAL_SEND) { 1670f66f451Sopenharmony_ci put_iac(4, IAC,SB,TELOPT_TTYPE,TELQUAL_IS); 1680f66f451Sopenharmony_ci str_iac(TT.ttype); 1690f66f451Sopenharmony_ci put_iac(2, IAC,SE); 1700f66f451Sopenharmony_ci } 1710f66f451Sopenharmony_ci break; 1720f66f451Sopenharmony_ci default: break; 1730f66f451Sopenharmony_ci } 1740f66f451Sopenharmony_ci} 1750f66f451Sopenharmony_ci 1760f66f451Sopenharmony_ci/* 1770f66f451Sopenharmony_ci * handles server's DO DONT WILL WONT requests. 1780f66f451Sopenharmony_ci * supports ECHO, SGA, TTYPE, NAWS 1790f66f451Sopenharmony_ci */ 1800f66f451Sopenharmony_cistatic void handle_ddww(char ddww) 1810f66f451Sopenharmony_ci{ 1820f66f451Sopenharmony_ci char opt = TT.buff[TT.pbuff++]; 1830f66f451Sopenharmony_ci 1840f66f451Sopenharmony_ci switch (opt) { 1850f66f451Sopenharmony_ci case TELOPT_ECHO: /* ECHO */ 1860f66f451Sopenharmony_ci if (ddww == DO) put_iac(3, IAC,WONT,TELOPT_ECHO); 1870f66f451Sopenharmony_ci if(ddww == DONT) break; 1880f66f451Sopenharmony_ci if (TT.flags & UF_ECHO) { 1890f66f451Sopenharmony_ci if (ddww == WILL) return; 1900f66f451Sopenharmony_ci } else if (ddww == WONT) return; 1910f66f451Sopenharmony_ci if (TT.term_mode != CM_OFF) TT.flags ^= UF_ECHO; 1920f66f451Sopenharmony_ci (TT.flags & UF_ECHO)? put_iac(3, IAC,DO,TELOPT_ECHO) : 1930f66f451Sopenharmony_ci put_iac(3, IAC,DONT,TELOPT_ECHO); 1940f66f451Sopenharmony_ci set_mode(); 1950f66f451Sopenharmony_ci printf("\r\n"); 1960f66f451Sopenharmony_ci break; 1970f66f451Sopenharmony_ci 1980f66f451Sopenharmony_ci case TELOPT_SGA: /* Supress GO Ahead */ 1990f66f451Sopenharmony_ci if (TT.flags & UF_SGA){ if (ddww == WILL) return; 2000f66f451Sopenharmony_ci } else if (ddww == WONT) return; 2010f66f451Sopenharmony_ci 2020f66f451Sopenharmony_ci TT.flags ^= UF_SGA; 2030f66f451Sopenharmony_ci (TT.flags & UF_SGA)? put_iac(3, IAC,DO,TELOPT_SGA) : 2040f66f451Sopenharmony_ci put_iac(3, IAC,DONT,TELOPT_SGA); 2050f66f451Sopenharmony_ci break; 2060f66f451Sopenharmony_ci 2070f66f451Sopenharmony_ci case TELOPT_TTYPE: /* Terminal Type */ 2080f66f451Sopenharmony_ci (TT.ttype)? put_iac(3, IAC,WILL,TELOPT_TTYPE): 2090f66f451Sopenharmony_ci put_iac(3, IAC,WONT,TELOPT_TTYPE); 2100f66f451Sopenharmony_ci break; 2110f66f451Sopenharmony_ci 2120f66f451Sopenharmony_ci case TELOPT_NAWS: /* Window Size */ 2130f66f451Sopenharmony_ci put_iac(3, IAC,WILL,TELOPT_NAWS); 2140f66f451Sopenharmony_ci put_iac(9, IAC,SB,TELOPT_NAWS,(TT.win_width >> 8) & 0xff, 2150f66f451Sopenharmony_ci TT.win_width & 0xff,(TT.win_height >> 8) & 0xff, 2160f66f451Sopenharmony_ci TT.win_height & 0xff,IAC,SE); 2170f66f451Sopenharmony_ci break; 2180f66f451Sopenharmony_ci 2190f66f451Sopenharmony_ci default: /* Default behaviour is to say NO */ 2200f66f451Sopenharmony_ci if(ddww == WILL) put_iac(3, IAC,DONT,opt); 2210f66f451Sopenharmony_ci if(ddww == DO) put_iac(3, IAC,WONT,opt); 2220f66f451Sopenharmony_ci break; 2230f66f451Sopenharmony_ci } 2240f66f451Sopenharmony_ci} 2250f66f451Sopenharmony_ci 2260f66f451Sopenharmony_ci/* 2270f66f451Sopenharmony_ci * parses data which is read from server of length LEN. 2280f66f451Sopenharmony_ci * and passes it to console. 2290f66f451Sopenharmony_ci */ 2300f66f451Sopenharmony_cistatic int read_server(int len) 2310f66f451Sopenharmony_ci{ 2320f66f451Sopenharmony_ci int i = 0; 2330f66f451Sopenharmony_ci char curr; 2340f66f451Sopenharmony_ci TT.pbuff = 0; 2350f66f451Sopenharmony_ci 2360f66f451Sopenharmony_ci do { 2370f66f451Sopenharmony_ci curr = TT.buff[TT.pbuff++]; 2380f66f451Sopenharmony_ci if (curr == IAC) { 2390f66f451Sopenharmony_ci curr = TT.buff[TT.pbuff++]; 2400f66f451Sopenharmony_ci switch (curr) { 2410f66f451Sopenharmony_ci case DO: /* FALLTHROUGH */ 2420f66f451Sopenharmony_ci case DONT: /* FALLTHROUGH */ 2430f66f451Sopenharmony_ci case WILL: /* FALLTHROUGH */ 2440f66f451Sopenharmony_ci case WONT: 2450f66f451Sopenharmony_ci handle_ddww(curr); 2460f66f451Sopenharmony_ci break; 2470f66f451Sopenharmony_ci case SB: 2480f66f451Sopenharmony_ci handle_negotiations(); 2490f66f451Sopenharmony_ci break; 2500f66f451Sopenharmony_ci case SE: 2510f66f451Sopenharmony_ci break; 2520f66f451Sopenharmony_ci default: break; 2530f66f451Sopenharmony_ci } 2540f66f451Sopenharmony_ci } else { 2550f66f451Sopenharmony_ci toybuf[i++] = curr; 2560f66f451Sopenharmony_ci if (curr == '\r') { curr = TT.buff[TT.pbuff++]; 2570f66f451Sopenharmony_ci if (curr != '\0') TT.pbuff--; 2580f66f451Sopenharmony_ci } 2590f66f451Sopenharmony_ci } 2600f66f451Sopenharmony_ci } while (TT.pbuff < len); 2610f66f451Sopenharmony_ci 2620f66f451Sopenharmony_ci if (i) xwrite(STDIN_FILENO, toybuf, i); 2630f66f451Sopenharmony_ci return 0; 2640f66f451Sopenharmony_ci} 2650f66f451Sopenharmony_ci 2660f66f451Sopenharmony_ci/* 2670f66f451Sopenharmony_ci * parses data which is read from console of length LEN 2680f66f451Sopenharmony_ci * and passes it to server. 2690f66f451Sopenharmony_ci */ 2700f66f451Sopenharmony_cistatic void write_server(int len) 2710f66f451Sopenharmony_ci{ 2720f66f451Sopenharmony_ci char *c = (char*)TT.buff; 2730f66f451Sopenharmony_ci int i = 0; 2740f66f451Sopenharmony_ci 2750f66f451Sopenharmony_ci for (; len > 0; len--, c++) { 2760f66f451Sopenharmony_ci if (*c == 0x1d) { 2770f66f451Sopenharmony_ci handle_esc(); 2780f66f451Sopenharmony_ci return; 2790f66f451Sopenharmony_ci } 2800f66f451Sopenharmony_ci toybuf[i++] = *c; 2810f66f451Sopenharmony_ci if (*c == IAC) toybuf[i++] = *c; /* IAC -> IAC IAC */ 2820f66f451Sopenharmony_ci else if (*c == '\r') toybuf[i++] = '\0'; /* CR -> CR NUL */ 2830f66f451Sopenharmony_ci } 2840f66f451Sopenharmony_ci if(i) xwrite(TT.sfd, toybuf, i); 2850f66f451Sopenharmony_ci} 2860f66f451Sopenharmony_ci 2870f66f451Sopenharmony_civoid telnet_main(void) 2880f66f451Sopenharmony_ci{ 2890f66f451Sopenharmony_ci char *port = "23"; 2900f66f451Sopenharmony_ci int set = 1, len; 2910f66f451Sopenharmony_ci struct pollfd pfds[2]; 2920f66f451Sopenharmony_ci 2930f66f451Sopenharmony_ci TT.win_width = 80; //columns 2940f66f451Sopenharmony_ci TT.win_height = 24; //rows 2950f66f451Sopenharmony_ci 2960f66f451Sopenharmony_ci if (toys.optc == 2) port = toys.optargs[1]; 2970f66f451Sopenharmony_ci 2980f66f451Sopenharmony_ci TT.ttype = getenv("TERM"); 2990f66f451Sopenharmony_ci if(!TT.ttype) TT.ttype = ""; 3000f66f451Sopenharmony_ci if(strlen(TT.ttype) > IACBUFSIZE-1) TT.ttype[IACBUFSIZE - 1] = '\0'; 3010f66f451Sopenharmony_ci 3020f66f451Sopenharmony_ci if (!tcgetattr(0, &TT.def_term)) { 3030f66f451Sopenharmony_ci TT.term_ok = 1; 3040f66f451Sopenharmony_ci TT.raw_term = TT.def_term; 3050f66f451Sopenharmony_ci cfmakeraw(&TT.raw_term); 3060f66f451Sopenharmony_ci } 3070f66f451Sopenharmony_ci terminal_size(&TT.win_width, &TT.win_height); 3080f66f451Sopenharmony_ci 3090f66f451Sopenharmony_ci TT.sfd = xconnectany(xgetaddrinfo(*toys.optargs, port, 0, SOCK_STREAM, 3100f66f451Sopenharmony_ci IPPROTO_TCP, 0)); 3110f66f451Sopenharmony_ci setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)); 3120f66f451Sopenharmony_ci setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set)); 3130f66f451Sopenharmony_ci 3140f66f451Sopenharmony_ci pfds[0].fd = STDIN_FILENO; 3150f66f451Sopenharmony_ci pfds[0].events = POLLIN; 3160f66f451Sopenharmony_ci pfds[1].fd = TT.sfd; 3170f66f451Sopenharmony_ci pfds[1].events = POLLIN; 3180f66f451Sopenharmony_ci 3190f66f451Sopenharmony_ci signal(SIGINT, generic_signal); 3200f66f451Sopenharmony_ci while(1) { 3210f66f451Sopenharmony_ci if(TT.piac) flush_iac(); 3220f66f451Sopenharmony_ci if(poll(pfds, 2, -1) < 0) { 3230f66f451Sopenharmony_ci if (toys.signal) handle_esc(); 3240f66f451Sopenharmony_ci else sleep(1); 3250f66f451Sopenharmony_ci 3260f66f451Sopenharmony_ci continue; 3270f66f451Sopenharmony_ci } 3280f66f451Sopenharmony_ci if(pfds[0].revents) { 3290f66f451Sopenharmony_ci len = read(STDIN_FILENO, TT.buff, DATABUFSIZE); 3300f66f451Sopenharmony_ci if(len > 0) write_server(len); 3310f66f451Sopenharmony_ci else return; 3320f66f451Sopenharmony_ci } 3330f66f451Sopenharmony_ci if(pfds[1].revents) { 3340f66f451Sopenharmony_ci len = read(TT.sfd, TT.buff, DATABUFSIZE); 3350f66f451Sopenharmony_ci if(len > 0) read_server(len); 3360f66f451Sopenharmony_ci else { 3370f66f451Sopenharmony_ci printf("Connection closed by foreign host\r\n"); 3380f66f451Sopenharmony_ci if(TT.term_ok) tcsetattr(0, TCSADRAIN, &TT.def_term); 3390f66f451Sopenharmony_ci exit(1); 3400f66f451Sopenharmony_ci } 3410f66f451Sopenharmony_ci } 3420f66f451Sopenharmony_ci } 3430f66f451Sopenharmony_ci} 344