1
0
mirror of https://github.com/solemnwarning/directplay-lite synced 2024-12-30 16:45:37 +01:00

Address handling improvements.

- IDirectPlay8Peer::EnumHosts() requires a device address to specify
  the service provider to emulate.

- IDirectPlay8Peer::EnumHosts() allows overriding the address/port
  that discovery messages are sent to.

- IDirectPlay8Peer::Host() requires at least one address. Addresses
  with different service providers are not supported yet.

- Implement IDirectPlay8Peer::GetPeerAddress() method.

- Populate pAddressSender in DPNMSG_ENUM_HOSTS_QUERY message.

- Popupate pAddressPlayer in DPNMSG_INDICATE_CONNECT message.

- Base host addresses created by IDirectPlay8Peer on service provider
  of device address given to Host() method.
This commit is contained in:
Daniel Collins 2018-10-03 21:21:56 +01:00
parent f8ee41f365
commit b1289366be
7 changed files with 300 additions and 30 deletions

View File

@ -1,3 +1,5 @@
#include <winsock2.h>
#include <assert.h>
#include <atomic>
#include <dplay8.h>
#include <objbase.h>
@ -5,6 +7,7 @@
#include <string.h>
#include <wchar.h>
#include <windows.h>
#include <ws2tcpip.h>
#include "DirectPlay8Address.hpp"
#include "Log.hpp"
@ -34,6 +37,55 @@ DirectPlay8Address::~DirectPlay8Address()
clear_components();
}
/* Construct a DirectPlay8Address which represents a host address.
*
* service_provider must be CLSID_DP8SP_TCPIP or CLSID_DP8SP_IPX.
* sa must be an IPv4 address with a valid IP and port.
*/
DirectPlay8Address *DirectPlay8Address::create_host_address(std::atomic<unsigned int> *global_refcount, GUID service_provider, const struct sockaddr *sa)
{
assert(service_provider == CLSID_DP8SP_TCPIP || service_provider == CLSID_DP8SP_IPX);
assert(sa->sa_family == AF_INET);
const struct sockaddr_in *sa_v4 = (const struct sockaddr_in*)(sa);
DirectPlay8Address *address = new DirectPlay8Address(global_refcount);
address->SetSP(&service_provider);
if(service_provider == CLSID_DP8SP_TCPIP)
{
/* TCP/IP service provider, just stick the IP in the hostname field. */
char hostname[16];
inet_ntop(AF_INET, &(sa_v4->sin_addr), hostname, sizeof(hostname));
address->AddComponent(DPNA_KEY_HOSTNAME,
hostname, strlen(hostname) + 1, DPNA_DATATYPE_STRING_ANSI);
}
else if(service_provider == CLSID_DP8SP_IPX)
{
/* IPX service provider.
*
* Network address is all zeros.
* Host address is IP address preceeded by two zero bytes.
*/
uint32_t ipaddr_he = ntohl(sa_v4->sin_addr.s_addr);
char hostname[32];
snprintf(hostname, sizeof(hostname), "00000000,0000%08X", (unsigned)(ipaddr_he));
address->AddComponent(DPNA_KEY_HOSTNAME,
hostname, strlen(hostname) + 1, DPNA_DATATYPE_STRING_ANSI);
}
DWORD port_dw = ntohs(sa_v4->sin_port);
address->AddComponent(DPNA_KEY_PORT, &port_dw, sizeof(port_dw), DPNA_DATATYPE_DWORD);
return address;
}
void DirectPlay8Address::clear_components()
{
for(auto c = components.begin(); c != components.end(); ++c)

View File

@ -1,6 +1,7 @@
#ifndef DPLITE_DIRECTPLAY8ADDRESS_HPP
#define DPLITE_DIRECTPLAY8ADDRESS_HPP
#include <winsock2.h>
#include <atomic>
#include <dplay8.h>
#include <objbase.h>
@ -69,6 +70,8 @@ class DirectPlay8Address: public IDirectPlay8Address
DirectPlay8Address(const DirectPlay8Address &src);
virtual ~DirectPlay8Address();
static DirectPlay8Address *create_host_address(std::atomic<unsigned int> *global_refcount, GUID service_provider, const struct sockaddr *sa);
/* IUnknown */
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
virtual ULONG STDMETHODCALLTYPE AddRef() override;

View File

@ -672,6 +672,11 @@ HRESULT DirectPlay8Peer::Host(CONST DPN_APPLICATION_DESC* CONST pdnAppDesc, IDir
/* Not supported yet. */
}
if(cDeviceInfo == 0)
{
return DPNERR_INVALIDPARAM;
}
/* Generate a random GUID for this session. */
HRESULT guid_err = CoCreateGuid(&instance_guid);
if(guid_err != S_OK)
@ -700,6 +705,7 @@ HRESULT DirectPlay8Peer::Host(CONST DPN_APPLICATION_DESC* CONST pdnAppDesc, IDir
(unsigned char*)(pdnAppDesc->pvApplicationReservedData) + pdnAppDesc->dwApplicationReservedDataSize);
}
GUID sp = GUID_NULL;
uint32_t ipaddr = htonl(INADDR_ANY);
uint16_t port = 0;
@ -707,6 +713,26 @@ HRESULT DirectPlay8Peer::Host(CONST DPN_APPLICATION_DESC* CONST pdnAppDesc, IDir
{
DirectPlay8Address *addr = (DirectPlay8Address*)(prgpDeviceInfo[i]);
GUID this_sp;
if(addr->GetSP(&this_sp) != S_OK)
{
return DPNERR_INVALIDDEVICEADDRESS;
}
if(sp != GUID_NULL && this_sp != sp)
{
/* Multiple service providers specified, don't support this yet. */
return E_NOTIMPL;
}
if(this_sp != CLSID_DP8SP_TCPIP && this_sp != CLSID_DP8SP_IPX)
{
/* Only support TCP/IP and IPX addresses at this time. */
return DPNERR_INVALIDDEVICEADDRESS;
}
sp = this_sp;
DWORD addr_port_value;
DWORD addr_port_size = sizeof(addr_port_value);
DWORD addr_port_type;
@ -725,6 +751,8 @@ HRESULT DirectPlay8Peer::Host(CONST DPN_APPLICATION_DESC* CONST pdnAppDesc, IDir
}
}
service_provider = sp;
if(port == 0)
{
for(int p = AUTO_PORT_MIN; p <= AUTO_PORT_MAX; ++p)
@ -1274,7 +1302,22 @@ HRESULT DirectPlay8Peer::GetPeerInfo(CONST DPNID dpnid, DPN_PLAYER_INFO* CONST p
HRESULT DirectPlay8Peer::GetPeerAddress(CONST DPNID dpnid, IDirectPlay8Address** CONST pAddress, CONST DWORD dwFlags)
{
UNIMPLEMENTED("DirectPlay8Peer::GetPeerAddress");
std::unique_lock<std::mutex> l(lock);
Peer *peer = get_peer_by_player_id(dpnid);
if(peer == NULL)
{
return DPNERR_INVALIDPLAYER;
}
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = peer->ip;
sa.sin_port = htons(peer->port);
*pAddress = DirectPlay8Address::create_host_address(global_refcount, service_provider, (struct sockaddr*)(&sa));
return S_OK;
}
HRESULT DirectPlay8Peer::GetLocalHostAddresses(IDirectPlay8Address** CONST prgpAddress, DWORD* CONST pcAddress, CONST DWORD dwFlags)
@ -2245,8 +2288,11 @@ void DirectPlay8Peer::handle_host_enum_request(std::unique_lock<std::mutex> &l,
DPNMSG_ENUM_HOSTS_QUERY ehq;
memset(&ehq, 0, sizeof(ehq));
DirectPlay8Address *sender_address = DirectPlay8Address::create_host_address(
global_refcount, service_provider, (struct sockaddr*)(from_addr));
ehq.dwSize = sizeof(ehq);
ehq.pAddressSender = NULL; // TODO
ehq.pAddressSender = sender_address;
ehq.pAddressDevice = NULL; // TODO
if(!pd.is_null(1))
@ -2265,6 +2311,8 @@ void DirectPlay8Peer::handle_host_enum_request(std::unique_lock<std::mutex> &l,
HRESULT ehq_result = message_handler(message_handler_ctx, DPN_MSGID_ENUM_HOSTS_QUERY, &ehq);
l.lock();
sender_address->Release();
std::vector<unsigned char> response_data_buffer;
if(ehq.dwResponseDataSize > 0)
{
@ -2413,7 +2461,15 @@ void DirectPlay8Peer::handle_host_connect_request(std::unique_lock<std::mutex> &
ic.dwUserConnectDataSize = d.second;
}
ic.pAddressPlayer = NULL; /* TODO */
struct sockaddr_in peer_sa;
peer_sa.sin_family = AF_INET;
peer_sa.sin_addr.s_addr = peer->ip;
peer_sa.sin_port = htons(peer->port);
DirectPlay8Address *peer_address = DirectPlay8Address::create_host_address(
global_refcount, service_provider, (struct sockaddr*)(&peer_sa));
ic.pAddressPlayer = peer_address;
ic.pAddressDevice = NULL; /* TODO */
peer->state = Peer::PS_INDICATING;
@ -2422,6 +2478,8 @@ void DirectPlay8Peer::handle_host_connect_request(std::unique_lock<std::mutex> &
HRESULT ic_result = message_handler(message_handler_ctx, DPN_MSGID_INDICATE_CONNECT, &ic);
l.lock();
peer_address->Release();
std::vector<unsigned char> reply_data_buffer;
if(ic.dwReplyDataSize > 0)
{

View File

@ -47,6 +47,8 @@ class DirectPlay8Peer: public IDirectPlay8Peer
std::wstring password;
std::vector<unsigned char> application_data;
GUID service_provider;
/* Local IP and port for all our sockets, except discovery_socket. */
uint32_t local_ip;
uint16_t local_port;

View File

@ -1,7 +1,9 @@
#include <algorithm>
#include <memory>
#include <stdio.h>
#include <ws2tcpip.h>
#include "COMAPIException.hpp"
#include "DirectPlay8Address.hpp"
#include "HostEnumerator.hpp"
#include "Messages.hpp"
@ -35,13 +37,88 @@ HostEnumerator::HostEnumerator(
next_tx_at(0),
req_cancel(false)
{
/* TODO: Use address in pdpaddrHost, if provided. */
if(pdpaddrDeviceInfo == NULL)
{
throw COMAPIException(DPNERR_INVALIDPARAM);
}
if(pdpaddrDeviceInfo->GetSP(&service_provider) != S_OK)
{
throw COMAPIException(DPNERR_INVALIDDEVICEADDRESS);
}
memset(&send_addr, 0, sizeof(send_addr));
send_addr.sin_family = AF_INET;
send_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
send_addr.sin_port = htons(DISCOVERY_PORT);
if(pdpaddrHost != NULL)
{
GUID host_sp;
if(pdpaddrHost->GetSP(&host_sp) != S_OK)
{
throw COMAPIException(DPNERR_INVALIDHOSTADDRESS);
}
if(host_sp != service_provider)
{
/* Service Provider in host address must match device address. */
throw COMAPIException(DPNERR_INVALIDPARAM);
}
/* Hostname component overrides discovery address, if provided. */
wchar_t hostname_value[128];
DWORD hostname_size = sizeof(hostname_value);
DWORD hostname_type;
if(pdpaddrHost->GetComponentByName(DPNA_KEY_HOSTNAME, hostname_value, &hostname_size, &hostname_type) == S_OK)
{
if(hostname_type != DPNA_DATATYPE_STRING)
{
throw COMAPIException(DPNERR_INVALIDHOSTADDRESS);
}
if(host_sp == CLSID_DP8SP_TCPIP)
{
struct in_addr hostname_addr;
if(InetPtonW(AF_INET, hostname_value, &hostname_addr) == 1)
{
send_addr.sin_addr = hostname_addr;
}
else{
throw COMAPIException(DPNERR_INVALIDHOSTADDRESS);
}
}
else if(host_sp == CLSID_DP8SP_IPX)
{
unsigned ip;
if(swscanf(hostname_value, L"00000000,0000%08X", &ip) != 1)
{
throw COMAPIException(DPNERR_INVALIDHOSTADDRESS);
}
send_addr.sin_addr.s_addr = htonl(ip);
}
}
/* Port component overrides discovery port, if provided. */
DWORD port_value;
DWORD port_size = sizeof(port_value);
DWORD port_type;
if(pdpaddrHost->GetComponentByName(DPNA_KEY_PORT, &port_value, &port_size, &port_type) == S_OK)
{
if(port_type != DPNA_DATATYPE_DWORD || port_value > 65535)
{
throw COMAPIException(DPNERR_INVALIDHOSTADDRESS);
}
send_addr.sin_port = htons(port_value);
}
}
if(pApplicationDesc != NULL)
{
application_guid = pApplicationDesc->guidApplication;
@ -248,26 +325,15 @@ void HostEnumerator::handle_packet(const void *data, size_t size, struct sockadd
* port for the host.
*/
IDirectPlay8Address *sender_address = new DirectPlay8Address(global_refcount);
sender_address->SetSP(&CLSID_DP8SP_TCPIP); /* TODO: Be IPX if application previously gave us an IPX address? */
char from_addr_ip_s[16];
inet_ntop(AF_INET, &(from_addr->sin_addr), from_addr_ip_s, sizeof(from_addr_ip_s));
sender_address->AddComponent(DPNA_KEY_HOSTNAME,
from_addr_ip_s, strlen(from_addr_ip_s) + 1, DPNA_DATATYPE_STRING_ANSI);
DWORD from_port_dw = ntohs(from_addr->sin_port);
sender_address->AddComponent(DPNA_KEY_PORT,
&from_port_dw, sizeof(from_port_dw), DPNA_DATATYPE_DWORD);
IDirectPlay8Address *sender_address = DirectPlay8Address::create_host_address(
global_refcount, service_provider, (struct sockaddr*)(from_addr));
/* Build a DirectPlay8Address with the interface we received the response on.
* TODO: Actually do this.
*/
IDirectPlay8Address *device_address = new DirectPlay8Address(global_refcount);
device_address->SetSP(&CLSID_DP8SP_TCPIP); /* TODO: Be IPX if application previously gave us an IPX address? */
device_address->SetSP(&service_provider);
DPNMSG_ENUM_HOSTS_RESPONSE message;
memset(&message, 0, sizeof(message));

View File

@ -29,6 +29,7 @@ class HostEnumerator
std::function<void(HRESULT)> complete_cb;
GUID service_provider;
struct sockaddr_in send_addr;
GUID application_guid; /* GUID of application to search for, or GUID_NULL */

View File

@ -136,7 +136,12 @@ struct SessionHost
app_desc.guidApplication = application_guid;
app_desc.pwszSessionName = (wchar_t*)(session_description);
if(dp8p->Host(&app_desc, NULL, 0, NULL, NULL, (void*)(0xB00), 0) != S_OK)
IDP8AddressInstance address;
address->SetSP(&CLSID_DP8SP_TCPIP);
IDirectPlay8Address *addresses[] = { address };
if(dp8p->Host(&app_desc, addresses, 1, NULL, NULL, (void*)(0xB00), 0) != S_OK)
{
throw std::runtime_error("DirectPlay8Peer::Host failed");
}
@ -338,12 +343,15 @@ TEST(DirectPlay8Peer, EnumHostsSync)
ASSERT_EQ(client->Initialize(&client_cb, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
DWORD start = GetTickCount();
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -423,12 +431,15 @@ TEST(DirectPlay8Peer, EnumHostsAsync)
ASSERT_EQ(client->Initialize(&callback, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
DWORD start = GetTickCount();
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -492,12 +503,15 @@ TEST(DirectPlay8Peer, EnumHostsAsyncCancelByHandle)
ASSERT_EQ(client->Initialize(&callback, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
DWORD start = GetTickCount();
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -555,12 +569,15 @@ TEST(DirectPlay8Peer, EnumHostsAsyncCancelAllEnums)
ASSERT_EQ(client->Initialize(&callback, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
DWORD start = GetTickCount();
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -618,12 +635,15 @@ TEST(DirectPlay8Peer, EnumHostsAsyncCancelAllOperations)
ASSERT_EQ(client->Initialize(&callback, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
DWORD start = GetTickCount();
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -681,12 +701,15 @@ TEST(DirectPlay8Peer, EnumHostsAsyncCancelByClose)
ASSERT_EQ(client->Initialize(&callback, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
DWORD start = GetTickCount();
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -794,10 +817,13 @@ TEST(DirectPlay8Peer, EnumHostsFilterByApplicationGUID)
app_desc.dwSize = sizeof(app_desc);
app_desc.guidApplication = APP_GUID_2;
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
ASSERT_EQ(client->EnumHosts(
&app_desc, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -857,10 +883,13 @@ TEST(DirectPlay8Peer, EnumHostsFilterByNULLApplicationGUID)
app_desc.dwSize = sizeof(app_desc);
app_desc.guidApplication = GUID_NULL;
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
ASSERT_EQ(client->EnumHosts(
&app_desc, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -939,10 +968,13 @@ TEST(DirectPlay8Peer, EnumHostsDataInQuery)
ASSERT_EQ(client->Initialize(&client_cb, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
(void*)(DATA), /* pvUserEnumData */
sizeof(DATA), /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -1036,10 +1068,13 @@ TEST(DirectPlay8Peer, EnumHostsDataInResponse)
ASSERT_EQ(client->Initialize(&client_cb, &callback_shim, 0), S_OK);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
NULL, /* pdpaddrHost */
NULL, /* pdpaddrDeviceInfo */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
@ -1059,7 +1094,60 @@ TEST(DirectPlay8Peer, EnumHostsDataInResponse)
EXPECT_TRUE(got_return_buffer);
}
/* TODO: Test enumerating a session directly. */
TEST(DirectPlay8Peer, EnumHostsSpecifyPort)
{
SessionHost a1s1(APP_GUID_1, L"Application 1 Session 1", PORT);
SessionHost a1s2(APP_GUID_1, L"Application 1 Session 2", PORT + 1);
std::map<GUID, FoundSession, CompareGUID> sessions;
std::function<HRESULT(DWORD,PVOID)> client_cb =
[&sessions]
(DWORD dwMessageType, PVOID pMessage)
{
if(dwMessageType == DPN_MSGID_ENUM_HOSTS_RESPONSE)
{
DPNMSG_ENUM_HOSTS_RESPONSE *ehr = (DPNMSG_ENUM_HOSTS_RESPONSE*)(pMessage);
sessions.emplace(
ehr->pApplicationDescription->guidInstance,
FoundSession(
ehr->pApplicationDescription->guidApplication,
ehr->pApplicationDescription->pwszSessionName));
}
return DPN_OK;
};
IDP8PeerInstance client;
ASSERT_EQ(client->Initialize(&client_cb, &callback_shim, 0), S_OK);
IDP8AddressInstance host_address(L"127.0.0.1", PORT);
IDP8AddressInstance device_address;
device_address->SetSP(&CLSID_DP8SP_TCPIP);
ASSERT_EQ(client->EnumHosts(
NULL, /* pApplicationDesc */
host_address, /* pdpaddrHost */
device_address, /* pdpaddrDeviceInfo */
NULL, /* pvUserEnumData */
0, /* dwUserEnumDataSize */
3, /* dwEnumCount */
500, /* dwRetryInterval */
500, /* dwTimeOut*/
NULL, /* pvUserContext */
NULL, /* pAsyncHandle */
DPNENUMHOSTS_SYNC /* dwFlags */
), S_OK);
FoundSession expect_sessions[] = {
FoundSession(APP_GUID_1, L"Application 1 Session 1"),
};
EXPECT_SESSIONS(sessions, expect_sessions, expect_sessions + 1);
}
TEST(DirectPlay8Peer, ConnectSync)
{