/* Run the given command line as if with exec. What we actually do is fork the command line as a subprocess, then loop, relaying data between the socket and the subprocess. This allows Ncat to handle SSL from the socket and give plain text to the subprocess, and also allows things like logging and line delays. Never returns. */ void netexec(struct fdinfo *info, char *cmdexec) { int child_stdin[2]; int child_stdout[2]; int pid; int crlf_state; char buf[DEFAULT_TCP_BUF_LEN]; int maxfd; if (o.debug) { switch (o.execmode) { case EXEC_SHELL: logdebug("Executing with shell: %s\n", cmdexec); break; #ifdef HAVE_LUA case EXEC_LUA: logdebug("Executing as lua script: %s\n", cmdexec); break; #endif default: logdebug("Executing: %s\n", cmdexec); break; } } if (pipe(child_stdin) == -1 || pipe(child_stdout) == -1) bye("Can't create child pipes: %s", strerror(errno)); pid = fork(); if (pid == -1) bye("Error in fork: %s", strerror(errno)); if (pid == 0) { /* This is the child process. Exec the command. */ close(child_stdin[1]); close(child_stdout[0]); /* We might have turned off SIGPIPE handling in ncat_listen.c. Since the child process SIGPIPE might mean that the connection got broken, ignoring it could result in an infinite loop if the code here ignores the error codes of read()/write() calls. So, just in case, let's restore SIGPIPE so that writing to a broken pipe results in killing the child process. */ Signal(SIGPIPE, SIG_DFL); /* rearrange stdin and stdout */ Dup2(child_stdin[0], STDIN_FILENO); Dup2(child_stdout[1], STDOUT_FILENO); switch (o.execmode) { char **cmdargs; case EXEC_SHELL: execl("/bin/sh", "sh", "-c", cmdexec, (void *) NULL); break; #ifdef HAVE_LUA case EXEC_LUA: lua_run(); break; #endif default: cmdargs = cmdline_split(cmdexec); execv(cmdargs[0], cmdargs); break; } /* exec failed. */ die("exec"); } close(child_stdin[0]); close(child_stdout[1]); maxfd = child_stdout[0]; if (info->fd > maxfd) maxfd = info->fd; /* This is the parent process. Enter a "caretaker" loop that reads from the socket and writes to the suprocess, and reads from the subprocess and writes to the socket. We exit the loop on any read error (or EOF). On a write error we just close the opposite side of the conversation. */ crlf_state = 0; for (;;) { fd_set fds; int r, n_r; FD_ZERO(&fds); FD_SET(info->fd, &fds); FD_SET(child_stdout[0], &fds); r = fselect(maxfd + 1, &fds, NULL, NULL, NULL); if (r == -1) { if (errno == EINTR) continue; else break; } if (FD_ISSET(info->fd, &fds)) { int pending; do { n_r = ncat_recv(info, buf, sizeof(buf), &pending); if (n_r <= 0) goto loop_end; write_loop(child_stdin[1], buf, n_r); } while (pending); } if (FD_ISSET(child_stdout[0], &fds)) { char *crlf = NULL, *wbuf; n_r = read(child_stdout[0], buf, sizeof(buf)); if (n_r <= 0) break; wbuf = buf; if (o.crlf) { if (fix_line_endings((char *) buf, &n_r, &crlf, &crlf_state)) wbuf = crlf; } ncat_send(info, wbuf, n_r); if (crlf != NULL) free(crlf); } } loop_end: #ifdef HAVE_OPENSSL if (info->ssl != NULL) { SSL_shutdown(info->ssl); SSL_free(info->ssl); } #endif close(info->fd); exit(0); }
/* Run the given command line as if with exec. What we actually do is fork the command line as a subprocess, then loop, relaying data between the socket and the subprocess. This allows Ncat to handle SSL from the socket and give plain text to the subprocess, and also allows things like logging and line delays. Never returns. */ void netexec(struct fdinfo *info, char *cmdexec) { int child_stdin[2]; int child_stdout[2]; int pid; int crlf_state; char buf[DEFAULT_TCP_BUF_LEN]; int maxfd; if (o.debug) { if (o.shellexec) logdebug("Executing with shell: %s\n", cmdexec); else logdebug("Executing: %s\n", cmdexec); } if (pipe(child_stdin) == -1 || pipe(child_stdout) == -1) bye("Can't create child pipes: %s", strerror(errno)); pid = fork(); if (pid == -1) bye("Error in fork: %s", strerror(errno)); if (pid == 0) { /* This is the child process. Exec the command. */ close(child_stdin[1]); close(child_stdout[0]); /* rearrange stdin and stdout */ Dup2(child_stdin[0], STDIN_FILENO); Dup2(child_stdout[1], STDOUT_FILENO); if (o.shellexec) { execl("/bin/sh", "sh", "-c", cmdexec, (void *) NULL); } else { char **cmdargs; cmdargs = cmdline_split(cmdexec); execv(cmdargs[0], cmdargs); } /* exec failed.*/ die("exec"); } close(child_stdin[0]); close(child_stdout[1]); maxfd = child_stdout[0]; if (info->fd > maxfd) maxfd = info->fd; /* This is the parent process. Enter a "caretaker" loop that reads from the socket and writes to the suprocess, and reads from the subprocess and writes to the socket. We exit the loop on any read error (or EOF). On a write error we just close the opposite side of the conversation. */ crlf_state = 0; for (;;) { fd_set fds; int r, n_r, n_w; FD_ZERO(&fds); FD_SET(info->fd, &fds); FD_SET(child_stdout[0], &fds); r = fselect(maxfd + 1, &fds, NULL, NULL, NULL); if (r == -1) { if (errno == EINTR) continue; else break; } if (FD_ISSET(info->fd, &fds)) { int pending; do { n_r = ncat_recv(info, buf, sizeof(buf), &pending); if (n_r <= 0) goto loop_end; n_w = write_loop(child_stdin[1], buf, n_r); } while (pending); } if (FD_ISSET(child_stdout[0], &fds)) { char *crlf = NULL, *wbuf; n_r = read(child_stdout[0], buf, sizeof(buf)); if (n_r <= 0) break; wbuf = buf; if (o.crlf) { if (fix_line_endings((char *) buf, &n_r, &crlf, &crlf_state)) wbuf = crlf; } n_w = ncat_send(info, wbuf, n_r); if (crlf != NULL) free(crlf); } } loop_end: #ifdef HAVE_OPENSSL if (info->ssl != NULL) { SSL_shutdown(info->ssl); SSL_free(info->ssl); } #endif close(info->fd); exit(0); }