From: Anton Baskanov baskanov@gmail.com
--- dlls/dpwsockx/dpwsockx_dll.h | 11 + dlls/dpwsockx/dpwsockx_main.c | 366 ++++++++++++++++++++++++++++------ 2 files changed, 312 insertions(+), 65 deletions(-)
diff --git a/dlls/dpwsockx/dpwsockx_dll.h b/dlls/dpwsockx/dpwsockx_dll.h index 12191d4bcdb..8e83247460d 100644 --- a/dlls/dpwsockx/dpwsockx_dll.h +++ b/dlls/dpwsockx/dpwsockx_dll.h @@ -80,11 +80,21 @@ typedef struct SOCKADDR_IN addr;
SOCKET tcpSock; + WSAOVERLAPPED overlapped; } DPWS_OUT_CONNECTION;
typedef struct { + struct rb_entry entry; + ULONG ref; DPWS_OUT_CONNECTION *connection; +} DPWS_CONNECTION_REF; + +typedef struct +{ + struct rb_entry entry; + DPID id; + struct rb_tree connectionRefs; } DPWS_PLAYER;
typedef struct tagDPWS_DATA @@ -98,6 +108,7 @@ typedef struct tagDPWS_DATA
CRITICAL_SECTION sendCs; DPWS_PLAYER nameserver; + DPWS_CONNECTION_REF nameserverConnectionRef; struct rb_tree connections;
BOOL started; diff --git a/dlls/dpwsockx/dpwsockx_main.c b/dlls/dpwsockx/dpwsockx_main.c index 5551422253b..678706a79cd 100644 --- a/dlls/dpwsockx/dpwsockx_main.c +++ b/dlls/dpwsockx/dpwsockx_main.c @@ -54,6 +54,13 @@ static int DPWS_CompareConnections( const void *key, const struct rb_entry *entr return 0; }
+static int DPWS_CompareConnectionRefs( const void *key, const struct rb_entry *entry ) +{ + DPWS_CONNECTION_REF *connectionRef = RB_ENTRY_VALUE( entry, DPWS_CONNECTION_REF, entry ); + + return DPWS_CompareConnections( key, &connectionRef->connection->entry ); +} + static HRESULT DPWS_BindToFreePort( SOCKET sock, SOCKADDR_IN *addr, int startPort, int endPort ) { int port; @@ -478,6 +485,75 @@ static HRESULT WINAPI DPWSCB_Send( LPDPSP_SENDDATA data ) return DPERR_UNSUPPORTED; }
+static HRESULT WINAPI DPWSCB_AddPlayerToGroup( DPSP_ADDPLAYERTOGROUPDATA *data ) +{ + DPWS_CONNECTION_REF *playerConnectionRef; + DPWS_CONNECTION_REF *groupConnectionRef; + struct rb_entry *entry; + DPWS_PLAYER *player; + DPWS_PLAYER *group; + DWORD playerSize; + DWORD groupSize; + HRESULT hr; + + TRACE( "(%ld,%ld,%p)\n", data->idPlayer, data->idGroup, data->lpISP ); + + hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idPlayer, (void **) &player, &playerSize, + DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idGroup, (void **) &group, &groupSize, + DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + RB_FOR_EACH_ENTRY( playerConnectionRef, &player->connectionRefs, DPWS_CONNECTION_REF, entry ) + { + entry = rb_get( &group->connectionRefs, &playerConnectionRef->connection->addr ); + if ( entry ) + { + groupConnectionRef = RB_ENTRY_VALUE( entry, DPWS_CONNECTION_REF, entry ); + ++groupConnectionRef->ref; + continue; + } + groupConnectionRef = calloc( 1, sizeof( DPWS_CONNECTION_REF ) ); + if ( !groupConnectionRef ) + return DPERR_OUTOFMEMORY; + groupConnectionRef->ref = 1; + groupConnectionRef->connection = playerConnectionRef->connection; + rb_put( &group->connectionRefs, &groupConnectionRef->connection->addr, + &groupConnectionRef->entry ); + } + + return DP_OK; +} + +static HRESULT WINAPI DPWSCB_CreateGroup( DPSP_CREATEGROUPDATA *data ) +{ + DPWS_PLAYER groupPlaceholder = { 0 }; + DPWS_PLAYER *group; + DWORD groupSize; + HRESULT hr; + + TRACE( "(%ld,0x%08lx,%p,%p)\n", data->idGroup, data->dwFlags, data->lpSPMessageHeader, + data->lpISP ); + + hr = IDirectPlaySP_SetSPPlayerData( data->lpISP, data->idGroup, &groupPlaceholder, + sizeof( groupPlaceholder ), DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idGroup, (void **) &group, &groupSize, + DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + rb_init( &group->connectionRefs, DPWS_CompareConnectionRefs ); + + return DP_OK; +} + static HRESULT DPWS_GetConnection( DPWS_DATA *dpwsData, SOCKADDR_IN *addr, DPWS_OUT_CONNECTION **connection ) { @@ -504,6 +580,7 @@ static HRESULT DPWS_GetConnection( DPWS_DATA *dpwsData, SOCKADDR_IN *addr,
static HRESULT WINAPI DPWSCB_CreatePlayer( LPDPSP_CREATEPLAYERDATA data ) { + DPWS_CONNECTION_REF *tcpConnectionRef; DPWS_PLAYER playerPlaceholder = { 0 }; DPWS_PLAYERDATA *playerData; DWORD playerDataSize; @@ -563,24 +640,82 @@ static HRESULT WINAPI DPWSCB_CreatePlayer( LPDPSP_CREATEPLAYERDATA data ) if ( FAILED( hr ) ) return hr;
+ rb_init( &player->connectionRefs, DPWS_CompareConnectionRefs ); + if ( data->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL ) return DP_OK;
- hr = DPWS_GetConnection( dpwsData, &playerData->tcpAddr, &player->connection ); + tcpConnectionRef = calloc( 1, sizeof( DPWS_CONNECTION_REF ) ); + if ( !tcpConnectionRef ) + { + free( player ); + return DPERR_OUTOFMEMORY; + } + + tcpConnectionRef->ref = 1; + + hr = DPWS_GetConnection( dpwsData, &playerData->tcpAddr, &tcpConnectionRef->connection ); if ( FAILED( hr ) ) { + free( tcpConnectionRef ); free( player ); return hr; }
+ rb_put( &player->connectionRefs, &tcpConnectionRef->connection->addr, + &tcpConnectionRef->entry ); + + return DP_OK; +} + +static HRESULT WINAPI DPWSCB_DeleteGroup( DPSP_DELETEGROUPDATA *data ) +{ + DPWS_CONNECTION_REF *connectionRef2; + DPWS_CONNECTION_REF *connectionRef; + DPWS_PLAYER *group; + DWORD groupSize; + HRESULT hr; + + TRACE( "(%ld,0x%08lx,%p)\n", data->idGroup, data->dwFlags, data->lpISP ); + + hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idGroup, (void **) &group, &groupSize, + DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + RB_FOR_EACH_ENTRY_DESTRUCTOR( connectionRef, connectionRef2, &group->connectionRefs, + DPWS_CONNECTION_REF, entry ) + { + rb_remove( &group->connectionRefs, &connectionRef->entry ); + free( connectionRef ); + } + return DP_OK; }
static HRESULT WINAPI DPWSCB_DeletePlayer( LPDPSP_DELETEPLAYERDATA data ) { + DPWS_CONNECTION_REF *connectionRef2; + DPWS_CONNECTION_REF *connectionRef; + DPWS_PLAYER *player; + DWORD playerSize; + HRESULT hr; + TRACE( "(%ld,0x%08lx,%p)\n", data->idPlayer, data->dwFlags, data->lpISP );
+ hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idPlayer, (void **) &player, &playerSize, + DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + RB_FOR_EACH_ENTRY_DESTRUCTOR( connectionRef, connectionRef2, &player->connectionRefs, + DPWS_CONNECTION_REF, entry ) + { + rb_remove( &player->connectionRefs, &connectionRef->entry ); + free( connectionRef ); + } + return DP_OK; }
@@ -653,10 +788,56 @@ static HRESULT WINAPI DPWSCB_Open( LPDPSP_OPENDATA data ) nameserverAddr.sin_addr = header->SockAddr.sin_addr; nameserverAddr.sin_port = header->SockAddr.sin_port;
- hr = DPWS_GetConnection( dpwsData, &nameserverAddr, &dpwsData->nameserver.connection ); + rb_init( &dpwsData->nameserver.connectionRefs, DPWS_CompareConnectionRefs ); + + hr = DPWS_GetConnection( dpwsData, &nameserverAddr, + &dpwsData->nameserverConnectionRef.connection ); + if ( FAILED( hr ) ) + return hr; + + rb_put( &dpwsData->nameserver.connectionRefs, + &dpwsData->nameserverConnectionRef.connection->addr, + &dpwsData->nameserverConnectionRef.entry ); + + return DP_OK; +} + +static HRESULT WINAPI DPWSCB_RemovePlayerFromGroup( DPSP_REMOVEPLAYERFROMGROUPDATA *data ) +{ + DPWS_CONNECTION_REF *playerConnectionRef; + DPWS_CONNECTION_REF *groupConnectionRef; + struct rb_entry *entry; + DPWS_PLAYER *player; + DPWS_PLAYER *group; + DWORD playerSize; + DWORD groupSize; + HRESULT hr; + + TRACE( "(%ld,%ld,%p)\n", data->idPlayer, data->idGroup, data->lpISP ); + + hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idPlayer, (void **) &player, &playerSize, + DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idGroup, (void **) &group, &groupSize, + DPSET_LOCAL ); if ( FAILED( hr ) ) return hr;
+ RB_FOR_EACH_ENTRY( playerConnectionRef, &player->connectionRefs, DPWS_CONNECTION_REF, entry ) + { + entry = rb_get( &group->connectionRefs, &playerConnectionRef->connection->addr ); + if ( !entry ) + continue; + groupConnectionRef = RB_ENTRY_VALUE( entry, DPWS_CONNECTION_REF, entry ); + --groupConnectionRef->ref; + if ( groupConnectionRef->ref ) + continue; + rb_remove( &player->connectionRefs, &groupConnectionRef->entry ); + free( groupConnectionRef ); + } + return DP_OK; }
@@ -703,14 +884,99 @@ static HRESULT WINAPI DPWSCB_GetAddressChoices( LPDPSP_GETADDRESSCHOICESDATA dat return DPERR_UNSUPPORTED; }
-static HRESULT WINAPI DPWSCB_SendEx( LPDPSP_SENDEXDATA data ) +static HRESULT DPWS_SendImpl( DPWS_DATA *dpwsData, DPWS_PLAYER *player, SGBUFFER *buffers, + DWORD bufferCount, DWORD messageSize ) { - DPWS_OUT_CONNECTION *connection; + DPWS_CONNECTION_REF *connectionRef; DPSP_MSG_HEADER header = { 0 }; + HRESULT sendResult; + + header.mixed = DPSP_MSG_MAKE_MIXED( messageSize, DPSP_MSG_TOKEN_REMOTE ); + header.SockAddr.sin_family = AF_INET; + header.SockAddr.sin_port = dpwsData->tcpAddr.sin_port; + + buffers[ 0 ].pData = (unsigned char *) &header; + + EnterCriticalSection( &dpwsData->sendCs ); + + RB_FOR_EACH_ENTRY( connectionRef, &player->connectionRefs, DPWS_CONNECTION_REF, entry ) + { + DPWS_OUT_CONNECTION *connection = connectionRef->connection; + + if ( connection->tcpSock != INVALID_SOCKET ) + continue; + + connection->tcpSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if ( connection->tcpSock == INVALID_SOCKET ) + { + ERR( "socket() failed\n" ); + LeaveCriticalSection( &dpwsData->sendCs ); + return DPERR_UNAVAILABLE; + } + + if ( SOCKET_ERROR == connect( connection->tcpSock, (SOCKADDR *) &connection->addr, + sizeof( connection->addr ) ) ) + { + ERR( "connect() failed\n" ); + closesocket( connection->tcpSock ); + connection->tcpSock = INVALID_SOCKET; + LeaveCriticalSection( &dpwsData->sendCs ); + return DPERR_UNAVAILABLE; + } + } + + RB_FOR_EACH_ENTRY( connectionRef, &player->connectionRefs, DPWS_CONNECTION_REF, entry ) + { + DPWS_OUT_CONNECTION *connection = connectionRef->connection; + DWORD transferred; + + if ( SOCKET_ERROR == WSASend( connection->tcpSock, (WSABUF *) buffers, bufferCount, + &transferred, 0, &connection->overlapped, NULL ) ) + { + if ( WSAGetLastError() != WSA_IO_PENDING ) + { + ERR( "WSASend() failed\n" ); + LeaveCriticalSection( &dpwsData->sendCs ); + return DPERR_UNAVAILABLE; + } + } + } + + sendResult = DP_OK; + + RB_FOR_EACH_ENTRY( connectionRef, &player->connectionRefs, DPWS_CONNECTION_REF, entry ) + { + DPWS_OUT_CONNECTION *connection = connectionRef->connection; + DWORD transferred; + + if ( !WSAGetOverlappedResult( connection->tcpSock, &connection->overlapped, &transferred, + TRUE, NULL ) ) + { + ERR( "WSAGetOverlappedResult() failed\n" ); + sendResult = DPERR_UNAVAILABLE; + continue; + } + + if ( transferred < messageSize ) + { + ERR( "lost connection\n" ); + closesocket( connection->tcpSock ); + connection->tcpSock = INVALID_SOCKET; + sendResult = DPERR_CONNECTIONLOST; + continue; + } + } + + LeaveCriticalSection( &dpwsData->sendCs ); + + return sendResult; +} + +static HRESULT WINAPI DPWSCB_SendEx( LPDPSP_SENDEXDATA data ) +{ DPWS_PLAYER *player; DPWS_DATA *dpwsData; DWORD dpwsDataSize; - DWORD transferred; HRESULT hr;
TRACE( "(%p,0x%08lx,%ld,%ld,%p,%ld,%ld,%ld,%ld,%p,%p,%u)\n", @@ -735,14 +1001,6 @@ static HRESULT WINAPI DPWSCB_SendEx( LPDPSP_SENDEXDATA data ) if ( FAILED( hr ) ) return hr;
- header.mixed = DPSP_MSG_MAKE_MIXED( data->dwMessageSize, DPSP_MSG_TOKEN_REMOTE ); - header.SockAddr.sin_family = AF_INET; - header.SockAddr.sin_port = dpwsData->tcpAddr.sin_port; - - data->lpSendBuffers[ 0 ].pData = (unsigned char *) &header; - - EnterCriticalSection( &dpwsData->sendCs ); - if ( data->idPlayerTo ) { DWORD playerSize; @@ -756,62 +1014,40 @@ static HRESULT WINAPI DPWSCB_SendEx( LPDPSP_SENDEXDATA data ) player = &dpwsData->nameserver; }
- connection = player->connection; - - if ( connection->tcpSock == INVALID_SOCKET ) - { - connection->tcpSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); - if ( connection->tcpSock == INVALID_SOCKET ) - { - ERR( "socket() failed\n" ); - LeaveCriticalSection( &dpwsData->sendCs ); - return DPERR_UNAVAILABLE; - } - - if ( SOCKET_ERROR == connect( connection->tcpSock, (SOCKADDR *) &connection->addr, - sizeof( connection->addr ) ) ) - { - ERR( "connect() failed\n" ); - closesocket( connection->tcpSock ); - connection->tcpSock = INVALID_SOCKET; - LeaveCriticalSection( &dpwsData->sendCs ); - return DPERR_UNAVAILABLE; - } - } - - if ( SOCKET_ERROR == WSASend( connection->tcpSock, (WSABUF *) data->lpSendBuffers, - data->cBuffers, &transferred, 0, NULL, NULL ) ) - { - if ( WSAGetLastError() != WSA_IO_PENDING ) - { - ERR( "WSASend() failed\n" ); - LeaveCriticalSection( &dpwsData->sendCs ); - return DPERR_UNAVAILABLE; - } - } - - if ( transferred < data->dwMessageSize ) - { - ERR( "lost connection\n" ); - closesocket( connection->tcpSock ); - connection->tcpSock = INVALID_SOCKET; - LeaveCriticalSection( &dpwsData->sendCs ); - return DPERR_CONNECTIONLOST; - } - - LeaveCriticalSection( &dpwsData->sendCs ); - - return DP_OK; + return DPWS_SendImpl( dpwsData, player, data->lpSendBuffers, data->cBuffers, + data->dwMessageSize ); }
static HRESULT WINAPI DPWSCB_SendToGroupEx( LPDPSP_SENDTOGROUPEXDATA data ) { - FIXME( "(%p,0x%08lx,%ld,%ld,%p,%ld,%ld,%ld,%ld,%p,%p) stub\n", + DPWS_DATA *dpwsData; + DPWS_PLAYER *group; + DWORD dpwsDataSize; + DWORD groupSize; + HRESULT hr; + + TRACE( "(%p,0x%08lx,%ld,%ld,%p,%ld,%ld,%ld,%ld,%p,%p)\n", data->lpISP, data->dwFlags, data->idGroupTo, data->idPlayerFrom, data->lpSendBuffers, data->cBuffers, data->dwMessageSize, data->dwPriority, data->dwTimeout, data->lpDPContext, data->lpdwSPMsgID ); - return DPERR_UNSUPPORTED; + + if ( data->dwFlags & DPSEND_ASYNC ) + { + FIXME("asynchronous send is not yet supported\n"); + return DPERR_UNSUPPORTED; + } + + hr = IDirectPlaySP_GetSPData( data->lpISP, (void **) &dpwsData, &dpwsDataSize, DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + hr = IDirectPlaySP_GetSPPlayerData( data->lpISP, data->idGroupTo, (void **) &group, &groupSize, + DPSET_LOCAL ); + if ( FAILED( hr ) ) + return hr; + + return DPWS_SendImpl( dpwsData, group, data->lpSendBuffers, data->cBuffers, data->dwMessageSize ); }
static HRESULT WINAPI DPWSCB_Cancel( LPDPSP_CANCELDATA data ) @@ -835,11 +1071,15 @@ static void setup_callbacks( LPDPSP_SPCALLBACKS lpCB ) lpCB->EnumSessions = DPWSCB_EnumSessions; lpCB->Reply = DPWSCB_Reply; lpCB->Send = DPWSCB_Send; + lpCB->AddPlayerToGroup = DPWSCB_AddPlayerToGroup; + lpCB->CreateGroup = DPWSCB_CreateGroup; lpCB->CreatePlayer = DPWSCB_CreatePlayer; + lpCB->DeleteGroup = DPWSCB_DeleteGroup; lpCB->DeletePlayer = DPWSCB_DeletePlayer; lpCB->GetAddress = DPWSCB_GetAddress; lpCB->GetCaps = DPWSCB_GetCaps; lpCB->Open = DPWSCB_Open; + lpCB->RemovePlayerFromGroup = DPWSCB_RemovePlayerFromGroup; lpCB->CloseEx = DPWSCB_CloseEx; lpCB->ShutdownEx = DPWSCB_ShutdownEx; lpCB->GetAddressChoices = DPWSCB_GetAddressChoices; @@ -848,11 +1088,7 @@ static void setup_callbacks( LPDPSP_SPCALLBACKS lpCB ) lpCB->Cancel = DPWSCB_Cancel; lpCB->GetMessageQueue = DPWSCB_GetMessageQueue;
- lpCB->AddPlayerToGroup = NULL; lpCB->Close = NULL; - lpCB->CreateGroup = NULL; - lpCB->DeleteGroup = NULL; - lpCB->RemovePlayerFromGroup = NULL; lpCB->SendToGroup = NULL; lpCB->Shutdown = NULL; }