diff --git a/src/DirectPlay8Peer.cpp b/src/DirectPlay8Peer.cpp index 8311649..dd256ef 100644 --- a/src/DirectPlay8Peer.cpp +++ b/src/DirectPlay8Peer.cpp @@ -279,25 +279,72 @@ HRESULT DirectPlay8Peer::Connect(CONST DPN_APPLICATION_DESC* CONST pdnAppDesc, I /* TODO: Get bind address/port from pDeviceInfo, if specified. */ + /* Extract remote IP and port from pHostAddr. */ + uint32_t r_ipaddr; uint16_t r_port; + GUID host_sp; + if(pHostAddr->GetSP(&host_sp) != S_OK) { - wchar_t buf[128]; - DWORD bsize = sizeof(buf); - DWORD type; - - pHostAddr->GetComponentByName(DPNA_KEY_HOSTNAME, buf, &bsize, &type); - InetPtonW(AF_INET, buf, &r_ipaddr); + return DPNERR_INVALIDHOSTADDRESS; } + wchar_t hostname_value[128]; + DWORD hostname_size = sizeof(hostname_value); + DWORD hostname_type; + + if(pHostAddr->GetComponentByName(DPNA_KEY_HOSTNAME, hostname_value, &hostname_size, &hostname_type) == S_OK) { - DWORD buf; - DWORD bsize = sizeof(buf); - DWORD type; + if(hostname_type != DPNA_DATATYPE_STRING) + { + return DPNERR_INVALIDHOSTADDRESS; + } - pHostAddr->GetComponentByName(DPNA_KEY_PORT, &buf, &bsize, &type); - r_port = buf; + if(host_sp == CLSID_DP8SP_TCPIP) + { + struct in_addr hostname_addr; + if(InetPtonW(AF_INET, hostname_value, &hostname_addr) == 1) + { + r_ipaddr = hostname_addr.s_addr; + } + else{ + return DPNERR_INVALIDHOSTADDRESS; + } + } + else if(host_sp == CLSID_DP8SP_IPX) + { + unsigned ip; + if(swscanf(hostname_value, L"00000000,0000%08X", &ip) != 1) + { + return DPNERR_INVALIDHOSTADDRESS; + } + + r_ipaddr = htonl(ip); + } + else{ + return DPNERR_INVALIDHOSTADDRESS; + } + } + else{ + return DPNERR_INVALIDHOSTADDRESS; + } + + DWORD port_value; + DWORD port_size = sizeof(port_value); + DWORD port_type; + + if(pHostAddr->GetComponentByName(DPNA_KEY_PORT, &port_value, &port_size, &port_type) == S_OK) + { + if(port_type != DPNA_DATATYPE_DWORD || port_value > 65535) + { + return DPNERR_INVALIDHOSTADDRESS; + } + + r_port = port_value; + } + else{ + return DPNERR_INVALIDHOSTADDRESS; } if(l_port == 0) diff --git a/tests/DirectPlay8Peer.cpp b/tests/DirectPlay8Peer.cpp index 83eca3a..5c5dd75 100644 --- a/tests/DirectPlay8Peer.cpp +++ b/tests/DirectPlay8Peer.cpp @@ -81,6 +81,16 @@ class IDP8AddressInstance } } + IDP8AddressInstance(GUID service_provider, const wchar_t *hostname, DWORD port): IDP8AddressInstance() + { + if(instance->SetSP(&service_provider) != S_OK + || instance->AddComponent(DPNA_KEY_HOSTNAME, hostname, ((wcslen(hostname) + 1) * sizeof(wchar_t)), DPNA_DATATYPE_STRING) != S_OK + || instance->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD) != S_OK) + { + throw std::runtime_error("Address setup failed"); + } + } + ~IDP8AddressInstance() { #ifdef INSTANTIATE_FROM_COM @@ -1719,6 +1729,206 @@ TEST(DirectPlay8Peer, ConnectAsyncFail) EXPECT_EQ(p1_seq, 1); } +TEST(DirectPlay8Peer, ConnectToIPX) +{ + std::atomic testing(true); + + std::atomic host_seq(0), p1_seq(0); + DPNID host_player_id = -1, p1_player_id = -1; + + SessionHost host(APP_GUID_1, L"Session 1", PORT, + [&testing, &host_seq, &host_player_id, &p1_player_id] + (DWORD dwMessageType, PVOID pMessage) + { + if(!testing) + { + return DPN_OK; + } + + int seq = ++host_seq; + + switch(seq) + { + case 1: + EXPECT_EQ(dwMessageType, DPN_MSGID_CREATE_PLAYER); + + if(dwMessageType == DPN_MSGID_CREATE_PLAYER) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + host_player_id = cp->dpnidPlayer; + + EXPECT_EQ(cp->dwSize, sizeof(DPNMSG_CREATE_PLAYER)); + EXPECT_EQ(cp->pvPlayerContext, (void*)(0xB00)); + + cp->pvPlayerContext = (void*)(0xB00B00); + } + + break; + + case 2: + EXPECT_EQ(dwMessageType, DPN_MSGID_INDICATE_CONNECT); + + if(dwMessageType == DPN_MSGID_INDICATE_CONNECT) + { + DPNMSG_INDICATE_CONNECT *ic = (DPNMSG_INDICATE_CONNECT*)(pMessage); + + EXPECT_EQ(ic->dwSize, sizeof(DPNMSG_INDICATE_CONNECT)); + + EXPECT_EQ(ic->pvUserConnectData, (void*)(NULL)); + EXPECT_EQ(ic->dwUserConnectDataSize, 0); + + EXPECT_EQ(ic->pvReplyData, (void*)(NULL)); + EXPECT_EQ(ic->dwReplyDataSize, 0); + + EXPECT_EQ(ic->pvReplyContext, (void*)(NULL)); + EXPECT_EQ(ic->pvPlayerContext, (void*)(NULL)); + + /* TODO: Check pAddressPlayer, pAddressDevice */ + + ic->pvPlayerContext = (void*)(0xB441); + } + + break; + + case 3: + EXPECT_EQ(dwMessageType, DPN_MSGID_CREATE_PLAYER); + + if(dwMessageType == DPN_MSGID_CREATE_PLAYER) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + p1_player_id = cp->dpnidPlayer; + + EXPECT_EQ(cp->dwSize, sizeof(DPNMSG_CREATE_PLAYER)); + EXPECT_EQ(cp->pvPlayerContext, (void*)(0xB441)); + + cp->pvPlayerContext = (void*)(0xFEED); + } + + break; + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }); + + /* Give the host instance a moment to settle. */ + Sleep(1000); + + DPNID p1_cp_dpnidPlayer = -1, p1_cc_dpnidLocal = -1; + + std::function p1_cb = + [&testing, &p1_seq, &host_player_id, &p1_player_id, &p1_cp_dpnidPlayer, &p1_cc_dpnidLocal] + (DWORD dwMessageType, PVOID pMessage) + { + if(!testing) + { + return DPN_OK; + } + + int seq = ++p1_seq; + + switch(seq) + { + case 1: + EXPECT_EQ(dwMessageType, DPN_MSGID_CREATE_PLAYER); + + if(dwMessageType == DPN_MSGID_CREATE_PLAYER) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + p1_cp_dpnidPlayer = cp->dpnidPlayer; + + EXPECT_EQ(cp->dwSize, sizeof(DPNMSG_CREATE_PLAYER)); + EXPECT_EQ(cp->pvPlayerContext, (void*)(0xBCDE)); + + cp->pvPlayerContext = (void*)(0xCDEF); + } + + break; + + case 2: + EXPECT_EQ(dwMessageType, DPN_MSGID_CREATE_PLAYER); + + if(dwMessageType == DPN_MSGID_CREATE_PLAYER) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + + EXPECT_EQ(cp->dwSize, sizeof(DPNMSG_CREATE_PLAYER)); + EXPECT_EQ(cp->dpnidPlayer, host_player_id); + EXPECT_EQ(cp->pvPlayerContext, (void*)(0)); + + cp->pvPlayerContext = (void*)(0xBAA); + } + + break; + + case 3: + EXPECT_EQ(dwMessageType, DPN_MSGID_CONNECT_COMPLETE); + + if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE) + { + DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage); + + EXPECT_EQ(cc->dwSize, sizeof(DPNMSG_CONNECT_COMPLETE)); + EXPECT_EQ(cc->hAsyncOp, 0); + EXPECT_EQ(cc->hResultCode, S_OK); + + EXPECT_EQ(cc->pvApplicationReplyData, (PVOID)(NULL)); + EXPECT_EQ(cc->dwApplicationReplyDataSize, 0); + + p1_cc_dpnidLocal = cc->dpnidLocal; + } + + break; + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance p1; + + ASSERT_EQ(p1->Initialize(&p1_cb, &callback_shim, 0), S_OK); + + DPN_APPLICATION_DESC connect_to_app; + memset(&connect_to_app, 0, sizeof(connect_to_app)); + + connect_to_app.dwSize = sizeof(connect_to_app); + connect_to_app.guidApplication = APP_GUID_1; + + IDP8AddressInstance connect_to_addr(CLSID_DP8SP_IPX, L"00000000,00007F000001", PORT); + + EXPECT_EQ(p1->Connect( + &connect_to_app, /* pdnAppDesc */ + connect_to_addr, /* pHostAddr */ + NULL, /* pDeviceInfo */ + NULL, /* pdnSecurity */ + NULL, /* pdnCredentials */ + NULL, /* pvUserConnectData */ + 0, /* dwUserConnectDataSize */ + (void*)(0xBCDE), /* pvPlayerContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCONNECT_SYNC /* dwFlags */ + ), S_OK); + + /* Give the host instance a moment to settle. */ + Sleep(1000); + + testing = false; + + EXPECT_EQ(host_seq, 3); + EXPECT_EQ(p1_seq, 3); + + EXPECT_EQ(p1_cp_dpnidPlayer, p1_player_id); + EXPECT_EQ(p1_cc_dpnidLocal, p1_player_id); +} + TEST(DirectPlay8Peer, GetApplicationDesc) { const unsigned char APP_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };