static int ncat_listen_stream(int proto) { int rc, i, fds_ready; fd_set listen_fds; struct timeval tv; struct timeval *tvp = NULL; unsigned int num_sockets; /* clear out structs */ FD_ZERO(&master_readfds); FD_ZERO(&master_writefds); FD_ZERO(&master_broadcastfds); FD_ZERO(&listen_fds); #ifdef HAVE_OPENSSL FD_ZERO(&sslpending_fds); #endif zmem(&client_fdlist, sizeof(client_fdlist)); zmem(&broadcast_fdlist, sizeof(broadcast_fdlist)); #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 #ifdef HAVE_OPENSSL if (o.ssl) setup_ssl_listen(); #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"); rc = open("/dev/null", O_RDONLY); if (rc >= 0 && rc != STDIN_FILENO) { /* Oh well, we tried */ logdebug("Couldn't reserve STDIN_FILENO\n"); close(rc); } } #endif /* We need a list of fds to keep current fdmax. The second parameter is a number added to the supplied connection limit, that will compensate maxfds for the added by default listen and stdin sockets. */ init_fdlist(&client_fdlist, sadd(o.conn_limit, num_listenaddrs + 1)); for (i = 0; i < NUM_LISTEN_ADDRS; i++) listen_socket[i] = -1; num_sockets = 0; for (i = 0; i < num_listenaddrs; i++) { /* setup the main listening socket */ listen_socket[num_sockets] = do_listen(SOCK_STREAM, proto, &listenaddrs[i]); if (listen_socket[num_sockets] == -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; } /* Make our listening socket non-blocking because there are timing issues * which could cause us to block on accept() even though select() says it's * readable. See UNPv1 2nd ed, p422 for more. */ unblock_socket(listen_socket[num_sockets]); /* setup select sets and max fd */ FD_SET(listen_socket[num_sockets], &master_readfds); add_fd(&client_fdlist, listen_socket[num_sockets]); FD_SET(listen_socket[num_sockets], &listen_fds); 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."); } add_fd(&client_fdlist, STDIN_FILENO); init_fdlist(&broadcast_fdlist, o.conn_limit); if (o.idletimeout > 0) tvp = &tv; while (1) { /* We pass these temporary descriptor sets to fselect, since fselect modifies the sets it receives. */ fd_set readfds = master_readfds, writefds = master_writefds; struct fdinfo *fdi = NULL; if (o.debug > 1) logdebug("selecting, fdmax %d\n", client_fdlist.fdmax); if (o.debug > 1 && o.broker) logdebug("Broker connection count is %d\n", get_conn_count()); if (o.idletimeout > 0) ms_to_timeval(tvp, o.idletimeout); fds_ready = fselect(client_fdlist.fdmax + 1, &readfds, &writefds, 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); /* * FIXME: optimize this loop to look only at the fds in the fd list, * doing it this way means that if you have one descriptor that is very * large, say 500, and none close to it, that you'll loop many times for * nothing. */ for (i = 0; i <= client_fdlist.fdmax && fds_ready > 0; i++) { /* Loop through descriptors until there's something to read */ if (!FD_ISSET(i, &readfds) && !FD_ISSET(i, &writefds)) continue; if (o.debug > 1) logdebug("fd %d is ready\n", i); #ifdef HAVE_OPENSSL /* Is this an ssl socket pending a handshake? If so handle it. */ if (o.ssl && FD_ISSET(i, &sslpending_fds)) { FD_CLR(i, &master_readfds); FD_CLR(i, &master_writefds); fdi = get_fdinfo(&client_fdlist, i); ncat_assert(fdi != NULL); switch (ssl_handshake(fdi)) { case NCAT_SSL_HANDSHAKE_COMPLETED: /* Clear from sslpending_fds once ssl is established */ FD_CLR(i, &sslpending_fds); post_handle_connection(*fdi); break; case NCAT_SSL_HANDSHAKE_PENDING_WRITE: FD_SET(i, &master_writefds); break; case NCAT_SSL_HANDSHAKE_PENDING_READ: FD_SET(i, &master_readfds); break; case NCAT_SSL_HANDSHAKE_FAILED: default: SSL_free(fdi->ssl); Close(fdi->fd); FD_CLR(i, &sslpending_fds); FD_CLR(i, &master_readfds); rm_fd(&client_fdlist, i); /* Are we in single listening mode(without -k)? If so then we should quit also. */ if (!o.keepopen && !o.broker) return 1; --conn_inc; break; } } else #endif if (FD_ISSET(i, &listen_fds)) { /* we have a new connection request */ handle_connection(i); } else if (i == STDIN_FILENO) { if (o.broker) { read_and_broadcast(i); } else { /* Read from stdin and write to all clients. */ rc = read_stdin(); if (rc == 0) { if (o.proto != IPPROTO_TCP || (o.proto == IPPROTO_TCP && o.sendonly)) { /* There will be nothing more to send. If we're not receiving anything, we can quit here. */ return 0; } if (!o.noshutdown) shutdown_sockets(SHUT_WR); } if (rc < 0) return 1; } } else if (!o.sendonly) { if (o.broker) { read_and_broadcast(i); } else { /* Read from a client and write to stdout. */ rc = read_socket(i); if (rc <= 0 && !o.keepopen) return rc == 0 ? 0 : 1; } } fds_ready--; } } return 0; }
/* Accept a connection on a listening socket. Allow or deny the connection. Fork a command if o.cmdexec is set. Otherwise, add the new socket to the watch set. */ static void handle_connection(int socket_accept) { union sockaddr_u remoteaddr; socklen_t ss_len; struct fdinfo s = { 0 }; int conn_count; zmem(&s, sizeof(s)); zmem(&remoteaddr, sizeof(remoteaddr.storage)); ss_len = sizeof(remoteaddr.storage); errno = 0; s.fd = accept(socket_accept, &remoteaddr.sockaddr, &ss_len); if (s.fd < 0) { if (o.debug) logdebug("Error in accept: %s\n", strerror(errno)); close(s.fd); return; } if (o.verbose) { #if HAVE_SYS_UN_H if (remoteaddr.sockaddr.sa_family == AF_UNIX) loguser("Connection from a client on Unix domain socket.\n"); else #endif if (o.chat) loguser("Connection from %s on file descriptor %d.\n", inet_socktop(&remoteaddr), s.fd); else loguser("Connection from %s.\n", inet_socktop(&remoteaddr)); } if (!o.keepopen && !o.broker) { int i; for (i = 0; i < num_listenaddrs; i++) { Close(listen_socket[i]); FD_CLR(listen_socket[i], &master_readfds); rm_fd(&client_fdlist, listen_socket[i]); } } if (o.verbose) { #if HAVE_SYS_UN_H if (remoteaddr.sockaddr.sa_family == AF_UNIX) loguser("Connection from %s.\n", remoteaddr.un.sun_path); else #endif loguser("Connection from %s:%hu.\n", inet_socktop(&remoteaddr), inet_port(&remoteaddr)); } /* 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); Close(s.fd); return; } if (!allow_access(&remoteaddr)) { if (o.verbose) loguser("New connection denied: not allowed\n"); Close(s.fd); return; } s.remoteaddr = remoteaddr; conn_inc++; unblock_socket(s.fd); #ifdef HAVE_OPENSSL if (o.ssl) { /* Add the socket to the necessary descriptor lists. */ FD_SET(s.fd, &sslpending_fds); FD_SET(s.fd, &master_readfds); FD_SET(s.fd, &master_writefds); /* Add it to our list of fds too for maintaining maxfd. */ if (add_fdinfo(&client_fdlist, &s) < 0) bye("add_fdinfo() failed."); } else #endif post_handle_connection(s); }
static int ncat_listen_stream(int proto) { int rc, i, fds_ready; fd_set listen_fds; /* clear out structs */ FD_ZERO(&master_readfds); FD_ZERO(&master_writefds); FD_ZERO(&master_broadcastfds); FD_ZERO(&listen_fds); #ifdef HAVE_OPENSSL FD_ZERO(&sslpending_fds); #endif zmem(&client_fdlist, sizeof(client_fdlist)); zmem(&broadcast_fdlist, sizeof(broadcast_fdlist)); #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 #ifdef HAVE_OPENSSL if (o.ssl) setup_ssl_listen(); #endif /* We need a list of fds to keep current fdmax. The second parameter is a number added to the supplied connection limit, that will compensate maxfds for the added by default listen and stdin sockets. */ init_fdlist(&client_fdlist, sadd(o.conn_limit, num_listenaddrs + 1)); for (i = 0; i < NUM_LISTEN_ADDRS; i++) listen_socket[i] = -1; for (i = 0; i < num_listenaddrs; i++) { /* setup the main listening socket */ listen_socket[i] = do_listen(SOCK_STREAM, proto, &listenaddrs[i]); /* Make our listening socket non-blocking because there are timing issues * which could cause us to block on accept() even though select() says it's * readable. See UNPv1 2nd ed, p422 for more. */ unblock_socket(listen_socket[i]); /* setup select sets and max fd */ FD_SET(listen_socket[i], &master_readfds); add_fd(&client_fdlist, listen_socket[i]); FD_SET(listen_socket[i], &listen_fds); } add_fd(&client_fdlist, STDIN_FILENO); init_fdlist(&broadcast_fdlist, o.conn_limit); while (1) { /* We pass these temporary descriptor sets to fselect, since fselect modifies the sets it receives. */ fd_set readfds = master_readfds, writefds = master_writefds; struct fdinfo *fdi = NULL; if (o.debug > 1) logdebug("selecting, fdmax %d\n", client_fdlist.fdmax); if (o.debug > 1 && o.broker) logdebug("Broker connection count is %d\n", get_conn_count()); fds_ready = fselect(client_fdlist.fdmax + 1, &readfds, &writefds, NULL, NULL); if (o.debug > 1) logdebug("select returned %d fds ready\n", fds_ready); /* * FIXME: optimize this loop to look only at the fds in the fd list, * doing it this way means that if you have one descriptor that is very * large, say 500, and none close to it, that you'll loop many times for * nothing. */ for (i = 0; i <= client_fdlist.fdmax && fds_ready > 0; i++) { /* Loop through descriptors until there's something to read */ if (!FD_ISSET(i, &readfds) && !FD_ISSET(i, &writefds)) continue; if (o.debug > 1) logdebug("fd %d is ready\n", i); #ifdef HAVE_OPENSSL /* Is this an ssl socket pending a handshake? If so handle it. */ if (o.ssl && FD_ISSET(i, &sslpending_fds)) { FD_CLR(i, &master_readfds); FD_CLR(i, &master_writefds); fdi = get_fdinfo(&client_fdlist, i); switch(ssl_handshake(fdi)){ case NCAT_SSL_HANDSHAKE_COMPLETED: /* Clear from sslpending_fds once ssl is established */ FD_CLR(i, &sslpending_fds); rm_fd(&client_fdlist, i); post_handle_connection(*fdi); break; case NCAT_SSL_HANDSHAKE_PENDING_WRITE: FD_SET(i, &master_writefds); break; case NCAT_SSL_HANDSHAKE_PENDING_READ: FD_SET(i, &master_readfds); break; case NCAT_SSL_HANDSHAKE_FAILED: default: SSL_free(fdi->ssl); Close(fdi->fd); FD_CLR(i, &sslpending_fds); FD_CLR(i, &master_readfds); rm_fd(&client_fdlist, i); /* Are we in single listening mode(without -k)? If so then we should quit also. */ if (!o.keepopen && !o.broker) return 1; --conn_inc; break; } } else #endif if (FD_ISSET(i, &listen_fds)) { /* we have a new connection request */ handle_connection(i); } else if (i == STDIN_FILENO) { if(o.broker) { read_and_broadcast(i); }else { /* Read from stdin and write to all clients. */ rc = read_stdin(); if (rc == 0 && o.sendonly) /* There will be nothing more to send. If we're not receiving anything, we can quit here. */ return 0; if (rc < 0) return 1; } } else if (!o.sendonly) { if(o.broker) { read_and_broadcast(i); }else { /* Read from a client and write to stdout. */ rc = read_socket(i); if (rc <= 0 && !o.keepopen) return rc == 0 ? 0 : 1; } } fds_ready--; } } return 0; }