Hi, this patch tries to implement a small part of the extended Winsock 2 functionality in Wine. It is very experimental, I have only made sure everything compiles. It is _NOT_ intended for people to try and run. I am submitting this patch because I am pretty new to Wine hacking and I'd appreciate some Wine gurus to audit this for obvious stupidity, style incompatiblities, etc. Features implemented: - WSAAccept(), including callback functionality. - WSASocket(): support for dwFlags parameter. The real difficult stuff will follow when I start looking into WSARecv(), WSASend(), and WSAGetOverlappedResults(). Thus it'd be nice if someone pointed out my misconceptions before I put real work into the latter. Thanks & regards, 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 Index: dlls/winsock/socket.c =================================================================== RCS file: /home/wine/wine/dlls/winsock/socket.c,v retrieving revision 1.65 diff -u -r1.65 socket.c --- dlls/winsock/socket.c 2001/10/14 16:25:47 1.65 +++ dlls/winsock/socket.c 2001/11/07 16:36:53 @@ -154,6 +154,7 @@ int WS_dup_pe(struct protoent* p_pe, int flag); int WS_dup_se(struct servent* p_se, int flag); int WINAPI WSOCK32_getpeername(SOCKET s, ws_sockaddr *name, int *namelen); +int WINAPI WSOCK32_getsockname(SOCKET s, ws_sockaddr *name, int *namelen); typedef void WIN_hostent; typedef void WIN_protoent; @@ -938,6 +939,131 @@ } /*********************************************************************** + * WSAAccept (WS2_32.26) + */ +SOCKET WINAPI WSAAccept(SOCKET s, + ws_sockaddr* addr, + LPINT addrlen32, + LPCONDITIONPROC cond_cb, + DWORD cb_data ) +{ + + /* Code copied largely from WSOCK32_accept() */ + /* FIXME: Can we simply call WSOCK32_accept() instead ? */ + /* I wasn't sure because of the AsyncSelect stuff at the end, + which presumably should only be called if the connection is accepted */ + + SOCKET as; + int fd = _get_sock_fd(s); + unsigned omask; + + TRACE("%04x cond=%08x data=%08x\n", (UINT16) s, (UINT) cond_cb, (UINT) cb_data); + + if (fd == -1) + { + SetLastError(WSAENOTSOCK); + return INVALID_SOCKET; + } + + if (_is_blocking(s)) + { + /* block here */ + do_block(fd, 5); + _sync_sock_state(s); /* let wineserver notice connection */ + /* retrieve any error codes from it */ + SetLastError(_get_sock_error(s, FD_ACCEPT_BIT)); + /* FIXME: care about the error? */ + } + + close(fd); + + SERVER_START_REQ( accept_socket ) + { + req->lhandle = s; + req->access = GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE; + req->inherit = TRUE; + set_error( SERVER_CALL() ); + as = (SOCKET)req->handle; + } + SERVER_END_REQ; + + if (!as) + { + SetLastError(WSAENOTSOCK); + return INVALID_SOCKET; + } + + if (cond_cb) + { + /* We need our own buffers for Caller and Callee addresses. */ + /* FIXME: large enough / too large ? */ + const int SOCKADDR_LEN=128; + INT caller_len = SOCKADDR_LEN, callee_len = SOCKADDR_LEN; + char _caller_addr[SOCKADDR_LEN], _callee_addr[SOCKADDR_LEN]; + ws_sockaddr *caller_addr = (ws_sockaddr*) _caller_addr; + ws_sockaddr *callee_addr = (ws_sockaddr*) _callee_addr; + WSABUF caller_buf = { 0, _caller_addr }; + WSABUF callee_buf = { 0, _callee_addr }; + WSABUF callee_data_buf = { 0, NULL }; + GROUP group = 0; + int cb_result; + + WSOCK32_getpeername (as, caller_addr, &caller_len); + WSOCK32_getsockname (as, callee_addr, &callee_len); + caller_buf.len = caller_len; + callee_buf.len = callee_len; + + TRACE ("Calling callback\n"); + cb_result = cond_cb (&caller_buf, /* Caller ID */ + NULL, /* Caller data unsupported */ + NULL, NULL, /* QOS unsupported */ + &callee_buf, /* Callee id */ + &callee_data_buf, /* Callee data unsupported */ + &group, /* Group unsupported */ + cb_data); + + switch (cb_result) + { + case CF_ACCEPT: + TRACE ("Connection accepted\n"); + break; + case CF_REJECT: + closesocket (as); + TRACE ("Connection refused\n"); + SetLastError (WSAECONNREFUSED); + return INVALID_SOCKET; + case CF_DEFER: + TRACE ("Connection deferred\n"); + SERVER_START_REQ (set_socket_deferred) + { + req->handle = s; + req->deferred = as; + SERVER_CALL (); + } + SERVER_END_REQ; + SetLastError (WSATRY_AGAIN); + return INVALID_SOCKET; + default: + WARN ("Invalid return value\n"); + SetLastError (WSAEINVAL); + return INVALID_SOCKET; + } + + if (addrlen32 && addr && *addrlen32 >= caller_len) + memcpy (addr, caller_addr, caller_len); + + } else + WSOCK32_getpeername (as, addr, addrlen32); + + omask = _get_sock_mask( s ); + if (omask & FD_WINE_SERVEVENT) + ws2_async_accept(s, as); + + return as; +} + + +/*********************************************************************** * bind (WS2_32.2) */ int WINAPI WSOCK32_bind(SOCKET s, const ws_sockaddr* name, int namelen) @@ -1016,7 +1142,21 @@ */ INT WINAPI WSOCK32_closesocket(SOCKET s) { + SOCKET deferred; TRACE("socket %08x\n", s); + + /* Close a deferred socket if present */ + SERVER_START_REQ ( get_socket_deferred ) + { + req->handle = s; + SERVER_CALL (); + deferred = req->deferred; + } + SERVER_END_REQ; + + if (deferred) + WSOCK32_closesocket (deferred); + if (CloseHandle(s)) return 0; return SOCKET_ERROR; } @@ -2898,10 +3038,24 @@ g, dwFlags) are ignored. */ - TRACE("af=%d type=%d protocol=%d protocol_info=%p group=%d flags=0x%lx\n", + SOCKET s; + + TRACE("af=%d type=%d protocol=%d protocol_info=%p group=%d flags=0x%lx\n", af, type, protocol, lpProtocolInfo, g, dwFlags ); - return ( WSOCK32_socket (af, type, protocol) ); + s = WSOCK32_socket (af, type, protocol); + + if (dwFlags && s != INVALID_SOCKET) + { + SERVER_START_REQ ( set_socket_flags ) + { + req->handle = s; + req->flags = dwFlags; + SERVER_CALL(); + } + SERVER_END_REQ; + } + return s; } Index: dlls/winsock/ws2_32.spec =================================================================== RCS file: /home/wine/wine/dlls/winsock/ws2_32.spec,v retrieving revision 1.13 diff -u -r1.13 ws2_32.spec --- dlls/winsock/ws2_32.spec 2001/10/02 17:46:59 1.13 +++ dlls/winsock/ws2_32.spec 2001/11/07 16:36:53 @@ -38,7 +38,7 @@ 23 stdcall socket(long long long) WSOCK32_socket 24 stdcall WSApSetPostRoutine(ptr) WSApSetPostRoutine 25 stub WPUCompleteOverlappedRequest -26 stub WSAAccept +26 stdcall WSAAccept(long ptr ptr ptr long) WSAAccept 27 stub WSAAddressToStringA 28 stub WSAAddressToStringW 29 stdcall WSACloseEvent(long) WSACloseEvent Index: include/winsock2.h =================================================================== RCS file: /home/wine/wine/include/winsock2.h,v retrieving revision 1.10 diff -u -r1.10 winsock2.h --- include/winsock2.h 2001/10/02 17:46:59 1.10 +++ include/winsock2.h 2001/11/07 16:36:54 @@ -225,10 +225,44 @@ CHAR* buf; } WSABUF, *LPWSABUF; -typedef struct _OVERLAPPED * LPWSAOVERLAPPED; typedef HANDLE WSAEVENT; + +typedef struct _WSAOVERLAPPED { + DWORD Internal; + DWORD InternalHigh; + DWORD Offset; + DWORD OffsetHigh; + WSAEVENT hEvent; +} WSAOVERLAPPED, *LPWSAOVERLAPPED; + +#define WSA_IO_PENDING (WSAEWOULDBLOCK) +#define WSA_IO_INCOMPLETE (WSAEWOULDBLOCK) +#define WSA_INVALID_HANDLE (WSAENOTSOCK) +#define WSA_INVALID_PARAMETER (WSAEINVAL) +#define WSA_NOT_ENOUGH_MEMORY (WSAENOBUFS) +#define WSA_OPERATION_ABORTED (WSAEINTR) + +#define WSA_INVALID_EVENT ((WSAEVENT)NULL) +#define WSA_MAXIMUM_WAIT_EVENTS (MAXIMUM_WAIT_OBJECTS) +#define WSA_WAIT_FAILED ((DWORD)-1L) +#define WSA_WAIT_EVENT_0 ((DWORD)0) +#define WSA_WAIT_TIMEOUT ((DWORD)0x102L) +#define WSA_INFINITE ((DWORD)-1L) + +/* + * WinSock 2 extension -- manifest constants for WSASocket() + */ +#define WSA_FLAG_OVERLAPPED 0x01 +#define WSA_FLAG_MULTIPOINT_C_ROOT 0x02 +#define WSA_FLAG_MULTIPOINT_C_LEAF 0x04 +#define WSA_FLAG_MULTIPOINT_D_ROOT 0x08 +#define WSA_FLAG_MULTIPOINT_D_LEAF 0x10 + typedef unsigned int GROUP; +/* FIXME: We don't support QOS */ +typedef DWORD QOS, *LPQOS; + typedef void CALLBACK (*LPWSAOVERLAPPED_COMPLETION_ROUTINE) ( DWORD dwError, @@ -237,7 +271,27 @@ DWORD dwFlags ); +/* + * WinSock 2 extension -- manifest constants for return values of the condition function + */ +#define CF_ACCEPT 0x0000 +#define CF_REJECT 0x0001 +#define CF_DEFER 0x0002 +/* FIXME: The lpCallerData / lpCalleeData arguments are unsupported */ +typedef int CALLBACK (*LPCONDITIONPROC) +( + LPWSABUF lpCallerId, + LPWSABUF lpCallerData, + LPQOS lpSQOS, + LPQOS lpGQOS, + LPWSABUF lpCalleeId, + LPWSABUF lpCalleeData, + GROUP *g, + DWORD dwCallbackData +); + + /* Function declarations */ int WINAPI WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents); int WINAPI WSAEventSelect(SOCKET s, WSAEVENT hEventObject, long lNetworkEvents); @@ -251,6 +305,9 @@ LPWSAPROTOCOL_INFOA lpProtocolInfo, GROUP g, DWORD dwFlags); INT WINAPI ioctlsocket(SOCKET s, LONG cmd, ULONG *argp); + +/* FIXME: omitted the deviating WIN64 declaration */ +SOCKET WINAPI WSAAccept(SOCKET,ws_sockaddr*,LPINT,LPCONDITIONPROC,DWORD); #include "poppack.h" Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.16 diff -u -r1.16 protocol.def --- server/protocol.def 2001/10/24 00:23:26 1.16 +++ server/protocol.def 2001/11/07 16:36:55 @@ -661,6 +661,31 @@ handle_t event; /* event object */ @END +/* Set Winsock2 flags */ +(a)REQ(set_socket_flags) + handle_t handle; /* handle to the socket */ + unsigned int flags; /* Winsock2 flags */ +(a)END + +/* Get Winsock2 flags */ +(a)REQ(get_socket_flags) + handle_t handle; /* handle to the socket */ +(a)REPLY + unsigned int flags; /* Winsock2 flags */ +(a)END + +/* Get deferred socket to be accept()ed later, for WSAAccept() */ +(a)REQ(get_socket_deferred) + handle_t handle; /* handle to the socket */ +(a)REPLY + handle_t deferred; /* deferred socket */ +(a)END + +/* Set deferred socket to be accept()ed later, for WSAAccept() */ +(a)REQ(set_socket_deferred) + handle_t handle; /* handle to the socket */ + handle_t deferred; /* deferred socket */ +(a)END /* Get socket event parameters */ @REQ(get_socket_event) Index: server/sock.c =================================================================== RCS file: /home/wine/wine/server/sock.c,v retrieving revision 1.21 diff -u -r1.21 sock.c --- server/sock.c 2001/10/05 19:45:45 1.21 +++ server/sock.c 2001/11/07 16:36:55 @@ -47,6 +47,8 @@ unsigned int pmask; /* pending events */ struct event *event; /* event object */ int errors[FD_MAX_EVENTS]; /* event errors */ + unsigned int flags; /* Winsock 2 socket flags */ + handle_t deferred; /* deferred socket to be accept()ed later */ }; static void sock_dump( struct object *obj, int verbose ); @@ -324,6 +326,8 @@ sock->hmask = 0; sock->pmask = 0; sock->event = NULL; + sock->flags = 0; + sock->deferred = 0; sock_reselect( sock ); clear_error(); return &sock->obj; @@ -342,36 +346,51 @@ GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops); if (!sock) return NULL; - /* Try to accept(2). We can't be safe that this an already connected socket - * or that accept() is allowed on it. In those cases we will get -1/errno - * return. - */ - slen = sizeof(saddr); - acceptfd = accept(sock->obj.fd,&saddr,&slen); - if (acceptfd==-1) { - sock_set_error(); - release_object( sock ); - return NULL; - } - if (!(acceptsock = alloc_object( &sock_ops, -1 ))) - { - release_object( sock ); - return NULL; + + if (sock->deferred) { + + /* If a previous WSAAccept() deferred accepting this request, try it again. */ + /* We do this no matter if called through accept() or WSAAccept(), */ + /* because this is the way Windows does it, too. */ + + acceptsock = (struct sock*)get_handle_obj(current->process,sock->deferred, + GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops); + sock->deferred = 0; + } else { + /* Try to accept(2). We can't be safe that this an already connected socket + * or that accept() is allowed on it. In those cases we will get -1/errno + * return. + */ + slen = sizeof(saddr); + acceptfd = accept(sock->obj.fd,&saddr,&slen); + if (acceptfd==-1) { + sock_set_error(); + release_object( sock ); + return NULL; + } + if (!(acceptsock = alloc_object( &sock_ops, -1 ))) + { + release_object( sock ); + return NULL; + } + + /* newly created socket gets the same properties of the listening socket */ + fcntl(acceptfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */ + acceptsock->obj.fd = acceptfd; + acceptsock->state = FD_WINE_CONNECTED|FD_READ|FD_WRITE; + if (sock->state & FD_WINE_NONBLOCKING) + acceptsock->state |= FD_WINE_NONBLOCKING; + acceptsock->mask = sock->mask; + acceptsock->hmask = 0; + acceptsock->pmask = 0; + acceptsock->event = NULL; + acceptsock->flags = sock->flags; + acceptsock->deferred = 0; } - /* newly created socket gets the same properties of the listening socket */ - fcntl(acceptfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */ - acceptsock->obj.fd = acceptfd; - acceptsock->state = FD_WINE_CONNECTED|FD_READ|FD_WRITE; - if (sock->state & FD_WINE_NONBLOCKING) - acceptsock->state |= FD_WINE_NONBLOCKING; - acceptsock->mask = sock->mask; - acceptsock->hmask = 0; - acceptsock->pmask = 0; - acceptsock->event = NULL; if (sock->event && !(sock->mask & FD_WINE_SERVEVENT)) - acceptsock->event = (struct event *)grab_object( sock->event ); - + acceptsock->event = (struct event *)grab_object( sock->event ); + sock_reselect( acceptsock ); clear_error(); sock->pmask &= ~FD_ACCEPT; @@ -589,3 +608,37 @@ release_object( &sock->obj ); } +DECL_HANDLER(get_socket_deferred) +{ + struct sock *sock; + + sock=(struct sock*)get_handle_obj(current->process,req->handle,GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops); + req->deferred = sock->deferred; +} + +DECL_HANDLER(set_socket_deferred) +{ + struct sock *sock; + + sock=(struct sock*)get_handle_obj(current->process,req->handle,GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops); + + sock->deferred = req->deferred; + release_object (&sock->obj); +} + +DECL_HANDLER(get_socket_flags) +{ + struct sock *sock; + + sock=(struct sock*)get_handle_obj(current->process,req->handle,GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops); + req->flags = sock->flags; +} + +DECL_HANDLER(set_socket_flags) +{ + struct sock *sock; + + sock=(struct sock*)get_handle_obj(current->process,req->handle,GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,&sock_ops); + sock->flags = req->flags; +} +