1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-18 21:49:01 +00:00

Merged nsock-engines from nmap-exp. This rewrite of the nsock library adds

support for system-specific scalable IO notification facilities without breaking
portability. This initial version comes with an epoll(7)-based engine for Linux
and a select(2)-based fallback engine for all other operating systems.

This required an important refactoring of the library but the external API was
preserved.

The rewrite also tries to bring the coding standards of nmap to nsock.

See http://labs.unix-junkies.org/nsock_engines.html for the details.
This commit is contained in:
henri
2012-01-05 01:08:16 +00:00
parent 15f74d395f
commit 856cd00a17
31 changed files with 4496 additions and 2774 deletions

View File

@@ -1,4 +1,3 @@
/***************************************************************************
* nsock_iod.c -- This contains the functions relating to nsock_iod (and *
* its nsock internal manifistation -- nsockiod. This is is similar to a *
@@ -69,32 +68,31 @@
#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.
nsi_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 is like a "file descriptor" for the nsock library. You use it to
* request events. And here is how you create an nsock_iod. nsi_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 nsi_new(nsock_pool nsockp, void *userdata) {
return nsi_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
*/
/* 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 nsi_new2(nsock_pool nsockp, int sd, void *userdata) {
mspool *nsp = (mspool *) nsockp;
mspool *nsp = (mspool *)nsockp;
msiod *nsi;
nsi = (msiod *) gh_list_pop(&nsp->free_iods);
if (!nsi) nsi = (msiod * ) safe_malloc(sizeof(msiod));
nsi = (msiod *)gh_list_pop(&nsp->free_iods);
if (!nsi) {
nsi = (msiod *)safe_malloc(sizeof(msiod));
memset(nsi, 0, sizeof(*nsi));
}
memset(nsi, 0, sizeof(*nsi));
if (sd == -1) {
if (sd == -1) {
nsi->sd = -1;
nsi->state = NSIOD_STATE_INITIAL;
} else if (sd == STDIN_FILENO) {
@@ -110,16 +108,22 @@ nsock_iod nsi_new2(nsock_pool nsockp, int sd, void *userdata) {
nsi->state = NSIOD_STATE_UNKNOWN;
}
nsi->locallen = 0;
nsi->userdata = userdata;
nsi->nsp = (mspool *) nsockp;
nsi->events_pending = 0;
nsi->first_connect = NULL;
nsi->first_read = NULL;
nsi->first_write = NULL;
#if HAVE_PCAP
nsi->first_pcap_read = NULL;
#endif
nsi->readsd_count = 0;
nsi->writesd_count = 0;
nsi->write_count = 0;
nsi->readpcapsd_count = 0;
nsi->read_count=0;
nsi->write_count=0;
nsi->userdata = userdata;
nsi->nsp = (mspool *)nsockp;
nsi->read_count = 0;
nsi->write_count = 0;
nsi->hostname = NULL;
@@ -131,75 +135,82 @@ nsock_iod nsi_new2(nsock_pool nsockp, int sd, void *userdata) {
#endif
nsi->id = nsp->next_iod_serial++;
if (nsi->id == 0) nsi->id = nsp->next_iod_serial++;
if (nsi->id == 0)
nsi->id = nsp->next_iod_serial++;
/* The nsp keeps track of active msiods so it can delete them
if it is deleted */
nsi->entry_in_nsp_active_iods = gh_list_append(&nsi->nsp->active_iods, nsi);
return (nsock_iod) nsi;
/* The nsp keeps track of active msiods so it can delete them if it is deleted */
nsi->entry_in_nsp_active_iods = gh_list_append(&nsp->active_iods, nsi);
return (nsock_iod)nsi;
}
/* Defined in nsock_core.c. */
int socket_count_zero(msiod *iod, mspool *ms);
/* If msiod_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 nsi_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 quiit
the program) */
/* If msiod_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
* nsi_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
* quiit the program) */
void nsi_delete(nsock_iod nsockiod, int pending_response) {
msiod *nsi = (msiod *) nsockiod;
gh_list *elist_ar[3];
int elist;
gh_list_elem *currev_elem, *next_elem;
msevent *currev;
msiod *nsi = (msiod *)nsockiod;
gh_list_elem *evlist_ar[3];
gh_list *corresp_list[3];
int i;
gh_list_elem *current, *next;
assert(nsi);
if (nsi->nsp->tracelevel > 1)
nsock_trace(nsi->nsp, "nsi_delete() (IOD #%lu)", nsi->id);
if (nsi->state == NSIOD_STATE_DELETED) {
fatal("nsi_delete() called on nsock_iod which appears to have already been deleted");
/* This nsi is already marked as deleted, will probably be removed from the
* list very soon. Just return to avoid breaking reentrancy. */
return;
}
if (nsi->events_pending > 0) {
/* shit -- they killed the msiod while an event was still pending
on it. Maybe I should store the pending events in the msiod.
On the other hand, this should be a pretty rare occurance and
so I'll save space and hassle by just locating the events here
by searching through the active events list */
/* shit -- they killed the msiod while an event was still pending on it.
* Maybe I should store the pending events in the msiod. On the other hand,
* this should be a pretty rare occurance 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("nsi_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);
elist_ar[0] = &(nsi->nsp->evl.read_events);
elist_ar[1] = &(nsi->nsp->evl.write_events);
elist_ar[2] = &(nsi->nsp->evl.connect_events);
for(elist = 0; elist < 3 && nsi->events_pending > 0; elist++) {
currev_elem = GH_LIST_FIRST_ELEM(elist_ar[elist]);
while(currev_elem) {
currev = (msevent *) GH_LIST_ELEM_DATA(currev_elem);
next_elem = GH_LIST_ELEM_NEXT(currev_elem);
if (currev->iod == nsi) {
/* OK - we found an event pending on this IOD. Kill it. */
/* printf("Found an outstanding event (out of %d), removing\n", nsi->events_pending); */
msevent_cancel(nsi->nsp, currev, elist_ar[elist], currev_elem, pending_response == NSOCK_PENDING_NOTIFY);
}
if (nsi->events_pending == 0)
break;
currev_elem = next_elem;
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;
corresp_list[0] = &nsi->nsp->connect_events;
corresp_list[1] = &nsi->nsp->read_events;
corresp_list[2] = &nsi->nsp->write_events;
for (i = 0; i < 3 && nsi->events_pending > 0; i++) {
for (current = evlist_ar[i]; current != NULL; current = next) {
next = GH_LIST_ELEM_NEXT(current);
msevent *nse = (msevent *)GH_LIST_ELEM_DATA(current);
/* we're done with this list of events for the current IOD */
if (nse->iod != nsi)
break;
msevent_cancel(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. */
* weren't already decremented to zero. */
if (nsi->sd >= 0)
socket_count_zero(nsi, nsi->nsp);
@@ -209,22 +220,24 @@ void nsi_delete(nsock_iod nsockiod, int pending_response) {
/* Close any SSL resources */
if (nsi->ssl) {
/* No longer free session because copy nsi stores is not reference counted */
/* if (nsi->ssl_session)
SSL_SESSION_free(nsi->ssl_session);
nsi->ssl_session = NULL; */
#if 0
if (nsi->ssl_session)
SSL_SESSION_free(nsi->ssl_session);
nsi->ssl_session = NULL;
#endif
if (SSL_shutdown(nsi->ssl) == -1) {
if (nsi->nsp->tracelevel > 1)
nsock_trace(nsi->nsp,
"nsi_delete(): SSL shutdown failed (%s) on NSI %li",
ERR_reason_error_string(SSL_get_error(nsi->ssl, -1)),
nsi->id);
nsock_trace(nsi->nsp, "nsi_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. */
/* 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
@@ -240,32 +253,29 @@ void nsi_delete(nsock_iod nsockiod, int pending_response) {
free(nsi->ipopts);
#if HAVE_PCAP
if(nsi->pcap){
mspcap *mp = (mspcap *) nsi->pcap;
if(mp->pt){
if (nsi->pcap){
mspcap *mp = (mspcap *)nsi->pcap;
if (mp->pt){
pcap_close(mp->pt);
mp->pt=NULL;
mp->pt = NULL;
}
if(mp->pcap_desc){
// Should I close pcap_desc or pcap_close does this for me?
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;
if (mp->pcap_device) {
free(mp->pcap_device);
mp->pcap_device = NULL;
}
free(mp);
nsi->pcap = NULL;
}
#endif
gh_list_remove_elem(&nsi->nsp->active_iods, nsi->entry_in_nsp_active_iods);
gh_list_prepend(&nsi->nsp->free_iods, nsi);
}
/* 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). */
/* 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 nsi_id(nsock_iod nsockiod) {
assert(nsockiod);
return ((msiod *)nsockiod)->id;
@@ -280,7 +290,7 @@ nsock_ssl nsi_getssl(nsock_iod nsockiod) {
#endif
}
/* Returns the SSL_SESSION of an nsock_iod, and increments it's usage count */
/* Returns the SSL_SESSION of an nsock_iod, and increments it's usage count. */
nsock_ssl_session nsi_get1_ssl_session(nsock_iod nsockiod) {
#if HAVE_OPENSSL
return SSL_get1_session(((msiod *)nsockiod)->ssl);
@@ -289,7 +299,7 @@ nsock_ssl_session nsi_get1_ssl_session(nsock_iod nsockiod) {
#endif
}
/* Returns the SSL_SESSION without incrementing usage count */
/* Returns the SSL_SESSION without incrementing usage count. */
nsock_ssl_session nsi_get0_ssl_session(nsock_iod nsockiod) {
#if HAVE_OPENSSL
return SSL_get0_session(((msiod *)nsockiod)->ssl);
@@ -298,103 +308,107 @@ nsock_ssl_session nsi_get0_ssl_session(nsock_iod nsockiod) {
#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) */
/* 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(msiod *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 msiod so you can retrieve it during a callback. */
/* Sometimes it is useful to store a pointer to information inside the msiod so
* you can retrieve it during a callback. */
void nsi_setud(nsock_iod nsockiod, void *data) {
assert(nsockiod);
((msiod *)nsockiod)->userdata = data;
}
/* And the function above wouldn't make much sense if we didn't have a way
to retrieve that data ... */
/* And the function above wouldn't make much sense if we didn't have a way to
* retrieve that data... */
void *nsi_getud(nsock_iod nsockiod) {
assert(nsockiod);
return ((msiod *)nsockiod)->userdata;
}
/* Returns 1 if an NSI is communicating via SSL, 0 otherwise */
/* Returns 1 if an NSI is communicating via SSL, 0 otherwise. */
int nsi_checkssl(nsock_iod nsockiod) {
return ((msiod *)nsockiod)->ssl? 1 : 0;
return (((msiod *)nsockiod)->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. */
/* 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 nsi_peerport(nsock_iod nsockiod) {
msiod *nsi = (msiod *) nsockiod;
msiod *nsi = (msiod *)nsockiod;
int fam;
if (nsi->peerlen <= 0)
return -1;
fam = ((struct sockaddr_in *) &nsi->peer)->sin_family;
fam = ((struct sockaddr_in *)&nsi->peer)->sin_family;
if (fam == AF_INET)
return ntohs(((struct sockaddr_in *) &nsi->peer)->sin_port);
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);
return ntohs(((struct sockaddr_in6 *)&nsi->peer)->sin6_port);
#endif
return -1;
}
/* Sets the local address to bind to before connect() */
int nsi_set_localaddr(nsock_iod nsi, struct sockaddr_storage *ss, size_t sslen)
{
msiod *iod = (msiod *) nsi;
int nsi_set_localaddr(nsock_iod nsi, struct sockaddr_storage *ss, size_t sslen) {
msiod *iod = (msiod *)nsi;
assert(iod);
assert(iod);
if (sslen > sizeof(iod->local))
return -1;
if (sslen > sizeof(iod->local))
return -1;
memcpy(&iod->local, ss, sslen);
iod->locallen = sslen;
return 0;
memcpy(&iod->local, ss, sslen);
iod->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 nsi_set_ipoptions(nsock_iod nsi, void *opts, size_t optslen)
{
msiod *iod = (msiod *) nsi;
/* 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 nsi_set_ipoptions(nsock_iod nsi, void *opts, size_t optslen) {
msiod *iod = (msiod *)nsi;
assert(iod);
assert(iod);
if (optslen > 44)
return -1;
if (optslen > 44)
return -1;
iod->ipopts = safe_malloc(optslen);
memcpy(iod->ipopts, opts, optslen);
iod->ipoptslen = optslen;
return 0;
iod->ipopts = safe_malloc(optslen);
memcpy(iod->ipopts, opts, optslen);
iod->ipoptslen = optslen;
return 0;
}
/* I didn't want to do this. Its an ugly hack, but I suspect it will
be neccessary. 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 */
/* I didn't want to do this. Its an ugly hack, but I suspect it will be
* neccessary. 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 nsi_getsd(nsock_iod nsockiod) {
msiod *iod = (msiod *)nsockiod;
assert(nsockiod);
return ((msiod *)nsockiod)->sd;
#if HAVE_PCAP
if (iod->pcap)
return ((mspcap *)iod->pcap)->pcap_desc;
else
#endif
return iod->sd;
}
unsigned long nsi_get_read_count(nsock_iod nsockiod){
@@ -405,17 +419,18 @@ unsigned long nsi_get_read_count(nsock_iod nsockiod){
unsigned long nsi_get_write_count(nsock_iod nsockiod){
assert(nsockiod);
return ((msiod *)nsockiod)->write_count;
}
int nsi_set_hostname(nsock_iod nsi, const char *hostname) {
msiod *iod = (msiod *) nsi;
msiod *iod = (msiod *)nsi;
if (iod->hostname != NULL)
free(iod->hostname);
iod->hostname = strdup(hostname);
if (iod->hostname == NULL)
return -1;
return 0;
}