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