Matchmaking: -anetdll implements enough of the DP protocol for strict peer to peer matchmaking, if needed. This is evident from the wireshark traces, as the "DP" control bytes listed at the bottom of this document are present in direct peer connection with no ActiveNet matchmaking server process: anetdll AND winet keep track of IPs inside lists for handles. anet determines self address by initing WINET.DLL with commInit then calling commPlayerInfo on handle 1 and getting this address uses this address in all SYN packets (this is where the problem occurs) anet calls winet to receive packet. winet returns handle (or PLAYER_NONE). if PLAYER_NONE: examine the address inside the data (not the source IP in packet) build new handle with this address call WINET's "commSayHi" to add this address to it's list, and obtain handle ACK the packet (to the wrong address if NAT!) and SYN back with our perceived address (problem with NAT!) if valid handle returned: communication continues, perhaps checking IPs for consistency if expecting our address to be inside of a packet at a certain location: ensure the data there matches our address to ensure we do not act upon another client's data by accident (PROBLEM WITH NAT: external and internal IP will ALWAYS differ, so this will NEVER work) fixed process: -change to 6 byte representation (to allow port number-IP combination) -change perceived self address to AAAADEADBEEF (6 bytes) so that when packets sent, the receiving end will know to replace this with the source port and source IP (where AAAA is where the port number would go and DEADBEEF is where the IP would go) -add code immediately after receive packet to accomplish that -add code to detect if the packet contains the address we're sending to inside of it and replace this with BBBBABADC0DA, to ensure that upon receive, the destination knows the packet was intended for it. The problem lies with NATs: when a client sends a packet to client behind a NAT, the packet will contain the NAT's external IP and port. When the NAT forwards this to the client, the data section of the packet will still contain this exact address (though the destination IP and port will be modified in the packet header of course). Because the client, through it's own mechanisms, will believe that it's address is the internal IP address (or DEADBEEF), when it checks the address in the packet against it's own, they will always differ. When receiving a packet with BBBBABADC0DA in it, the client will then change that to AAAADEADBEEF (our perceived address, thanks to a modification above), as we are certain that if we have received this address constant, it was intended for our machine. This allows the game engine to believe the packet was sent to it's intended destination (us) and it will therefore not discard the packet. maxAddrLen should be A bytes to support full IPX (which would not have changed) --Brainstorming-- --Will need to modify-- ONLY WINET commPlayerInfo (report 6 byte sockAddr for handle) dpio's IP address size (where is this initalized?) (theory: gets host IP and size with handle 1 with commPlayerInfo: TRUE, taken care of by above) Address2Handle (do 6 byte comparison) JUMPHACKS: DEADBEEFAAAA and ABADC0DABBBB JUMPHACKS: init self IP and port to AAAA:DEADBEEF commSayHi: accept 6 byte and write 6 byte --Stack of dpio_get-- 30h: &bufLen 2Ch: &buffer 2Ch: commRxPktReq_t (8 bytes) 1Ch: commRxPktResp_t (9 bytes) --Defn: pTcp-- 00h: socket Ptr 04h: size of list 08h: broadcast sockAddr 18h: host sockAddr 28h: ... rest of array ... --Defn: commRxPktReq_t-- 8 bytes 00h: &buffer 04h: length (4 bytes) --Defn: commRxPktResp_t-- 9 bytes 00h: ret (1 byte) 01h: handle 05h: length --Defn: commDriverInfoResp_t-- 5 bytes 00h: ret (will be 0) 01h: ptr to AVKEGEL6 string --Defn: commPlayerInfoReq_t-- 00h: commHandle --Defn: commPlayerInfoResp_t-- 00h: ret (BYTE) 05h: ptr to IP 09h: IP size (DWORD) 0Dh: 0 (DWORD) 11h: 0 (DWORD) --Defn: commSayHiReq_t-- 00h: &IPaddress (NOT whole buffer!) 04h: integer (which has been seen to be 4) --Defn: commSayHiResp_t-- 00h: ret (1 byte) 01h: commHdl (4 bytes) commHdl: 4 bytes --Defn: dpio_t-- 00h: reliable_enabled or data needs to be processed? 10h: &conns 14h: max_playerHdls (4 bytes) 18h: callback (function pointer) 1Ch: 4th argument for callback (DWORD) 30h: &now 34h: &localMsgQ 38h: expected sequence count? 3Ch: IP address size 40h: circular buffer at address 0008h and 1FEh, 32 corresponding elements each --Defn: in_conn-- <- think this is dpio_conn_t 0000h: pkttime 0004h: ?? (initialized to 1) (may be exponential backoff factor) 0008h: base address of array0, 255 bytes per element 00h: ?? (packet type) 02h: packet number (2 bytes) 04h: bufLen (1 byte) 05h: buffer array fits perfectly 1FE8h: base address of array1 of DWORDS which signify whether buffer is cleared or not in corresponding array0 elem fits exactly in space before 2068h. 2068h: element to access next call to read (WORD) (window_base) 206Ah: something read when 40EAh flag 2 is not set (WORD) (orig window base) 206Ch: element to access next call to write (WORD) (next_pktnum) 40E0h: IP found here (10d bytes max for address, exact size of IPX (maxlen is 10d because IPX has largest address) 40EAh: (WORD) (sometimes contains 7, sometimes 5, is initialized to 1) (appears to be a flag?) (40h ORed in when unexpected pktnum) 40ECh: callback (same as in dpio) 40F0h: callback's arg4 (same as in dpio) --Defn: conn-- 00h: handle (4 bytes) 04h: &in_conn (circular buffer of buffers for handle?) --Defn: conns-- 00h: &array of conn 04h: size of element (possibly for variable length addressing, perhaps the conn will contain this. no, looks like) 08h: size of array --Defn: dpio_conn_t-- 2070: 40E0: IP address (variable length) ?? --Sources-- dp_PING_PACKET_ID: (64 42) -- dp_PING_RESP_PACKET_ID: (64 43) dp_ERROR_PACKET_ID: (64 67) dp_SESSION_PACKET_ID: (64 53) dp_ENUMSESSIONS_PACKET_ID: (64 45) -- dp_ENUMPLAYERS_PACKET_ID: (64 50) -- dp_ADDPLAYER_PACKET_ID: (64 41) -- dp_DELPLAYER_PACKET_ID: (64 44) -- dp_KEEPALIVE_PACKET_ID: (64 4B) dp_SERVER_LOGOUT_PACKET_ID: (64 4F) dp_PLAYERLIST_PACKET_ID: (64 4C) -- dp_REQUEST_OPEN_PACKET_ID: (64 58) dp_INDIRECT_JOIN_PACKET_ID: (64 49) --- dpio_SYN_PACKET_ID: (64 59) 7 -- pDat = buffer + sizeof(dp_packetType_t) dpio_FIN_PACKET_ID: (64 46) dpio_DATA_PACKET_ID: (64 54) 8h 13h n/a dpio_ACK_PACKET_ID: (64 55) -- dpio_GATHER_PACKET_ID: (64 47) --- dp_JOIN_PACKET_ID: (64 4A) dp_LEAVE_PACKET_ID: (64 51) dp_SELECTED_SESSION_PACKET_ID (64 73) dp_VOTE_PACKET_ID (64 56) dp_VICTORY_PACKET_ID (64 57) dp_LOGIN_PACKET_ID (65 31) data: offset in dpio_data_packet_t at 3 srcAdr = pDat->data + 2 (5 bytes in?) pDat is of dpio_data_packet_t pAck is of dpio_ack_packet_t pktnum l ?? ?? ?? cb 3f 55 64 53 00 => 8 "Router DLL Test" cc 3f 56 64 4c 2f 01 6d 88 05 00 02 => 13h "Journeyman" 1a b3 02 64 4f 64:54:cc:3f:56:64:4c:2f 01:6d:88:05:00:02:00:03 00:6c:3d:c0:a8:01:7b:00 00:00:00:00:00:4a:6f:75 72:6e:65:79:6d:61:6e:00 00:00:00:00:00:00:00:00 00:00:00:00:00:05:00:be 98:c0:a8:01:6b:00:00:00 00:00:00:00:00:00:00:00 00:00:00:00:00:00:00:00 00:00:00:00:00:00:00:00 00:00:00 64 53 => 8h? y -- 64 4c => 13h? y (but in 2 cases this was the dest IP) 64 4c is a player entry!! 64 4c (off C short 2) => y 64 4c (off C short 1) => n -- 64 33 => NA? y 64 76 =? NA? ---- Two options: 1) 0xDEADBEEF the address on Tx, copy the src addr into any locations in packet with 0xDEADBEEF there 2) Analyze and map, see if I can sneak the src addr into a part of the commrxpktresp_t structure or commrxpktreq_t structure Observed behaviour: Src addr and embedded addr mismatch: -Is embedded addr's first 3 bytes == host IP's first 3 bytes? +Yes: IGNORE +No: Add No mismatch: Works ?? WHY ?? Does the caller of commRxPkt have access to pTcp? Where is it getting the host IP? If so, could I use the zero part of the host IP sockaddr_in struct to store the rxed packet's src IP, and override some part of anet to look there? Anet.dll would no longer work with other options but... it'd work for internet play.