1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-15 20:29:03 +00:00

Added IOCP integration for Nsock

This commit is contained in:
tudor
2016-08-09 12:44:55 +00:00
parent 3ba4a87c75
commit 1aa7958e23
11 changed files with 949 additions and 3 deletions

View File

@@ -257,7 +257,7 @@ int inheritable_socket(int af, int style, int protocol) {
/* WSASocket is just like socket, except that the sockets it creates are
inheritable by subprocesses (such as are created by CreateProcess), while
those created by socket are not. */
return WSASocket(af, style, protocol, NULL, 0, 0);
return WSASocket(af, style, protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
#else
return socket(af, style, protocol);
#endif

View File

@@ -104,6 +104,7 @@
/* WSAPoll() isn't available before Vista */
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600)
#define HAVE_POLL 1
#define HAVE_IOCP 1
#endif
#endif /* NSOCK_WINCONFIG_H */

View File

@@ -186,6 +186,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\engine_epoll.c" />
<ClCompile Include="src\engine_iocp.c" />
<ClCompile Include="src\engine_kqueue.c" />
<ClCompile Include="src\engine_poll.c" />
<ClCompile Include="src\engine_select.c" />
@@ -197,6 +198,7 @@
<ClCompile Include="src\nsock_core.c" />
<ClCompile Include="src\nsock_engines.c" />
<ClCompile Include="src\nsock_event.c" />
<ClCompile Include="src\nsock_iocp.c" />
<ClCompile Include="src\nsock_iod.c" />
<ClCompile Include="src\nsock_log.c" />
<ClCompile Include="src\nsock_pcap.c" />
@@ -217,6 +219,7 @@
<ClInclude Include="src\netutils.h" />
<ClInclude Include="include\nsock.h" />
<ClInclude Include="src\nsock_internal.h" />
<ClInclude Include="src\nsock_iocp.h" />
<ClInclude Include="src\nsock_log.h" />
<ClInclude Include="src\nsock_pcap.h" />
<ClInclude Include="src\nsock_proxy.h" />

338
nsock/src/engine_iocp.c Normal file
View File

@@ -0,0 +1,338 @@
/***************************************************************************
* engine_iocp.c -- I/O Completion Ports based IO engine. *
* *
***********************IMPORTANT NSOCK LICENSE TERMS***********************
* *
* The nsock parallel socket event library is (C) 1999-2016 Insecure.Com *
* 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, Insecure.Com LLC *
* may be willing to sell alternative licenses (contact *
* sales@insecure.com ). *
* *
* As a special exception to the GPL terms, Insecure.Com 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 (Insecure.Com 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$ */
#if WIN32
#include "nsock_winconfig.h"
#endif
#if HAVE_IOCP
#include <Winsock2.h>
#include <Mswsock.h>
#include "nsock_internal.h"
#include "nsock_iocp.h"
#include "nsock_log.h"
#if HAVE_PCAP
#include "nsock_pcap.h"
#endif
/* --- ENGINE INTERFACE PROTOTYPES --- */
static int iocp_init(struct npool *nsp);
static void iocp_destroy(struct npool *nsp);
static int iocp_iod_register(struct npool *nsp, struct niod *iod, int ev);
static int iocp_iod_unregister(struct npool *nsp, struct niod *iod);
static int iocp_iod_modify(struct npool *nsp, struct niod *iod, int ev_set, int ev_clr);
static int iocp_loop(struct npool *nsp, int msec_timeout);
/* ---- ENGINE DEFINITION ---- */
struct io_engine engine_iocp = {
"iocp",
iocp_init,
iocp_destroy,
iocp_iod_register,
iocp_iod_unregister,
iocp_iod_modify,
iocp_loop
};
/* --- INTERNAL PROTOTYPES --- */
static void iterate_through_event_lists(struct npool *nsp);
/* 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
#ifndef PCAP_CAN_DO_SELECT
int pcap_read_on_nonselect(struct npool *nsp);
#endif
#endif
/* defined in nsock_event.c */
void update_first_events(struct nevent *nse);
extern struct timeval nsock_tod;
int iocp_init(struct npool *nsp) {
struct iocp_engine_info *iinfo;
iinfo = (struct iocp_engine_info *)safe_malloc(sizeof(struct iocp_engine_info));
iinfo->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
iinfo->capacity = 10;
iinfo->eov = NULL;
iinfo->entries_removed = 0;
iinfo->eov_list = (OVERLAPPED_ENTRY *)safe_malloc(iinfo->capacity * sizeof(OVERLAPPED_ENTRY));
nsp->engine_data = (void *)iinfo;
return 1;
}
void iocp_destroy(struct npool *nsp) {
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
assert(iinfo != NULL);
CloseHandle(iinfo->iocp);
free(iinfo->eov_list);
free(iinfo);
}
int iocp_iod_register(struct npool *nsp, struct niod *iod, int ev) {
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
HANDLE result;
assert(!IOD_PROPGET(iod, IOD_REGISTERED));
iod->watched_events = ev;
result = CreateIoCompletionPort((HANDLE)iod->sd, iinfo->iocp, NULL, 0);
assert(result);
IOD_PROPSET(iod, IOD_REGISTERED);
return 1;
}
/* Sadly a socket can't be unassociated with a completion port */
int iocp_iod_unregister(struct npool *nsp, struct niod *iod) {
if (IOD_PROPGET(iod, IOD_REGISTERED)) {
/* Nuke all uncompleted operations on that iod */
CancelIo((HANDLE)iod->sd);
IOD_PROPCLR(iod, IOD_REGISTERED);
}
return 1;
}
/* Can't do much here for IOCP */
int iocp_iod_modify(struct npool *nsp, struct niod *iod, int ev_set, int ev_clr) {
int new_events;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
assert((ev_set & ev_clr) == 0);
assert(IOD_PROPGET(iod, IOD_REGISTERED));
new_events = iod->watched_events;
new_events |= ev_set;
new_events &= ~ev_clr;
if (new_events == iod->watched_events)
return 1; /* nothing to do */
iod->watched_events = new_events;
return 1;
}
int iocp_loop(struct npool *nsp, int msec_timeout) {
int event_msecs; /* msecs before an event goes off */
int combined_msecs;
int sock_err = 0;
BOOL bRet;
int total_events;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
assert(msec_timeout >= -1);
if (nsp->events_pending == 0)
return 0; /* No need to wait on 0 events ... */
struct nevent *nse;
/* Make sure the preallocated space for the retrieved events is big enough */
total_events = gh_list_count(&nsp->connect_events) + gh_list_count(&nsp->read_events) + gh_list_count(&nsp->write_events);
if (iinfo->capacity < total_events) {
iinfo->capacity *= 2;
iinfo->eov_list = (OVERLAPPED_ENTRY *)safe_realloc(iinfo->eov_list, iinfo->capacity * sizeof(OVERLAPPED_ENTRY));
}
nsock_log_debug_all("wait for events");
nse = next_expirable_event(nsp);
if (!nse)
event_msecs = -1; /* None of the events specified a timeout */
else
event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod));
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
/* Force a low timeout when capturing packets on systems where
* the pcap descriptor is not select()able. */
if (gh_list_count(&nsp->pcap_read_events) > 0)
if (event_msecs > PCAP_POLL_INTERVAL)
event_msecs = PCAP_POLL_INTERVAL;
#endif
#endif
/* We cast to unsigned because we want -1 to be very high (since it means no
* timeout) */
combined_msecs = MIN((unsigned)event_msecs, (unsigned)msec_timeout);
#if HAVE_PCAP
#ifndef PCAP_CAN_DO_SELECT
/* do non-blocking read on pcap devices that doesn't support select()
* If there is anything read, just leave this loop. */
if (pcap_read_on_nonselect(nsp)) {
/* okay, something was read. */
}
else
#endif
#endif
{
/* It is mandatory these values are reset before calling GetQueuedCompletionStatusEx */
iinfo->entries_removed = 0;
memset(iinfo->eov_list, 0, iinfo->capacity * sizeof(OVERLAPPED_ENTRY));
bRet = GetQueuedCompletionStatusEx(iinfo->iocp, iinfo->eov_list, iinfo->capacity, &iinfo->entries_removed, combined_msecs, FALSE);
}
gettimeofday(&nsock_tod, NULL); /* Due to iocp delay */
if (!bRet) {
sock_err = socket_errno();
if (!iinfo->eov && sock_err != WAIT_TIMEOUT) {
nsock_log_error("nsock_loop error %d: %s", sock_err, socket_strerror(sock_err));
nsp->errnum = sock_err;
return -1;
}
}
iterate_through_event_lists(nsp);
return 1;
}
/* ---- INTERNAL FUNCTIONS ---- */
/* Iterate through all the event lists (such as connect_events, read_events,
* timer_events, etc) and take action for those that have completed (due to
* timeout, i/o, etc) */
void iterate_through_event_lists(struct npool *nsp) {
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
for (int i = 0; i < iinfo->entries_removed; i++) {
iinfo->eov = (struct extended_overlapped *)iinfo->eov_list[i].lpOverlapped;
/* We can't rely on iinfo->entries_removed to tell us the real number of
* events to process */
if (!iinfo->eov)
continue;
/* Is this the eov of an event that expired? */
if (iinfo->eov->nse->id != iinfo->eov->nse_id || iinfo->eov->nse->event_done) {
iinfo->eov = NULL;
continue;
}
struct niod *nsi = iinfo->eov->nse->iod;
struct nevent *nse = iinfo->eov->nse;
gh_list_t *evlist = NULL;
int ev = 0;
switch (nse->type) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
ev = EV_READ;
evlist = &nsp->connect_events;
break;
case NSE_TYPE_READ:
ev = EV_READ;
evlist = &nsp->read_events;
break;
case NSE_TYPE_WRITE:
ev = EV_WRITE;
evlist = &nsp->write_events;
break;
}
assert(evlist);
process_event(nsp, evlist, nse, ev);
if (!nse->event_done)
initiate_overlapped_event(nsp, nse);
else
free_eov(nsp, nse->eov);
if (nse->event_done) {
/* event is done, remove it from the event list and update IOD pointers
* to the first events of each kind */
update_first_events(nse);
gh_list_remove(evlist, &nse->nodeq_io);
gh_list_append(&nsp->free_events, &nse->nodeq_io);
if (nse->timeout.tv_sec)
gh_heap_remove(&nsp->expirables, &nse->expire);
}
if (nsi->state == NSIOD_STATE_DELETED) {
gh_list_remove(&nsp->active_iods, &nsi->nodeq);
gh_list_prepend(&nsp->free_iods, &nsi->nodeq);
}
iinfo->eov = NULL;
}
/* iterate through timers and expired events */
process_expired_events(nsp);
}
#endif /* HAVE_IOCP */

View File

@@ -64,6 +64,10 @@
#include <errno.h>
#include <string.h>
#if HAVE_IOCP
#include "nsock_iocp.h"
#endif
static int mksock_bind_addr(struct npool *ms, struct niod *iod) {
int rc;
@@ -253,6 +257,12 @@ void nsock_connect_internal(struct npool *ms, struct nevent *nse, int type, int
memcpy(&iod->peer, ss, sslen);
iod->peerlen = sslen;
#if HAVE_IOCP
/* The connection will be initiated when the event is added to the iod. */
if (engine_is_iocp(ms))
return;
#endif
if (connect(iod->sd, (struct sockaddr *)ss, sslen) == -1) {
int err = socket_errno();

View File

@@ -86,6 +86,10 @@
#include "nsock_pcap.h"
#endif
#if HAVE_IOCP
#include "nsock_iocp.h"
#endif
/* Nsock time of day -- we update this at least once per nsock_loop round (and
* after most calls that are likely to block). Other nsock files should grab
@@ -287,6 +291,12 @@ static int iod_add_event(struct niod *iod, struct nevent *nse) {
default:
fatal("Unknown event type (%d) for IOD #%lu\n", nse->type, iod->id);
}
#if HAVE_IOCP
if (engine_is_iocp(nsp))
initiate_overlapped_event(nsp, nse);
#endif
return 0;
}
@@ -342,6 +352,12 @@ void handle_connect_result(struct npool *ms, struct nevent *nse, enum nse_status
/* First we want to determine whether the socket really is connected */
if (getsockopt(iod->sd, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen) != 0)
optval = socket_errno(); /* Stupid Solaris */
#if HAVE_IOCP
else if (engine_is_iocp(ms)) {
if (get_overlapped_result(nse->iod, nse, NULL) == -1)
optval = socket_errno();
}
#endif
switch (optval) {
case 0:
@@ -542,6 +558,11 @@ void handle_write_result(struct npool *ms, struct nevent *nse, enum nse_status s
if (iod->ssl)
res = SSL_write(iod->ssl, str, bytesleft);
else
#endif
#if HAVE_IOCP
if (engine_is_iocp(ms)) {
res = get_overlapped_result(iod, nse, NULL);
} else
#endif
if (nse->writeinfo.dest.ss_family == AF_UNSPEC)
res = send(nse->iod->sd, str, bytesleft, 0);
@@ -613,7 +634,7 @@ void handle_timer_result(struct npool *ms, struct nevent *nse, enum nse_status s
/* Returns -1 if an error, otherwise the number of newly written bytes */
static int do_actual_read(struct npool *ms, struct nevent *nse) {
char buf[8192];
char buf[READ_BUFFER_SZ];
int buflen = 0;
struct niod *iod = nse->iod;
int err = 0;
@@ -627,8 +648,13 @@ static int do_actual_read(struct npool *ms, struct nevent *nse) {
do {
struct sockaddr_storage peer;
socklen_t peerlen;
peerlen = sizeof(peer);
#if HAVE_IOCP
if (engine_is_iocp(ms)) {
buflen = get_overlapped_result(iod, nse, buf);
peerlen = 0;
} else
#endif
buflen = recvfrom(iod->sd, buf, sizeof(buf), 0, (struct sockaddr *)&peer, &peerlen);
/* Using recv() was failing, at least on UNIX, for non-network sockets
@@ -1217,6 +1243,11 @@ void process_expired_events(struct npool *nsp) {
if (!event_timedout(nse))
break;
#if HAVE_IOCP
if (engine_is_iocp(nsp))
terminate_overlapped_event(nsp, nse);
#endif
gh_heap_pop(&nsp->expirables);
process_event(nsp, NULL, nse, EV_NONE);
assert(nse->event_done);

View File

@@ -63,6 +63,13 @@
#include "nsock_internal.h"
#if HAVE_IOCP
extern struct io_engine engine_iocp;
#define ENGINE_IOCP &engine_iocp,
#else
#define ENGINE_IOCP
#endif /* HAVE_IOCP */
#if HAVE_EPOLL
extern struct io_engine engine_epoll;
#define ENGINE_EPOLL &engine_epoll,
@@ -94,6 +101,7 @@ static struct io_engine *available_engines[] = {
ENGINE_EPOLL
ENGINE_KQUEUE
ENGINE_POLL
ENGINE_IOCP
ENGINE_SELECT
NULL
};
@@ -145,6 +153,9 @@ int nsock_set_default_engine(char *engine) {
const char *nsock_list_engines(void) {
return
#if HAVE_IOCP
"iocp "
#endif
#if HAVE_EPOLL
"epoll "
#endif

View File

@@ -110,6 +110,8 @@
/* ------------------- CONSTANTS ------------------- */
#define READ_BUFFER_SZ 8192
enum nsock_read_types {
NSOCK_READLINES,
NSOCK_READBYTES,
@@ -177,6 +179,11 @@ struct npool {
/* When an event is deleted, we stick it here for later reuse */
gh_list_t free_events;
#if HAVE_IOCP
gh_list_t active_eovs;
gh_list_t free_eovs;
#endif
/* Number of events pending (total) on all lists */
int events_pending;
@@ -360,6 +367,10 @@ struct nevent {
* that other crap */
unsigned int event_done: 1;
unsigned int eof: 1;
#if HAVE_IOCP
struct extended_overlapped *eov;
#endif
};
@@ -502,5 +513,9 @@ static inline struct nevent *lnode_nevent2(gh_lnode_t *lnode) {
return container_of(lnode, struct nevent, nodeq_pcap);
}
static inline int engine_is_iocp(struct npool *nsp) {
return !strcmp(nsp->engine->name, "iocp");
}
#endif /* NSOCK_INTERNAL_H */

388
nsock/src/nsock_iocp.c Normal file
View File

@@ -0,0 +1,388 @@
/***************************************************************************
* nsock_iocp.c -- This contains operations required by IOCP *
* *
***********************IMPORTANT NSOCK LICENSE TERMS***********************
* *
* The nsock parallel socket event library is (C) 1999-2016 Insecure.Com *
* 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, Insecure.Com LLC *
* may be willing to sell alternative licenses (contact *
* sales@insecure.com ). *
* *
* As a special exception to the GPL terms, Insecure.Com 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 (Insecure.Com 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$ */
#if WIN32
#include "nsock_winconfig.h"
#endif
#if HAVE_IOCP
#include <Winsock2.h>
#include <Mswsock.h>
#include "nsock_internal.h"
#include "nsock_iocp.h"
#include "nsock_log.h"
static int errcode_is_failure(int err) {
#ifndef WIN32
return err != EINTR && err != EAGAIN && err != EBUSY;
#else
return err != EINTR && err != EAGAIN && err != WSA_IO_PENDING && err != ERROR_NETNAME_DELETED;
#endif
}
static int map_faulty_errors(int err) {
/* This actually happens https://svn.boost.org/trac/boost/ticket/10744 */
switch (err) {
case ERROR_NETWORK_UNREACHABLE: return WSAENETUNREACH;
case ERROR_HOST_UNREACHABLE: return WSAEHOSTUNREACH;
case ERROR_CONNECTION_REFUSED: return WSAECONNREFUSED;
case ERROR_SEM_TIMEOUT: return WSAETIMEDOUT;
}
return err;
}
static struct extended_overlapped *new_eov(struct npool *nsp, struct nevent *nse) {
struct extended_overlapped *eov;
gh_lnode_t *lnode;
if(nse->eov) {
memset(&nse->eov->ov, 0, sizeof(OVERLAPPED));
return nse->eov;
}
lnode = gh_list_pop(&nsp->free_eovs);
if (!lnode)
eov = (struct extended_overlapped *)safe_malloc(sizeof(struct extended_overlapped));
else
eov = container_of(lnode, struct extended_overlapped, nodeq);
memset(eov, 0, sizeof(struct extended_overlapped));
nse->eov = eov;
eov->nse = nse;
eov->nse_id = nse->id;
eov->err = 0;
gh_list_prepend(&nsp->active_eovs, &eov->nodeq);
/* Make the read buffer equal to the size of the buffer in do_actual_read() */
if (nse->type == NSE_TYPE_READ && !eov->readbuf)
eov->readbuf = (char*)safe_malloc(READ_BUFFER_SZ * sizeof(char*));
return eov;
}
static void call_connect_overlapped(struct npool *nsp, struct nevent *nse) {
BOOL ok;
DWORD numBytes = 0;
SOCKET sock = nse->iod->sd;
GUID guid = WSAID_CONNECTEX;
struct sockaddr_in addr;
LPFN_CONNECTEX ConnectExPtr = NULL;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nse->iod->nsp->engine_data;
struct extended_overlapped *eov = new_eov(nsp, nse);
int ret;
struct sockaddr_storage *ss = &nse->iod->peer;
size_t sslen = nse->iod->peerlen;
if (nse->iod->lastproto != IPPROTO_TCP) {
if (connect(sock, (struct sockaddr *)ss, sslen) == -1) {
int err = socket_errno();
nse->event_done = 1;
nse->status = NSE_STATUS_ERROR;
nse->errnum = err;
free_eov(nsp, eov);
} else {
BOOL bRet = PostQueuedCompletionStatus(*(HANDLE *)iinfo, -1, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
assert(bRet);
}
return;
}
ret = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
(void*)&guid, sizeof(guid), (void*)&ConnectExPtr, sizeof(ConnectExPtr),
&numBytes, NULL, NULL);
assert(!ret);
/* ConnectEx doesn't automatically bind the socket */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;
if (!nse->iod->locallen) {
ret = bind(sock, (SOCKADDR*)&addr, sizeof(addr));
if(ret) {
int err = socket_errno();
nse->event_done = 1;
nse->status = NSE_STATUS_ERROR;
nse->errnum = err;
free_eov(nsp, eov);
return;
}
}
ok = ConnectExPtr(sock, (SOCKADDR*)ss, sslen, NULL, 0, NULL, (LPOVERLAPPED)eov);
if (!ok) {
int err = socket_errno();
if (err != ERROR_IO_PENDING) {
nse->event_done = 1;
nse->status = NSE_STATUS_ERROR;
nse->errnum = err;
free_eov(nsp, eov);
}
}
}
static void call_read_overlapped(struct nevent *nse) {
DWORD flags = 0;
int err = 0;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nse->iod->nsp->engine_data;
struct extended_overlapped *eov = new_eov(nse->iod->nsp, nse);
eov->wsabuf.buf = eov->readbuf;
eov->wsabuf.len = 8192;
err = WSARecvFrom(nse->iod->sd, &eov->wsabuf, 1, NULL, &flags,
(struct sockaddr *)&nse->iod->peer, (LPINT)&nse->iod->peerlen, (LPOVERLAPPED)eov, NULL);
if (err) {
err = socket_errno();
if (errcode_is_failure(err)) {
eov->err = err;
BOOL bRet = PostQueuedCompletionStatus(iinfo->iocp, -1, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
assert(bRet);
}
}
}
static void call_write_overlapped(struct nevent *nse) {
int err;
char *str;
int bytesleft;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nse->iod->nsp->engine_data;
struct extended_overlapped *eov = new_eov(nse->iod->nsp, nse);
str = fs_str(&nse->iobuf) + nse->writeinfo.written_so_far;
bytesleft = fs_length(&nse->iobuf) - nse->writeinfo.written_so_far;
eov->wsabuf.buf = str;
eov->wsabuf.len = bytesleft;
if (nse->writeinfo.dest.ss_family == AF_UNSPEC)
err = WSASend(nse->iod->sd, &eov->wsabuf, 1, NULL, 0, (LPWSAOVERLAPPED)eov, NULL);
else
err = WSASendTo(nse->iod->sd, &eov->wsabuf, 1, NULL, 0,
(struct sockaddr *)&nse->writeinfo.dest, (int)nse->writeinfo.destlen,
(LPWSAOVERLAPPED)eov, NULL);
if (err) {
err = socket_errno();
if (errcode_is_failure(err)) {
eov->err = err;
BOOL bRet = PostQueuedCompletionStatus(iinfo->iocp, -1, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
assert(bRet);
}
}
}
/* Either initiate a I/O read or force a SSL_read */
static void initiate_read(struct npool *nsp, struct nevent *nse) {
BOOL bRet;
struct extended_overlapped *eov;
if (!strcmp(nsp->engine->name, "iocp")) {
if (!nse->iod->ssl)
call_read_overlapped(nse);
else {
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
eov = new_eov(nse->iod->nsp, nse);
bRet = PostQueuedCompletionStatus(iinfo->iocp, 0, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
assert(bRet);
}
}
}
/* Either initiate a I/O write or force a SSL_write */
static void initiate_write(struct npool *nsp, struct nevent *nse) {
BOOL bRet;
struct extended_overlapped *eov;
if (!strcmp(nsp->engine->name, "iocp")) {
if (!nse->iod->ssl)
call_write_overlapped(nse);
else {
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
eov = new_eov(nse->iod->nsp, nse);
bRet = PostQueuedCompletionStatus(iinfo->iocp, 0, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
assert(bRet);
}
}
}
/* Force a PCAP read */
static void initiate_pcap_read(struct npool *nsp, struct nevent *nse) {
BOOL bRet;
struct extended_overlapped *eov;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
eov = new_eov(nse->iod->nsp, nse);
bRet = PostQueuedCompletionStatus(iinfo->iocp, 0, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
assert(bRet);
}
static void initiate_connect(struct npool *nsp, struct nevent *nse) {
int sslconnect_inprogress = 0;
BOOL bRet;
struct extended_overlapped *eov;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
#if HAVE_OPENSSL
sslconnect_inprogress = nse->type == NSE_TYPE_CONNECT_SSL && nse->iod &&
(nse->sslinfo.ssl_desire == SSL_ERROR_WANT_READ ||
nse->sslinfo.ssl_desire == SSL_ERROR_WANT_WRITE);
#endif
if (sslconnect_inprogress) {
eov = new_eov(nse->iod->nsp, nse);
bRet = PostQueuedCompletionStatus(iinfo->iocp, 0, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
assert(bRet);
}
else
call_connect_overlapped(nsp, nse);
}
/* Start the overlapped I/O operation */
void initiate_overlapped_event(struct npool *nsp, struct nevent *nse) {
if (!engine_is_iocp(nsp))
return;
switch (nse->type) {
case NSE_TYPE_CONNECT:
case NSE_TYPE_CONNECT_SSL:
initiate_connect(nsp, nse);
break;
case NSE_TYPE_READ:
initiate_read(nsp, nse);
break;
case NSE_TYPE_WRITE:
initiate_write(nsp, nse);
break;
#if HAVE_PCAP
case NSE_TYPE_PCAP_READ:
initiate_pcap_read(nsp, nse);
break;
#endif
default: fatal("Event type(%d) not supported by engine IOCP\n", nse->type);
}
}
/* Terminate an overlapped I/O operation that expired */
void terminate_overlapped_event(struct npool *nsp, struct nevent *nse) {
if (!HasOverlappedIoCompleted((LPOVERLAPPED)nse->eov))
CancelIoEx((HANDLE)nse->iod->sd, (LPOVERLAPPED)nse->eov);
}
/* Retrieve the ammount of bytes transferred or set the appropriate error */
int get_overlapped_result(struct niod *iod, struct nevent *nse, char *buf) {
DWORD dwRes = 0;
int err;
struct npool *nsp = nse->iod->nsp;
static struct extended_overlapped *old_eov = NULL;
struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
struct extended_overlapped *eov = iinfo->eov;
if (eov->err) {
SetLastError(map_faulty_errors(eov->err));
return -1;
}
if (!GetOverlappedResult((HANDLE)iod->sd, (LPOVERLAPPED)eov, &dwRes, FALSE)) {
err = socket_errno();
if (errcode_is_failure(err)) {
SetLastError(map_faulty_errors(err));
return -1;
}
}
if (nse->type == NSE_TYPE_READ && buf)
memcpy(buf, eov->wsabuf.buf, dwRes);
/* If the read buffer wasn't big enough, subsequent calls from do_actual_read will make us
read with recvfrom the rest of the returned data */
if (nse->type == NSE_TYPE_READ && dwRes == eov->wsabuf.len && old_eov == eov) {
struct sockaddr_storage peer;
socklen_t peerlen = sizeof(peer);
dwRes = recvfrom(iod->sd, buf, sizeof(buf), 0, (struct sockaddr *)&peer, &peerlen);
}
if (!nse->type == NSE_TYPE_READ || (nse->type == NSE_TYPE_READ && dwRes < eov->wsabuf.len)) {
old_eov = NULL;
} else if (nse->type == NSE_TYPE_READ && dwRes == eov->wsabuf.len) {
old_eov = eov;
}
return dwRes;
}
void free_eov(struct npool *nsp, struct extended_overlapped *eov) {
if (eov->readbuf) {
free(eov->readbuf);
eov->readbuf = NULL;
}
gh_list_remove(&nsp->active_eovs, &eov->nodeq);
gh_list_prepend(&nsp->free_eovs, &eov->nodeq);
}
#endif /* HAVE_IOCP */

117
nsock/src/nsock_iocp.h Normal file
View File

@@ -0,0 +1,117 @@
/***************************************************************************
* nsock_iocp.h -- Header for the overlapped operations in nsock_iocp.c. *
* *
***********************IMPORTANT NSOCK LICENSE TERMS***********************
* *
* The nsock parallel socket event library is (C) 1999-2016 Insecure.Com *
* 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, Insecure.Com LLC *
* may be willing to sell alternative licenses (contact *
* sales@insecure.com ). *
* *
* As a special exception to the GPL terms, Insecure.Com 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 (Insecure.Com 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). *
* *
***************************************************************************/
#ifndef NSOCK_IOCP_H
#define NSOCK_IOCP_H
#include "nsock_internal.h"
#ifdef HAVE_IOCP
/*
* Engine specific data structure
*/
struct iocp_engine_info {
/* The handle to the Completion Port*/
HANDLE iocp;
/* We put the current eov to be processed here in order to be retrieved by nsock_core */
struct extended_overlapped *eov;
/* The overlapped_entry list used to retrieve completed packets from the port */
OVERLAPPED_ENTRY *eov_list;
unsigned long capacity;
/* How many Completion Packets we actually retreieved */
unsigned long entries_removed;
};
struct extended_overlapped {
/* Overlapped structure used for overlapped operations */
OVERLAPPED ov;
/* Did we get an error when we initiated the operation?
Put the error code here and post it to the main loop */
int err;
/* The event may have expired and was recycled, we can't trust
a pointer to the nevent structure to tell us the real nevent */
nsock_event_id nse_id;
/* A pointer to the event */
struct nevent *nse;
/* Needed for WSARecv/WSASend */
WSABUF wsabuf;
/* This is the buffer we will read data in */
char *readbuf;
/* The struct npool keeps track of EOVs that have been allocated so that it
* can destroy them if the msp is deleted. This pointer makes it easy to
* remove this struct extended_overlapped from the allocated list when necessary */
gh_lnode_t nodeq;
};
void initiate_overlapped_event(struct npool *nsp, struct nevent *nse);
void terminate_overlapped_event(struct npool *nsp, struct nevent *nse);
int get_overlapped_result(struct niod *iod, struct nevent *nse, char *buf);
void free_eov(struct npool *nsp, struct extended_overlapped *eov);
#endif /* HAVE_IOCP */
#endif /* NSOCK_IOCP_H */

View File

@@ -71,6 +71,10 @@
#endif
#include <signal.h>
#if HAVE_IOCP
#include "nsock_iocp.h"
#endif
extern struct timeval nsock_tod;
@@ -173,6 +177,11 @@ nsock_pool nsock_pool_new(void *userdata) {
gh_list_init(&nsp->free_iods);
gh_list_init(&nsp->free_events);
#if HAVE_IOCP
gh_list_init(&nsp->active_eovs);
gh_list_init(&nsp->free_eovs);
#endif
nsp->next_event_serial = 1;
nsp->device = NULL;
@@ -279,10 +288,33 @@ void nsock_pool_delete(nsock_pool ms_pool) {
free(nse);
}
#if HAVE_IOCP
struct extended_overlapped *eov;
while ((current = gh_list_pop(&nsp->active_eovs))) {
eov = container_of(current, struct extended_overlapped, nodeq);
if (eov->readbuf) {
free(eov->readbuf);
eov->readbuf = NULL;
}
free(eov);
}
while ((current = gh_list_pop(&nsp->free_eovs))) {
eov = container_of(current, struct extended_overlapped, nodeq);
free(eov);
}
#endif
gh_list_free(&nsp->active_iods);
gh_list_free(&nsp->free_iods);
gh_list_free(&nsp->free_events);
#if HAVE_IOCP
gh_list_free(&nsp->active_eovs);
gh_list_free(&nsp->free_eovs);
#endif
nsock_engine_destroy(nsp);
#if HAVE_OPENSSL