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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user