"Fossies" - the Fresh Open Source Software Archive

Member "nmap-7.91/nsock/src/engine_iocp.c" (9 Oct 2020, 27424 Bytes) of package /linux/misc/nmap-7.91.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "engine_iocp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 7.90_vs_7.91.

    1 /***************************************************************************
    2  * engine_iocp.c -- I/O Completion Ports based IO engine.                  *
    3  *                                                                         *
    4  ***********************IMPORTANT NSOCK LICENSE TERMS***********************
    5  *                                                                         *
    6  * The nsock parallel socket event library is (C) 1999-2020 Insecure.Com   *
    7  * LLC This library is free software; you may redistribute and/or          *
    8  * modify it under the terms of the GNU General Public License as          *
    9  * published by the Free Software Foundation; Version 2.  This guarantees  *
   10  * your right to use, modify, and redistribute this software under certain *
   11  * conditions.  If this license is unacceptable to you, Insecure.Com LLC   *
   12  * may be willing to sell alternative licenses (contact                    *
   13  * sales@insecure.com ).                                                   *
   14  *                                                                         *
   15  * As a special exception to the GPL terms, Insecure.Com LLC grants        *
   16  * permission to link the code of this program with any version of the     *
   17  * OpenSSL library which is distributed under a license identical to that  *
   18  * listed in the included docs/licenses/OpenSSL.txt file, and distribute   *
   19  * linked combinations including the two. You must obey the GNU GPL in all *
   20  * respects for all of the code used other than OpenSSL.  If you modify    *
   21  * this file, you may extend this exception to your version of the file,   *
   22  * but you are not obligated to do so.                                     *
   23  *                                                                         *
   24  * If you received these files with a written license agreement stating    *
   25  * terms other than the (GPL) terms above, then that alternative license   *
   26  * agreement takes precedence over this comment.                           *
   27  *                                                                         *
   28  * Source is provided to this software because we believe users have a     *
   29  * right to know exactly what a program is going to do before they run it. *
   30  * This also allows you to audit the software for security holes.          *
   31  *                                                                         *
   32  * Source code also allows you to port Nmap to new platforms, fix bugs,    *
   33  * and add new features.  You are highly encouraged to send your changes   *
   34  * to the dev@nmap.org mailing list for possible incorporation into the    *
   35  * main distribution.  By sending these changes to Fyodor or one of the    *
   36  * Insecure.Org development mailing lists, or checking them into the Nmap  *
   37  * source code repository, it is understood (unless you specify otherwise) *
   38  * that you are offering the Nmap Project (Insecure.Com LLC) the           *
   39  * unlimited, non-exclusive right to reuse, modify, and relicense the      *
   40  * code.  Nmap will always be available Open Source, but this is important *
   41  * because the inability to relicense code has caused devastating problems *
   42  * for other Free Software projects (such as KDE and NASM).  We also       *
   43  * occasionally relicense the code to third parties as discussed above.    *
   44  * If you wish to specify special license conditions of your               *
   45  * contributions, just say so when you send them.                          *
   46  *                                                                         *
   47  * This program is distributed in the hope that it will be useful, but     *
   48  * WITHOUT ANY WARRANTY; without even the implied warranty of              *
   49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
   50  * General Public License v2.0 for more details                            *
   51  * (http://www.gnu.org/licenses/gpl-2.0.html).                             *
   52  *                                                                         *
   53  ***************************************************************************/
   54 
   55 /* $Id$ */
   56 
   57 #if WIN32
   58 #include "nsock_winconfig.h"
   59 #endif
   60 
   61 #if HAVE_IOCP
   62 
   63 #include <Winsock2.h>
   64 #include <Mswsock.h>
   65 
   66 #include "nsock_internal.h"
   67 #include "nsock_log.h"
   68 
   69 #if HAVE_PCAP
   70 #include "nsock_pcap.h"
   71 #endif
   72 
   73 
   74 /* --- ENGINE INTERFACE PROTOTYPES --- */
   75 static int iocp_init(struct npool *nsp);
   76 static void iocp_destroy(struct npool *nsp);
   77 static int iocp_iod_register(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev);
   78 static int iocp_iod_unregister(struct npool *nsp, struct niod *iod);
   79 static int iocp_iod_modify(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev_set, int ev_clr);
   80 static int iocp_loop(struct npool *nsp, int msec_timeout);
   81 
   82 int iocp_iod_connect(struct npool *nsp, int sockfd, const struct sockaddr *addr, socklen_t addrlen);
   83 int iocp_iod_read(struct npool *nsp, int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
   84 int iocp_iod_write(struct npool *nsp, int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
   85 
   86 struct io_operations iocp_io_operations = {
   87   iocp_iod_connect,
   88   iocp_iod_read,
   89   iocp_iod_write
   90 };
   91 
   92 /* ---- ENGINE DEFINITION ---- */
   93 struct io_engine engine_iocp = {
   94   "iocp",
   95   iocp_init,
   96   iocp_destroy,
   97   iocp_iod_register,
   98   iocp_iod_unregister,
   99   iocp_iod_modify,
  100   iocp_loop,
  101   &iocp_io_operations
  102 };
  103 
  104 /*
  105 * Engine specific data structure
  106 */
  107 struct iocp_engine_info {
  108   /* The handle to the Completion Port*/
  109   HANDLE iocp;
  110 
  111   /* We put the current eov to be processed here in order to be retrieved by nsock_core */
  112   struct extended_overlapped *eov;
  113 
  114   /* The overlapped_entry list used to retrieve completed packets from the port */
  115   OVERLAPPED_ENTRY *eov_list;
  116   unsigned long capacity;
  117 
  118   /* How many Completion Packets we actually retreieved */
  119   unsigned long entries_removed;
  120 
  121   gh_list_t active_eovs;
  122   gh_list_t free_eovs;
  123 };
  124 
  125 struct extended_overlapped {
  126   /* Overlapped structure used for overlapped operations */
  127   OVERLAPPED ov;
  128 
  129   /* Did we get an error when we initiated the operation?
  130   Put the error code here and post it to the main loop */
  131   int err;
  132 
  133   /* The event may have expired and was recycled, we can't trust
  134   a pointer to the nevent structure to tell us the real nevent */
  135   nsock_event_id nse_id;
  136 
  137   /* A pointer to the event */
  138   struct nevent *nse;
  139 
  140   /* Needed for WSARecv/WSASend */
  141   WSABUF wsabuf;
  142 
  143   /* This is the buffer we will read data in */
  144   char *readbuf;
  145 
  146   /* The struct npool keeps track of EOVs that have been allocated so that it
  147   * can destroy them if the msp is deleted.  This pointer makes it easy to
  148   * remove this struct extended_overlapped from the allocated list when necessary */
  149   gh_lnode_t nodeq;
  150 
  151   int eov_received;
  152 };
  153 
  154 /* --- INTERNAL PROTOTYPES --- */
  155 static void iterate_through_event_lists(struct npool *nsp);
  156 static void iterate_through_pcap_events(struct npool *nsp);
  157 static void terminate_overlapped_event(struct npool *nsp, struct nevent *nse);
  158 static void initiate_overlapped_event(struct npool *nsp, struct nevent *nse);
  159 static int get_overlapped_result(struct npool *nsp, int fd, const void *buffer, size_t count);
  160 static void force_operation(struct npool *nsp, struct nevent *nse);
  161 static void free_eov(struct npool *nsp, struct extended_overlapped *eov);
  162 static int map_faulty_errors(int err);
  163 
  164 /* defined in nsock_core.c */
  165 void process_iod_events(struct npool *nsp, struct niod *nsi, int ev);
  166 void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int ev);
  167 void process_expired_events(struct npool *nsp);
  168 #if HAVE_PCAP
  169 #ifndef PCAP_CAN_DO_SELECT
  170 int pcap_read_on_nonselect(struct npool *nsp);
  171 #endif
  172 #endif
  173 
  174 /* defined in nsock_event.c */
  175 void update_first_events(struct nevent *nse);
  176 
  177 
  178 extern struct timeval nsock_tod;
  179 
  180 int iocp_init(struct npool *nsp) {
  181   struct iocp_engine_info *iinfo;
  182 
  183   iinfo = (struct iocp_engine_info *)safe_malloc(sizeof(struct iocp_engine_info));
  184 
  185   gh_list_init(&iinfo->active_eovs);
  186   gh_list_init(&iinfo->free_eovs);
  187 
  188   iinfo->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
  189   iinfo->capacity = 10;
  190   iinfo->eov = NULL;
  191   iinfo->entries_removed = 0;
  192   iinfo->eov_list = (OVERLAPPED_ENTRY *)safe_malloc(iinfo->capacity * sizeof(OVERLAPPED_ENTRY));
  193   nsp->engine_data = (void *)iinfo;
  194 
  195   return 1;
  196 }
  197 
  198 void iocp_destroy(struct npool *nsp) {
  199   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  200 
  201   assert(iinfo != NULL);
  202 
  203   struct extended_overlapped *eov;
  204   gh_lnode_t *current;
  205 
  206   while ((current = gh_list_pop(&iinfo->active_eovs))) {
  207     eov = container_of(current, struct extended_overlapped, nodeq);
  208     if (eov->readbuf) {
  209       free(eov->readbuf);
  210       eov->readbuf = NULL;
  211     }
  212     free(eov);
  213   }
  214 
  215   while ((current = gh_list_pop(&iinfo->free_eovs))) {
  216     eov = container_of(current, struct extended_overlapped, nodeq);
  217     free(eov);
  218   }
  219 
  220   gh_list_free(&iinfo->active_eovs);
  221   gh_list_free(&iinfo->free_eovs);
  222 
  223   CloseHandle(iinfo->iocp);
  224   free(iinfo->eov_list);
  225 
  226   free(iinfo);
  227 }
  228 
  229 int iocp_iod_register(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev) {
  230   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  231   HANDLE result;
  232 
  233   assert(!IOD_PROPGET(iod, IOD_REGISTERED));
  234   iod->watched_events = ev;
  235   result = CreateIoCompletionPort((HANDLE)iod->sd, iinfo->iocp, NULL, 0);
  236   assert(result);
  237 
  238   IOD_PROPSET(iod, IOD_REGISTERED);
  239 
  240   initiate_overlapped_event(nsp, nse);
  241 
  242   return 1;
  243 }
  244 
  245 /* Sadly a socket can't be unassociated with a completion port */
  246 int iocp_iod_unregister(struct npool *nsp, struct niod *iod) {
  247 
  248   if (IOD_PROPGET(iod, IOD_REGISTERED)) {
  249     /* Nuke all uncompleted operations on that iod */
  250     CancelIo((HANDLE)iod->sd);
  251     IOD_PROPCLR(iod, IOD_REGISTERED);
  252   }
  253 
  254   return 1;
  255 }
  256 
  257 int iocp_iod_modify(struct npool *nsp, struct niod *iod, struct nevent *nse, int ev_set, int ev_clr) {
  258   int new_events;
  259   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  260 
  261   assert((ev_set & ev_clr) == 0);
  262   assert(IOD_PROPGET(iod, IOD_REGISTERED));
  263 
  264   new_events = iod->watched_events;
  265   new_events |= ev_set;
  266   new_events &= ~ev_clr;
  267 
  268   if (ev_set != EV_NONE)
  269     initiate_overlapped_event(nsp, nse);
  270   else if (ev_clr != EV_NONE)
  271     terminate_overlapped_event(nsp, nse);
  272 
  273   if (new_events == iod->watched_events)
  274     return 1; /* nothing to do */
  275 
  276   iod->watched_events = new_events;
  277 
  278   return 1;
  279 }
  280 
  281 int iocp_loop(struct npool *nsp, int msec_timeout) {
  282   int event_msecs; /* msecs before an event goes off */
  283   int combined_msecs;
  284   int sock_err = 0;
  285   BOOL bRet;
  286   unsigned long total_events;
  287   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  288 
  289   assert(msec_timeout >= -1);
  290 
  291   if (nsp->events_pending == 0)
  292     return 0; /* No need to wait on 0 events ... */
  293 
  294 
  295   struct nevent *nse;
  296 
  297   /* Make sure the preallocated space for the retrieved events is big enough */
  298   total_events = gh_list_count(&nsp->connect_events) + gh_list_count(&nsp->read_events) + gh_list_count(&nsp->write_events);
  299   if (iinfo->capacity < total_events) {
  300     iinfo->capacity *= 2;
  301     iinfo->eov_list = (OVERLAPPED_ENTRY *)safe_realloc(iinfo->eov_list, iinfo->capacity * sizeof(OVERLAPPED_ENTRY));
  302   }
  303 
  304   nsock_log_debug_all("wait for events");
  305 
  306   nse = next_expirable_event(nsp);
  307   if (!nse)
  308     event_msecs = -1; /* None of the events specified a timeout */
  309   else
  310     event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod));
  311 
  312 #if HAVE_PCAP
  313 #ifndef PCAP_CAN_DO_SELECT
  314   /* Force a low timeout when capturing packets on systems where
  315   * the pcap descriptor is not select()able. */
  316   if (gh_list_count(&nsp->pcap_read_events) > 0)
  317   if (event_msecs > PCAP_POLL_INTERVAL)
  318     event_msecs = PCAP_POLL_INTERVAL;
  319 #endif
  320 #endif
  321 
  322   /* We cast to unsigned because we want -1 to be very high (since it means no
  323   * timeout) */
  324   combined_msecs = MIN((unsigned)event_msecs, (unsigned)msec_timeout);
  325 
  326 #if HAVE_PCAP
  327 #ifndef PCAP_CAN_DO_SELECT
  328   /* do non-blocking read on pcap devices that doesn't support select()
  329   * If there is anything read, just leave this loop. */
  330   if (pcap_read_on_nonselect(nsp)) {
  331     /* okay, something was read. */
  332     gettimeofday(&nsock_tod, NULL);
  333     iterate_through_pcap_events(nsp);
  334   }
  335   else
  336 #endif
  337 #endif
  338   {
  339     /* It is mandatory these values are reset before calling GetQueuedCompletionStatusEx */
  340     iinfo->entries_removed = 0;
  341     memset(iinfo->eov_list, 0, iinfo->capacity * sizeof(OVERLAPPED_ENTRY));
  342     bRet = GetQueuedCompletionStatusEx(iinfo->iocp, iinfo->eov_list, iinfo->capacity, &iinfo->entries_removed, combined_msecs, FALSE);
  343 
  344     gettimeofday(&nsock_tod, NULL); /* Due to iocp delay */
  345     if (!bRet) {
  346       sock_err = socket_errno();
  347       if (!iinfo->eov && sock_err != WAIT_TIMEOUT) {
  348         nsock_log_error("nsock_loop error %d: %s", sock_err, socket_strerror(sock_err));
  349         nsp->errnum = sock_err;
  350         return -1;
  351       }
  352     }
  353   }
  354 
  355   iterate_through_event_lists(nsp);
  356 
  357   return 1;
  358 }
  359 
  360 
  361 /* ---- INTERNAL FUNCTIONS ---- */
  362 
  363 #if HAVE_PCAP
  364 /* Iterate through pcap events separately, since these are not tracked in iocp_engine_info */
  365 void iterate_through_pcap_events(struct npool *nsp) {
  366   gh_lnode_t *current, *next, *last;
  367 
  368   last = gh_list_last_elem(&nsp->active_iods);
  369 
  370   for (current = gh_list_first_elem(&nsp->active_iods);
  371        current != NULL && gh_lnode_prev(current) != last;
  372        current = next) {
  373     struct niod *nsi = container_of(current, struct niod, nodeq);
  374 
  375     if (nsi->pcap && nsi->state != NSIOD_STATE_DELETED && nsi->events_pending)
  376     {
  377       process_iod_events(nsp, nsi, EV_READ);
  378     }
  379 
  380     next = gh_lnode_next(current);
  381     if (nsi->state == NSIOD_STATE_DELETED) {
  382       gh_list_remove(&nsp->active_iods, current);
  383       gh_list_prepend(&nsp->free_iods, current);
  384     }
  385   }
  386 }
  387 #endif
  388 
  389 /* Iterate through all the event lists (such as connect_events, read_events,
  390 * timer_events, etc) and take action for those that have completed (due to
  391 * timeout, i/o, etc) */
  392 void iterate_through_event_lists(struct npool *nsp) {
  393   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  394 
  395   for (unsigned long i = 0; i < iinfo->entries_removed; i++) {
  396 
  397     iinfo->eov = (struct extended_overlapped *)iinfo->eov_list[i].lpOverlapped;
  398     /* We can't rely on iinfo->entries_removed to tell us the real number of
  399      * events to process */
  400     if (!iinfo->eov || !iinfo->eov->nse)
  401       continue;
  402 
  403     /* We check if this is from a cancelled operation */
  404     if (iinfo->eov->nse->id != iinfo->eov->nse_id ||
  405         iinfo->eov->nse->event_done) {
  406       free_eov(nsp, iinfo->eov);
  407       iinfo->eov = NULL;
  408       continue;
  409     }
  410 
  411     if (!HasOverlappedIoCompleted((OVERLAPPED *)iinfo->eov))
  412       continue;
  413 
  414     struct niod *nsi = iinfo->eov->nse->iod;
  415     struct nevent *nse = iinfo->eov->nse;
  416     gh_list_t *evlist = NULL;
  417     int ev = 0;
  418 
  419     switch (nse->type) {
  420       case NSE_TYPE_CONNECT:
  421       case NSE_TYPE_CONNECT_SSL:
  422         ev = EV_READ;
  423         evlist = &nsp->connect_events;
  424         break;
  425       case NSE_TYPE_READ:
  426         ev = EV_READ;
  427         evlist = &nsp->read_events;
  428         break;
  429       case NSE_TYPE_WRITE:
  430         ev = EV_WRITE;
  431         evlist = &nsp->write_events;
  432         break;
  433     }
  434 
  435     /* Setting the connect error for nsock_core to get in handle_connect_result */
  436     if (nse->type == NSE_TYPE_CONNECT || nse->type == NSE_TYPE_CONNECT_SSL) {
  437       setsockopt(nse->iod->sd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
  438       DWORD dwRes;
  439       if (!GetOverlappedResult((HANDLE)nse->iod->sd, (LPOVERLAPPED)iinfo->eov, &dwRes, FALSE)) {
  440         int err = map_faulty_errors(socket_errno());
  441         if (err)
  442           setsockopt(nse->iod->sd, SOL_SOCKET, SO_ERROR, (char *)&err, sizeof(err));
  443       }
  444     }
  445 
  446     process_event(nsp, evlist, nse, ev);
  447 
  448     if (nse->event_done) {
  449       /* event is done, remove it from the event list and update IOD pointers
  450       * to the first events of each kind */
  451       update_first_events(nse);
  452       gh_list_remove(evlist, &nse->nodeq_io);
  453       gh_list_append(&nsp->free_events, &nse->nodeq_io);
  454 
  455       if (nse->timeout.tv_sec)
  456         gh_heap_remove(&nsp->expirables, &nse->expire);
  457     } else
  458       initiate_overlapped_event(nsp, nse);
  459 
  460     if (nsi->state == NSIOD_STATE_DELETED) {
  461       gh_list_remove(&nsp->active_iods, &nsi->nodeq);
  462       gh_list_prepend(&nsp->free_iods, &nsi->nodeq);
  463     }
  464 
  465     iinfo->eov = NULL;
  466   }
  467 
  468   /* iterate through timers and expired events */
  469   process_expired_events(nsp);
  470 }
  471 
  472 static int errcode_is_failure(int err) {
  473 #ifndef WIN32
  474   return err != EINTR && err != EAGAIN && err != EBUSY;
  475 #else
  476   return err != EINTR && err != EAGAIN && err != WSA_IO_PENDING && err != ERROR_NETNAME_DELETED;
  477 #endif
  478 }
  479 
  480 static int map_faulty_errors(int err) {
  481   /* This actually happens https://svn.boost.org/trac/boost/ticket/10744 */
  482   switch (err) {
  483     case ERROR_NETWORK_UNREACHABLE: return WSAENETUNREACH;
  484     case ERROR_HOST_UNREACHABLE: return WSAEHOSTUNREACH;
  485     case ERROR_CONNECTION_REFUSED: return WSAECONNREFUSED;
  486     case ERROR_SEM_TIMEOUT: return WSAETIMEDOUT;
  487   }
  488   return err;
  489 }
  490 
  491 static struct extended_overlapped *new_eov(struct npool *nsp, struct nevent *nse) {
  492   struct extended_overlapped *eov;
  493   gh_lnode_t *lnode;
  494   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  495 
  496   lnode = gh_list_pop(&iinfo->free_eovs);
  497   if (!lnode)
  498     eov = (struct extended_overlapped *)safe_malloc(sizeof(struct extended_overlapped));
  499   else
  500     eov = container_of(lnode, struct extended_overlapped, nodeq);
  501 
  502   memset(eov, 0, sizeof(struct extended_overlapped));
  503   nse->eov = eov;
  504   eov->nse = nse;
  505   eov->nse_id = nse->id;
  506   eov->err = 0;
  507   eov->eov_received = false;
  508   gh_list_prepend(&iinfo->active_eovs, &eov->nodeq);
  509 
  510   /* Make the read buffer equal to the size of the buffer in do_actual_read() */
  511   if (nse->type == NSE_TYPE_READ && !eov->readbuf && !nse->iod->ssl)
  512     eov->readbuf = (char*)safe_malloc(READ_BUFFER_SZ * sizeof(char));
  513 
  514   return eov;
  515 }
  516 
  517 /* This needs to be called after getting the overlapped event in */
  518 static void free_eov(struct npool *nsp, struct extended_overlapped *eov) {
  519   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  520   struct nevent *nse = eov->nse;
  521 
  522   gh_list_remove(&iinfo->active_eovs, &eov->nodeq);
  523 
  524   if (eov->readbuf) {
  525     free(eov->readbuf);
  526     eov->readbuf = NULL;
  527   }
  528 
  529   gh_list_prepend(&iinfo->free_eovs, &eov->nodeq);
  530 
  531   eov->nse = NULL;
  532   if (nse)
  533     nse->eov = NULL;
  534 }
  535 
  536 
  537 static void call_connect_overlapped(struct npool *nsp, struct nevent *nse) {
  538   BOOL ok;
  539   DWORD numBytes = 0;
  540   int one = 1;
  541   SOCKET sock = nse->iod->sd;
  542   GUID guid = WSAID_CONNECTEX;
  543   struct sockaddr_in addr;
  544   LPFN_CONNECTEX ConnectExPtr = NULL;
  545   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nse->iod->nsp->engine_data;
  546   struct extended_overlapped *eov = new_eov(nsp, nse);
  547   int ret;
  548   struct sockaddr_storage *ss = &nse->iod->peer;
  549   size_t sslen = nse->iod->peerlen;
  550 
  551   if (nse->iod->lastproto != IPPROTO_TCP) {
  552     if (connect(sock, (struct sockaddr *)ss, sslen) == -1) {
  553       int err = socket_errno();
  554       nse->event_done = 1;
  555       nse->status = NSE_STATUS_ERROR;
  556       nse->errnum = err;
  557     } else {
  558       force_operation(nsp, nse);
  559     }
  560     return;
  561   }
  562 
  563   ret = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
  564     (void*)&guid, sizeof(guid), (void*)&ConnectExPtr, sizeof(ConnectExPtr),
  565     &numBytes, NULL, NULL);
  566   if (ret)
  567     fatal("Error initiating event type(%d)", nse->type);
  568 
  569   ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one));
  570   if (ret == -1) {
  571     int err = socket_errno();
  572     nse->event_done = 1;
  573     nse->status = NSE_STATUS_ERROR;
  574     nse->errnum = err;
  575     return;
  576   }
  577 
  578   /* ConnectEx doesn't automatically bind the socket */
  579   memset(&addr, 0, sizeof(addr));
  580   addr.sin_family = AF_INET;
  581   addr.sin_addr.s_addr = INADDR_ANY;
  582   addr.sin_port = 0;
  583   if (!nse->iod->locallen) {
  584     ret = bind(sock, (SOCKADDR*)&addr, sizeof(addr));
  585     if (ret) {
  586       int err = socket_errno();
  587       nse->event_done = 1;
  588       nse->status = NSE_STATUS_ERROR;
  589       nse->errnum = err;
  590       return;
  591     }
  592   }
  593 
  594   ok = ConnectExPtr(sock, (SOCKADDR*)ss, sslen, NULL, 0, NULL, (LPOVERLAPPED)eov);
  595   if (!ok) {
  596     int err = socket_errno();
  597     if (err != ERROR_IO_PENDING) {
  598       nse->event_done = 1;
  599       nse->status = NSE_STATUS_ERROR;
  600       nse->errnum = err;
  601     }
  602   }
  603 }
  604 
  605 static void call_read_overlapped(struct nevent *nse) {
  606   DWORD flags = 0;
  607   int err = 0;
  608   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nse->iod->nsp->engine_data;
  609 
  610   struct extended_overlapped *eov = new_eov(nse->iod->nsp, nse);
  611 
  612   eov->wsabuf.buf = eov->readbuf;
  613   eov->wsabuf.len = READ_BUFFER_SZ;
  614 
  615   err = WSARecvFrom(nse->iod->sd, &eov->wsabuf, 1, NULL, &flags,
  616     (struct sockaddr *)&nse->iod->peer, (LPINT)&nse->iod->peerlen, (LPOVERLAPPED)eov, NULL);
  617   if (err) {
  618     err = socket_errno();
  619     if (errcode_is_failure(err)) {
  620       // WSARecvFrom with overlapped I/O may generate ERROR_PORT_UNREACHABLE on ICMP error.
  621       // We'll translate that so Nsock-using software doesn't have to know about it.
  622       eov->err = (err == ERROR_PORT_UNREACHABLE ? ECONNREFUSED : err);
  623       /* Send the error to the main loop to be picked up by the appropriate handler */
  624       BOOL bRet = PostQueuedCompletionStatus(iinfo->iocp, -1, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
  625       if (!bRet)
  626         fatal("Error initiating event type(%d)", nse->type);
  627     }
  628   }
  629 }
  630 
  631 static void call_write_overlapped(struct nevent *nse) {
  632   int err;
  633   char *str;
  634   int bytesleft;
  635   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nse->iod->nsp->engine_data;
  636 
  637   struct extended_overlapped *eov = new_eov(nse->iod->nsp, nse);
  638 
  639   str = fs_str(&nse->iobuf) + nse->writeinfo.written_so_far;
  640   bytesleft = fs_length(&nse->iobuf) - nse->writeinfo.written_so_far;
  641 
  642   eov->wsabuf.buf = str;
  643   eov->wsabuf.len = bytesleft;
  644 
  645   if (nse->writeinfo.dest.ss_family == AF_UNSPEC)
  646     err = WSASend(nse->iod->sd, &eov->wsabuf, 1, NULL, 0, (LPWSAOVERLAPPED)eov, NULL);
  647   else
  648     err = WSASendTo(nse->iod->sd, &eov->wsabuf, 1, NULL, 0,
  649     (struct sockaddr *)&nse->writeinfo.dest, (int)nse->writeinfo.destlen,
  650     (LPWSAOVERLAPPED)eov, NULL);
  651   if (err) {
  652     err = socket_errno();
  653     if (errcode_is_failure(err)) {
  654       eov->err = err;
  655       /* Send the error to the main loop to be picked up by the appropriate handler */
  656       BOOL bRet = PostQueuedCompletionStatus(iinfo->iocp, -1, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
  657       if (!bRet)
  658         fatal("Error initiating event type(%d)", nse->type);
  659     }
  660   }
  661 }
  662 
  663 /* Anything that isn't an overlapped operation uses this to get processed by the main loop */
  664 static void force_operation(struct npool *nsp, struct nevent *nse) {
  665   BOOL bRet;
  666   struct extended_overlapped *eov;
  667 
  668   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  669   eov = new_eov(nse->iod->nsp, nse);
  670 
  671   bRet = PostQueuedCompletionStatus(iinfo->iocp, 0, (ULONG_PTR)nse->iod, (LPOVERLAPPED)eov);
  672   if (!bRet)
  673     fatal("Error initiating event type(%d)", nse->type);
  674 }
  675 
  676 /* Either initiate a I/O read or force a SSL_read */
  677 static void initiate_read(struct npool *nsp, struct nevent *nse) {
  678   if (!nse->iod->ssl)
  679     call_read_overlapped(nse);
  680   else
  681     force_operation(nsp, nse);
  682 }
  683 
  684 /* Either initiate a I/O write or force a SSL_write */
  685 static void initiate_write(struct npool *nsp, struct nevent *nse) {
  686   if (!nse->iod->ssl)
  687     call_write_overlapped(nse);
  688   else
  689     force_operation(nsp, nse);
  690 }
  691 
  692 /* Force a PCAP read */
  693 static void initiate_pcap_read(struct npool *nsp, struct nevent *nse) {
  694   force_operation(nsp, nse);
  695 }
  696 
  697 static void initiate_connect(struct npool *nsp, struct nevent *nse) {
  698   int sslconnect_inprogress = 0;
  699   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  700 
  701 #if HAVE_OPENSSL
  702   sslconnect_inprogress = nse->type == NSE_TYPE_CONNECT_SSL && nse->iod &&
  703     (nse->sslinfo.ssl_desire == SSL_ERROR_WANT_READ ||
  704     nse->sslinfo.ssl_desire == SSL_ERROR_WANT_WRITE);
  705 #endif
  706 
  707   if (sslconnect_inprogress)
  708     force_operation(nsp, nse);
  709   else
  710     call_connect_overlapped(nsp, nse);
  711 }
  712 
  713 /* Start the overlapped I/O operation */
  714 static void initiate_overlapped_event(struct npool *nsp, struct nevent *nse) {
  715   if (nse->eov)
  716     terminate_overlapped_event(nsp, nse);
  717 
  718   switch (nse->type) {
  719   case NSE_TYPE_CONNECT:
  720   case NSE_TYPE_CONNECT_SSL:
  721     initiate_connect(nsp, nse);
  722     break;
  723   case NSE_TYPE_READ:
  724     initiate_read(nsp, nse);
  725     break;
  726   case NSE_TYPE_WRITE:
  727     initiate_write(nsp, nse);
  728     break;
  729 #if HAVE_PCAP
  730   case NSE_TYPE_PCAP_READ:
  731     initiate_pcap_read(nsp, nse);
  732     break;
  733 #endif
  734   default: fatal("Event type(%d) not supported by engine IOCP\n", nse->type);
  735   }
  736 }
  737 
  738 /* Terminate an overlapped I/O operation that expired */
  739 static void terminate_overlapped_event(struct npool *nsp, struct nevent *nse) {
  740   bool eov_done = true;
  741 
  742   if (nse->eov) {
  743     if (!HasOverlappedIoCompleted((LPOVERLAPPED)nse->eov)) {
  744       CancelIoEx((HANDLE)nse->iod->sd, (LPOVERLAPPED)nse->eov);
  745       eov_done = false;
  746     }
  747 
  748     if (eov_done)
  749       free_eov(nsp, nse->eov);
  750   }
  751 }
  752 
  753 /* Retrieve the amount of bytes transferred or set the appropriate error */
  754 static int get_overlapped_result(struct npool *nsp, int fd, const void *buffer, size_t count) {
  755   char *buf = (char *)buffer;
  756   DWORD dwRes = 0;
  757   int err;
  758   static struct extended_overlapped *old_eov = NULL;
  759   struct iocp_engine_info *iinfo = (struct iocp_engine_info *)nsp->engine_data;
  760 
  761   struct extended_overlapped *eov = iinfo->eov;
  762   struct nevent *nse = eov->nse;
  763 
  764   /* If the operation failed at initialization, set the error for nsock_core.c to see */
  765   if (eov->err) {
  766     SetLastError(map_faulty_errors(eov->err));
  767     return -1;
  768   }
  769 
  770   if (!GetOverlappedResult((HANDLE)fd, (LPOVERLAPPED)eov, &dwRes, FALSE)) {
  771     err = socket_errno();
  772     if (errcode_is_failure(err)) {
  773       eov->eov_received = true;
  774       SetLastError(map_faulty_errors(err));
  775       return -1;
  776     }
  777   }
  778   eov->eov_received = true;
  779 
  780   if (nse->type == NSE_TYPE_READ && buf)
  781     memcpy(buf, eov->wsabuf.buf, dwRes);
  782 
  783   /* If the read buffer wasn't big enough, subsequent calls from do_actual_read will make us
  784   read with recvfrom the rest of the returned data */
  785   if (nse->type == NSE_TYPE_READ && dwRes == eov->wsabuf.len && old_eov == eov) {
  786     struct sockaddr_storage peer;
  787     socklen_t peerlen = sizeof(peer);
  788     dwRes = recvfrom(fd, buf, READ_BUFFER_SZ, 0, (struct sockaddr *)&peer, &peerlen);
  789   }
  790 
  791   if (nse->type != NSE_TYPE_READ || (nse->type == NSE_TYPE_READ && dwRes < eov->wsabuf.len)) {
  792     old_eov = NULL;
  793   } else if (nse->type == NSE_TYPE_READ && dwRes == eov->wsabuf.len) {
  794     old_eov = eov;
  795   }
  796 
  797   return dwRes;
  798 }
  799 
  800 int iocp_iod_connect(struct npool *nsp, int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
  801   return 0;
  802 }
  803 
  804 int iocp_iod_read(struct npool *nsp, int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) {
  805   return get_overlapped_result(nsp, sockfd, buf, len);
  806 }
  807 
  808 int iocp_iod_write(struct npool *nsp, int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
  809   return get_overlapped_result(nsp, sockfd, buf, len);
  810 }
  811 
  812 #endif /* HAVE_IOCP */