Commit a662f86f authored by Marc Hoersken's avatar Marc Hoersken
Browse files

socklift.c: Added select_ws function to support Windows

WinSock select() does not support standard file descriptors,
it can only check SOCKETs. The following function is an attempt
to create a select() function with support for other handles.
parent d335aa2c
Loading
Loading
Loading
Loading
+144 −12
Original line number Diff line number Diff line
@@ -399,6 +399,146 @@ static void lograw(unsigned char *buffer, ssize_t len)
    logmsg("'%s'", data);
}

/*
 * WinSock select() does not support standard file descriptors,
 * it can only check SOCKETs. The following function is an attempt
 * to re-create a select() function with support for other handle types.
 */
#ifdef USE_WINSOCK
/*
 * select() function with support for WINSOCK2 sockets and all
 * other handle types supported by WaitForMultipleObjectsEx().
 *
 * TODO: Differentiate between read/write/except for non-SOCKET handles.
 *
 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028.aspx
 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms741572.aspx
 */
static int select_ws(int nfds, fd_set *readfds, fd_set *writefds,
                     fd_set *exceptfds, struct timeval *timeout)
{
  int ret = 0, fds = 0, nfd = 0, idx = 0, wsa = 0, error = 0;
  long networkevents;
  DWORD milliseconds, wait;
  WSAEVENT wsaevent, *wsaevents;
  WSANETWORKEVENTS wsanetevents;
  HANDLE *handles;
  int *fdarr;

  /* allocate internal array for the original input handles */
  fdarr = malloc(sizeof(int)*nfds);
  if(fdarr == NULL) {
    errno = ENOMEM;
    return -1;
  }

  /* allocate internal array for the internal event handles */
  handles = malloc(sizeof(HANDLE)*nfds);
  if(handles == NULL) {
    errno = ENOMEM;
    return -1;
  }

  /* allocate internal array for the internal WINSOCK2 events */
  wsaevents = malloc(sizeof(WSAEVENT)*nfds);
  if(wsaevents == NULL) {
    errno = ENOMEM;
    return -1;
  }

  /* loop over the handles in the input descriptor sets */
  for(fds = 0; fds < nfds; fds++) {
    networkevents = 0;
    handles[nfd] = 0;

    if(FD_ISSET(fds, readfds))
      networkevents |= FD_READ;

    if(FD_ISSET(fds, writefds))
      networkevents |= FD_WRITE;

    if(FD_ISSET(fds, exceptfds))
      networkevents |= FD_CLOSE;

    if(networkevents) {
      fdarr[nfd] = fds;
      if(fds == fileno(stdin)) {
        handles[nfd] = GetStdHandle(STD_INPUT_HANDLE);
      }
      else if(fds == fileno(stdout)) {
        handles[nfd] = GetStdHandle(STD_OUTPUT_HANDLE);
      }
      else if(fds == fileno(stderr)) {
        handles[nfd] = GetStdHandle(STD_ERROR_HANDLE);
      }
      else {
        wsaevent = WSACreateEvent();
        if(wsaevent != WSA_INVALID_EVENT) {
          error = WSAEventSelect(fds, wsaevent, networkevents);
          if(error != SOCKET_ERROR) {
            handles[nfd] = wsaevent;
            wsaevents[wsa++] = wsaevent;
          }
          else {
            handles[nfd] = (HANDLE) fds;
            WSACloseEvent(wsaevent);
          }
        }
      }
      nfd++;
    }
  }

  /* convert struct timeval to milliseconds */
  if(timeout) {
    milliseconds = ((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000));
  }
  else {
    milliseconds = INFINITE;
  }

  /* wait for one of the internal handles to trigger */
  wait = WaitForMultipleObjectsEx(nfd, handles, FALSE, milliseconds, FALSE);

  /* loop over the internal handles returned in the descriptors */
  for(idx = 0; idx < nfd; idx++) {
    if(wait != WAIT_OBJECT_0 + idx) {
      /* remove from all descriptor sets since this handle did not trigger */
      FD_CLR(fdarr[idx], readfds);
      FD_CLR(fdarr[idx], writefds);
      FD_CLR(fdarr[idx], exceptfds);
    }
    else {
      /* first try to handle the event with the WINSOCK2 functions */
      error = WSAEnumNetworkEvents(fdarr[idx], NULL, &wsanetevents);
      if(error != SOCKET_ERROR) {
        /* remove from descriptor set if not ready for read */
        if(!(wsanetevents.lNetworkEvents & FD_READ))
          FD_CLR(fdarr[idx], readfds);

        /* remove from descriptor set if not ready for write */
        if(!(wsanetevents.lNetworkEvents & FD_WRITE))
          FD_CLR(fdarr[idx], writefds);

        /* remove from descriptor set if not exceptional */
        if(!(wsanetevents.lNetworkEvents & FD_CLOSE))
          FD_CLR(fdarr[idx], exceptfds);
      }
      ret++;
    }
  }

  for(idx = 0; idx < wsa; idx++)
    WSACloseEvent(wsaevents[idx]);

  free(wsaevents);
  free(handles);
  free(fdarr);

  return ret;
}
#endif

/*
  sockfdp is a pointer to an established stream or CURL_SOCKET_BAD

@@ -447,18 +587,6 @@ static bool juggle(curl_socket_t *sockfdp,
  FD_ZERO(&fds_write);
  FD_ZERO(&fds_err);

#ifdef USE_WINSOCK
  /*
  ** WinSock select() does not support standard file descriptors,
  ** it can only check SOCKETs. Since this program in its current
  ** state will not work on WinSock based systems, next line is
  ** commented out to allow warning-free compilation awaiting the
  ** day it will be fixed to also run on WinSock systems.
  */
#else
  FD_SET(fileno(stdin), &fds_read);
#endif

  switch(*mode) {

  case PASSIVE_LISTEN:
@@ -511,7 +639,11 @@ static bool juggle(curl_socket_t *sockfdp,

  do {

#ifdef USE_WINSOCK
    rc = select_ws((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout);
#else
    rc = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout);
#endif

    if(got_exit_signal) {
      logmsg("signalled to die, exiting...");