summaryrefslogtreecommitdiffstats
path: root/nsock/src/nsock_iod.c
diff options
context:
space:
mode:
Diffstat (limited to 'nsock/src/nsock_iod.c')
-rw-r--r--nsock/src/nsock_iod.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/nsock/src/nsock_iod.c b/nsock/src/nsock_iod.c
new file mode 100644
index 0000000..d74ab5e
--- /dev/null
+++ b/nsock/src/nsock_iod.c
@@ -0,0 +1,455 @@
+/***************************************************************************
+ * nsock_iod.c -- This contains the functions relating to nsock_iod (and *
+ * its nsock internal manifestation -- nsockiod. This is is similar to a *
+ * file descriptor in that you create it and then use it to initiate *
+ * connections, read/write data, etc. *
+ * *
+ ***********************IMPORTANT NSOCK LICENSE TERMS***********************
+ *
+ * The nsock parallel socket event library is (C) 1999-2023 Nmap Software LLC
+ * This library is free software; you may redistribute and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; Version 2. This guarantees your right to use, modify, and
+ * redistribute this software under certain conditions. If this license is
+ * unacceptable to you, Nmap Software LLC may be willing to sell alternative
+ * licenses (contact sales@nmap.com ).
+ *
+ * As a special exception to the GPL terms, Nmap Software LLC grants permission
+ * to link the code of this program with any version of the OpenSSL library
+ * which is distributed under a license identical to that listed in the included
+ * docs/licenses/OpenSSL.txt file, and distribute linked combinations including
+ * the two. You must obey the GNU GPL in all respects for all of the code used
+ * other than OpenSSL. If you modify this file, you may extend this exception to
+ * your version of the file, but you are not obligated to do so.
+ *
+ * If you received these files with a written license agreement stating terms
+ * other than the (GPL) terms above, then that alternative license agreement
+ * takes precedence over this comment.
+ *
+ * Source is provided to this software because we believe users have a right to
+ * know exactly what a program is going to do before they run it. This also
+ * allows you to audit the software for security holes.
+ *
+ * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+ * new features. You are highly encouraged to send your changes to the
+ * dev@nmap.org mailing list for possible incorporation into the main
+ * distribution. By sending these changes to Fyodor or one of the Insecure.Org
+ * development mailing lists, or checking them into the Nmap source code
+ * repository, it is understood (unless you specify otherwise) that you are
+ * offering the Nmap Project (Nmap Software LLC) the unlimited, non-exclusive
+ * right to reuse, modify, and relicense the code. Nmap will always be available
+ * Open Source, but this is important because the inability to relicense code
+ * has caused devastating problems for other Free Software projects (such as KDE
+ * and NASM). We also occasionally relicense the code to third parties as
+ * discussed above. If you wish to specify special license conditions of your
+ * contributions, just say so when you send them.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License v2.0 for more
+ * details (http://www.gnu.org/licenses/gpl-2.0.html).
+ *
+ ***************************************************************************/
+
+/* $Id$ */
+
+#include "nsock.h"
+#include "nsock_internal.h"
+#include "nsock_log.h"
+#include "gh_list.h"
+#include "netutils.h"
+
+#if HAVE_PCAP
+#include "nsock_pcap.h"
+#endif
+
+#include <string.h>
+
+
+/* nsock_iod is like a "file descriptor" for the nsock library. You use it to
+ * request events. And here is how you create an nsock_iod. nsock_iod_new returns
+ * NULL if the iod cannot be allocated. Pass NULL as userdata if you don't want
+ * to immediately associate any user data with the iod. */
+nsock_iod nsock_iod_new(nsock_pool nsockp, void *userdata) {
+ return nsock_iod_new2(nsockp, -1, userdata);
+}
+
+/* This version allows you to associate an existing sd with the msi so that you
+ * can read/write it using the nsock infrastructure. For example, you may want
+ * to watch for data from STDIN_FILENO at the same time as you read/write
+ * various sockets. STDIN_FILENO is a special case, however. Any other sd is
+ * dup()ed, so you may close or otherwise manipulate your copy. The duped copy
+ * will be destroyed when the nsi is destroyed. */
+nsock_iod nsock_iod_new2(nsock_pool nsockp, int sd, void *userdata) {
+ struct npool *nsp = (struct npool *)nsockp;
+ gh_lnode_t *lnode;
+ struct niod *nsi;
+
+ lnode = gh_list_pop(&nsp->free_iods);
+ if (!lnode) {
+ nsi = (struct niod *)safe_malloc(sizeof(*nsi));
+ memset(nsi, 0, sizeof(*nsi));
+ } else {
+ nsi = container_of(lnode, struct niod, nodeq);
+ }
+
+ if (sd == -1) {
+ nsi->sd = -1;
+ nsi->state = NSIOD_STATE_INITIAL;
+ } else if (sd == STDIN_FILENO) {
+ nsi->sd = STDIN_FILENO;
+ nsi->state = NSIOD_STATE_UNKNOWN;
+ } else {
+ nsi->sd = dup_socket(sd);
+ if (nsi->sd == -1) {
+ free(nsi);
+ return NULL;
+ }
+ unblock_socket(nsi->sd);
+ nsi->state = NSIOD_STATE_UNKNOWN;
+ }
+
+ nsi->first_connect = NULL;
+ nsi->first_read = NULL;
+ nsi->first_write = NULL;
+#if HAVE_PCAP
+ nsi->first_pcap_read = NULL;
+ nsi->readpcapsd_count = 0;
+#endif
+ nsi->readsd_count = 0;
+ nsi->write_count = 0;
+
+ nsi->userdata = userdata;
+ nsi->nsp = (struct npool *)nsockp;
+
+ nsi->_flags = 0;
+
+ nsi->read_count = 0;
+ nsi->write_count = 0;
+
+ nsi->hostname = NULL;
+
+ nsi->ipopts = NULL;
+ nsi->ipoptslen = 0;
+
+#if HAVE_OPENSSL
+ nsi->ssl_session = NULL;
+#endif
+
+ if (nsp->px_chain) {
+ nsi->px_ctx = proxy_chain_context_new(nsp);
+ } else {
+ nsi->px_ctx = NULL;
+ }
+
+ nsi->id = nsp->next_iod_serial++;
+ if (nsi->id == 0)
+ nsi->id = nsp->next_iod_serial++;
+
+ /* The nsp keeps track of active iods so it can delete them if it is deleted */
+ gh_list_append(&nsp->active_iods, &nsi->nodeq);
+
+ nsock_log_info("nsock_iod_new (IOD #%lu)", nsi->id);
+
+ return (nsock_iod)nsi;
+}
+
+/* Defined in nsock_core.c. */
+int socket_count_zero(struct niod *iod, struct npool *ms);
+
+/* If nsock_iod_new returned success, you must free the iod when you are done with
+ * it to conserve memory (and in some cases, sockets). After this call,
+ * nsockiod may no longer be used -- you need to create a new one with
+ * nsock_iod_new(). pending_response tells what to do with any events that are
+ * pending on this nsock_iod. This can be NSOCK_PENDING_NOTIFY (send a KILL
+ * notification to each event), NSOCK_PENDING_SILENT (do not send notification
+ * to the killed events), or NSOCK_PENDING_ERROR (print an error message and
+ * quit the program) */
+void nsock_iod_delete(nsock_iod nsockiod, enum nsock_del_mode pending_response) {
+#if HAVE_PCAP
+#define NUM_EVT_TYPES 4
+#else
+#define NUM_EVT_TYPES 3
+#endif
+ struct niod *nsi = (struct niod *)nsockiod;
+ gh_lnode_t *evlist_ar[NUM_EVT_TYPES];
+ gh_list_t *corresp_list[NUM_EVT_TYPES];
+ int i;
+ gh_lnode_t *current, *next;
+
+ assert(nsi);
+
+ if (nsi->state == NSIOD_STATE_DELETED) {
+ /* This nsi is already marked as deleted, will probably be removed from the
+ * list very soon. Just return to avoid breaking reentrancy. */
+ return;
+ }
+
+ nsock_log_info("nsock_iod_delete (IOD #%lu)", nsi->id);
+
+ if (nsi->events_pending > 0) {
+ /* shit -- they killed the struct niod while an event was still pending on it.
+ * Maybe I should store the pending events in the iod. On the other hand,
+ * this should be a pretty rare occurrence and so I'll save space and hassle
+ * by just locating the events here by searching through the active events
+ * list */
+ if (pending_response == NSOCK_PENDING_ERROR)
+ fatal("nsock_iod_delete called with argument NSOCK_PENDING_ERROR on a nsock_iod that has %d pending event(s) associated with it", nsi->events_pending);
+
+ assert(pending_response == NSOCK_PENDING_NOTIFY || pending_response == NSOCK_PENDING_SILENT);
+
+ evlist_ar[0] = nsi->first_connect;
+ evlist_ar[1] = nsi->first_read;
+ evlist_ar[2] = nsi->first_write;
+#if HAVE_PCAP
+ evlist_ar[3] = nsi->first_pcap_read;
+#endif
+
+ corresp_list[0] = &nsi->nsp->connect_events;
+ corresp_list[1] = &nsi->nsp->read_events;
+ corresp_list[2] = &nsi->nsp->write_events;
+#if HAVE_PCAP
+ corresp_list[3] = &nsi->nsp->pcap_read_events;
+#endif
+
+ for (i = 0; i < NUM_EVT_TYPES && nsi->events_pending > 0; i++) {
+ for (current = evlist_ar[i]; current != NULL; current = next) {
+ struct nevent *nse;
+
+ next = gh_lnode_next(current);
+ nse = lnode_nevent(current);
+
+ /* we're done with this list of events for the current IOD */
+ if (nse->iod != nsi)
+ break;
+
+ nevent_delete(nsi->nsp, nse, corresp_list[i], current, pending_response == NSOCK_PENDING_NOTIFY);
+ }
+ }
+ }
+
+ if (nsi->events_pending != 0)
+ fatal("Trying to delete NSI, but could not find %d of the purportedly pending events on that IOD.\n", nsi->events_pending);
+
+ /* Make sure we no longer select on this socket, in case the socket counts
+ * weren't already decremented to zero. */
+ if (nsi->sd >= 0)
+ socket_count_zero(nsi, nsi->nsp);
+
+ free(nsi->hostname);
+
+#if HAVE_OPENSSL
+ /* Close any SSL resources */
+ if (nsi->ssl) {
+ /* No longer free session because copy nsi stores is not reference counted */
+#if 0
+ if (nsi->ssl_session)
+ SSL_SESSION_free(nsi->ssl_session);
+ nsi->ssl_session = NULL;
+#endif
+
+ if (SSL_shutdown(nsi->ssl) == -1) {
+ nsock_log_info("nsock_iod_delete: SSL shutdown failed (%s) on NSI %li",
+ ERR_reason_error_string(SSL_get_error(nsi->ssl, -1)), nsi->id);
+ }
+
+ /* I don't really care if the SSL_shutdown() succeeded politely. I could
+ * make the SD blocking temporarily for this, but I'm hoping it will succeed
+ * 95% of the time because we can usually write to a socket. */
+ SSL_free(nsi->ssl);
+ nsi->ssl = NULL;
+ }
+#endif
+
+ if (nsi->sd >= 0 && nsi->sd != STDIN_FILENO) {
+ close(nsi->sd);
+ nsi->sd = -1;
+ }
+
+ nsi->state = NSIOD_STATE_DELETED;
+ nsi->userdata = NULL;
+
+ if (nsi->ipoptslen)
+ free(nsi->ipopts);
+
+#if HAVE_PCAP
+ if (nsi->pcap){
+ mspcap *mp = (mspcap *)nsi->pcap;
+
+ if (mp->pt){
+ pcap_close(mp->pt);
+ mp->pt = NULL;
+ }
+ if (mp->pcap_desc) {
+ /* pcap_close() will close the associated pcap descriptor */
+ mp->pcap_desc = -1;
+ }
+ if (mp->pcap_device) {
+ free(mp->pcap_device);
+ mp->pcap_device = NULL;
+ }
+ free(mp);
+ nsi->pcap = NULL;
+ }
+#endif
+
+ if (nsi->px_ctx)
+ proxy_chain_context_delete(nsi->px_ctx);
+}
+
+/* Returns the ID of an nsock_iod . This ID is always unique amongst ids for a
+ * given nspool (unless you blow through billions of them). */
+unsigned long nsock_iod_id(nsock_iod nsockiod) {
+ assert(nsockiod);
+ return ((struct niod *)nsockiod)->id;
+}
+
+/* Returns the SSL object inside an nsock_iod, or NULL if unset. */
+nsock_ssl nsock_iod_get_ssl(nsock_iod iod) {
+#if HAVE_OPENSSL
+ return ((struct niod *)iod)->ssl;
+#else
+ return NULL;
+#endif
+}
+
+/* Returns the SSL_SESSION of an nsock_iod.
+ * Increments its usage count if inc_ref is not zero. */
+nsock_ssl_session nsock_iod_get_ssl_session(nsock_iod iod, int inc_ref) {
+#if HAVE_OPENSSL
+ if (inc_ref)
+ return SSL_get1_session(((struct niod *)iod)->ssl);
+ else
+ return SSL_get0_session(((struct niod *)iod)->ssl);
+#else
+ return NULL;
+#endif
+}
+
+/* sets the ssl session of an nsock_iod, increments usage count. The session
+ * should not have been set yet (as no freeing is done) */
+#if HAVE_OPENSSL
+void nsi_set_ssl_session(struct niod *iod, SSL_SESSION *sessid) {
+ if (sessid) {
+ iod->ssl_session = sessid;
+ /* No reference counting for the copy stored briefly in nsiod */
+ }
+}
+#endif
+
+/* Sometimes it is useful to store a pointer to information inside the struct niod so
+ * you can retrieve it during a callback. */
+void nsock_iod_set_udata(nsock_iod iod, void *udata) {
+ assert(iod);
+ ((struct niod *)iod)->userdata = udata;
+}
+
+/* And the function above wouldn't make much sense if we didn't have a way to
+ * retrieve that data... */
+void *nsock_iod_get_udata(nsock_iod iod) {
+ assert(iod);
+ return ((struct niod *)iod)->userdata;
+}
+
+/* Returns 1 if an NSI is communicating via SSL, 0 otherwise. */
+int nsock_iod_check_ssl(nsock_iod iod) {
+ return (((struct niod *)iod)->ssl) ? 1 : 0;
+}
+
+/* Returns the remote peer port (or -1 if unavailable). Note the return value
+ * is a whole int so that -1 can be distinguished from 65535. Port is returned
+ * in host byte order. */
+int nsock_iod_get_peerport(nsock_iod iod) {
+ struct niod *nsi = (struct niod *)iod;
+ int fam;
+
+ if (nsi->peerlen <= 0)
+ return -1;
+
+ fam = ((struct sockaddr_in *)&nsi->peer)->sin_family;
+
+ if (fam == AF_INET)
+ return ntohs(((struct sockaddr_in *)&nsi->peer)->sin_port);
+#if HAVE_IPV6
+ else if (fam == AF_INET6)
+ return ntohs(((struct sockaddr_in6 *)&nsi->peer)->sin6_port);
+#endif
+
+ return -1;
+}
+
+/* Sets the local address to bind to before connect() */
+int nsock_iod_set_localaddr(nsock_iod iod, struct sockaddr_storage *ss,
+ size_t sslen) {
+ struct niod *nsi = (struct niod *)iod;
+
+ assert(nsi);
+
+ if (sslen > sizeof(nsi->local))
+ return -1;
+
+ memcpy(&nsi->local, ss, sslen);
+ nsi->locallen = sslen;
+ return 0;
+}
+
+/* Sets IPv4 options to apply before connect(). It makes a copy of the options,
+ * so you can free() yours if necessary. This copy is freed when the iod is
+ * destroyed. */
+int nsock_iod_set_ipoptions(nsock_iod iod, void *opts, size_t optslen) {
+ struct niod *nsi = (struct niod *)iod;
+
+ assert(nsi);
+
+ if (optslen > 44)
+ return -1;
+
+ nsi->ipopts = safe_malloc(optslen);
+ memcpy(nsi->ipopts, opts, optslen);
+ nsi->ipoptslen = optslen;
+ return 0;
+}
+
+/* I didn't want to do this. Its an ugly hack, but I suspect it will be
+ * necessary. I certainly can't reproduce in nsock EVERYTHING you might want
+ * to do with a socket. So I'm offering you this function to obtain the socket
+ * descriptor which is (usually) wrapped in a nsock_iod). You can do
+ * "reasonable" things with it, like setting socket receive buffers. But don't
+ * create havok by closing the descriptor! If the descriptor you get back is
+ * -1, the iod does not currently possess a valid descriptor */
+int nsock_iod_get_sd(nsock_iod iod) {
+ struct niod *nsi = (struct niod *)iod;
+
+ assert(nsi);
+
+#if HAVE_PCAP
+ if (nsi->pcap)
+ return ((mspcap *)nsi->pcap)->pcap_desc;
+ else
+#endif
+ return nsi->sd;
+}
+
+unsigned long nsock_iod_get_read_count(nsock_iod iod){
+ assert(iod);
+ return ((struct niod *)iod)->read_count;
+}
+
+unsigned long nsock_iod_get_write_count(nsock_iod iod){
+ assert(iod);
+ return ((struct niod *)iod)->write_count;
+}
+
+int nsock_iod_set_hostname(nsock_iod iod, const char *hostname) {
+ struct niod *nsi = (struct niod *)iod;
+
+ if (nsi->hostname != NULL)
+ free(nsi->hostname);
+
+ nsi->hostname = strdup(hostname);
+ if (nsi->hostname == NULL)
+ return -1;
+
+ return 0;
+}
+