#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 =
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);
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 ),
/* ----------------------------------- 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;
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)
case ENOBUFS: return WSAENOBUFS;
case 0: return 0;
- default:
+ default:
WARN("Unknown h_errno %d!\n", loc_errno);
return WSAEOPNOTSUPP;
}
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;
}
}
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;
else
{
result = 0;
- status = wsaErrno(); /* FIXME: is this correct ???? */
+ status = wsaErrStatus();
}
}
break;
}
else
{
- /* We set the status to a winsock error code and check for that
- later in NtStatusToWSAError () */
- status = wsaErrno();
+ status = wsaErrStatus();
result = 0;
}
}
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;
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 */
_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;
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)
*/
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))
{
/* 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;
}
else
{
- SetLastError(wsaErrno());
+ SetLastError(ret);
}
release_sock_fd( s, fd );
}
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)
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;
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;
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:
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:
{
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);
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;
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;
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;
}
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;
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 );
}
}
-/***********************************************************************
- * 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;
}
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;
}
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)
*/
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;
* 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;
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;
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)
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
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;
}
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);
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)
*/
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)
{
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 );
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;
}
*
* 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;
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);
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;
}
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;
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;
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;
}
}
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)
*/