Home | History | Annotate | Download | only in pending
      1 /* telnetd.c - Telnet Server
      2  *
      3  * Copyright 2013 Sandeep Sharma <sandeep.jack2756 (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5  *
      6 USE_TELNETD(NEWTOY(telnetd, "w#<0b:p#<0>65535=23f:l:FSKi[!wi]", TOYFLAG_USR|TOYFLAG_BIN))
      7 
      8 config TELNETD
      9   bool "telnetd"
     10   default n
     11   help
     12     Handle incoming telnet connections
     13 
     14     -l LOGIN  Exec LOGIN on connect
     15     -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue
     16     -K Close connection as soon as login exits
     17     -p PORT   Port to listen on
     18     -b ADDR[:PORT]  Address to bind to
     19     -F Run in foreground
     20     -i Inetd mode
     21     -w SEC    Inetd 'wait' mode, linger time SEC
     22     -S Log to syslog (implied by -i or without -F and -w)
     23 */
     24 
     25 #define FOR_telnetd
     26 #include "toys.h"
     27 #include <utmp.h>
     28 GLOBALS(
     29     char *login_path;
     30     char *issue_path;
     31     int port;
     32     char *host_addr;
     33     long w_sec;
     34 
     35     int gmax_fd;
     36     pid_t fork_pid;
     37 )
     38 
     39 
     40 # define IAC         255  /* interpret as command: */
     41 # define DONT        254  /* you are not to use option */
     42 # define DO          253  /* please, you use option */
     43 # define WONT        252  /* I won't use option */
     44 # define WILL        251  /* I will use option */
     45 # define SB          250  /* interpret as subnegotiation */
     46 # define SE          240  /* end sub negotiation */
     47 # define NOP         241  /* No Operation */
     48 # define TELOPT_ECHO   1  /* echo */
     49 # define TELOPT_SGA    3  /* suppress go ahead */
     50 # define TELOPT_TTYPE 24  /* terminal type */
     51 # define TELOPT_NAWS  31  /* window size */
     52 
     53 #define BUFSIZE 4*1024
     54 struct term_session {
     55   int new_fd, pty_fd;
     56   pid_t child_pid;
     57   int buff1_avail, buff2_avail;
     58   int buff1_written, buff2_written;
     59   int rem;  //unprocessed data from socket
     60   char buff1[BUFSIZE], buff2[BUFSIZE];
     61   struct term_session *next;
     62 };
     63 
     64 struct term_session *session_list = NULL;
     65 
     66 static void get_sockaddr(char *host, void *buf)
     67 {
     68   in_port_t port_num = htons(TT.port);
     69   struct addrinfo hints, *result;
     70   int status, af = AF_UNSPEC;
     71   char *s;
     72 
     73   // [ipv6]:port or exactly one :
     74   if (*host == '[') {
     75     host++;
     76     s = strchr(host, ']');
     77     if (s) *s++ = 0;
     78     else error_exit("bad address '%s'", host-1);
     79     af = AF_INET6;
     80   } else {
     81     s = strrchr(host, ':');
     82     if (s && strchr(host, ':') == s) {
     83       *s = 0;
     84       af = AF_INET;
     85     } else if (s && strchr(host, ':') != s) {
     86       af = AF_INET6;
     87       s = 0;
     88     }
     89   }
     90 
     91   if (s++) {
     92     char *ss;
     93     unsigned long p = strtoul(s, &ss, 0);
     94     if (!*s || *ss || p > 65535) error_exit("bad port '%s'", s);
     95     port_num = htons(p);
     96   }
     97 
     98   memset(&hints, 0 , sizeof(struct addrinfo));
     99   hints.ai_family = af;
    100   hints.ai_socktype = SOCK_STREAM;
    101 
    102   status = getaddrinfo(host, NULL, &hints, &result);
    103   if (status) error_exit("bad address '%s' : %s", host, gai_strerror(status));
    104 
    105   memcpy(buf, result->ai_addr, result->ai_addrlen);
    106   freeaddrinfo(result);
    107 
    108   if (af == AF_INET) ((struct sockaddr_in*)buf)->sin_port = port_num;
    109   else ((struct sockaddr_in6*)buf)->sin6_port = port_num;
    110 }
    111 
    112 static void utmp_entry(void)
    113 {
    114   struct utmp entry;
    115   struct utmp *utp_ptr;
    116   pid_t pid = getpid();
    117 
    118   utmpname(_PATH_UTMP);
    119   setutent(); //start from start
    120   while ((utp_ptr = getutent()) != NULL) {
    121     if (utp_ptr->ut_pid == pid && utp_ptr->ut_type >= INIT_PROCESS) break;
    122   }
    123   if (!utp_ptr) entry.ut_type = DEAD_PROCESS;
    124   time(&entry.ut_time);
    125   setutent();
    126   pututline(&entry);
    127 }
    128 
    129 static int listen_socket(void)
    130 {
    131   int s, af = AF_INET, yes = 1;
    132   char buf[sizeof(struct sockaddr_storage)];
    133 
    134   memset(buf, 0, sizeof(buf));
    135   if (toys.optflags & FLAG_b) {
    136     get_sockaddr(TT.host_addr, buf);
    137     af = ((struct sockaddr *)buf)->sa_family;
    138   } else {
    139     ((struct sockaddr_in*)buf)->sin_port = htons(TT.port);
    140     ((struct sockaddr_in*)buf)->sin_family = af;
    141   }
    142   s = xsocket(af, SOCK_STREAM, 0);
    143   if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) == -1)
    144     perror_exit("setsockopt");
    145 
    146   if (bind(s, (struct sockaddr *)buf, ((af == AF_INET)?
    147           (sizeof(struct sockaddr_in)):(sizeof(struct sockaddr_in6)))) == -1) {
    148     close(s);
    149     perror_exit("bind");
    150   }
    151 
    152   if (listen(s, 1) < 0) perror_exit("listen");
    153   return s;
    154 }
    155 
    156 static void write_issue(char *tty)
    157 {
    158   int size;
    159   char ch = 0;
    160   struct utsname u;
    161   int fd = open(TT.issue_path, O_RDONLY);
    162 
    163   if (fd < 0) return ;
    164   uname(&u);
    165   while ((size = readall(fd, &ch, 1)) > 0) {
    166     if (ch == '\\' || ch == '%') {
    167       if (readall(fd, &ch, 1) <= 0) perror_exit("readall!");
    168       if (ch == 's') fputs(u.sysname, stdout);
    169       if (ch == 'n'|| ch == 'h') fputs(u.nodename, stdout);
    170       if (ch == 'r') fputs(u.release, stdout);
    171       if (ch == 'm') fputs(u.machine, stdout);
    172       if (ch == 'l') fputs(tty, stdout);
    173     }
    174     else if (ch == '\n') {
    175       fputs("\n\r\0", stdout);
    176     } else fputc(ch, stdout);
    177   }
    178   fflush(NULL);
    179   close(fd);
    180 }
    181 
    182 static int new_session(int sockfd)
    183 {
    184   char *argv_login[2]; //arguments for execvp cmd, NULL
    185   char tty_name[30]; //tty name length.
    186   int fd, flags, i = 1;
    187   char intial_iacs[] = {IAC, DO, TELOPT_ECHO, IAC, DO, TELOPT_NAWS,
    188     IAC, WILL, TELOPT_ECHO, IAC, WILL, TELOPT_SGA };
    189 
    190   setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i));
    191   flags = fcntl(sockfd, F_GETFL);
    192   fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
    193   if (toys.optflags & FLAG_i) fcntl((sockfd + 1), F_SETFL, flags | O_NONBLOCK);
    194 
    195   writeall((toys.optflags & FLAG_i)?1:sockfd, intial_iacs, sizeof(intial_iacs));
    196   if ((TT.fork_pid = forkpty(&fd, tty_name, NULL, NULL)) > 0) {
    197     flags = fcntl(fd, F_GETFL);
    198     fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    199     return fd;
    200   }
    201   if (TT.fork_pid < 0) perror_exit("fork");
    202   write_issue(tty_name);
    203   argv_login[0] = strdup(TT.login_path);
    204   argv_login[1] = NULL;
    205   execvp(argv_login[0], argv_login);
    206   exit(EXIT_FAILURE);
    207 }
    208 
    209 static int handle_iacs(struct term_session *tm, int c, int fd)
    210 {
    211   char *curr ,*start,*end;
    212   int i = 0;
    213 
    214   curr = start = tm->buff2+tm->buff2_avail;
    215   end = tm->buff2 + c -1;
    216   tm->rem = 0;
    217   while (curr <= end) {
    218     if (*curr != IAC){
    219 
    220       if (*curr != '\r') {
    221         toybuf[i++] = *curr++;
    222         continue;
    223       } else {
    224         toybuf[i++] = *curr++;
    225         curr++;
    226         if (curr < end && (*curr == '\n' || *curr == '\0'))
    227           curr++;
    228         continue;
    229       }
    230     }
    231 
    232     if ((curr + 1) > end) {
    233       tm->rem = 1;
    234       break;
    235     }
    236     if (*(curr+1) == IAC) { //IAC as data --> IAC IAC
    237       toybuf[i++] = *(curr+1);
    238       curr += 2; //IAC IAC --> 2 bytes
    239       continue;
    240     }
    241     if (*(curr + 1) == NOP || *(curr + 1) == SE) {
    242       curr += 2;
    243       continue;
    244     }
    245 
    246     if (*(curr + 1) == SB ) {
    247       if (*(curr+2) == TELOPT_NAWS) {
    248         struct winsize ws;
    249         if ((curr+8) >= end) {  //ensure we have data to process.
    250           tm->rem = end - curr;
    251           break;
    252         }
    253         ws.ws_col = (curr[3] << 8) | curr[4];
    254         ws.ws_row = (curr[5] << 8) | curr[6];
    255         ioctl(fd, TIOCSWINSZ, (char *)&ws);
    256         curr += 9;
    257         continue;
    258       } else { //eat non-supported sub neg. options.
    259         curr++, tm->rem++;
    260         while (*curr != IAC && curr <= end) {
    261           curr++;
    262           tm->rem++;
    263         }
    264         if (*curr == IAC) {
    265           tm->rem = 0;
    266           continue;
    267         } else break;
    268       }
    269     }
    270     curr += 3; //skip non-supported 3 bytes.
    271   }
    272   memcpy(start, toybuf, i);
    273   memcpy(start + i, end - tm->rem, tm->rem); //put remaining if we break;
    274   return i;
    275 }
    276 
    277 static int dup_iacs(char *start, int fd, int len)
    278 {
    279   char arr[] = {IAC, IAC};
    280   char *needle = NULL;
    281   int ret = 0, c, count = 0;
    282 
    283   while (len) {
    284     if (*start == IAC) {
    285       count = writeall(fd, arr, sizeof(arr));
    286       if (count != 2) break; //short write
    287       start++;
    288       ret++;
    289       len--;
    290       continue;
    291     }
    292     needle = memchr(start, IAC, len);
    293     if (needle) c = needle - start;
    294     else c = len;
    295     count = writeall(fd, start, c);
    296     if (count < 0) break;
    297     len -= count;
    298     ret += count;
    299     start += count;
    300   }
    301   return ret;
    302 }
    303 
    304 void telnetd_main(void)
    305 {
    306   errno = 0;
    307   fd_set rd, wr;
    308   struct term_session *tm = NULL;
    309   struct timeval tv, *tv_ptr = NULL;
    310   int pty_fd, new_fd, c = 0, w, master_fd = 0;
    311   int inetd_m = toys.optflags & FLAG_i;
    312 
    313   if (!(toys.optflags & FLAG_l)) TT.login_path = "/bin/login";
    314   if (!(toys.optflags & FLAG_f)) TT.issue_path = "/etc/issue.net";
    315   if (toys.optflags & FLAG_w) toys.optflags |= FLAG_F;
    316   if (!inetd_m) {
    317     master_fd = listen_socket();
    318     fcntl(master_fd, F_SETFD, FD_CLOEXEC);
    319     if (master_fd > TT.gmax_fd) TT.gmax_fd = master_fd;
    320     if (!(toys.optflags & FLAG_F)) daemon(0, 0);
    321   } else {
    322     pty_fd = new_session(master_fd); //master_fd = 0
    323     if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd;
    324     tm = xzalloc(sizeof(struct term_session));
    325     tm->child_pid = TT.fork_pid;
    326     tm->new_fd = 0;
    327     tm->pty_fd = pty_fd;
    328     if (session_list) {
    329       tm->next = session_list;
    330       session_list = tm;
    331     } else session_list = tm;
    332   }
    333 
    334   if ((toys.optflags & FLAG_w) && !session_list) {
    335     tv.tv_sec = TT.w_sec;
    336     tv.tv_usec = 0;
    337     tv_ptr = &tv;
    338   }
    339   signal(SIGCHLD, generic_signal);
    340 
    341   for (;;) {
    342     FD_ZERO(&rd);
    343     FD_ZERO(&wr);
    344     if (!inetd_m) FD_SET(master_fd, &rd);
    345 
    346     tm = session_list;
    347     while (tm) {
    348 
    349       if (tm->pty_fd > 0 && tm->buff1_avail < BUFSIZE) FD_SET(tm->pty_fd, &rd);
    350       if (tm->new_fd >= 0 && tm->buff2_avail < BUFSIZE) FD_SET(tm->new_fd, &rd);
    351       if (tm->pty_fd > 0 && (tm->buff2_avail - tm->buff2_written) > 0)
    352         FD_SET(tm->pty_fd, &wr);
    353       if (tm->new_fd >= 0 && (tm->buff1_avail - tm->buff1_written) > 0)
    354         FD_SET(tm->new_fd, &wr);
    355       tm = tm->next;
    356     }
    357 
    358 
    359     int r = select(TT.gmax_fd + 1, &rd, &wr, NULL, tv_ptr);
    360     if (!r) return; //timeout
    361     if (r < -1) continue;
    362 
    363     if (!inetd_m && FD_ISSET(master_fd, &rd)) { //accept new connection
    364       new_fd = accept(master_fd, NULL, NULL);
    365       if (new_fd < 0) continue;
    366       tv_ptr = NULL;
    367       fcntl(new_fd, F_SETFD, FD_CLOEXEC);
    368       if (new_fd > TT.gmax_fd) TT.gmax_fd = new_fd;
    369       pty_fd = new_session(new_fd);
    370       if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd;
    371 
    372       tm = xzalloc(sizeof(struct term_session));
    373       tm->child_pid = TT.fork_pid;
    374       tm->new_fd = new_fd;
    375       tm->pty_fd = pty_fd;
    376       if (session_list) {
    377         tm->next = session_list;
    378         session_list = tm;
    379       } else session_list = tm;
    380     }
    381 
    382     tm = session_list;
    383     for (;tm;tm=tm->next) {
    384       if (FD_ISSET(tm->pty_fd, &rd)) {
    385         if ((c = read(tm->pty_fd, tm->buff1 + tm->buff1_avail,
    386                 BUFSIZE-tm->buff1_avail)) <= 0) break;
    387         tm->buff1_avail += c;
    388         if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + inetd_m,
    389                 tm->buff1_avail - tm->buff1_written)) < 0) break;
    390         tm->buff1_written += w;
    391       }
    392       if (FD_ISSET(tm->new_fd, &rd)) {
    393         if ((c = read(tm->new_fd, tm->buff2+tm->buff2_avail,
    394                 BUFSIZE-tm->buff2_avail)) <= 0) break;
    395         c = handle_iacs(tm, c, tm->pty_fd);
    396         tm->buff2_avail += c;
    397         if ((w = write(tm->pty_fd, tm->buff2+ tm->buff2_written,
    398                 tm->buff2_avail - tm->buff2_written)) < 0) break;
    399         tm->buff2_written += w;
    400       }
    401       if (FD_ISSET(tm->pty_fd, &wr)) {
    402         if ((w = write(tm->pty_fd,  tm->buff2 + tm->buff2_written,
    403                 tm->buff2_avail - tm->buff2_written)) < 0) break;
    404         tm->buff2_written += w;
    405       }
    406       if (FD_ISSET(tm->new_fd, &wr)) {
    407         if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + inetd_m,
    408                 tm->buff1_avail - tm->buff1_written)) < 0) break;
    409         tm->buff1_written += w;
    410       }
    411       if (tm->buff1_written == tm->buff1_avail)
    412         tm->buff1_written = tm->buff1_avail = 0;
    413       if (tm->buff2_written == tm->buff2_avail)
    414         tm->buff2_written = tm->buff2_avail = 0;
    415       fflush(NULL);
    416     }
    417 
    418     // Loop to handle (unknown number of) SIGCHLD notifications
    419     while (toys.signal) {
    420       int status;
    421       struct term_session *prev = NULL;
    422       pid_t pid;
    423 
    424       // funny little dance to avoid race conditions.
    425       toys.signal = 0;
    426       pid = waitpid(-1, &status, WNOHANG);
    427       if (pid < 0) break;
    428       toys.signal++;
    429 
    430 
    431       for (tm = session_list; tm; tm = tm->next) {
    432         if (tm->child_pid == pid) break;
    433         prev = tm;
    434       }
    435       if (!tm) return; // reparented child we don't care about
    436 
    437       if (toys.optflags & FLAG_i) exit(EXIT_SUCCESS);
    438       if (!prev) session_list = session_list->next;
    439       else prev->next = tm->next;
    440       utmp_entry();
    441       xclose(tm->pty_fd);
    442       xclose(tm->new_fd);
    443       free(tm);
    444     }
    445   }
    446 }
    447