/*************************************************************************** * nsock_internal.h -- PRIVATE interface definitions for the guts of the * * nsock parallel socket event library. Applications calling this library * * should NOT include this. even LOOK at these :). * * * ***********************IMPORTANT NSOCK LICENSE TERMS*********************** * * The nsock parallel socket event library is (C) 1999-2024 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$ */ #ifndef NSOCK_INTERNAL_H #define NSOCK_INTERNAL_H #include #ifdef HAVE_CONFIG_H #include "nsock_config.h" #include "nbase_config.h" #endif #ifdef WIN32 #include "nbase_winconfig.h" #include #endif #include "gh_list.h" #include "gh_heap.h" #include "filespace.h" #include "nsock.h" /* The public interface -- I need it for some enum defs */ #include "nsock_ssl.h" #include "nsock_proxy.h" #if HAVE_SYS_TIME_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if HAVE_SYS_UN_H #include #endif #ifndef IPPROTO_SCTP #define IPPROTO_SCTP 132 #endif /* ------------------- CONSTANTS ------------------- */ #define READ_BUFFER_SZ 8192 enum nsock_read_types { NSOCK_READLINES, NSOCK_READBYTES, NSOCK_READ }; enum iod_state { NSIOD_STATE_DELETED, NSIOD_STATE_INITIAL, /* sd was provided to us in nsock_iod_new2 (see nsock_iod.c) */ NSIOD_STATE_UNKNOWN, NSIOD_STATE_CONNECTED_TCP, NSIOD_STATE_CONNECTED_UDP }; /* XXX: ensure that these values can be OR'ed when adding new ones */ #define EV_NONE 0x00 #define EV_READ 0x01 #define EV_WRITE 0x02 #define EV_EXCEPT 0x04 /* ------------------- STRUCTURES ------------------- */ extern struct timeval nsock_tod; struct readinfo { enum nsock_read_types read_type; /* num lines; num bytes; whatever (depends on read_type) */ int num; }; struct writeinfo { struct sockaddr_storage dest; size_t destlen; /* Number of bytes successfully written */ int written_so_far; }; /* Remember that callers of this library should NOT be accessing these * fields directly */ struct npool { /* User data, NULL if unset */ void *userdata; /* IO Engine vtable */ struct io_engine *engine; /* IO Engine internal data */ void *engine_data; /* Active network events */ gh_list_t connect_events; gh_list_t read_events; gh_list_t write_events; #if HAVE_PCAP gh_list_t pcap_read_events; #endif gh_heap_t expirables; /* Active iods and related lists of events */ gh_list_t active_iods; /* struct niod structures that have been freed for reuse */ gh_list_t free_iods; /* When an event is deleted, we stick it here for later reuse */ gh_list_t free_events; /* Number of events pending (total) on all lists */ int events_pending; /* Serial # of next event (used to create next nsock_event_id */ unsigned long next_event_serial; /* Serial # of next iod to be created */ unsigned long next_iod_serial; /* If nsock_loop() returns NSOCK_LOOP_ERROR, this is where we describe the * error (errnum fashion) */ int errnum; /* If true, new sockets will have SO_BROADCAST set */ int broadcast; /* Interface to bind to; only supported on Linux with SO_BINDTODEVICE sockopt. */ const char *device; /* If true, exit the next iteration of nsock_loop with a status of * NSOCK_LOOP_QUIT. */ int quit; #if HAVE_OPENSSL /* The SSL Context (options and such) */ SSL_CTX *sslctx; #ifndef OPENSSL_NO_DTLS SSL_CTX *dtlsctx; #endif #endif /* Optional proxy chain (NULL is not set). Can only be set once per NSP (using * nsock_proxychain_new() or nsock_pool_set_proxychain(). */ struct proxy_chain *px_chain; }; /* nsock_iod is like a "file descriptor" for the nsock library. You use it to * request events. */ struct niod { /* The socket descriptor related to the event */ int sd; /* Number of pending events on this iod */ int events_pending; /* Pending events */ gh_lnode_t *first_connect; gh_lnode_t *first_read; gh_lnode_t *first_write; #if HAVE_PCAP gh_lnode_t *first_pcap_read; #endif int readsd_count; int writesd_count; #if HAVE_PCAP int readpcapsd_count; #endif int watched_events; /* The struct npool used to create the iod (used for deletion) */ struct npool *nsp; enum iod_state state; /* The host and port we are connected to using sd (saves a call to getpeername) */ struct sockaddr_storage peer; /* The host and port to bind to with sd */ struct sockaddr_storage local; /* The length of peer/local actually used (sizeof(sockaddr_in) or * sizeof(sockaddr_in6), SUN_LEN(sockaddr_un), or 0 if peer/local * has not been filled in */ size_t locallen; size_t peerlen; /* -1 if none yet, otherwise IPPROTO_TCP, etc. */ int lastproto; /* The struct npool keeps track of NIODs that have been allocated so that it * can destroy them if the msp is deleted. This pointer makes it easy to * remove this struct niod from the allocated list when necessary */ gh_lnode_t nodeq; #define IOD_REGISTERED (1) #define IOD_PROCESSED (1 << 1) /* internally used by engine_kqueue.c */ #define IOD_STDIN (1 << 2) #define IOD_EOF (1 << 3) #define IOD_PROPSET(iod, flag) ((iod)->_flags |= (flag)) #define IOD_PROPCLR(iod, flag) ((iod)->_flags &= ~(flag)) #define IOD_PROPGET(iod, flag) (((iod)->_flags & (flag)) != 0) char _flags; /* Used for SSL Server Name Indication. */ char *hostname; #if HAVE_OPENSSL /* An SSL connection (or NULL if none) */ SSL *ssl; /* SSL SESSION ID (or NULL if none) */ SSL_SESSION *ssl_session; #else /* Because there are many if (nsi->ssl) cases in the code */ char *ssl; #endif /* Every iod has an id which is always unique for the same nspool (unless you * create billions of them) */ unsigned long id; /* No. of bytes read from the sd*/ unsigned long read_count; /* No. of bytes written to the sd */ unsigned long write_count; void *userdata; /* IP options to set on socket before connect() */ void *ipopts; int ipoptslen; /* Pointer to mspcap struct (used only if pcap support is included) */ void *pcap; struct proxy_chain_context *px_ctx; /* IO Engine internal data */ int engine_info; }; /* nsock_event_t handles a single event. Its ID is generally returned when the * event is created, and the event is included in callbacks */ struct nevent { /* Every event has an ID which is unique for a given nsock unless you blow * through more than 500,000,000 events */ nsock_event_id id; enum nse_type type; enum nse_status status; /* For write events, this is the data to be written, for read events, this is * what we will read into */ struct filespace iobuf; /* The timeout of the event -- absolute time * except that tv_sec == 0 means no timeout */ struct timeval timeout; /* Info pertaining to READ requests */ struct readinfo readinfo; /* Info pertaining to WRITE requests */ struct writeinfo writeinfo; #if HAVE_OPENSSL struct sslinfo sslinfo; #endif /* If we return a status of NSE_STATUS_ERROR, this must be set */ int errnum; /* The nsock I/O descriptor related to event (if applicable) */ struct niod *iod; /* The handler to call when event is complete */ nsock_ev_handler handler; /* slot in the expirable binheap */ gh_hnode_t expire; /* For some reasons (see nsock_pcap.c) we register pcap events as both read * and pcap_read events when in PCAP_BSD_SELECT_HACK mode. We then need two * gh_lnode_t handles. To make code simpler, we _always_ use _nodeq_pcap for * pcap_read events and _nodeq_io for the other ones. * When not in PCAP_BSD_SELECT_HACK mode we define both handles as members * of an union to optimize memory footprint. */ gh_lnode_t nodeq_io; gh_lnode_t nodeq_pcap; /* Optional (NULL if unset) pointer to pass to the handler */ void *userdata; /* If this event is all filled out and ready for immediate delivery, * event_done is nonzero. Used when event is finished at unexpected time and * we want to dispatch it later to avoid duplicating stat update code and all * that other crap */ unsigned int event_done: 1; unsigned int eof: 1; #if HAVE_IOCP struct extended_overlapped *eov; #endif }; struct io_operations { int(*iod_connect)(struct npool *nsp, int sockfd, const struct sockaddr *addr, socklen_t addrlen); int(*iod_read)(struct npool *nsp, int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); int(*iod_write)(struct npool *nsp, int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); }; struct io_engine { /* Human readable identifier for this engine. */ const char *name; /* Engine constructor */ int (*init)(struct npool *nsp); /* Engine destructor */ void (*destroy)(struct npool *nsp); /* Register a new IOD to the engine */ int(*iod_register)(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev); /* Remove a registered IOD */ int(*iod_unregister)(struct npool *nsp, struct niod *iod); /* Modify events for a registered IOD. * - ev_set represent the events to add * - ev_clr represent the events to delete (if set) */ int (*iod_modify)(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev_set, int ev_clr); /* Main engine loop */ int (*loop)(struct npool *nsp, int msec_timeout); /* I/O operations */ struct io_operations *io_operations; }; /* ----------- NSOCK I/O ENGINE CONVENIENCE WRAPPERS ------------ */ static inline int nsock_engine_init(struct npool *nsp) { return nsp->engine->init(nsp); } static inline void nsock_engine_destroy(struct npool *nsp) { nsp->engine->destroy(nsp); return; } static inline int nsock_engine_iod_register(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev) { return nsp->engine->iod_register(nsp, iod, nse, ev); } static inline int nsock_engine_iod_unregister(struct npool *nsp, struct niod *iod) { return nsp->engine->iod_unregister(nsp, iod); } static inline int nsock_engine_iod_modify(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev_set, int ev_clr) { return nsp->engine->iod_modify(nsp, iod, nse, ev_set, ev_clr); } static inline int nsock_engine_loop(struct npool *nsp, int msec_timeout) { return nsp->engine->loop(nsp, msec_timeout); } /* ------------------- PROTOTYPES ------------------- */ int event_timedout(struct nevent *nse); /* Get a new nsock_event_id, given a type */ nsock_event_id get_new_event_id(struct npool *nsp, enum nse_type type); /* Take an event ID and return the type (NSE_TYPE_CONNECT, etc */ enum nse_type get_event_id_type(nsock_event_id event_id); /* Create a new event structure -- must be deleted later with event_delete, * unless it returns NULL (failure). NULL can be passed in for the struct niod and * the userdata if not available. */ struct nevent *event_new(struct npool *nsp, enum nse_type type, struct niod *iod, int timeout_msecs, nsock_ev_handler handler, void *userdata); /* An internal function for cancelling an event when you already have a pointer * to the struct nevent (use nsock_event_cancel if you just have an ID). The * event_list passed in should correspond to the type of the event. For * example, with NSE_TYPE_READ, you would pass in &iod->read_events;. elem * is the list element in event_list which holds the event. Pass a nonzero for * notify if you want the program owning the event to be notified that it has * been cancelled */ int nevent_delete(struct npool *nsp, struct nevent *nse, gh_list_t *event_list, gh_lnode_t *elem, int notify); /* Unlink an event from any lists and add it to free_events. * Calls update_first_events. Resets timeout and removes event from expirables */ int nevent_unref(struct npool *nsp, struct nevent *nse); /* Adjust various statistics, dispatches the event handler (if notify is * nonzero) and then deletes the event. This function does NOT delete the event * from any lists it might be on (eg nsp->read_list etc.) nse->event_done * MUST be true when you call this */ void event_dispatch_and_delete(struct npool *nsp, struct nevent *nse, int notify); /* Free an struct nevent which was allocated with event_new, including all internal * resources. Note -- we assume that nse->iod->events_pending (if it exists) * has ALREADY been decremented (done during event_dispatch_and_delete) -- so * remember to do this if you call event_delete() directly */ void event_delete(struct npool *nsp, struct nevent *nse); /* Add an event to the appropriate nsp event list, handles housekeeping such as * adjusting the descriptor select/poll lists, registering the timeout value, * etc. */ void nsock_pool_add_event(struct npool *nsp, struct nevent *nse); void nsock_connect_internal(struct npool *ms, struct nevent *nse, int type, int proto, struct sockaddr_storage *ss, size_t sslen, unsigned int port); /* Comments on using the following handle_*_result functions are available in nsock_core.c */ /* handle_connect_results assumes that select or poll have already shown the * descriptor to be active */ void handle_connect_result(struct npool *ms, struct nevent *nse, enum nse_status status); void handle_read_result(struct npool *ms, struct nevent *nse, enum nse_status status); void handle_write_result(struct npool *ms, struct nevent *nse, enum nse_status status); void handle_timer_result(struct npool *ms, struct nevent *nse, enum nse_status status); #if HAVE_PCAP void handle_pcap_read_result(struct npool *ms, struct nevent *nse, enum nse_status status); #endif /* An event has been completed and the handler is about to be called. This * function writes out tracing data about the event if necessary */ void nsock_trace_handler_callback(struct npool *ms, struct nevent *nse); #if HAVE_OPENSSL /* Sets the ssl session of an nsock_iod, increments usage count. The session * should not have been set yet (as no freeing is done) */ void nsi_set_ssl_session(struct niod *iod, SSL_SESSION *sessid); #endif static inline struct nevent *next_expirable_event(struct npool *nsp) { gh_hnode_t *hnode; hnode = gh_heap_min(&nsp->expirables); if (!hnode) return NULL; return container_of(hnode, struct nevent, expire); } static inline struct nevent *lnode_nevent(gh_lnode_t *lnode) { return container_of(lnode, struct nevent, nodeq_io); } static inline struct nevent *lnode_nevent2(gh_lnode_t *lnode) { return container_of(lnode, struct nevent, nodeq_pcap); } /* defined in nsock_core.c */ void process_iod_events(struct npool *nsp, struct niod *nsi, int ev); void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int ev); void process_expired_events(struct npool *nsp); #if HAVE_PCAP int pcap_read_on_nonselect(struct npool *nsp); void iterate_through_pcap_events(struct npool *nsp); #endif /* defined in nsock_engines.c */ struct io_engine *get_io_engine(void); #endif /* NSOCK_INTERNAL_H */