1 /* telnetd.c - Telnet Server 2 * 3 * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com> 4 * Copyright 2013 Kyungwan Han <asura321@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 xbind(s, (struct sockaddr *)buf, ((af == AF_INET)? 147 (sizeof(struct sockaddr_in)):(sizeof(struct sockaddr_in6)))); 148 149 if (listen(s, 1) < 0) perror_exit("listen"); 150 return s; 151 } 152 153 static void write_issue(char *tty) 154 { 155 int size; 156 char ch = 0; 157 struct utsname u; 158 int fd = open(TT.issue_path, O_RDONLY); 159 160 if (fd < 0) return ; 161 uname(&u); 162 while ((size = readall(fd, &ch, 1)) > 0) { 163 if (ch == '\\' || ch == '%') { 164 if (readall(fd, &ch, 1) <= 0) perror_exit("readall!"); 165 if (ch == 's') fputs(u.sysname, stdout); 166 if (ch == 'n'|| ch == 'h') fputs(u.nodename, stdout); 167 if (ch == 'r') fputs(u.release, stdout); 168 if (ch == 'm') fputs(u.machine, stdout); 169 if (ch == 'l') fputs(tty, stdout); 170 } 171 else if (ch == '\n') { 172 fputs("\n\r\0", stdout); 173 } else fputc(ch, stdout); 174 } 175 fflush(NULL); 176 close(fd); 177 } 178 179 static int new_session(int sockfd) 180 { 181 char *argv_login[2]; //arguments for execvp cmd, NULL 182 char tty_name[30]; //tty name length. 183 int fd, flags, i = 1; 184 char intial_iacs[] = {IAC, DO, TELOPT_ECHO, IAC, DO, TELOPT_NAWS, 185 IAC, WILL, TELOPT_ECHO, IAC, WILL, TELOPT_SGA }; 186 187 setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)); 188 flags = fcntl(sockfd, F_GETFL); 189 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 190 if (toys.optflags & FLAG_i) fcntl((sockfd + 1), F_SETFL, flags | O_NONBLOCK); 191 192 writeall((toys.optflags & FLAG_i)?1:sockfd, intial_iacs, sizeof(intial_iacs)); 193 if ((TT.fork_pid = forkpty(&fd, tty_name, NULL, NULL)) > 0) { 194 flags = fcntl(fd, F_GETFL); 195 fcntl(fd, F_SETFL, flags | O_NONBLOCK); 196 return fd; 197 } 198 if (TT.fork_pid < 0) perror_exit("fork"); 199 write_issue(tty_name); 200 argv_login[0] = strdup(TT.login_path); 201 argv_login[1] = NULL; 202 execvp(argv_login[0], argv_login); 203 exit(EXIT_FAILURE); 204 } 205 206 static int handle_iacs(struct term_session *tm, int c, int fd) 207 { 208 char *curr ,*start,*end; 209 int i = 0; 210 211 curr = start = tm->buff2+tm->buff2_avail; 212 end = tm->buff2 + c -1; 213 tm->rem = 0; 214 while (curr <= end) { 215 if (*curr != IAC){ 216 217 if (*curr != '\r') { 218 toybuf[i++] = *curr++; 219 continue; 220 } else { 221 toybuf[i++] = *curr++; 222 curr++; 223 if (curr < end && (*curr == '\n' || *curr == '\0')) 224 curr++; 225 continue; 226 } 227 } 228 229 if ((curr + 1) > end) { 230 tm->rem = 1; 231 break; 232 } 233 if (*(curr+1) == IAC) { //IAC as data --> IAC IAC 234 toybuf[i++] = *(curr+1); 235 curr += 2; //IAC IAC --> 2 bytes 236 continue; 237 } 238 if (*(curr + 1) == NOP || *(curr + 1) == SE) { 239 curr += 2; 240 continue; 241 } 242 243 if (*(curr + 1) == SB ) { 244 if (*(curr+2) == TELOPT_NAWS) { 245 struct winsize ws; 246 if ((curr+8) >= end) { //ensure we have data to process. 247 tm->rem = end - curr; 248 break; 249 } 250 ws.ws_col = (curr[3] << 8) | curr[4]; 251 ws.ws_row = (curr[5] << 8) | curr[6]; 252 ioctl(fd, TIOCSWINSZ, (char *)&ws); 253 curr += 9; 254 continue; 255 } else { //eat non-supported sub neg. options. 256 curr++, tm->rem++; 257 while (*curr != IAC && curr <= end) { 258 curr++; 259 tm->rem++; 260 } 261 if (*curr == IAC) { 262 tm->rem = 0; 263 continue; 264 } else break; 265 } 266 } 267 curr += 3; //skip non-supported 3 bytes. 268 } 269 memcpy(start, toybuf, i); 270 memcpy(start + i, end - tm->rem, tm->rem); //put remaining if we break; 271 return i; 272 } 273 274 static int dup_iacs(char *start, int fd, int len) 275 { 276 char arr[] = {IAC, IAC}; 277 char *needle = NULL; 278 int ret = 0, c, count = 0; 279 280 while (len) { 281 if (*start == IAC) { 282 count = writeall(fd, arr, sizeof(arr)); 283 if (count != 2) break; //short write 284 start++; 285 ret++; 286 len--; 287 continue; 288 } 289 needle = memchr(start, IAC, len); 290 if (needle) c = needle - start; 291 else c = len; 292 count = writeall(fd, start, c); 293 if (count < 0) break; 294 len -= count; 295 ret += count; 296 start += count; 297 } 298 return ret; 299 } 300 301 void telnetd_main(void) 302 { 303 errno = 0; 304 fd_set rd, wr; 305 struct term_session *tm = NULL; 306 struct timeval tv, *tv_ptr = NULL; 307 int pty_fd, new_fd, c = 0, w, master_fd = 0; 308 int inetd_m = toys.optflags & FLAG_i; 309 310 if (!(toys.optflags & FLAG_l)) TT.login_path = "/bin/login"; 311 if (!(toys.optflags & FLAG_f)) TT.issue_path = "/etc/issue.net"; 312 if (toys.optflags & FLAG_w) toys.optflags |= FLAG_F; 313 if (!inetd_m) { 314 master_fd = listen_socket(); 315 fcntl(master_fd, F_SETFD, FD_CLOEXEC); 316 if (master_fd > TT.gmax_fd) TT.gmax_fd = master_fd; 317 if (!(toys.optflags & FLAG_F)) daemon(0, 0); 318 } else { 319 pty_fd = new_session(master_fd); //master_fd = 0 320 if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd; 321 tm = xzalloc(sizeof(struct term_session)); 322 tm->child_pid = TT.fork_pid; 323 tm->new_fd = 0; 324 tm->pty_fd = pty_fd; 325 if (session_list) { 326 tm->next = session_list; 327 session_list = tm; 328 } else session_list = tm; 329 } 330 331 if ((toys.optflags & FLAG_w) && !session_list) { 332 tv.tv_sec = TT.w_sec; 333 tv.tv_usec = 0; 334 tv_ptr = &tv; 335 } 336 signal(SIGCHLD, generic_signal); 337 338 for (;;) { 339 FD_ZERO(&rd); 340 FD_ZERO(&wr); 341 if (!inetd_m) FD_SET(master_fd, &rd); 342 343 tm = session_list; 344 while (tm) { 345 346 if (tm->pty_fd > 0 && tm->buff1_avail < BUFSIZE) FD_SET(tm->pty_fd, &rd); 347 if (tm->new_fd >= 0 && tm->buff2_avail < BUFSIZE) FD_SET(tm->new_fd, &rd); 348 if (tm->pty_fd > 0 && (tm->buff2_avail - tm->buff2_written) > 0) 349 FD_SET(tm->pty_fd, &wr); 350 if (tm->new_fd >= 0 && (tm->buff1_avail - tm->buff1_written) > 0) 351 FD_SET(tm->new_fd, &wr); 352 tm = tm->next; 353 } 354 355 356 int r = select(TT.gmax_fd + 1, &rd, &wr, NULL, tv_ptr); 357 if (!r) return; //timeout 358 if (r < -1) continue; 359 360 if (!inetd_m && FD_ISSET(master_fd, &rd)) { //accept new connection 361 new_fd = accept(master_fd, NULL, NULL); 362 if (new_fd < 0) continue; 363 tv_ptr = NULL; 364 fcntl(new_fd, F_SETFD, FD_CLOEXEC); 365 if (new_fd > TT.gmax_fd) TT.gmax_fd = new_fd; 366 pty_fd = new_session(new_fd); 367 if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd; 368 369 tm = xzalloc(sizeof(struct term_session)); 370 tm->child_pid = TT.fork_pid; 371 tm->new_fd = new_fd; 372 tm->pty_fd = pty_fd; 373 if (session_list) { 374 tm->next = session_list; 375 session_list = tm; 376 } else session_list = tm; 377 } 378 379 tm = session_list; 380 for (;tm;tm=tm->next) { 381 if (FD_ISSET(tm->pty_fd, &rd)) { 382 if ((c = read(tm->pty_fd, tm->buff1 + tm->buff1_avail, 383 BUFSIZE-tm->buff1_avail)) <= 0) break; 384 tm->buff1_avail += c; 385 if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + inetd_m, 386 tm->buff1_avail - tm->buff1_written)) < 0) break; 387 tm->buff1_written += w; 388 } 389 if (FD_ISSET(tm->new_fd, &rd)) { 390 if ((c = read(tm->new_fd, tm->buff2+tm->buff2_avail, 391 BUFSIZE-tm->buff2_avail)) <= 0) break; 392 c = handle_iacs(tm, c, tm->pty_fd); 393 tm->buff2_avail += c; 394 if ((w = write(tm->pty_fd, tm->buff2+ tm->buff2_written, 395 tm->buff2_avail - tm->buff2_written)) < 0) break; 396 tm->buff2_written += w; 397 } 398 if (FD_ISSET(tm->pty_fd, &wr)) { 399 if ((w = write(tm->pty_fd, tm->buff2 + tm->buff2_written, 400 tm->buff2_avail - tm->buff2_written)) < 0) break; 401 tm->buff2_written += w; 402 } 403 if (FD_ISSET(tm->new_fd, &wr)) { 404 if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + inetd_m, 405 tm->buff1_avail - tm->buff1_written)) < 0) break; 406 tm->buff1_written += w; 407 } 408 if (tm->buff1_written == tm->buff1_avail) 409 tm->buff1_written = tm->buff1_avail = 0; 410 if (tm->buff2_written == tm->buff2_avail) 411 tm->buff2_written = tm->buff2_avail = 0; 412 fflush(NULL); 413 } 414 415 // Loop to handle (unknown number of) SIGCHLD notifications 416 while (toys.signal) { 417 int status; 418 struct term_session *prev = NULL; 419 pid_t pid; 420 421 // funny little dance to avoid race conditions. 422 toys.signal = 0; 423 pid = waitpid(-1, &status, WNOHANG); 424 if (pid < 0) break; 425 toys.signal++; 426 427 428 for (tm = session_list; tm; tm = tm->next) { 429 if (tm->child_pid == pid) break; 430 prev = tm; 431 } 432 if (!tm) return; // reparented child we don't care about 433 434 if (toys.optflags & FLAG_i) exit(EXIT_SUCCESS); 435 if (!prev) session_list = session_list->next; 436 else prev->next = tm->next; 437 utmp_entry(); 438 xclose(tm->pty_fd); 439 xclose(tm->new_fd); 440 free(tm); 441 } 442 } 443 } 444