telnetd.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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. config TELNETD
  8. bool "telnetd"
  9. default n
  10. help
  11. Handle incoming telnet connections
  12. -l LOGIN Exec LOGIN on connect
  13. -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue
  14. -K Close connection as soon as login exits
  15. -p PORT Port to listen on
  16. -b ADDR[:PORT] Address to bind to
  17. -F Run in foreground
  18. -i Inetd mode
  19. -w SEC Inetd 'wait' mode, linger time SEC
  20. -S Log to syslog (implied by -i or without -F and -w)
  21. */
  22. #define FOR_telnetd
  23. #include "toys.h"
  24. #include <arpa/telnet.h>
  25. GLOBALS(
  26. char *login_path;
  27. char *issue_path;
  28. int port;
  29. char *host_addr;
  30. long w_sec;
  31. int gmax_fd;
  32. pid_t fork_pid;
  33. )
  34. #define BUFSIZE 4*1024
  35. struct term_session {
  36. int new_fd, pty_fd;
  37. pid_t child_pid;
  38. int buff1_avail, buff2_avail;
  39. int buff1_written, buff2_written;
  40. int rem; //unprocessed data from socket
  41. char buff1[BUFSIZE], buff2[BUFSIZE];
  42. struct term_session *next;
  43. };
  44. struct term_session *session_list = NULL;
  45. static void get_sockaddr(char *host, void *buf)
  46. {
  47. in_port_t port_num = htons(TT.port);
  48. struct addrinfo hints, *result;
  49. int status, af = AF_UNSPEC;
  50. char *s;
  51. // [ipv6]:port or exactly one :
  52. if (*host == '[') {
  53. host++;
  54. s = strchr(host, ']');
  55. if (s) *s++ = 0;
  56. else error_exit("bad address '%s'", host-1);
  57. af = AF_INET6;
  58. } else {
  59. s = strrchr(host, ':');
  60. if (s && strchr(host, ':') == s) {
  61. *s = 0;
  62. af = AF_INET;
  63. } else if (s && strchr(host, ':') != s) {
  64. af = AF_INET6;
  65. s = 0;
  66. }
  67. }
  68. if (s++) {
  69. char *ss;
  70. unsigned long p = strtoul(s, &ss, 0);
  71. if (!*s || *ss || p > 65535) error_exit("bad port '%s'", s);
  72. port_num = htons(p);
  73. }
  74. memset(&hints, 0 , sizeof(struct addrinfo));
  75. hints.ai_family = af;
  76. hints.ai_socktype = SOCK_STREAM;
  77. status = getaddrinfo(host, NULL, &hints, &result);
  78. if (status) error_exit("bad address '%s' : %s", host, gai_strerror(status));
  79. memcpy(buf, result->ai_addr, result->ai_addrlen);
  80. freeaddrinfo(result);
  81. if (af == AF_INET) ((struct sockaddr_in*)buf)->sin_port = port_num;
  82. else ((struct sockaddr_in6*)buf)->sin6_port = port_num;
  83. }
  84. static int listen_socket(void)
  85. {
  86. int s, af = AF_INET, yes = 1;
  87. char buf[sizeof(struct sockaddr_storage)];
  88. memset(buf, 0, sizeof(buf));
  89. if (FLAG(b)) {
  90. get_sockaddr(TT.host_addr, buf);
  91. af = ((struct sockaddr *)buf)->sa_family;
  92. } else {
  93. ((struct sockaddr_in*)buf)->sin_port = htons(TT.port);
  94. ((struct sockaddr_in*)buf)->sin_family = af;
  95. }
  96. s = xsocket(af, SOCK_STREAM, 0);
  97. xsetsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
  98. xbind(s, (struct sockaddr *)buf, ((af == AF_INET)?
  99. (sizeof(struct sockaddr_in)):(sizeof(struct sockaddr_in6))));
  100. if (listen(s, 1) < 0) perror_exit("listen");
  101. return s;
  102. }
  103. static void write_issue(char *tty)
  104. {
  105. int size;
  106. char ch = 0;
  107. struct utsname u;
  108. int fd = open(TT.issue_path, O_RDONLY);
  109. if (fd < 0) return ;
  110. uname(&u);
  111. while ((size = readall(fd, &ch, 1)) > 0) {
  112. if (ch == '\\' || ch == '%') {
  113. if (readall(fd, &ch, 1) <= 0) perror_exit("readall!");
  114. if (ch == 's') fputs(u.sysname, stdout);
  115. if (ch == 'n'|| ch == 'h') fputs(u.nodename, stdout);
  116. if (ch == 'r') fputs(u.release, stdout);
  117. if (ch == 'm') fputs(u.machine, stdout);
  118. if (ch == 'l') fputs(tty, stdout);
  119. }
  120. else if (ch == '\n') {
  121. fputs("\n\r\0", stdout);
  122. } else fputc(ch, stdout);
  123. }
  124. fflush(NULL);
  125. close(fd);
  126. }
  127. static int new_session(int sockfd)
  128. {
  129. char *argv_login[] = {NULL, "-h", NULL, NULL};
  130. char tty_name[30]; //tty name length.
  131. int fd, i = 1;
  132. char intial_iacs[] = {IAC, DO, TELOPT_ECHO, IAC, DO, TELOPT_NAWS,
  133. IAC, WILL, TELOPT_ECHO, IAC, WILL, TELOPT_SGA };
  134. struct sockaddr_storage sa;
  135. socklen_t sl = sizeof(sa);
  136. setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i));
  137. writeall(FLAG(i)?1:sockfd, intial_iacs, sizeof(intial_iacs));
  138. if ((TT.fork_pid = forkpty(&fd, tty_name, NULL, NULL)) > 0) return fd;
  139. if (TT.fork_pid < 0) perror_exit("fork");
  140. if (getpeername(sockfd, (void *)&sa, &sl)) perror_exit("getpeername");
  141. if (getnameinfo((void *)&sa, sl, toybuf, sizeof(toybuf), NULL, 0, NI_NUMERICHOST))
  142. perror_exit("getnameinfo");
  143. write_issue(tty_name);
  144. argv_login[0] = TT.login_path;
  145. argv_login[2] = toybuf;
  146. execvp(argv_login[0], argv_login);
  147. exit(EXIT_FAILURE);
  148. }
  149. static int handle_iacs(struct term_session *tm, int c, int fd)
  150. {
  151. char *curr ,*start,*end;
  152. int i = 0;
  153. curr = start = tm->buff2+tm->buff2_avail;
  154. end = tm->buff2 + c -1;
  155. tm->rem = 0;
  156. while (curr <= end) {
  157. if (*curr != IAC){
  158. if (*curr != '\r') {
  159. toybuf[i++] = *curr++;
  160. continue;
  161. } else {
  162. toybuf[i++] = *curr++;
  163. curr++;
  164. if (curr < end && (*curr == '\n' || *curr == '\0'))
  165. curr++;
  166. continue;
  167. }
  168. }
  169. if ((curr + 1) > end) {
  170. tm->rem = 1;
  171. break;
  172. }
  173. if (*(curr+1) == IAC) { //IAC as data --> IAC IAC
  174. toybuf[i++] = *(curr+1);
  175. curr += 2; //IAC IAC --> 2 bytes
  176. continue;
  177. }
  178. if (*(curr + 1) == NOP || *(curr + 1) == SE) {
  179. curr += 2;
  180. continue;
  181. }
  182. if (*(curr + 1) == SB ) {
  183. if (*(curr+2) == TELOPT_NAWS) {
  184. struct winsize ws;
  185. if ((curr+8) >= end) { //ensure we have data to process.
  186. tm->rem = end - curr;
  187. break;
  188. }
  189. ws.ws_col = (curr[3] << 8) | curr[4];
  190. ws.ws_row = (curr[5] << 8) | curr[6];
  191. ioctl(fd, TIOCSWINSZ, (char *)&ws);
  192. curr += 9;
  193. continue;
  194. } else { //eat non-supported sub neg. options.
  195. curr++, tm->rem++;
  196. while (*curr != IAC && curr <= end) {
  197. curr++;
  198. tm->rem++;
  199. }
  200. if (*curr == IAC) {
  201. tm->rem = 0;
  202. continue;
  203. } else break;
  204. }
  205. }
  206. curr += 3; //skip non-supported 3 bytes.
  207. }
  208. memcpy(start, toybuf, i);
  209. memcpy(start + i, end - tm->rem, tm->rem); //put remaining if we break;
  210. return i;
  211. }
  212. static int dup_iacs(char *start, int fd, int len)
  213. {
  214. char arr[] = {IAC, IAC};
  215. char *needle = NULL;
  216. int ret = 0, c, count = 0;
  217. while (len) {
  218. if (*start == IAC) {
  219. count = writeall(fd, arr, sizeof(arr));
  220. if (count != 2) break; //short write
  221. start++;
  222. ret++;
  223. len--;
  224. continue;
  225. }
  226. needle = memchr(start, IAC, len);
  227. if (needle) c = needle - start;
  228. else c = len;
  229. count = writeall(fd, start, c);
  230. if (count < 0) break;
  231. len -= count;
  232. ret += count;
  233. start += count;
  234. }
  235. return ret;
  236. }
  237. void telnetd_main(void)
  238. {
  239. fd_set rd, wr;
  240. struct term_session *tm = NULL;
  241. struct timeval tv, *tv_ptr = NULL;
  242. int pty_fd, new_fd, c = 0, w, master_fd = 0;
  243. if (!FLAG(l)) TT.login_path = "/bin/login";
  244. if (!FLAG(f)) TT.issue_path = "/etc/issue.net";
  245. if (FLAG(w)) toys.optflags |= FLAG_F;
  246. if (!FLAG(i)) {
  247. master_fd = listen_socket();
  248. fcntl(master_fd, F_SETFD, FD_CLOEXEC);
  249. if (master_fd > TT.gmax_fd) TT.gmax_fd = master_fd;
  250. if (!FLAG(F)) daemon(0, 0);
  251. } else {
  252. pty_fd = new_session(master_fd); //master_fd = 0
  253. if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd;
  254. tm = xzalloc(sizeof(struct term_session));
  255. tm->child_pid = TT.fork_pid;
  256. tm->new_fd = 0;
  257. tm->pty_fd = pty_fd;
  258. if (session_list) {
  259. tm->next = session_list;
  260. session_list = tm;
  261. } else session_list = tm;
  262. }
  263. if (FLAG(w) && !session_list) {
  264. tv.tv_sec = TT.w_sec;
  265. tv.tv_usec = 0;
  266. tv_ptr = &tv;
  267. }
  268. signal(SIGCHLD, generic_signal);
  269. for (;;) {
  270. FD_ZERO(&rd);
  271. FD_ZERO(&wr);
  272. if (!FLAG(i)) FD_SET(master_fd, &rd);
  273. tm = session_list;
  274. while (tm) {
  275. if (tm->pty_fd > 0 && tm->buff1_avail < BUFSIZE) FD_SET(tm->pty_fd, &rd);
  276. if (tm->new_fd >= 0 && tm->buff2_avail < BUFSIZE) FD_SET(tm->new_fd, &rd);
  277. if (tm->pty_fd > 0 && (tm->buff2_avail - tm->buff2_written) > 0)
  278. FD_SET(tm->pty_fd, &wr);
  279. if (tm->new_fd >= 0 && (tm->buff1_avail - tm->buff1_written) > 0)
  280. FD_SET(tm->new_fd, &wr);
  281. tm = tm->next;
  282. }
  283. int r = select(TT.gmax_fd + 1, &rd, &wr, NULL, tv_ptr);
  284. if (!r) error_exit("select timed out");
  285. if (r < -1) continue;
  286. if (!FLAG(i) && FD_ISSET(master_fd, &rd)) { //accept new connection
  287. new_fd = accept(master_fd, NULL, NULL);
  288. if (new_fd < 0) continue;
  289. tv_ptr = NULL;
  290. fcntl(new_fd, F_SETFD, FD_CLOEXEC);
  291. if (new_fd > TT.gmax_fd) TT.gmax_fd = new_fd;
  292. pty_fd = new_session(new_fd);
  293. if (pty_fd > TT.gmax_fd) TT.gmax_fd = pty_fd;
  294. tm = xzalloc(sizeof(struct term_session));
  295. tm->child_pid = TT.fork_pid;
  296. tm->new_fd = new_fd;
  297. tm->pty_fd = pty_fd;
  298. if (session_list) {
  299. tm->next = session_list;
  300. session_list = tm;
  301. } else session_list = tm;
  302. }
  303. tm = session_list;
  304. for (;tm;tm=tm->next) {
  305. if (FD_ISSET(tm->pty_fd, &rd)) {
  306. if ((c = read(tm->pty_fd, tm->buff1 + tm->buff1_avail,
  307. BUFSIZE-tm->buff1_avail)) <= 0) break;
  308. tm->buff1_avail += c;
  309. if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + FLAG(i),
  310. tm->buff1_avail - tm->buff1_written)) < 0) break;
  311. tm->buff1_written += w;
  312. }
  313. if (FD_ISSET(tm->new_fd, &rd)) {
  314. if ((c = read(tm->new_fd, tm->buff2+tm->buff2_avail,
  315. BUFSIZE-tm->buff2_avail)) <= 0) {
  316. // The other side went away without a proper shutdown. Happens if
  317. // you exit telnet via ^]^D, leaving the socket in TIME_WAIT.
  318. xclose(tm->new_fd);
  319. tm->new_fd = -1;
  320. xclose(tm->pty_fd);
  321. tm->pty_fd = -1;
  322. break;
  323. }
  324. c = handle_iacs(tm, c, tm->pty_fd);
  325. tm->buff2_avail += c;
  326. if ((w = write(tm->pty_fd, tm->buff2+ tm->buff2_written,
  327. tm->buff2_avail - tm->buff2_written)) < 0) break;
  328. tm->buff2_written += w;
  329. }
  330. if (FD_ISSET(tm->pty_fd, &wr)) {
  331. if ((w = write(tm->pty_fd, tm->buff2 + tm->buff2_written,
  332. tm->buff2_avail - tm->buff2_written)) < 0) break;
  333. tm->buff2_written += w;
  334. }
  335. if (FD_ISSET(tm->new_fd, &wr)) {
  336. if ((w = dup_iacs(tm->buff1 + tm->buff1_written, tm->new_fd + FLAG(i),
  337. tm->buff1_avail - tm->buff1_written)) < 0) break;
  338. tm->buff1_written += w;
  339. }
  340. if (tm->buff1_written == tm->buff1_avail)
  341. tm->buff1_written = tm->buff1_avail = 0;
  342. if (tm->buff2_written == tm->buff2_avail)
  343. tm->buff2_written = tm->buff2_avail = 0;
  344. fflush(NULL);
  345. }
  346. // Loop to handle (unknown number of) SIGCHLD notifications
  347. while (toys.signal) {
  348. int status;
  349. struct term_session *prev = NULL;
  350. pid_t pid;
  351. // funny little dance to avoid race conditions.
  352. toys.signal = 0;
  353. pid = waitpid(-1, &status, WNOHANG);
  354. if (pid <= 0) break;
  355. toys.signal++;
  356. for (tm = session_list; tm; tm = tm->next) {
  357. if (tm->child_pid == pid) break;
  358. prev = tm;
  359. }
  360. if (!tm) error_exit("unexpected reparenting of %d", pid);
  361. if (FLAG(i)) exit(EXIT_SUCCESS);
  362. if (!prev) session_list = session_list->next;
  363. else prev->next = tm->next;
  364. xclose(tm->pty_fd);
  365. xclose(tm->new_fd);
  366. free(tm);
  367. }
  368. }
  369. }