gdiplus: Don't require an HDC for the convert_unit function.
[wine] / dlls / ws2_32 / socket.c
index 2cc8609..c226fda 100644 (file)
 #include "iphlpapi.h"
 #include "wine/server.h"
 #include "wine/debug.h"
+#include "wine/exception.h"
 #include "wine/unicode.h"
 
 #ifdef HAVE_IPX
 WINE_DEFAULT_DEBUG_CHANNEL(winsock);
 WINE_DECLARE_DEBUG_CHANNEL(winediag);
 
+
+/*
+ * The actual definition of WSASendTo/WSARecvFrom, wrapped in a different
+ * function name, so that internal calls from ws2_32 itself will not trigger
+ * programs like Garena, which hooks WSASendTo/WSARecvFrom calls.
+ */
+static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+                       LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
+                       const struct WS_sockaddr *to, int tolen,
+                       LPWSAOVERLAPPED lpOverlapped,
+                       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
+
+static int WS2_recvfrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+                         LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags,
+                         struct WS_sockaddr *lpFrom,
+                         LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
+                         LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
+
 /* critical section to protect some non-reentrant net function */
 static CRITICAL_SECTION csWSgetXXXbyYYY;
 static CRITICAL_SECTION_DEBUG critsect_debug =
@@ -278,11 +297,18 @@ struct per_thread_data
     int pe_len;
 };
 
+/* internal: routing description information */
+struct route {
+    struct in_addr addr;
+    DWORD interface;
+    DWORD metric;
+};
+
 static INT num_startup;          /* reference counter */
 static FARPROC blocking_hook = (FARPROC)WSA_DefaultBlockingHook;
 
 /* function prototypes */
-static struct WS_hostent *WS_create_he(char *name, int aliases, int addresses);
+static struct WS_hostent *WS_create_he(char *name, int aliases, int addresses, int fill_addresses);
 static struct WS_hostent *WS_dup_he(const struct hostent* p_he);
 static struct WS_protoent *WS_dup_pe(const struct protoent* p_pe);
 static struct WS_servent *WS_dup_se(const struct servent* p_se);
@@ -336,6 +362,21 @@ static const int ws_ip_map[][2] =
     MAP_OPTION( IP_TTL ),
 };
 
+static const int ws_ipv6_map[][2] =
+{
+#ifdef IPV6_ADD_MEMBERSHIP
+    MAP_OPTION( IPV6_ADD_MEMBERSHIP ),
+#endif
+#ifdef IPV6_DROP_MEMBERSHIP
+    MAP_OPTION( IPV6_DROP_MEMBERSHIP ),
+#endif
+    MAP_OPTION( IPV6_MULTICAST_IF ),
+    MAP_OPTION( IPV6_MULTICAST_HOPS ),
+    MAP_OPTION( IPV6_MULTICAST_LOOP ),
+    MAP_OPTION( IPV6_UNICAST_HOPS ),
+    MAP_OPTION( IPV6_V6ONLY ),
+};
+
 static const int ws_af_map[][2] =
 {
     MAP_OPTION( AF_UNSPEC ),
@@ -416,12 +457,53 @@ static const char magic_loopback_addr[] = {127, 12, 34, 56};
 
 /* ----------------------------------- error handling */
 
-static UINT wsaErrno(void)
+static NTSTATUS sock_get_ntstatus( int err )
 {
-    int        loc_errno = errno;
-    WARN("errno %d, (%s).\n", loc_errno, strerror(loc_errno));
+    switch ( err )
+    {
+        case EBADF:             return STATUS_INVALID_HANDLE;
+        case EBUSY:             return STATUS_DEVICE_BUSY;
+        case EPERM:
+        case EACCES:            return STATUS_ACCESS_DENIED;
+        case EFAULT:            return STATUS_NO_MEMORY;
+        case EINVAL:            return STATUS_INVALID_PARAMETER;
+        case ENFILE:
+        case EMFILE:            return STATUS_TOO_MANY_OPENED_FILES;
+        case EWOULDBLOCK:       return STATUS_CANT_WAIT;
+        case EINPROGRESS:       return STATUS_PENDING;
+        case EALREADY:          return STATUS_NETWORK_BUSY;
+        case ENOTSOCK:          return STATUS_OBJECT_TYPE_MISMATCH;
+        case EDESTADDRREQ:      return STATUS_INVALID_PARAMETER;
+        case EMSGSIZE:          return STATUS_BUFFER_OVERFLOW;
+        case EPROTONOSUPPORT:
+        case ESOCKTNOSUPPORT:
+        case EPFNOSUPPORT:
+        case EAFNOSUPPORT:
+        case EPROTOTYPE:        return STATUS_NOT_SUPPORTED;
+        case ENOPROTOOPT:       return STATUS_INVALID_PARAMETER;
+        case EOPNOTSUPP:        return STATUS_NOT_SUPPORTED;
+        case EADDRINUSE:        return STATUS_ADDRESS_ALREADY_ASSOCIATED;
+        case EADDRNOTAVAIL:     return STATUS_INVALID_PARAMETER;
+        case ECONNREFUSED:      return STATUS_CONNECTION_REFUSED;
+        case ESHUTDOWN:         return STATUS_PIPE_DISCONNECTED;
+        case ENOTCONN:          return STATUS_CONNECTION_DISCONNECTED;
+        case ETIMEDOUT:         return STATUS_IO_TIMEOUT;
+        case ENETUNREACH:       return STATUS_NETWORK_UNREACHABLE;
+        case ENETDOWN:          return STATUS_NETWORK_BUSY;
+        case EPIPE:
+        case ECONNRESET:        return STATUS_CONNECTION_RESET;
+        case ECONNABORTED:      return STATUS_CONNECTION_ABORTED;
+
+        case 0:                 return STATUS_SUCCESS;
+        default:
+            WARN("Unknown errno %d!\n", err);
+            return STATUS_UNSUCCESSFUL;
+    }
+}
 
-    switch(loc_errno)
+static UINT sock_get_error( int err )
+{
+       switch(err)
     {
        case EINTR:             return WSAEINTR;
        case EBADF:             return WSAEBADF;
@@ -479,17 +561,33 @@ static UINT wsaErrno(void)
        case EREMOTE:           return WSAEREMOTE;
 #endif
 
-       /* just in case we ever get here and there are no problems */
+       /* just in case we ever get here and there are no problems */
        case 0:                 return 0;
-        default:
-               WARN("Unknown errno %d!\n", loc_errno);
+       default:
+               WARN("Unknown errno %d!\n", err);
                return WSAEOPNOTSUPP;
     }
 }
 
-static UINT wsaHerrno(int loc_errno)
+static UINT wsaErrno(void)
 {
+    int        loc_errno = errno;
+    WARN("errno %d, (%s).\n", loc_errno, strerror(loc_errno));
 
+    return sock_get_error( loc_errno );
+}
+
+/* most ws2 overlapped functions return an ntstatus-based error code */
+static NTSTATUS wsaErrStatus(void)
+{
+    int        loc_errno = errno;
+    WARN("errno %d, (%s).\n", loc_errno, strerror(loc_errno));
+
+    return sock_get_ntstatus(loc_errno);
+}
+
+static UINT wsaHerrno(int loc_errno)
+{
     WARN("h_errno %d.\n", loc_errno);
 
     switch(loc_errno)
@@ -501,7 +599,7 @@ static UINT wsaHerrno(int loc_errno)
        case ENOBUFS:           return WSAENOBUFS;
 
        case 0:                 return 0;
-        default:
+       default:
                WARN("Unknown h_errno %d!\n", loc_errno);
                return WSAEOPNOTSUPP;
     }
@@ -513,24 +611,33 @@ static inline DWORD NtStatusToWSAError( const DWORD status )
     DWORD wserr;
     switch ( status )
     {
-    case STATUS_SUCCESS:              wserr = 0;                     break;
-    case STATUS_PENDING:              wserr = WSA_IO_PENDING;        break;
-    case STATUS_OBJECT_TYPE_MISMATCH: wserr = WSAENOTSOCK;           break;
-    case STATUS_INVALID_HANDLE:       wserr = WSAEBADF;              break;
-    case STATUS_INVALID_PARAMETER:    wserr = WSAEINVAL;             break;
-    case STATUS_PIPE_DISCONNECTED:    wserr = WSAESHUTDOWN;          break;
-    case STATUS_CANCELLED:            wserr = WSA_OPERATION_ABORTED; break;
-    case STATUS_TIMEOUT:              wserr = WSAETIMEDOUT;          break;
-    case STATUS_NO_MEMORY:            wserr = WSAEFAULT;             break;
+    case STATUS_SUCCESS:                    wserr = 0;                     break;
+    case STATUS_PENDING:                    wserr = WSA_IO_PENDING;        break;
+    case STATUS_OBJECT_TYPE_MISMATCH:       wserr = WSAENOTSOCK;           break;
+    case STATUS_INVALID_HANDLE:             wserr = WSAEBADF;              break;
+    case STATUS_INVALID_PARAMETER:          wserr = WSAEINVAL;             break;
+    case STATUS_PIPE_DISCONNECTED:          wserr = WSAESHUTDOWN;          break;
+    case STATUS_NETWORK_BUSY:               wserr = WSAEALREADY;           break;
+    case STATUS_NETWORK_UNREACHABLE:        wserr = WSAENETUNREACH;        break;
+    case STATUS_CONNECTION_REFUSED:         wserr = WSAECONNREFUSED;       break;
+    case STATUS_CONNECTION_DISCONNECTED:    wserr = WSAENOTCONN;           break;
+    case STATUS_CONNECTION_RESET:           wserr = WSAECONNRESET;         break;
+    case STATUS_CONNECTION_ABORTED:         wserr = WSAECONNABORTED;       break;
+    case STATUS_CANCELLED:                  wserr = WSA_OPERATION_ABORTED; break;
+    case STATUS_ADDRESS_ALREADY_ASSOCIATED: wserr = WSAEADDRINUSE;         break;
+    case STATUS_IO_TIMEOUT:
+    case STATUS_TIMEOUT:                    wserr = WSAETIMEDOUT;          break;
+    case STATUS_NO_MEMORY:                  wserr = WSAEFAULT;             break;
+    case STATUS_ACCESS_DENIED:              wserr = WSAEACCES;             break;
+    case STATUS_TOO_MANY_OPENED_FILES:      wserr = WSAEMFILE;             break;
+    case STATUS_CANT_WAIT:                  wserr = WSAEWOULDBLOCK;        break;
+    case STATUS_BUFFER_OVERFLOW:            wserr = WSAEMSGSIZE;           break;
+    case STATUS_NOT_SUPPORTED:              wserr = WSAEOPNOTSUPP;         break;
+    case STATUS_HOST_UNREACHABLE:           wserr = WSAEHOSTUNREACH;       break;
+
     default:
-        if ( status >= WSABASEERR && status <= WSABASEERR+1004 )
-            /* It is not an NT status code but a winsock error */
-            wserr = status;
-        else
-        {
-            wserr = RtlNtStatusToDosError( status );
-            FIXME( "Status code %08x converted to DOS error code %x\n", status, wserr );
-        }
+        wserr = RtlNtStatusToDosError( status );
+        FIXME( "Status code %08x converted to DOS error code %x\n", status, wserr );
     }
     return wserr;
 }
@@ -720,6 +827,17 @@ static int convert_sockopt(INT *level, INT *optname)
         }
        FIXME("Unknown IPPROTO_IP optname 0x%x\n", *optname);
        break;
+     case WS_IPPROTO_IPV6:
+        *level = IPPROTO_IPV6;
+        for(i=0; i<sizeof(ws_ipv6_map)/sizeof(ws_ipv6_map[0]); i++) {
+            if (ws_ipv6_map[i][0] == *optname )
+            {
+                *optname = ws_ipv6_map[i][1];
+                return 1;
+            }
+        }
+       FIXME("Unknown IPPROTO_IPV6 optname 0x%x\n", *optname);
+       break;
      default: FIXME("Unimplemented or unknown socket level\n");
   }
   return 0;
@@ -1357,7 +1475,7 @@ static NTSTATUS WS2_async_recv( void* user, IO_STATUS_BLOCK* iosb, NTSTATUS stat
             else
             {
                 result = 0;
-                status = wsaErrno(); /* FIXME: is this correct ???? */
+                status = wsaErrStatus();
             }
         }
         break;
@@ -1465,9 +1583,7 @@ static NTSTATUS WS2_async_send(void* user, IO_STATUS_BLOCK* iosb, NTSTATUS statu
             }
             else
             {
-                /* We set the status to a winsock error code and check for that
-                   later in NtStatusToWSAError () */
-                status = wsaErrno();
+                status = wsaErrStatus();
                 result = 0;
             }
         }
@@ -1503,8 +1619,8 @@ static NTSTATUS WS2_async_shutdown( void* user, PIO_STATUS_BLOCK iosb, NTSTATUS
         case ASYNC_TYPE_READ:   err = shutdown( fd, 0 );  break;
         case ASYNC_TYPE_WRITE:  err = shutdown( fd, 1 );  break;
         }
+        status = err ? wsaErrStatus() : STATUS_SUCCESS;
         wine_server_release_fd( wsa->hSocket, fd );
-        status = err ? wsaErrno() : STATUS_SUCCESS;
         break;
     }
     iosb->u.Status = status;
@@ -1582,7 +1698,7 @@ SOCKET WINAPI WS_accept(SOCKET s, struct WS_sockaddr *addr,
             if (addr) WS_getpeername(as, addr, addrlen32);
             return as;
         }
-        if (is_blocking && status == WSAEWOULDBLOCK)
+        if (is_blocking && status == STATUS_CANT_WAIT)
         {
             int fd = get_sock_fd( s, FILE_READ_DATA, NULL );
             /* block here */
@@ -1590,7 +1706,7 @@ SOCKET WINAPI WS_accept(SOCKET s, struct WS_sockaddr *addr,
             _sync_sock_state(s); /* let wineserver notice connection */
             release_sock_fd( s, fd );
         }
-    } while (is_blocking && status == WSAEWOULDBLOCK);
+    } while (is_blocking && status == STATUS_CANT_WAIT);
 
     set_error(status);
     return INVALID_SOCKET;
@@ -1687,6 +1803,33 @@ int WINAPI WS_closesocket(SOCKET s)
     return SOCKET_ERROR;
 }
 
+static int do_connect(int fd, const struct WS_sockaddr* name, int namelen)
+{
+    union generic_unix_sockaddr uaddr;
+    unsigned int uaddrlen = ws_sockaddr_ws2u(name, namelen, &uaddr);
+
+    if (!uaddrlen)
+        return WSAEFAULT;
+
+    if (name->sa_family == WS_AF_INET)
+    {
+        struct sockaddr_in *in4 = (struct sockaddr_in*) &uaddr;
+        if (memcmp(&in4->sin_addr, magic_loopback_addr, 4) == 0)
+        {
+            /* Trying to connect to magic replace-loopback address,
+                * assuming we really want to connect to localhost */
+            TRACE("Trying to connect to magic IP address, using "
+                    "INADDR_LOOPBACK instead.\n");
+            in4->sin_addr.s_addr = htonl(WS_INADDR_LOOPBACK);
+        }
+    }
+
+    if (connect(fd, &uaddr.addr, uaddrlen) == 0)
+        return 0;
+
+    return wsaErrno();
+}
+
 /***********************************************************************
  *             connect         (WS2_32.4)
  */
@@ -1698,37 +1841,15 @@ int WINAPI WS_connect(SOCKET s, const struct WS_sockaddr* name, int namelen)
 
     if (fd != -1)
     {
-        union generic_unix_sockaddr uaddr;
-        unsigned int uaddrlen = ws_sockaddr_ws2u(name, namelen, &uaddr);
+        int ret = do_connect(fd, name, namelen);
+        if (ret == 0)
+            goto connect_success;
 
-        if (!uaddrlen)
-        {
-            SetLastError(WSAEFAULT);
-        }
-        else
-        {
-            if (name->sa_family == WS_AF_INET)
-            {
-                struct sockaddr_in *in4 = (struct sockaddr_in*) &uaddr;
-                if (memcmp(&in4->sin_addr, magic_loopback_addr, 4) == 0)
-                {
-                    /* Trying to connect to magic replace-loopback address,
-                     * assuming we really want to connect to localhost */
-                    TRACE("Trying to connect to magic IP address, using "
-                         "INADDR_LOOPBACK instead.\n");
-                    in4->sin_addr.s_addr = htonl(WS_INADDR_LOOPBACK);
-                }
-            }
-
-            if (connect(fd, &uaddr.addr, uaddrlen) == 0)
-                goto connect_success;
-        }
-
-        if (errno == EINPROGRESS)
+        if (ret == WSAEINPROGRESS)
         {
             /* tell wineserver that a connection is in progress */
             _enable_event(SOCKET2HANDLE(s), FD_CONNECT|FD_READ|FD_WRITE,
-                          FD_CONNECT|FD_READ|FD_WRITE,
+                          FD_CONNECT,
                           FD_WINE_CONNECTED|FD_WINE_LISTENING);
             if (_is_blocking(s))
             {
@@ -1739,7 +1860,7 @@ int WINAPI WS_connect(SOCKET s, const struct WS_sockaddr* name, int namelen)
                 /* retrieve any error codes from it */
                 result = _get_sock_error(s, FD_CONNECT_BIT);
                 if (result)
-                    SetLastError(result);
+                    SetLastError(NtStatusToWSAError(result));
                 else
                 {
                     goto connect_success;
@@ -1752,7 +1873,7 @@ int WINAPI WS_connect(SOCKET s, const struct WS_sockaddr* name, int namelen)
         }
         else
         {
-            SetLastError(wsaErrno());
+            SetLastError(ret);
         }
         release_sock_fd( s, fd );
     }
@@ -1778,6 +1899,111 @@ int WINAPI WSAConnect( SOCKET s, const struct WS_sockaddr* name, int namelen,
     return WS_connect( s, name, namelen );
 }
 
+/***********************************************************************
+ *             ConnectEx
+ */
+static BOOL WINAPI WS2_ConnectEx(SOCKET s, const struct WS_sockaddr* name, int namelen,
+                          PVOID sendBuf, DWORD sendBufLen, LPDWORD sent, LPOVERLAPPED ov)
+{
+    int fd, ret, status;
+
+    if (!ov)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    fd = get_sock_fd( s, FILE_READ_DATA, NULL );
+    if (fd == -1)
+    {
+        SetLastError( WSAENOTSOCK );
+        return FALSE;
+    }
+
+    TRACE("socket %04lx, ptr %p %s, length %d, sendptr %p, len %d, ov %p\n",
+          s, name, debugstr_sockaddr(name), namelen, sendBuf, sendBufLen, ov);
+
+    /* FIXME: technically the socket has to be bound */
+    ret = do_connect(fd, name, namelen);
+    if (ret == 0)
+    {
+        WSABUF wsabuf;
+
+        _enable_event(SOCKET2HANDLE(s), FD_CONNECT|FD_READ|FD_WRITE,
+                            FD_WINE_CONNECTED|FD_READ|FD_WRITE,
+                            FD_CONNECT|FD_WINE_LISTENING);
+
+        wsabuf.len = sendBufLen;
+        wsabuf.buf = (char*) sendBuf;
+
+        /* WSASend takes care of completion if need be */
+        if (WSASend(s, &wsabuf, sendBuf ? 1 : 0, sent, 0, ov, NULL) != SOCKET_ERROR)
+            goto connection_success;
+    }
+    else if (ret == WSAEINPROGRESS)
+    {
+        struct ws2_async *wsa;
+        ULONG_PTR cvalue = (((ULONG_PTR)ov->hEvent & 1) == 0) ? (ULONG_PTR)ov : 0;
+
+        _enable_event(SOCKET2HANDLE(s), FD_CONNECT|FD_READ|FD_WRITE,
+                      FD_CONNECT,
+                      FD_WINE_CONNECTED|FD_WINE_LISTENING);
+
+        /* Indirectly call WSASend */
+        if (!(wsa = HeapAlloc( GetProcessHeap(), 0, sizeof(*wsa) )))
+        {
+            SetLastError(WSAEFAULT);
+        }
+        else
+        {
+            IO_STATUS_BLOCK *iosb = (IO_STATUS_BLOCK *)ov;
+            iosb->u.Status = STATUS_PENDING;
+            iosb->Information = 0;
+
+            wsa->hSocket     = SOCKET2HANDLE(s);
+            wsa->addr        = NULL;
+            wsa->addrlen.val = 0;
+            wsa->flags       = 0;
+            wsa->n_iovecs    = sendBuf ? 1 : 0;
+            wsa->first_iovec = 0;
+            wsa->completion_func = NULL;
+            wsa->iovec[0].iov_base = sendBuf;
+            wsa->iovec[0].iov_len  = sendBufLen;
+
+            SERVER_START_REQ( register_async )
+            {
+                req->type           = ASYNC_TYPE_WRITE;
+                req->async.handle   = wine_server_obj_handle( wsa->hSocket );
+                req->async.callback = wine_server_client_ptr( WS2_async_send );
+                req->async.iosb     = wine_server_client_ptr( iosb );
+                req->async.arg      = wine_server_client_ptr( wsa );
+                req->async.event    = wine_server_obj_handle( ov->hEvent );
+                req->async.cvalue   = cvalue;
+                status = wine_server_call( req );
+            }
+            SERVER_END_REQ;
+
+            if (status != STATUS_PENDING) HeapFree(GetProcessHeap(), 0, wsa);
+
+            /* If the connect already failed */
+            if (status == STATUS_PIPE_DISCONNECTED)
+                status = _get_sock_error(s, FD_CONNECT_BIT);
+            SetLastError( NtStatusToWSAError(status) );
+        }
+    }
+    else
+    {
+        SetLastError(ret);
+    }
+
+    release_sock_fd( s, fd );
+    return FALSE;
+
+connection_success:
+    release_sock_fd( s, fd );
+    return TRUE;
+}
+
 
 /***********************************************************************
  *             getpeername             (WS2_32.5)
@@ -1787,14 +2013,7 @@ int WINAPI WS_getpeername(SOCKET s, struct WS_sockaddr *name, int *namelen)
     int fd;
     int res;
 
-    TRACE("socket: %04lx, ptr %p, len %08x\n", s, name, *namelen);
-
-    /* Check if what we've received is valid. Should we use IsBadReadPtr? */
-    if( (name == NULL) || (namelen == NULL) )
-    {
-        SetLastError( WSAEFAULT );
-        return SOCKET_ERROR;
-    }
+    TRACE("socket: %04lx, ptr %p, len %08x\n", s, name, namelen?*namelen:0);
 
     fd = get_sock_fd( s, 0, NULL );
     res = SOCKET_ERROR;
@@ -1804,19 +2023,18 @@ int WINAPI WS_getpeername(SOCKET s, struct WS_sockaddr *name, int *namelen)
         union generic_unix_sockaddr uaddr;
         unsigned int uaddrlen = sizeof(uaddr);
 
-        if (getpeername(fd, &uaddr.addr, &uaddrlen) != 0)
+        if (getpeername(fd, &uaddr.addr, &uaddrlen) == 0)
         {
-            SetLastError(wsaErrno());
-        }
-        else if (ws_sockaddr_u2ws(&uaddr.addr, name, namelen) != 0)
-        {
-            /* The buffer was too small */
-            SetLastError(WSAEFAULT);
+            if (!name || !namelen)
+                SetLastError(WSAEFAULT);
+            else if (ws_sockaddr_u2ws(&uaddr.addr, name, namelen) != 0)
+                /* The buffer was too small */
+                SetLastError(WSAEFAULT);
+            else
+                res = 0;
         }
         else
-        {
-            res=0;
-        }
+            SetLastError(wsaErrno());
         release_sock_fd( s, fd );
     }
     return res;
@@ -2228,7 +2446,35 @@ INT WINAPI WS_getsockopt(SOCKET s, INT level,
         return SOCKET_ERROR;
 
     case WS_IPPROTO_IPV6:
-        FIXME("IPPROTO_IPV6 unimplemented (optname 0x%08x)\n", optname);
+        switch(optname)
+        {
+#ifdef IPV6_ADD_MEMBERSHIP
+        case WS_IPV6_ADD_MEMBERSHIP:
+#endif
+#ifdef IPV6_DROP_MEMBERSHIP
+        case WS_IPV6_DROP_MEMBERSHIP:
+#endif
+        case WS_IPV6_MULTICAST_IF:
+        case WS_IPV6_MULTICAST_HOPS:
+        case WS_IPV6_MULTICAST_LOOP:
+        case WS_IPV6_UNICAST_HOPS:
+        case WS_IPV6_V6ONLY:
+            if ( (fd = get_sock_fd( s, 0, NULL )) == -1)
+                return SOCKET_ERROR;
+            convert_sockopt(&level, &optname);
+            if (getsockopt(fd, level, optname, optval, (unsigned int *)optlen) != 0 )
+            {
+                SetLastError((errno == EBADF) ? WSAENOTSOCK : wsaErrno());
+                ret = SOCKET_ERROR;
+            }
+            release_sock_fd( s, fd );
+            return ret;
+        case WS_IPV6_DONTFRAG:
+            FIXME("WS_IPV6_DONTFRAG is always false!\n");
+            *(BOOL*)optval = FALSE;
+            return 0;
+        }
+        FIXME("Unknown IPPROTO_IPV6 optname 0x%08x\n", optname);
         return SOCKET_ERROR;
 
     default:
@@ -2589,9 +2835,55 @@ INT WINAPI WSAIoctl(SOCKET s,
        break;
 
    case WS_SIO_GET_EXTENSION_FUNCTION_POINTER:
-       FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER %s: stub\n", debugstr_guid(lpvInBuffer));
-       WSASetLastError(WSAEOPNOTSUPP);
-       return SOCKET_ERROR;
+   {
+        static const GUID connectex_guid = WSAID_CONNECTEX;
+        static const GUID disconnectex_guid = WSAID_DISCONNECTEX;
+        static const GUID acceptex_guid = WSAID_ACCEPTEX;
+        static const GUID getaccepexsockaddrs_guid = WSAID_GETACCEPTEXSOCKADDRS;
+        static const GUID transmitfile_guid = WSAID_TRANSMITFILE;
+        static const GUID transmitpackets_guid = WSAID_TRANSMITPACKETS;
+        static const GUID wsarecvmsg_guid = WSAID_WSARECVMSG;
+        static const GUID wsasendmsg_guid = WSAID_WSASENDMSG;
+
+        if ( IsEqualGUID(&connectex_guid, lpvInBuffer) )
+        {
+            *(LPFN_CONNECTEX *)lpbOutBuffer = WS2_ConnectEx;
+            return 0;
+        }
+        else if ( IsEqualGUID(&disconnectex_guid, lpvInBuffer) )
+        {
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented DisconnectEx\n");
+        }
+        else if ( IsEqualGUID(&acceptex_guid, lpvInBuffer) )
+        {
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented AcceptEx\n");
+        }
+        else if ( IsEqualGUID(&getaccepexsockaddrs_guid, lpvInBuffer) )
+        {
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented GetAcceptExSockaddrs\n");
+        }
+        else if ( IsEqualGUID(&transmitfile_guid, lpvInBuffer) )
+        {
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented TransmitFile\n");
+        }
+        else if ( IsEqualGUID(&transmitpackets_guid, lpvInBuffer) )
+        {
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented TransmitPackets\n");
+        }
+        else if ( IsEqualGUID(&wsarecvmsg_guid, lpvInBuffer) )
+        {
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented WSARecvMsg\n");
+        }
+        else if ( IsEqualGUID(&wsasendmsg_guid, lpvInBuffer) )
+        {
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER: unimplemented WSASendMsg\n");
+        }
+        else
+            FIXME("SIO_GET_EXTENSION_FUNCTION_POINTER %s: stub\n", debugstr_guid(lpvInBuffer));
+
+        WSASetLastError(WSAEOPNOTSUPP);
+        return SOCKET_ERROR;
+   }
 
    case WS_SIO_KEEPALIVE_VALS:
    {
@@ -2636,6 +2928,9 @@ INT WINAPI WSAIoctl(SOCKET s,
         release_sock_fd(s, fd);
         break;
    }
+   case WS_SIO_UDP_CONNRESET:
+       FIXME("WS_SIO_UDP_CONNRESET stub\n");
+       break;
    default:
        FIXME("unsupported WS_IOCTL cmd (%08x)\n", dwIoControlCode);
        WSASetLastError(WSAEOPNOTSUPP);
@@ -2756,7 +3051,7 @@ int WINAPI WS_recv(SOCKET s, char *buf, int len, int flags)
     wsabuf.len = len;
     wsabuf.buf = buf;
 
-    if ( WSARecvFrom(s, &wsabuf, 1, &n, &dwFlags, NULL, NULL, NULL, NULL) == SOCKET_ERROR )
+    if ( WS2_recvfrom(s, &wsabuf, 1, &n, &dwFlags, NULL, NULL, NULL, NULL) == SOCKET_ERROR )
         return SOCKET_ERROR;
     else
         return n;
@@ -2774,7 +3069,7 @@ int WINAPI WS_recvfrom(SOCKET s, char *buf, INT len, int flags,
     wsabuf.len = len;
     wsabuf.buf = buf;
 
-    if ( WSARecvFrom(s, &wsabuf, 1, &n, &dwFlags, from, fromlen, NULL, NULL) == SOCKET_ERROR )
+    if ( WS2_recvfrom(s, &wsabuf, 1, &n, &dwFlags, from, fromlen, NULL, NULL) == SOCKET_ERROR )
         return SOCKET_ERROR;
     else
         return n;
@@ -2936,15 +3231,13 @@ int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds,
 static void WS_AddCompletion( SOCKET sock, ULONG_PTR CompletionValue, NTSTATUS CompletionStatus,
                               ULONG Information )
 {
-    NTSTATUS status;
-
     SERVER_START_REQ( add_fd_completion )
     {
         req->handle      = wine_server_obj_handle( SOCKET2HANDLE(sock) );
         req->cvalue      = CompletionValue;
         req->status      = CompletionStatus;
         req->information = Information;
-        status = wine_server_call( req );
+        wine_server_call( req );
     }
     SERVER_END_REQ;
 }
@@ -2961,7 +3254,7 @@ int WINAPI WS_send(SOCKET s, const char *buf, int len, int flags)
     wsabuf.len = len;
     wsabuf.buf = (char*) buf;
 
-    if ( WSASendTo( s, &wsabuf, 1, &n, flags, NULL, 0, NULL, NULL) == SOCKET_ERROR )
+    if ( WS2_sendto( s, &wsabuf, 1, &n, flags, NULL, 0, NULL, NULL) == SOCKET_ERROR )
         return SOCKET_ERROR;
     else
         return n;
@@ -2975,7 +3268,7 @@ INT WINAPI WSASend( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
                     LPWSAOVERLAPPED lpOverlapped,
                     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
 {
-    return WSASendTo( s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags,
+    return WS2_sendto( s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags,
                       NULL, 0, lpOverlapped, lpCompletionRoutine );
 }
 
@@ -2988,14 +3281,11 @@ INT WINAPI WSASendDisconnect( SOCKET s, LPWSABUF lpBuffers )
 }
 
 
-/***********************************************************************
- *             WSASendTo               (WS2_32.74)
- */
-INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
-                      LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
-                      const struct WS_sockaddr *to, int tolen,
-                      LPWSAOVERLAPPED lpOverlapped,
-                      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
+static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+                       LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
+                       const struct WS_sockaddr *to, int tolen,
+                       LPWSAOVERLAPPED lpOverlapped,
+                       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
 {
     unsigned int i, options;
     int n, fd, err;
@@ -3044,8 +3334,9 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
     }
     if (n == -1 && errno != EAGAIN)
     {
+        int loc_errno = errno;
         err = wsaErrno();
-        if (cvalue) WS_AddCompletion( s, cvalue, err, 0 );
+        if (cvalue) WS_AddCompletion( s, cvalue, sock_get_ntstatus(loc_errno), 0 );
         goto error;
     }
 
@@ -3169,6 +3460,21 @@ error:
     return SOCKET_ERROR;
 }
 
+/***********************************************************************
+ *             WSASendTo               (WS2_32.74)
+ */
+INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+                      LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
+                      const struct WS_sockaddr *to, int tolen,
+                      LPWSAOVERLAPPED lpOverlapped,
+                      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
+{
+    return WS2_sendto( s, lpBuffers, dwBufferCount,
+                lpNumberOfBytesSent, dwFlags,
+                to, tolen,
+                lpOverlapped, lpCompletionRoutine );
+}
+
 /***********************************************************************
  *             sendto          (WS2_32.20)
  */
@@ -3181,7 +3487,7 @@ int WINAPI WS_sendto(SOCKET s, const char *buf, int len, int flags,
     wsabuf.len = len;
     wsabuf.buf = (char*) buf;
 
-    if ( WSASendTo(s, &wsabuf, 1, &n, flags, to, tolen, NULL, NULL) == SOCKET_ERROR )
+    if ( WS2_sendto(s, &wsabuf, 1, &n, flags, to, tolen, NULL, NULL) == SOCKET_ERROR )
         return SOCKET_ERROR;
     else
         return n;
@@ -3191,7 +3497,7 @@ int WINAPI WS_sendto(SOCKET s, const char *buf, int len, int flags,
  *             setsockopt              (WS2_32.21)
  */
 int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
-                                  const char *optval, int optlen)
+                         const char *optval, int optlen)
 {
     int fd;
     int woptval;
@@ -3202,7 +3508,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
           s, level, optname, optval, optlen);
 
     /* some broken apps pass the value directly instead of a pointer to it */
-    if(IS_INTRESOURCE(optval))
+    if(optlen && IS_INTRESOURCE(optval))
     {
         SetLastError(WSAEFAULT);
         return SOCKET_ERROR;
@@ -3280,6 +3586,12 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
             TRACE("Ignoring SO_EXCLUSIVEADDRUSE, is always set.\n");
             return 0;
 
+        /* After a ConnectEx call succeeds, the socket can't be used with half of the
+         * normal winsock functions on windows. We don't have that problem. */
+        case WS_SO_UPDATE_CONNECT_CONTEXT:
+            TRACE("Ignoring SO_UPDATE_CONNECT_CONTEXT, since our sockets are normal");
+            return 0;
+
         /* SO_OPENTYPE does not require a valid socket handle. */
         case WS_SO_OPENTYPE:
             if (!optlen || optlen < sizeof(int) || !optval)
@@ -3288,7 +3600,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
                 return SOCKET_ERROR;
             }
             get_per_thread_data()->opentype = *(const int *)optval;
-            TRACE("setting global SO_OPENTYPE = 0x%x\n", *((int*)optval) );
+            TRACE("setting global SO_OPENTYPE = 0x%x\n", *((const int*)optval) );
             return 0;
 
 #ifdef SO_RCVTIMEO
@@ -3303,7 +3615,7 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
                 tval.tv_usec = (*(const UINT32*)optval % 1000) * 1000;
                 tval.tv_sec = *(const UINT32*)optval / 1000;
                 /* min of 500 milliseconds */
-                if (tval.tv_sec == 0 && tval.tv_usec < 500000)
+                if (tval.tv_sec == 0 && tval.tv_usec && tval.tv_usec < 500000)
                     tval.tv_usec = 500000;
                 optlen = sizeof(struct timeval);
                 optval = (char*)&tval;
@@ -3403,6 +3715,31 @@ int WINAPI WS_setsockopt(SOCKET s, int level, int optname,
         }
         break;
 
+    case WS_IPPROTO_IPV6:
+        switch(optname)
+        {
+#ifdef IPV6_ADD_MEMBERSHIP
+        case WS_IPV6_ADD_MEMBERSHIP:
+#endif
+#ifdef IPV6_DROP_MEMBERSHIP
+        case WS_IPV6_DROP_MEMBERSHIP:
+#endif
+        case WS_IPV6_MULTICAST_IF:
+        case WS_IPV6_MULTICAST_HOPS:
+        case WS_IPV6_MULTICAST_LOOP:
+        case WS_IPV6_UNICAST_HOPS:
+        case WS_IPV6_V6ONLY:
+            convert_sockopt(&level, &optname);
+            break;
+        case WS_IPV6_DONTFRAG:
+            FIXME("IPV6_DONTFRAG is silently ignored!\n");
+            return 0;
+        default:
+            FIXME("Unknown IPPROTO_IPV6 optname 0x%08x\n", optname);
+            return SOCKET_ERROR;
+        }
+        break;
+
     default:
         WARN("Unknown level: 0x%08x\n", level);
         SetLastError(WSAEINVAL);
@@ -3548,6 +3885,132 @@ struct WS_hostent* WINAPI WS_gethostbyaddr(const char *addr, int len, int type)
     return retval;
 }
 
+/***********************************************************************
+ *             WS_get_local_ips                (INTERNAL)
+ *
+ * Returns the list of local IP addresses by going through the network
+ * adapters and using the local routing table to sort the addresses
+ * from highest routing priority to lowest routing priority. This
+ * functionality is inferred from the description for obtaining local
+ * IP addresses given in the Knowledge Base Article Q160215.
+ *
+ * Please note that the returned hostent is only freed when the thread
+ * closes and is replaced if another hostent is requested.
+ */
+static struct WS_hostent* WS_get_local_ips( char *hostname )
+{
+    int last_metric, numroutes = 0, i, j;
+    PIP_ADAPTER_INFO adapters = NULL, k;
+    struct WS_hostent *hostlist = NULL;
+    PMIB_IPFORWARDTABLE routes = NULL;
+    struct route *route_addrs = NULL;
+    DWORD adap_size, route_size;
+
+    /* Obtain the size of the adapter list and routing table, also allocate memory */
+    if (GetAdaptersInfo(NULL, &adap_size) != ERROR_BUFFER_OVERFLOW)
+        return NULL;
+    if (GetIpForwardTable(NULL, &route_size, FALSE) != ERROR_INSUFFICIENT_BUFFER)
+        return NULL;
+    adapters = HeapAlloc(GetProcessHeap(), 0, adap_size);
+    routes = HeapAlloc(GetProcessHeap(), 0, route_size);
+    route_addrs = HeapAlloc(GetProcessHeap(), 0, 0); /* HeapReAlloc doesn't work on NULL */
+    if (adapters == NULL || routes == NULL || route_addrs == NULL)
+        goto cleanup;
+    /* Obtain the adapter list and the full routing table */
+    if (GetAdaptersInfo(adapters, &adap_size) != NO_ERROR)
+        goto cleanup;
+    if (GetIpForwardTable(routes, &route_size, FALSE) != NO_ERROR)
+        goto cleanup;
+    /* Store the interface associated with each route */
+    for (i = 0; i < routes->dwNumEntries; i++)
+    {
+        DWORD ifindex, ifmetric, exists = FALSE;
+
+        if (routes->table[i].dwForwardType != MIB_IPROUTE_TYPE_DIRECT)
+            continue;
+        ifindex = routes->table[i].dwForwardIfIndex;
+        ifmetric = routes->table[i].dwForwardMetric1;
+        /* Only store the lowest valued metric for an interface */
+        for (j = 0; j < numroutes; j++)
+        {
+            if (route_addrs[j].interface == ifindex)
+            {
+                if (route_addrs[j].metric > ifmetric)
+                    route_addrs[j].metric = ifmetric;
+                exists = TRUE;
+            }
+        }
+        if (exists)
+            continue;
+        route_addrs = HeapReAlloc(GetProcessHeap(), 0, route_addrs, (numroutes+1)*sizeof(struct route));
+        if (route_addrs == NULL)
+            goto cleanup; /* Memory allocation error, fail gracefully */
+        route_addrs[numroutes].interface = ifindex;
+        route_addrs[numroutes].metric = ifmetric;
+        /* If no IP is found in the next step (for whatever reason)
+         * then fall back to the magic loopback address.
+         */
+        memcpy(&(route_addrs[numroutes].addr.s_addr), magic_loopback_addr, 4);
+        numroutes++;
+    }
+   if (numroutes == 0)
+       goto cleanup; /* No routes, fall back to the Magic IP */
+    /* Find the IP address associated with each found interface */
+    for (i = 0; i < numroutes; i++)
+    {
+        for (k = adapters; k != NULL; k = k->Next)
+        {
+            char *ip = k->IpAddressList.IpAddress.String;
+
+            if (route_addrs[i].interface == k->Index)
+                route_addrs[i].addr.s_addr = (in_addr_t) inet_addr(ip);
+        }
+    }
+    /* Allocate a hostent and enough memory for all the IPs,
+     * including the NULL at the end of the list.
+     */
+    hostlist = WS_create_he(hostname, 1, numroutes+1, TRUE);
+    if (hostlist == NULL)
+        goto cleanup; /* Failed to allocate a hostent for the list of IPs */
+    hostlist->h_addr_list[numroutes] = NULL; /* NULL-terminate the address list */
+    hostlist->h_aliases[0] = NULL; /* NULL-terminate the alias list */
+    hostlist->h_addrtype = AF_INET;
+    hostlist->h_length = sizeof(struct in_addr); /* = 4 */
+    /* Reorder the entries when placing them in the host list, Windows expects
+     * the IP list in order from highest priority to lowest (the critical thing
+     * is that most applications expect the first IP to be the default route).
+     */
+    last_metric = -1;
+    for (i = 0; i < numroutes; i++)
+    {
+       struct in_addr addr;
+       int metric = 0xFFFF;
+
+       memcpy(&addr, magic_loopback_addr, 4);
+       for (j = 0; j < numroutes; j++)
+       {
+           int this_metric = route_addrs[j].metric;
+
+           if (this_metric > last_metric && this_metric < metric)
+           {
+               addr = route_addrs[j].addr;
+               metric = this_metric;
+           }
+       }
+       last_metric = metric;
+       (*(struct in_addr *) hostlist->h_addr_list[i]) = addr;
+    }
+
+    /* Cleanup all allocated memory except the address list,
+     * the address list is used by the calling app.
+     */
+cleanup:
+    HeapFree(GetProcessHeap(), 0, route_addrs);
+    HeapFree(GetProcessHeap(), 0, adapters);
+    HeapFree(GetProcessHeap(), 0, routes);
+    return hostlist;
+}
+
 /***********************************************************************
  *             gethostbyname           (WS2_32.52)
  */
@@ -3561,35 +4024,44 @@ struct WS_hostent* WINAPI WS_gethostbyname(const char* name)
     struct hostent hostentry;
     int locerr = ENOBUFS;
 #endif
-    char buf[100];
-    if( !name || !name[0]) {
-        name = buf;
-        if( gethostname( buf, 100) == -1) {
-            SetLastError( WSAENOBUFS); /* appropriate ? */
-            return retval;
-        }
+    char hostname[100];
+    if( gethostname( hostname, 100) == -1) {
+        SetLastError( WSAENOBUFS); /* appropriate ? */
+        return retval;
     }
+    if( !name || !name[0]) {
+        name = hostname;
+    }
+    /* If the hostname of the local machine is requested then return the
+     * complete list of local IP addresses */
+    if(strcmp(name, hostname) == 0)
+        retval = WS_get_local_ips(hostname);
+    /* If any other hostname was requested (or the routing table lookup failed)
+     * then return the IP found by the host OS */
+    if(retval == NULL)
+    {
 #ifdef  HAVE_LINUX_GETHOSTBYNAME_R_6
-    host = NULL;
-    extrabuf=HeapAlloc(GetProcessHeap(),0,ebufsize) ;
-    while(extrabuf) {
-        int res = gethostbyname_r(name, &hostentry, extrabuf, ebufsize, &host, &locerr);
-        if( res != ERANGE) break;
-        ebufsize *=2;
-        extrabuf=HeapReAlloc(GetProcessHeap(),0,extrabuf,ebufsize) ;
-    }
-    if (!host) SetLastError((locerr < 0) ? wsaErrno() : wsaHerrno(locerr));
+        host = NULL;
+        extrabuf=HeapAlloc(GetProcessHeap(),0,ebufsize) ;
+        while(extrabuf) {
+            int res = gethostbyname_r(name, &hostentry, extrabuf, ebufsize, &host, &locerr);
+            if( res != ERANGE) break;
+            ebufsize *=2;
+            extrabuf=HeapReAlloc(GetProcessHeap(),0,extrabuf,ebufsize) ;
+        }
+        if (!host) SetLastError((locerr < 0) ? wsaErrno() : wsaHerrno(locerr));
 #else
-    EnterCriticalSection( &csWSgetXXXbyYYY );
-    host = gethostbyname(name);
-    if (!host) SetLastError((h_errno < 0) ? wsaErrno() : wsaHerrno(h_errno));
+        EnterCriticalSection( &csWSgetXXXbyYYY );
+        host = gethostbyname(name);
+        if (!host) SetLastError((h_errno < 0) ? wsaErrno() : wsaHerrno(h_errno));
 #endif
-    if (host) retval = WS_dup_he(host);
+        if (host) retval = WS_dup_he(host);
 #ifdef  HAVE_LINUX_GETHOSTBYNAME_R_6
-    HeapFree(GetProcessHeap(),0,extrabuf);
+        HeapFree(GetProcessHeap(),0,extrabuf);
 #else
-    LeaveCriticalSection( &csWSgetXXXbyYYY );
+        LeaveCriticalSection( &csWSgetXXXbyYYY );
 #endif
+    }
     if (retval && retval->h_addr_list[0][0] == 127 &&
         strcmp(name, "localhost") != 0)
     {
@@ -4092,6 +4564,8 @@ int WINAPI WS_gethostname(char *name, int namelen)
 int WINAPI WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEvent, LPWSANETWORKEVENTS lpEvent)
 {
     int ret;
+    int i;
+    int errors[FD_MAX_EVENTS];
 
     TRACE("%08lx, hEvent %p, lpEvent %p\n", s, hEvent, lpEvent );
 
@@ -4100,11 +4574,16 @@ int WINAPI WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEvent, LPWSANETWORKEVENTS lp
         req->handle  = wine_server_obj_handle( SOCKET2HANDLE(s) );
         req->service = TRUE;
         req->c_event = wine_server_obj_handle( hEvent );
-        wine_server_set_reply( req, lpEvent->iErrorCode, sizeof(lpEvent->iErrorCode) );
+        wine_server_set_reply( req, errors, sizeof(errors) );
         if (!(ret = wine_server_call(req))) lpEvent->lNetworkEvents = reply->pmask & reply->mask;
     }
     SERVER_END_REQ;
-    if (!ret) return 0;
+    if (!ret)
+    {
+        for (i = 0; i < FD_MAX_EVENTS; i++)
+            lpEvent->iErrorCode[i] = NtStatusToWSAError(errors[i]);
+        return 0;
+    }
     SetLastError(WSAEINVAL);
     return SOCKET_ERROR;
 }
@@ -4464,9 +4943,16 @@ static int list_dup(char** l_src, char** l_to, int item_size)
  *
  * Creates the entry with enough memory for the name, aliases
  * addresses, and the address pointers.  Also copies the name
- * and sets up all the pointers.
+ * and sets up all the pointers.  If "fill_addresses" is set then
+ * sufficient memory for the addresses is also allocated and the
+ * address pointers are set to this memory.
+ *
+ * NOTE: The alias and address lists must be allocated with room
+ * for the NULL item terminating the list.  This is true even if
+ * the list has no items ("aliases" and "addresses" must be
+ * at least "1", a truly empty list is invalid).
  */
-static struct WS_hostent *WS_create_he(char *name, int aliases, int addresses)
+static struct WS_hostent *WS_create_he(char *name, int aliases, int addresses, int fill_addresses)
 {
     struct WS_hostent *p_to;
     char *p;
@@ -4476,6 +4962,10 @@ static struct WS_hostent *WS_create_he(char *name, int aliases, int addresses)
                 sizeof(char *)*aliases +
                 sizeof(char *)*addresses);
 
+    /* Allocate enough memory for the addresses */
+    if (fill_addresses)
+        size += sizeof(struct in_addr)*addresses;
+
     if (!(p_to = check_buffer_he(size))) return NULL;
     memset(p_to, 0, size);
 
@@ -4484,15 +4974,22 @@ static struct WS_hostent *WS_create_he(char *name, int aliases, int addresses)
     strcpy(p, name);
     p += strlen(p) + 1;
 
-    if (aliases != 0)
+    p_to->h_aliases = (char **)p;
+    p += sizeof(char *)*aliases;
+    p_to->h_addr_list = (char **)p;
+    p += sizeof(char *)*addresses;
+    if (fill_addresses)
     {
-        p_to->h_aliases = (char **)p;
-        p += sizeof(char *)*aliases;
-    }
-    if (addresses != 0)
-    {
-        p_to->h_addr_list = (char **)p;
-        p += sizeof(char *)*addresses;
+        int i;
+
+        /* NOTE: h_aliases must be filled in manually, leave these
+         * pointers NULL (already set to NULL by memset earlier).
+         */
+
+        /* Fill in the list of address pointers */
+        for (i = 0; i < addresses; i++)
+            p_to->h_addr_list[i] = (p += sizeof(struct in_addr));
+        p += sizeof(struct in_addr);
     }
     return p_to;
 }
@@ -4507,7 +5004,7 @@ static struct WS_hostent *WS_dup_he(const struct hostent* p_he)
     int aliases = list_size(p_he->h_aliases, 0);
     struct WS_hostent *p_to;
 
-    p_to = WS_create_he(p_he->h_name, aliases, addresses);
+    p_to = WS_create_he(p_he->h_name, aliases, addresses, FALSE);
 
     if (!p_to) return NULL;
     p_to->h_addrtype = p_he->h_addrtype;
@@ -4580,17 +5077,14 @@ int WINAPI WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
                    LPWSAOVERLAPPED lpOverlapped,
                    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
 {
-    return WSARecvFrom(s, lpBuffers, dwBufferCount, NumberOfBytesReceived, lpFlags,
+    return WS2_recvfrom(s, lpBuffers, dwBufferCount, NumberOfBytesReceived, lpFlags,
                        NULL, NULL, lpOverlapped, lpCompletionRoutine);
 }
 
-/***********************************************************************
- *              WSARecvFrom             (WS2_32.69)
- */
-INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
-                        LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct WS_sockaddr *lpFrom,
-                        LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
-                        LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
+static int WS2_recvfrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+                         LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct WS_sockaddr *lpFrom,
+                         LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
+                         LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
 
 {
     unsigned int i, options;
@@ -4641,8 +5135,9 @@ INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
             if (errno == EINTR) continue;
             if (errno != EAGAIN)
             {
+                int loc_errno = errno;
                 err = wsaErrno();
-                if (cvalue) WS_AddCompletion( s, cvalue, err, 0 );
+                if (cvalue) WS_AddCompletion( s, cvalue, sock_get_ntstatus(loc_errno), 0 );
                 goto error;
             }
         }
@@ -4742,6 +5237,21 @@ error:
     return SOCKET_ERROR;
 }
 
+/***********************************************************************
+ *              WSARecvFrom             (WS2_32.69)
+ */
+INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
+                        LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct WS_sockaddr *lpFrom,
+                        LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
+                        LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
+
+{
+    return WS2_recvfrom( s, lpBuffers, dwBufferCount,
+                lpNumberOfBytesRecvd, lpFlags,
+                lpFrom, lpFromlen,
+                lpOverlapped, lpCompletionRoutine );
+}
+
 /***********************************************************************
  *              WSCInstallProvider             (WS2_32.88)
  */