This patch needs testing yet - I am sending it for people to review and point out errors right from the start. Currently I only verified that it compiles. ChangeLog: implement overlapped recv(), much like overlapped IO in files/file.c. make WS2_32 WSARecvFrom() the "working" function, all others are wrappers only. implement WSAGetOverlappedResult(). NOTE: My patches submitted yesterday and today (16/01) to wine-patches are necessary for this one to work. CVS base version is 2002-01-15. Martin -- Martin Wilck Phone: +49 5251 8 15113 Fujitsu Siemens Computers Fax: +49 5251 8 20409 Heinz-Nixdorf-Ring 1 mailto:Martin.Wilck(a)Fujitsu-Siemens.com D-33106 Paderborn http://www.fujitsu-siemens.com/primergy diff -ruX diffignore CVS/wine/dlls/winsock/socket.c MW/wine/dlls/winsock/socket.c --- CVS/wine/dlls/winsock/socket.c Wed Jan 16 18:14:19 2002 +++ MW/wine/dlls/winsock/socket.c Wed Jan 16 18:18:27 2002 @@ -85,6 +85,7 @@ #include "wine/winbase16.h" #include "wingdi.h" #include "winuser.h" +#include "winerror.h" #include "winsock2.h" #include "ws2tcpip.h" #include "wsipx.h" @@ -95,7 +96,6 @@ #include "file.h" #include "debugtools.h" - DEFAULT_DEBUG_CHANNEL(winsock); /* critical section to protect some non-rentrant net function */ @@ -167,11 +167,22 @@ if ( !as->user_overlapped ) HeapFree ( GetProcessHeap(), 0, as->overlapped ); + else + /* FIXME: is this correct? How else can we communicate the flags + to WSAGetOverlappedResult() ? */ + as->overlapped->Offset = as->flags; HeapFree ( GetProcessHeap(), 0, as->iovec ); HeapFree ( GetProcessHeap(), 0, as ); } +static void CALLBACK ws2_default_completion ( DWORD dwError, DWORD cbTransferred, + LPWSAOVERLAPPED lpOverlapped, + DWORD dwFlags ) +{ + NtSetEvent ( lpOverlapped->hEvent, NULL ); +} + /****************************************************************/ /* ----------------------------------- internal data */ @@ -307,14 +318,19 @@ static char* check_buffer(int size); -static int _get_sock_fd(SOCKET s) +static int _get_sock_fd_type (SOCKET s, enum fd_type *type, DWORD *flags) { - int fd = FILE_GetUnixHandle( s, GENERIC_READ ); + int fd = FILE_GetUnixHandleType ( s, GENERIC_READ, type, flags ); if (fd == -1) FIXME("handle %d is not a socket (GLE %ld)\n",s,GetLastError()); return fd; } +static int _get_sock_fd (SOCKET s) +{ + return _get_sock_fd_type (s, NULL, NULL); +} + static void _enable_event(SOCKET s, unsigned int event, unsigned int sstate, unsigned int cstate) { @@ -1887,34 +1903,16 @@ */ int WINAPI WS_recv(SOCKET s, char *buf, int len, int flags) { - int fd = _get_sock_fd(s); + DWORD n; + WSABUF wsabuf = { len, buf }; + DWORD dwFlags = flags; TRACE("socket %04x, buf %8x, len %d, flags %d\n", s, (unsigned)buf, len, flags); - if (fd != -1) - { - INT length; - - if (_is_blocking(s)) - { - /* block here */ - /* FIXME: OOB and exceptfds? */ - do_block(fd, 1); - } - if ((length = recv(fd, buf, len, flags)) >= 0) - { - TRACE(" -> %i bytes\n", length); - - close(fd); - _enable_event(s, FD_READ, 0, 0); - return length; - } - SetLastError(wsaErrno()); - close(fd); - } - else SetLastError(WSAENOTSOCK); - WARN(" -> ERROR\n"); - return SOCKET_ERROR; + if ( WSARecv (s, &wsabuf, 1, &n, &dwFlags, NULL, NULL) == SOCKET_ERROR ) + return SOCKET_ERROR; + else + return n; } /*********************************************************************** @@ -1932,60 +1930,14 @@ int WINAPI WS_recvfrom(SOCKET s, char *buf, INT len, int flags, struct WS_sockaddr *from, int *fromlen) { - int fd = _get_sock_fd(s); - int res; - - TRACE("socket %04x, ptr %08x, len %d, flags %d\n", s, (unsigned)buf, len, flags); -#if DEBUG_SOCKADDR - if (from) - dump_sockaddr(from); - else - DPRINTF("from = NULL\n"); -#endif + DWORD n; + WSABUF wsabuf = { len, buf }; + DWORD dwFlags = flags; - res=SOCKET_ERROR; - if (fd != -1) - { - struct sockaddr* uaddr; - int uaddrlen; - int length; - - if (_is_blocking(s)) - { - /* block here */ - /* FIXME: OOB and exceptfds */ - do_block(fd, 1); - } - - uaddr=ws_sockaddr_alloc(from,fromlen,&uaddrlen); - length=recvfrom(fd, buf, len, flags, uaddr, &uaddrlen); - if (length < 0) - { - SetLastError(wsaErrno()); - WARN(" -> ERROR\n"); - } - else if (ws_sockaddr_u2ws(uaddr,uaddrlen,from,fromlen) != 0) - { - /* The from buffer was too small, but we read the data - * anyway. Is that really bad? - */ - SetLastError(WSAEFAULT); - WARN(" -> WSAEFAULT\n"); - } - else - { - TRACE(" -> %i bytes\n", length); - _enable_event(s, FD_READ, 0, 0); - res=length; - } - close(fd); - } + if ( WSARecvFrom (s, &wsabuf, 1, &n, &dwFlags, from, fromlen, NULL, NULL) == SOCKET_ERROR ) + return SOCKET_ERROR; else - { - SetLastError(WSAENOTSOCK); - WARN(" -> WSAENOTSOCK\n"); - } - return res; + return n; } /*********************************************************************** @@ -2811,6 +2763,44 @@ return SOCKET_ERROR; } +/********************************************************************** + * WSAGetOverlappedResult (WS2_32.40) + */ +BOOL WINAPI WSAGetOverlappedResult ( SOCKET s, LPWSAOVERLAPPED lpOverlapped, + LPDWORD lpcbTransfer, BOOL fWait, + LPDWORD lpdwFlags ) +{ + DWORD r; + + TRACE ( "socket %d ovl %p trans %p, wait %d flags %p\n", + s, lpOverlapped, lpcbTransfer, fWait, lpdwFlags ); + + if ( !(lpOverlapped && lpOverlapped->hEvent) ) + { + ERR ( "Invalid pointer\n" ); + WSASetLastError (WSA_INVALID_PARAMETER); + return FALSE; + } + + do { + TRACE("waiting on %p\n",lpOverlapped); + r = WaitForSingleObjectEx (lpOverlapped->hEvent, fWait ? INFINITE : 0, TRUE); + TRACE("wait on %p returned %ld\n",lpOverlapped,r); + } while (r == STATUS_USER_APC); + + if ( lpcbTransfer ) + *lpcbTransfer = lpOverlapped->InternalHigh; + + if ( lpdwFlags ) + *lpdwFlags = lpOverlapped->Offset; /* see ws2_async_call_completion */ + + if (lpOverlapped->Internal == STATUS_PENDING ) + WSASetLastError ( WSA_IO_INCOMPLETE ); + + return (r == WAIT_OBJECT_0); +} + + /*********************************************************************** * WINSOCK_DoAsyncSelect */ @@ -3481,34 +3471,267 @@ /*********************************************************************** + * WSARecv (WS2_32.67) + */ +int WINAPI WSARecv (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, + LPDWORD NumberOfBytesReceived, LPDWORD lpFlags, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +{ + + return WSARecvFrom (s, lpBuffers, dwBufferCount, NumberOfBytesReceived, lpFlags, + NULL, NULL, lpOverlapped, lpCompletionRoutine); + +} + +/*********************************************************************** + * WS2_recv (INTERNAL) + */ +static int WS2_recv (int fd, struct iovec* iov, int count, + struct WS_sockaddr *lpFrom, LPINT lpFromlen, LPDWORD lpFlags) +{ + /* Uses recvmsg() in order to provide scatter-gather I/O */ + struct msghdr hdr; + int n; + TRACE ( "fd %d, iovec %p, count %d addr %p, len %p, flags %lx\n", + fd, iov, count, lpFrom, lpFromlen, *lpFlags); + + hdr.msg_name = NULL; + + if ( lpFrom ) + { +#if DEBUG_SOCKADDR + dump_sockaddr (lpFrom); +#endif + + hdr.msg_namelen = *lpFromlen; + hdr.msg_name = ws_sockaddr_alloc (lpFrom, lpFromlen, &hdr.msg_namelen); + if ( !hdr.msg_name ) + { + WSASetLastError ( WSAENOBUFS ); + n = -1; + goto out; + } + } + else + hdr.msg_namelen = 0; + + hdr.msg_iov = iov; + hdr.msg_iovlen = count; + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + hdr.msg_flags = 0; + + if ( (n = recvmsg (fd, &hdr, *lpFlags)) == -1 ) + goto out; + + if ( lpFrom && + ws_sockaddr_u2ws ( hdr.msg_name, hdr.msg_namelen, + lpFrom, lpFromlen ) != 0 ) + { + /* The from buffer was too small, but we read the data + * anyway. Is that really bad? + */ + WSASetLastError ( WSAEFAULT ); + WARN ( "Address buffer too small\n" ); + } + +out: + TRACE ( "recvmsg returned %d\n", n ); + + ws_sockaddr_free ( hdr.msg_name, lpFrom ); + return n; +} + + +/*********************************************************************** + * WS2_async_recv (INTERNAL) + */ +static void WS2_async_recv (async_private *as) +{ + ws2_async* wsa = (ws2_async*) as; + int result, err; + + TRACE ( "async %p\n", wsa ); + + result = WS2_recv ( wsa->async.fd, wsa->iovec, wsa->n_iovecs, + wsa->addr, wsa->addrlen, &wsa->flags ); + + if (result >= 0) + { + wsa->overlapped->Internal = STATUS_SUCCESS; + wsa->overlapped->InternalHigh = result; + TRACE ( "received %d bytes\n", result ); + return; + } + + err = errno; + if ( err == EAGAIN || err == EINTR ) + { + wsa->overlapped->Internal = STATUS_PENDING; + TRACE ( "still pending\n" ); + } + else + { + wsa->overlapped->Internal = STATUS_UNSUCCESSFUL; + TRACE ( "Error: %d\n", errno ); + } +} + +/*********************************************************************** * 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, + LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, + struct WS_sockaddr *lpFrom, LPINT lpFromlen, + LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ) { - DWORD dwCount; - INT rc; + struct iovec* iovec; + int fd, i, length, err = WSAENOTSOCK; + enum fd_type type; + DWORD flags; + ws2_async *wsa; + + fd = _get_sock_fd_type (s, &type, &flags); + + TRACE("socket %04x, wsabuf %p, nbufs %ld, flags %ld, from %p, " + "fromlen %ld, ovl %p, func %p, fd %d, type %d, flags %ld\n", + s, lpBuffers, dwBufferCount, *lpFlags, lpFrom, + (lpFromlen ? *lpFromlen : -1L), + lpOverlapped, lpCompletionRoutine, + fd, (int) type, flags); - FIXME( "(%i,%p,%lu,%p,%p,%p,%p,%p,%p: stub\n", - s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, - lpFrom, lpFromlen, lpOverlapped, lpCompletionRoutine ); + if (fd == -1) + { + err = WSAENOTSOCK; + goto error; + } - for( dwCount = 0, rc = 0; dwCount < dwBufferCount; dwCount++ ) - { + iovec = HeapAlloc (GetProcessHeap(), 0, + dwBufferCount * sizeof (struct iovec) ); + if ( !iovec ) + { + err = WSAENOBUFS; + goto err_close; + } - if( ( rc = WS_recvfrom(s, lpBuffers[ dwCount ].buf, (INT)lpBuffers[ dwCount ].len, - (INT)*lpFlags, lpFrom, lpFromlen ) ) != 0 ) + for (i = 0; i < dwBufferCount; i++) { - break; + iovec[i].iov_base = lpBuffers[i].buf; + iovec[i].iov_len = lpBuffers[i].len; } - } + if ( (lpOverlapped || lpCompletionRoutine) && flags & FD_FLAG_OVERLAPPED ) + { + /* Asynchronous recv */ - return rc; -} + wsa = HeapAlloc ( GetProcessHeap(), 0, sizeof (ws2_async) ); + if ( !wsa ) + { + err = WSAENOBUFS; + goto err_free; + } + + wsa->async.ops = &ws2_async_ops; + wsa->async.handle = (HANDLE) s; + wsa->async.fd = fd; + wsa->async.type = ASYNC_TYPE_READ; + wsa->async.func = WS2_async_recv; + wsa->user_overlapped = lpOverlapped; + if ( lpOverlapped ) + wsa->overlapped = lpOverlapped; + else + { + wsa->overlapped = HeapAlloc ( GetProcessHeap(), 0, + sizeof (WSAOVERLAPPED) ); + if ( !wsa->overlapped ) + { + err = WSAENOBUFS; + goto err_free_as; + } + } + wsa->completion_func = ( lpCompletionRoutine ? + lpCompletionRoutine : ws2_default_completion ); + wsa->iovec = iovec; + wsa->n_iovecs = dwBufferCount; + wsa->addr = lpFrom; + wsa->addrlen = lpFromlen; + wsa->flags = *lpFlags; + + wsa->overlapped->Internal = STATUS_USER_APC; + wsa->overlapped->InternalHigh = 0; + TRACE ( "async %p, overlapped: %p, user overlapped: %p, completion: %p\n", + wsa, wsa->overlapped, wsa->user_overlapped, wsa->completion_func ); + + if ( !register_async ( &wsa->async ) ) + { + err = wsaErrno (); + goto err_free_ov; + } + + /* Check for immediate completion + (only possible if lpOverlapped is non-NULL) */ + if ( !lpOverlapped ) + { + WSASetLastError ( WSA_IO_PENDING ); + return SOCKET_ERROR; + } + else if ( WSAGetOverlappedResult ( (HANDLE) s, lpOverlapped, + lpNumberOfBytesRecvd, FALSE, lpFlags) ) + { + return 0; + } + else if ( WSAGetLastError () == WSA_IO_INCOMPLETE ) + { + WSASetLastError ( WSA_IO_PENDING ); + return SOCKET_ERROR; + } + else + goto err_free_ov; + } + + /* Synchronous recv */ + + if (_is_blocking(s)) + { + /* block here */ + /* FIXME: OOB and exceptfds? */ + do_block(fd, 1); + } + + length = WS2_recv ( fd, iovec, dwBufferCount, lpFrom, lpFromlen, lpFlags ); + if ( length == -1 ) + { + err = wsaErrno(); + goto err_free; + } + + TRACE(" -> %i bytes\n", length); + + *lpNumberOfBytesRecvd = length; + + WS_FREE (iovec); + close(fd); + _enable_event(s, FD_READ, 0, 0); + + return 0; + +err_free_ov: + if ( !wsa->user_overlapped ) + HeapFree ( GetProcessHeap(), 0, wsa->overlapped ); +err_free_as: + HeapFree ( GetProcessHeap(), 0, wsa ); +err_free: + WS_FREE (iovec); +err_close: + close (fd); +error: + WARN(" -> ERROR %d\n", err); + SetLastError ( err ); + return SOCKET_ERROR; +} /*********************************************************************** * WSCInstallProvider (WS2_32.88) diff -ruX diffignore CVS/wine/dlls/winsock/ws2_32.spec MW/wine/dlls/winsock/ws2_32.spec --- CVS/wine/dlls/winsock/ws2_32.spec Tue Jan 15 20:17:38 2002 +++ MW/wine/dlls/winsock/ws2_32.spec Wed Jan 16 18:13:36 2002 @@ -52,7 +52,7 @@ 37 stub WSAEnumProtocolsA 38 stub WSAEnumProtocolsW 39 stdcall WSAEventSelect(long long long) WSAEventSelect -40 stub WSAGetOverlappedResult +40 stdcall WSAGetOverlappedResult(long ptr ptr long ptr) WSAGetOverlappedResult 41 stub WSAGetQOSByName 42 stub WSAGetServiceClassInfoA 43 stub WSAGetServiceClassInfoW @@ -79,7 +79,7 @@ 64 stub WSANtohl 65 stub WSANtohs 66 stub WSAProviderConfigChange -67 stub WSARecv +67 stdcall WSARecv(long ptr long ptr ptr ptr ptr) WSARecv 68 stub WSARecvDisconnect 69 stdcall WSARecvFrom(long ptr long ptr ptr ptr ptr ptr ptr ) WSARecvFrom 70 stub WSARemoveServiceClass diff -ruX diffignore CVS/wine/files/file.c MW/wine/files/file.c --- CVS/wine/files/file.c Wed Jan 16 18:14:09 2002 +++ MW/wine/files/file.c Wed Jan 16 17:34:34 2002 @@ -224,7 +224,7 @@ * Retrieve the Unix handle corresponding to a file handle. * Returns -1 on failure. */ -static int FILE_GetUnixHandleType( HANDLE handle, DWORD access, enum fd_type *type, DWORD *flags ) +int FILE_GetUnixHandleType( HANDLE handle, DWORD access, enum fd_type *type, DWORD *flags ) { int ret, fd = -1; diff -ruX diffignore CVS/wine/include/file.h MW/wine/include/file.h --- CVS/wine/include/file.h Wed Jan 16 18:14:04 2002 +++ MW/wine/include/file.h Wed Jan 16 18:15:12 2002 @@ -12,6 +12,9 @@ #include "winbase.h" #include "wine/windef16.h" /* HFILE16 */ +/* FIXME: including server/protocol.h here leads to a handle_t type conflict elsewhere */ +enum fd_type; /* declared in server_protocol.h */ + #define MAX_PATHNAME_LEN 1024 /* Definition of a full DOS file name */ @@ -97,6 +100,7 @@ extern int FILE_strncasecmp( const char *str1, const char *str2, int len ); extern void FILE_SetDosError(void); extern HANDLE FILE_DupUnixHandle( int fd, DWORD access, BOOL inherit ); +extern int FILE_GetUnixHandleType ( HANDLE handle, DWORD access, enum fd_type *type, DWORD *flags ); extern int FILE_GetUnixHandle( HANDLE handle, DWORD access ); extern BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info ); extern HFILE16 FILE_Dup2( HFILE16 hFile1, HFILE16 hFile2 );