static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); char *buf, *tmp = NULL; int nbytes; assert(type == NSE_TYPE_READ); if (status == NSE_STATUS_EOF) { if (o.sendonly) { /* In --send-only mode, exit after EOF on stdin. */ nsock_loop_quit(nsp); } else { shutdown(nsi_getsd(cs.sock_nsi), SHUT_WR); } return; } else if (status == NSE_STATUS_ERROR) { loguser("%s.\n", socket_strerror(nse_errorcode(evt))); exit(1); } else if (status == NSE_STATUS_TIMEOUT) { loguser("%s.\n", socket_strerror(ETIMEDOUT)); exit(1); } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { return; } else { assert(status == NSE_STATUS_SUCCESS); } buf = nse_readbuf(evt, &nbytes); /* read from stdin */ if (o.linedelay) ncat_delay_timer(o.linedelay); if (o.crlf) { if (fix_line_endings(buf, &nbytes, &tmp, &cs.crlf_state)) buf = tmp; } nsock_write(nsp, cs.sock_nsi, write_socket_handler, -1, NULL, buf, nbytes); ncat_log_send(buf, nbytes); if (tmp) free(tmp); refresh_idle_timer(nsp); }
/* Read from stdin and broadcast to all client sockets. Return the number of bytes read, or -1 on error. */ int read_stdin(void) { int nbytes; char buf[DEFAULT_TCP_BUF_LEN]; char *tempbuf = NULL; nbytes = read(STDIN_FILENO, buf, sizeof(buf)); if (nbytes <= 0) { if (nbytes < 0 && o.verbose) logdebug("Error reading from stdin: %s\n", strerror(errno)); if (nbytes == 0 && o.debug) logdebug("EOF on stdin\n"); /* Don't close the file because that allows a socket to be fd 0. */ FD_CLR(STDIN_FILENO, &master_readfds); /* Buf mark that we've seen EOF so it doesn't get re-added to the select list. */ stdin_eof = 1; return nbytes; } if (o.crlf) fix_line_endings((char *) buf, &nbytes, &tempbuf, &crlf_state); if (o.linedelay) ncat_delay_timer(o.linedelay); /* Write to everything in the broadcast set. */ if (tempbuf != NULL) { ncat_broadcast(&master_broadcastfds, &broadcast_fdlist, tempbuf, nbytes); free(tempbuf); tempbuf = NULL; } else { ncat_broadcast(&master_broadcastfds, &broadcast_fdlist, buf, nbytes); } return nbytes; }
/* Relay data between a socket and a process until the process dies or stops sending or receiving data. The socket descriptor and process pipe handles are in the data argument, which must be a pointer to struct subprocess_info. This function is a workaround for the fact that we can't just run a process after redirecting its input handles to a socket. If the process, for example, redirects its own stdin, it somehow confuses the socket and stdout stops working. This is exactly what ncat does (as part of the Windows stdin workaround), so it can't be ignored. This function can be invoked through CreateThread to simulate fork+exec, or called directly to simulate exec. It frees the subprocess_info struct and closes the socket and pipe handles before returning. Returns the exit code of the subprocess. */ static DWORD WINAPI subprocess_thread_func(void *data) { struct subprocess_info *info; char pipe_buffer[BUFSIZ]; OVERLAPPED overlap = { 0 }; HANDLE events[3]; DWORD ret, rc; int crlf_state = 0; info = (struct subprocess_info *) data; /* Three events we watch for: socket read, pipe read, and process end. */ events[0] = (HANDLE) WSACreateEvent(); WSAEventSelect(info->fdn.fd, events[0], FD_READ | FD_CLOSE); events[1] = info->child_out_r; events[2] = info->proc; /* To avoid blocking or polling, we use asynchronous I/O, or what Microsoft calls "overlapped" I/O, on the process pipe. WaitForMultipleObjects reports when the read operation is complete. */ ReadFile(info->child_out_r, pipe_buffer, sizeof(pipe_buffer), NULL, &overlap); /* Loop until EOF or error. */ for (;;) { DWORD n, nwritten; int i; i = WaitForMultipleObjects(3, events, FALSE, INFINITE); if (i == WAIT_OBJECT_0) { /* Read from socket, write to process. */ char buffer[BUFSIZ]; int pending; ResetEvent(events[0]); do { n = ncat_recv(&info->fdn, buffer, sizeof(buffer), &pending); if (n <= 0) goto loop_end; if (WriteFile(info->child_in_w, buffer, n, &nwritten, NULL) == 0) break; if (nwritten != n) goto loop_end; } while (pending); } else if (i == WAIT_OBJECT_0 + 1) { char *crlf = NULL, *wbuf; /* Read from process, write to socket. */ if (GetOverlappedResult(info->child_out_r, &overlap, &n, FALSE)) { int n_r; wbuf = pipe_buffer; n_r = n; if (o.crlf) { if (fix_line_endings((char *) pipe_buffer, &n_r, &crlf, &crlf_state)) wbuf = crlf; } /* The above call to WSAEventSelect puts the socket in non-blocking mode, but we want this send to block, not potentially return WSAEWOULDBLOCK. We call block_socket, but first we must clear out the select event. */ WSAEventSelect(info->fdn.fd, events[0], 0); block_socket(info->fdn.fd); nwritten = ncat_send(&info->fdn, wbuf, n_r); if (crlf != NULL) free(crlf); if (nwritten != n_r) break; /* Restore the select event (and non-block the socket again.) */ WSAEventSelect(info->fdn.fd, events[0], FD_READ | FD_CLOSE); /* Queue another ansychronous read. */ ReadFile(info->child_out_r, pipe_buffer, sizeof(pipe_buffer), NULL, &overlap); } else { if (GetLastError() != ERROR_IO_PENDING) /* Error or end of file. */ break; } } else if (i == WAIT_OBJECT_0 + 2) { /* The child died. There are no more writes left in the pipe because WaitForMultipleObjects guarantees events with lower indexes are handled first. */ break; } else { break; } } loop_end: WSACloseEvent(events[0]); rc = unregister_subprocess(info->proc); ncat_assert(rc != -1); GetExitCodeProcess(info->proc, &ret); if (ret == STILL_ACTIVE) { if (o.debug > 1) logdebug("Subprocess still running, terminating it.\n"); rc = TerminateProcess(info->proc, 0); if (rc == 0) { if (o.debug > 1) logdebug("TerminateProcess failed with code %d.\n", rc); } } GetExitCodeProcess(info->proc, &ret); if (o.debug > 1) logdebug("Subprocess ended with exit code %d.\n", ret); shutdown(info->fdn.fd, 2); subprocess_info_close(info); free(info); rc = WaitForSingleObject(pseudo_sigchld_mutex, INFINITE); ncat_assert(rc == WAIT_OBJECT_0); if (pseudo_sigchld_handler != NULL) pseudo_sigchld_handler(); rc = ReleaseMutex(pseudo_sigchld_mutex); ncat_assert(rc != 0); return ret; }
/* 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); }
/* Read from recv_fd and broadcast whatever is read to all other descriptors in read_fds, with the exception of stdin, listen_socket, and recv_fd itself. Handles EOL translation and chat mode. On read error or end of stream, closes the socket and removes it from the read_fds list. */ static void read_and_broadcast(int recv_fd) { struct fdinfo *fdn; int pending; fdn = get_fdinfo(&client_fdlist, recv_fd); ncat_assert(fdn != NULL); /* Loop while ncat_recv indicates data is pending. */ do { char buf[DEFAULT_TCP_BUF_LEN]; char *chatbuf, *outbuf; char *tempbuf = NULL; fd_set broadcastfds; int n; /* Behavior differs depending on whether this is stdin or a socket. */ if (recv_fd == STDIN_FILENO) { n = read(recv_fd, buf, sizeof(buf)); if (n <= 0) { if (n < 0 && o.verbose) logdebug("Error reading from stdin: %s\n", strerror(errno)); if (n == 0 && o.debug) logdebug("EOF on stdin\n"); /* Don't close the file because that allows a socket to be fd 0. */ FD_CLR(recv_fd, &master_readfds); /* But mark that we've seen EOF so it doesn't get re-added to the select list. */ stdin_eof = 1; return; } if (o.crlf) fix_line_endings((char *) buf, &n, &tempbuf, &crlf_state); pending = 0; } else { /* From a connected socket, not stdin. */ n = ncat_recv(fdn, buf, sizeof(buf), &pending); if (n <= 0) { if (o.debug) logdebug("Closing connection.\n"); #ifdef HAVE_OPENSSL if (o.ssl && fdn->ssl) { if (n == 0) SSL_shutdown(fdn->ssl); SSL_free(fdn->ssl); } #endif close(recv_fd); FD_CLR(recv_fd, &master_readfds); rm_fd(&client_fdlist, recv_fd); FD_CLR(recv_fd, &master_broadcastfds); rm_fd(&broadcast_fdlist, recv_fd); conn_inc--; if (conn_inc == 0) FD_CLR(STDIN_FILENO, &master_readfds); if (o.chat) chat_announce_disconnect(recv_fd); return; } } if (o.debug > 1) logdebug("Handling data from client %d.\n", recv_fd); chatbuf = NULL; /* tempbuf is in use if we read from STDIN and fixed EOL */ if (tempbuf == NULL) outbuf = buf; else outbuf = tempbuf; if (o.chat) { chatbuf = chat_filter(outbuf, n, recv_fd, &n); if (chatbuf == NULL) { if (o.verbose) logdebug("Error formatting chat message from fd %d\n", recv_fd); } else { outbuf = chatbuf; } } /* Send to everyone except the one who sent this message. */ broadcastfds = master_broadcastfds; FD_CLR(recv_fd, &broadcastfds); ncat_broadcast(&broadcastfds, &broadcast_fdlist, outbuf, n); free(chatbuf); free(tempbuf); tempbuf = NULL; } while (pending); }
/* This is sufficiently different from the TCP code (wrt SSL, etc) that it * resides in its own simpler function */ static int ncat_listen_dgram(int proto) { struct { int fd; union sockaddr_u addr; } sockfd[NUM_LISTEN_ADDRS]; int i, fdn = -1; int fdmax, nbytes, n, fds_ready; char buf[DEFAULT_UDP_BUF_LEN] = { 0 }; char *tempbuf = NULL; fd_set read_fds; union sockaddr_u remotess; socklen_t sslen = sizeof(remotess.storage); struct timeval tv; struct timeval *tvp = NULL; unsigned int num_sockets; for (i = 0; i < NUM_LISTEN_ADDRS; i++) { sockfd[i].fd = -1; sockfd[i].addr.storage.ss_family = AF_UNSPEC; } FD_ZERO(&read_fds); /* Initialize remotess struct so recvfrom() doesn't hit the fan.. */ zmem(&remotess.storage, sizeof(remotess.storage)); remotess.storage.ss_family = o.af; #ifdef WIN32 set_pseudo_sigchld_handler(decrease_conn_count); #else /* Reap on SIGCHLD */ Signal(SIGCHLD, sigchld_handler); /* Ignore the SIGPIPE that occurs when a client disconnects suddenly and we send data to it before noticing. */ Signal(SIGPIPE, SIG_IGN); #endif /* Not sure if this problem exists on Windows, but fcntl and /dev/null don't */ #ifndef WIN32 /* Check whether stdin is closed. Because we treat this fd specially, we * can't risk it being reopened for an incoming connection, so we'll hold * it open instead. */ if (fcntl(STDIN_FILENO, F_GETFD) == -1 && errno == EBADF) { logdebug("stdin is closed, attempting to reserve STDIN_FILENO\n"); i = open("/dev/null", O_RDONLY); if (i >= 0 && i != STDIN_FILENO) { /* Oh well, we tried */ logdebug("Couldn't reserve STDIN_FILENO\n"); close(i); } } #endif /* set for selecting udp listening sockets */ fd_set listen_fds; fd_list_t listen_fdlist; FD_ZERO(&listen_fds); init_fdlist(&listen_fdlist, num_listenaddrs); num_sockets = 0; for (i = 0; i < num_listenaddrs; i++) { /* create the UDP listen sockets */ sockfd[num_sockets].fd = do_listen(SOCK_DGRAM, proto, &listenaddrs[i]); if (sockfd[num_sockets].fd == -1) { if (o.debug > 0) logdebug("do_listen(\"%s\"): %s\n", inet_ntop_ez(&listenaddrs[i].storage, sizeof(listenaddrs[i].storage)), socket_strerror(socket_errno())); continue; } FD_SET(sockfd[num_sockets].fd, &listen_fds); add_fd(&listen_fdlist, sockfd[num_sockets].fd); sockfd[num_sockets].addr = listenaddrs[i]; num_sockets++; } if (num_sockets == 0) { if (num_listenaddrs == 1) bye("Unable to open listening socket on %s: %s", inet_ntop_ez(&listenaddrs[0].storage, sizeof(listenaddrs[0].storage)), socket_strerror(socket_errno())); else bye("Unable to open any listening sockets."); } if (o.idletimeout > 0) tvp = &tv; while (1) { int i, j, conn_count, socket_n; if (fdn != -1) { /*remove socket descriptor which is burnt */ FD_CLR(sockfd[fdn].fd, &listen_fds); rm_fd(&listen_fdlist, sockfd[fdn].fd); /* Rebuild the udp socket which got burnt */ sockfd[fdn].fd = do_listen(SOCK_DGRAM, proto, &sockfd[fdn].addr); if (sockfd[fdn].fd == -1) bye("do_listen: %s", socket_strerror(socket_errno())); FD_SET(sockfd[fdn].fd, &listen_fds); add_fd(&listen_fdlist, sockfd[fdn].fd); } fdn = -1; socket_n = -1; fd_set fds; FD_ZERO(&fds); while (1) { /* * We just select to get a list of sockets which we can talk to */ if (o.debug > 1) logdebug("selecting, fdmax %d\n", listen_fdlist.fdmax); fds = listen_fds; if (o.idletimeout > 0) ms_to_timeval(tvp, o.idletimeout); fds_ready = fselect(listen_fdlist.fdmax + 1, &fds, NULL, NULL, tvp); if (o.debug > 1) logdebug("select returned %d fds ready\n", fds_ready); if (fds_ready == 0) bye("Idle timeout expired (%d ms).", o.idletimeout); /* * Figure out which listening socket got a connection. This loop should * really call a function for each ready socket instead of breaking on * the first one. */ for (i = 0; i <= listen_fdlist.fdmax && fds_ready > 0; i++) { /* Loop through descriptors until there is something ready */ if (!FD_ISSET(i, &fds)) continue; /* Check each listening socket */ for (j = 0; j < num_sockets; j++) { if (i == sockfd[j].fd) { if (o.debug > 1) logdebug("Valid descriptor %d \n", i); fdn = j; socket_n = i; break; } } /* if we found a valid socket break */ if (fdn != -1) { fds_ready--; break; } } /* Make sure someone connected */ if (fdn == -1) continue; /* * We just peek so we can get the client connection details without * removing anything from the queue. Sigh. */ nbytes = recvfrom(socket_n, buf, sizeof(buf), MSG_PEEK, &remotess.sockaddr, &sslen); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } /* Check conditions that might cause us to deny the connection. */ conn_count = get_conn_count(); if (conn_count >= o.conn_limit) { if (o.verbose) loguser("New connection denied: connection limit reached (%d)\n", conn_count); } else if (!allow_access(&remotess)) { if (o.verbose) loguser("New connection denied: not allowed\n"); } else { /* Good to go. */ break; } /* Dump the current datagram */ nbytes = recv(socket_n, buf, sizeof(buf), 0); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } ncat_log_recv(buf, nbytes); } if (o.debug > 1) logdebug("Valid Connection from %d\n", socket_n); conn_inc++; /* * We're using connected udp. This has the down side of only * being able to handle one udp client at a time */ Connect(socket_n, &remotess.sockaddr, sslen); /* clean slate for buf */ zmem(buf, sizeof(buf)); /* are we executing a command? then do it */ if (o.cmdexec) { struct fdinfo info = { 0 }; info.fd = socket_n; if (o.keepopen) netrun(&info, o.cmdexec); else netexec(&info, o.cmdexec); continue; } FD_SET(socket_n, &read_fds); FD_SET(STDIN_FILENO, &read_fds); fdmax = socket_n; /* stdin -> socket and socket -> stdout */ while (1) { fd_set fds; fds = read_fds; if (o.debug > 1) logdebug("udp select'ing\n"); if (o.idletimeout > 0) ms_to_timeval(tvp, o.idletimeout); fds_ready = fselect(fdmax + 1, &fds, NULL, NULL, tvp); if (fds_ready == 0) bye("Idle timeout expired (%d ms).", o.idletimeout); if (FD_ISSET(STDIN_FILENO, &fds)) { nbytes = Read(STDIN_FILENO, buf, sizeof(buf)); if (nbytes <= 0) { if (nbytes < 0 && o.verbose) { logdebug("Error reading from stdin: %s\n", strerror(errno)); } else if (nbytes == 0 && o.debug) { logdebug("EOF on stdin\n"); } FD_CLR(STDIN_FILENO, &read_fds); if (nbytes < 0) return 1; continue; } if (o.crlf) fix_line_endings((char *) buf, &nbytes, &tempbuf, &crlf_state); if (!o.recvonly) { if (tempbuf != NULL) n = send(socket_n, tempbuf, nbytes, 0); else n = send(socket_n, buf, nbytes, 0); if (n < nbytes) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } ncat_log_send(buf, nbytes); } if (tempbuf != NULL) { free(tempbuf); tempbuf = NULL; } } if (FD_ISSET(socket_n, &fds)) { nbytes = recv(socket_n, buf, sizeof(buf), 0); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } ncat_log_recv(buf, nbytes); if (!o.sendonly) Write(STDOUT_FILENO, buf, nbytes); } zmem(buf, sizeof(buf)); } } return 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); }
/* This is sufficiently different from the TCP code (wrt SSL, etc) that it * resides in its own simpler function */ static int ncat_listen_dgram(int proto) { int sockfd[NUM_LISTEN_ADDRS]; int i, fdn = -1; int fdmax, nbytes, fds_ready; char buf[DEFAULT_UDP_BUF_LEN] = {0}; char *tempbuf = NULL; fd_set read_fds; union sockaddr_u remotess; socklen_t sslen = sizeof(remotess.storage); for (i = 0; i < NUM_LISTEN_ADDRS; i++) { sockfd[i] = -1; } FD_ZERO(&read_fds); /* Initialize remotess struct so recvfrom() doesn't hit the fan.. */ zmem(&remotess.storage, sizeof(remotess.storage)); remotess.storage.ss_family = o.af; #ifdef WIN32 set_pseudo_sigchld_handler(decrease_conn_count); #else /* Reap on SIGCHLD */ Signal(SIGCHLD, sigchld_handler); /* Ignore the SIGPIPE that occurs when a client disconnects suddenly and we send data to it before noticing. */ Signal(SIGPIPE, SIG_IGN); #endif /* set for selecting udp listening sockets */ fd_set listen_fds; fd_list_t listen_fdlist; FD_ZERO(&listen_fds); init_fdlist(&listen_fdlist, num_listenaddrs); for (i = 0; i < num_listenaddrs; i++) { /* create the UDP listen sockets */ sockfd[i] = do_listen(SOCK_DGRAM, proto, &listenaddrs[i]); FD_SET(sockfd[i],&listen_fds); add_fd(&listen_fdlist, sockfd[i]); } while (1) { int i, j, conn_count, socket_n; if (fdn != -1) { /*remove socket descriptor which is burnt */ FD_CLR(sockfd[fdn], &listen_fds); rm_fd(&listen_fdlist, sockfd[fdn]); /* Rebuild the udp socket which got burnt */ sockfd[fdn] = do_listen(SOCK_DGRAM, proto, &listenaddrs[fdn]); FD_SET(sockfd[fdn],&listen_fds); add_fd(&listen_fdlist, sockfd[fdn]); } fdn = -1; socket_n = -1; fd_set fds; FD_ZERO(&fds); while (1) { /* * We just select to get a list of sockets which we can talk to */ if (o.debug > 1) logdebug("selecting, fdmax %d\n", listen_fdlist.fdmax); fds = listen_fds; fds_ready = fselect(listen_fdlist.fdmax + 1, &fds, NULL, NULL, NULL); if (o.debug > 1) logdebug("select returned %d fds ready\n", fds_ready); /* * Figure out which listening socket got a connection. This loop should * really call a function for each ready socket instead of breaking on * the first one. */ for (i = 0; i <= listen_fdlist.fdmax && fds_ready >0; i++) { /* Loop through descriptors until there is something ready */ if (!FD_ISSET(i, &fds)) continue; /* Check each listening socket */ for (j = 0; j < num_listenaddrs; j++) { if (i == sockfd[j]) { if (o.debug >1) logdebug("Valid descriptor %d \n", i); fdn = j; socket_n = i; break; } } /* if we found a valid socket break */ if (fdn != -1) { fds_ready--; break; } } /* Make sure someone connected */ if (fdn == -1) continue; /* * We just peek so we can get the client connection details without * removing anything from the queue. Sigh. */ nbytes = Recvfrom(socket_n, buf, sizeof(buf), MSG_PEEK, &remotess.sockaddr, &sslen); /* Check conditions that might cause us to deny the connection. */ conn_count = get_conn_count(); if (conn_count >= o.conn_limit) { if (o.verbose) loguser("New connection denied: connection limit reached (%d)\n", conn_count); } else if (!allow_access(&remotess)) { if (o.verbose) loguser("New connection denied: not allowed\n"); } else { /* Good to go. */ break; } /* Dump the current datagram */ Recv(socket_n, buf, sizeof(buf), 0); } if (o.debug > 1) logdebug("Valid Connection from %d\n", socket_n); conn_inc++; /* * We're using connected udp. This has the down side of only * being able to handle one udp client at a time */ Connect(socket_n, &remotess.sockaddr, sslen); /* clean slate for buf */ zmem(buf, sizeof(buf)); /* are we executing a command? then do it */ if (o.cmdexec) { struct fdinfo info = { 0 }; info.fd = socket_n; if (o.keepopen) netrun(&info, o.cmdexec); else netexec(&info, o.cmdexec); continue; } FD_SET(socket_n, &read_fds); FD_SET(STDIN_FILENO, &read_fds); fdmax = socket_n; /* stdin -> socket and socket -> stdout */ while (1) { fd_set fds; fds = read_fds; if (o.debug > 1) logdebug("udp select'ing\n"); fds_ready = fselect(fdmax + 1, &fds, NULL, NULL, NULL); if (FD_ISSET(STDIN_FILENO, &fds)) { nbytes = Read(STDIN_FILENO, buf, sizeof(buf)); if (nbytes < 0) { loguser("%s.\n", strerror(errno)); return 1; } else if (nbytes == 0) { return 0; } if (o.crlf) fix_line_endings((char *) buf, &nbytes, &tempbuf, &crlf_state); if (!o.recvonly) { if (tempbuf != NULL) send(socket_n, tempbuf, nbytes, 0); else send(socket_n, buf, nbytes, 0); } if (tempbuf != NULL) { free(tempbuf); tempbuf = NULL; } } if (FD_ISSET(socket_n, &fds)) { nbytes = recv(socket_n, buf, sizeof(buf), 0); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } if (!o.sendonly) Write(STDOUT_FILENO, buf, nbytes); } zmem(buf, sizeof(buf)); } } return 0; }