From 6c15c0de3d30baa6c26aafd2bbb2340b3240ea00 Mon Sep 17 00:00:00 2001 From: Gregor Richards Date: Wed, 5 Oct 2016 21:12:42 -0400 Subject: [PATCH] Support for architecture-dependence serialization quirks in Netplay As well as the implementation magic, we now send a platform magic in our connection header. If the core reports platform dependence and the platform magic differs relevantly, the connection will be refused. Since netplay_send_info (client handshake) and netplay_get_info (server handshake) were practically identical, they've also been merged into a single netplay_handshake. --- network/netplay/netplay.c | 4 + network/netplay/netplay_common.c | 250 +++++++++++++++--------------- network/netplay/netplay_net.c | 8 +- network/netplay/netplay_private.h | 18 +-- 4 files changed, 140 insertions(+), 140 deletions(-) diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index 9aea43cc03..3b0d36beff 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -1471,6 +1471,10 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port) quirks |= NETPLAY_QUIRK_NO_TRANSMISSION; if (serialization_quirks & NETPLAY_QUIRK_MAP_INITIALIZATION) quirks |= NETPLAY_QUIRK_INITIALIZATION; + if (serialization_quirks & NETPLAY_QUIRK_MAP_ENDIAN_DEPENDENT) + quirks |= NETPLAY_QUIRK_ENDIAN_DEPENDENT; + if (serialization_quirks & NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT) + quirks |= NETPLAY_QUIRK_PLATFORM_DEPENDENT; if (netplay_is_client) { diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c index 98ece3813f..782f8b9b42 100644 --- a/network/netplay/netplay_common.c +++ b/network/netplay/netplay_common.c @@ -125,24 +125,61 @@ uint32_t netplay_impl_magic(void) return res; } -bool netplay_send_info(netplay_t *netplay) +/** + * netplay_platform_magic + * + * Just enough info to tell us if our platforms mismatch: Endianness and a + * couple of type sizes. + * + * Format: + * bit 31: Reserved + * bit 30: 1 for big endian + * bits 29-15: sizeof(size_t) + * bits 14-0: sizeof(long) + */ +static uint32_t netplay_platform_magic(void) +{ + uint32_t ret = + ((1 == htonl(1)) << 30) + |(sizeof(size_t) << 15) + |(sizeof(long)); + return ret; +} + +/** + * netplay_endian_mismatch + * + * Do the platform magics mismatch on endianness? + */ +static bool netplay_endian_mismatch(uint32_t pma, uint32_t pmb) +{ + uint32_t ebit = (1<<30); + return (pma & ebit) != (pmb & ebit); +} + +bool netplay_handshake(netplay_t *netplay) { unsigned sram_size, remote_sram_size; retro_ctx_memory_info_t mem_info; char msg[512] = {0}; uint32_t *content_crc_ptr = NULL; void *sram = NULL; - uint32_t header[3] = {0}; + uint32_t header[4] = {0}; size_t i; + uint32_t local_pmagic, remote_pmagic; + bool is_server = netplay->is_server; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); content_get_crc(&content_crc_ptr); + local_pmagic = netplay_platform_magic(); + header[0] = htonl(*content_crc_ptr); header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); + header[3] = htonl(local_pmagic); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) return false; @@ -176,53 +213,104 @@ bool netplay_send_info(netplay_t *netplay) RARCH_WARN("Content SRAM sizes do not correspond.\n"); } - if (!netplay_send_nickname(netplay, netplay->fd)) + /* We only care about platform magic if our core is quirky */ + remote_pmagic = ntohl(header[3]); + if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) && + netplay_endian_mismatch(local_pmagic, remote_pmagic)) { - RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST)); + RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n"); + return false; + } + if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) && + (local_pmagic != remote_pmagic)) + { + RARCH_ERR("Platform mismatch with a platform-sensitive core.\n"); return false; } - /* Get SRAM data from User 1. */ - if (sram_size != 0 && sram_size == remote_sram_size) + /* Client sends nickname first, server replies with nickname */ + if (!is_server) { - sram = mem_info.data; - - if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) + if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); + msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST)); + return false; + } + } + + if (!netplay_get_nickname(netplay, netplay->fd)) + { + if (is_server) + RARCH_ERR("%s\n", + msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT)); + else + RARCH_ERR("%s\n", + msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST)); + return false; + } + + if (is_server) + { + if (!netplay_send_nickname(netplay, netplay->fd)) + { + RARCH_ERR("%s\n", + msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); + return false; + } + } + + /* Server sends SRAM, client receives */ + if (is_server) + { + /* Send SRAM data to the client */ + sram = mem_info.data; + + if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false)) + { + RARCH_ERR("%s\n", + msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT)); return false; } } - else if (remote_sram_size != 0) + else { - /* We can't load this, but we still need to get rid of the data */ - uint32_t quickbuf; - while (remote_sram_size > 0) + /* Get SRAM data from User 1. */ + if (sram_size != 0 && sram_size == remote_sram_size) { - if (!socket_receive_all_blocking(netplay->fd, &quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size)) + sram = mem_info.data; + + if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); return false; } - if (remote_sram_size > sizeof(uint32_t)) - remote_sram_size -= sizeof(uint32_t); - else - remote_sram_size = 0; + + } + else if (remote_sram_size != 0) + { + /* We can't load this, but we still need to get rid of the data */ + uint32_t quickbuf; + while (remote_sram_size > 0) + { + if (!socket_receive_all_blocking(netplay->fd, &quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size)) + { + RARCH_ERR("%s\n", + msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); + return false; + } + if (remote_sram_size > sizeof(uint32_t)) + remote_sram_size -= sizeof(uint32_t); + else + remote_sram_size = 0; + } + } } - if (!netplay_get_nickname(netplay, netplay->fd)) - { - RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST)); - return false; - } - /* Reset our frame count so it's consistent with the server */ netplay->self_frame_count = netplay->other_frame_count = 0; netplay->read_frame_count = 1; @@ -242,111 +330,19 @@ bool netplay_send_info(netplay_t *netplay) } } - snprintf(msg, sizeof(msg), "%s: \"%s\"", - msg_hash_to_str(MSG_CONNECTED_TO), - netplay->other_nick); - RARCH_LOG("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false); - - return true; -} - -bool netplay_get_info(netplay_t *netplay) -{ - unsigned sram_size, remote_sram_size; - uint32_t header[3]; - retro_ctx_memory_info_t mem_info; - uint32_t *content_crc_ptr = NULL; - const void *sram = NULL; - size_t i; - - /* FIXME: There's a huge amount of duplication between send_info and - * get_info */ - - mem_info.id = RETRO_MEMORY_SAVE_RAM; - - core_get_memory(&mem_info); - content_get_crc(&content_crc_ptr); - - header[0] = htonl(*content_crc_ptr); - header[1] = htonl(netplay_impl_magic()); - header[2] = htonl(mem_info.size); - - if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) - return false; - - if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) + if (is_server) { - RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT)); - return false; + netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); } - - if (*content_crc_ptr != ntohl(header[0])) + else { - RARCH_ERR("%s\n", msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER)); - return false; + snprintf(msg, sizeof(msg), "%s: \"%s\"", + msg_hash_to_str(MSG_CONNECTED_TO), + netplay->other_nick); + RARCH_LOG("%s\n", msg); + runloop_msg_queue_push(msg, 1, 180, false); } - if (netplay_impl_magic() != ntohl(header[1])) - { - RARCH_ERR("Implementations differ, make sure you're using exact same " - "libretro implementations and RetroArch version.\n"); - return false; - } - - sram_size = mem_info.size; - remote_sram_size = ntohl(header[2]); - if (sram_size != 0 && remote_sram_size != 0 && sram_size != remote_sram_size) - { - RARCH_WARN("Content SRAM sizes do not correspond.\n"); - } - - if (!netplay_get_nickname(netplay, netplay->fd)) - { - RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT)); - return false; - } - - /* Send SRAM data to our User 2. */ - sram = mem_info.data; - - if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false)) - { - RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT)); - return false; - } - - if (!netplay_send_nickname(netplay, netplay->fd)) - { - RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); - return false; - } - - /* Reset our frame count so it's consistent with the client */ - netplay->self_frame_count = netplay->other_frame_count = 0; - netplay->read_frame_count = 1; - for (i = 0; i < netplay->buffer_size; i++) - { - netplay->buffer[i].used = false; - if (i == netplay->self_ptr) - { - netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0); - netplay->buffer[i].have_remote = true; - netplay->other_ptr = i; - netplay->read_ptr = NEXT_PTR(i); - } - else - { - netplay->buffer[i].used = false; - } - } - - netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); - return true; } diff --git a/network/netplay/netplay_net.c b/network/netplay/netplay_net.c index 93f8e18c29..c594587e0f 100644 --- a/network/netplay/netplay_net.c +++ b/network/netplay/netplay_net.c @@ -149,15 +149,15 @@ static bool netplay_net_pre_frame(netplay_t *netplay) RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n"); #endif - /* Connection header */ - if (netplay_get_info(netplay)) + /* Establish the connection */ + if (netplay_handshake(netplay)) { netplay->has_connection = true; /* Send them the savestate */ if (!(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION))) { - netplay_load_savestate(netplay, NULL, true); + netplay->force_send_savestate = true; } else { @@ -332,7 +332,7 @@ static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames) { if (!netplay_is_server(netplay)) { - if (!netplay_send_info(netplay)) + if (!netplay_handshake(netplay)) return false; netplay->has_connection = true; } diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index ae80b5eb53..0a7e38254d 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -35,7 +35,7 @@ #define MAX_SPECTATORS 16 #define RARCH_DEFAULT_PORT 55435 -#define NETPLAY_PROTOCOL_VERSION 1 +#define NETPLAY_PROTOCOL_VERSION 2 #define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1) #define NEXT_PTR(x) ((x + 1) % netplay->buffer_size) @@ -45,6 +45,8 @@ #define NETPLAY_QUIRK_NO_SAVESTATES (1<<0) #define NETPLAY_QUIRK_NO_TRANSMISSION (1<<1) #define NETPLAY_QUIRK_INITIALIZATION (1<<2) +#define NETPLAY_QUIRK_ENDIAN_DEPENDENT (1<<3) +#define NETPLAY_QUIRK_PLATFORM_DEPENDENT (1<<4) /* Mapping of serialization quirks to netplay quirks. */ #define NETPLAY_QUIRK_MAP_UNDERSTOOD \ @@ -56,11 +58,13 @@ #define NETPLAY_QUIRK_MAP_NO_SAVESTATES \ (RETRO_SERIALIZATION_QUIRK_INCOMPLETE) #define NETPLAY_QUIRK_MAP_NO_TRANSMISSION \ - (RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION \ - |RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT \ - |RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT) + (RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION) #define NETPLAY_QUIRK_MAP_INITIALIZATION \ (RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE) +#define NETPLAY_QUIRK_MAP_ENDIAN_DEPENDENT \ + (RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT) +#define NETPLAY_QUIRK_MAP_PLATFORM_DEPENDENT \ + (RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT) struct delta_frame { @@ -214,14 +218,10 @@ bool netplay_get_nickname(netplay_t *netplay, int fd); bool netplay_send_nickname(netplay_t *netplay, int fd); -bool netplay_send_info(netplay_t *netplay); +bool netplay_handshake(netplay_t *netplay); uint32_t netplay_impl_magic(void); -bool netplay_send_info(netplay_t *netplay); - -bool netplay_get_info(netplay_t *netplay); - bool netplay_is_server(netplay_t* netplay); bool netplay_is_spectate(netplay_t* netplay);