toybox/toys/pending/telnet.c
<<
>>
Prefs
   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
   9USE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN))
  10
  11config 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
  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// sets terminal mode: LINE or CHARACTER based om internal stat.
  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// flushes all data in IAC buff to server.
  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// puts DATA in iac buff of length LEN and updates iac buff pointer.
  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// puts string STR in iac buff and updates iac buff pointer.
  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 * handles telnet SUB NEGOTIATIONS
 157 * only terminal type is supported.
 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 * handles server's DO DONT WILL WONT requests.
 178 * supports ECHO, SGA, TTYPE, NAWS
 179 */
 180static 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 */
 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:    /* 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 */
 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; /* 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
 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; //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 = 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