From 5c206c89f04b1c523765ed0ee134d45b4d8faf4d Mon Sep 17 00:00:00 2001 From: Gregor Richards Date: Mon, 10 Oct 2016 11:52:54 -0400 Subject: [PATCH 1/2] Add a netplay advertisement server for LAN netplay detection. --- network/netplay/netplay.c | 33 +++++++++- network/netplay/netplay_common.c | 100 ++++++++++++++++++++++++++++++ network/netplay/netplay_private.h | 2 + 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index b9acb1bf5f..2c8cb5358f 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -54,6 +54,9 @@ enum static bool netplay_enabled = false; static bool netplay_is_client = false; +/* Used to advertise or request advertisement of Netplay */ +static int netplay_ad_fd = -1; + /* Used while Netplay is running */ static netplay_t *netplay_data = NULL; @@ -167,6 +170,28 @@ static bool init_tcp_socket(netplay_t *netplay, const char *server, return ret; } +static bool init_ad_socket(netplay_t *netplay, uint16_t port) +{ + int fd = socket_init((void**)&netplay->addr, port, NULL, SOCKET_TYPE_DATAGRAM); + + if (fd < 0) + goto error; + + if (!socket_bind(fd, (void*)netplay->addr)) + { + socket_close(fd); + goto error; + } + + netplay_ad_fd = fd; + + return true; + +error: + RARCH_ERR("Failed to initialize netplay advertisement socket.\n"); + return false; +} + static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) { if (!network_init()) @@ -1291,6 +1316,13 @@ bool netplay_pre_frame(netplay_t *netplay) netplay_try_init_serialization(netplay); } + /* Advertise our server if applicable */ + if (netplay->is_server) + { + if (netplay_ad_fd >= 0 || init_ad_socket(netplay, RARCH_DEFAULT_PORT)) + netplay_ad_server(netplay, netplay_ad_fd); + } + if (!netplay->net_cbs->pre_frame(netplay)) return false; @@ -1427,7 +1459,6 @@ void deinit_netplay(void) if (netplay_data) netplay_free(netplay_data); netplay_data = NULL; - netplay_enabled = false; } /** diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c index 782f8b9b42..f3c6401645 100644 --- a/network/netplay/netplay_common.c +++ b/network/netplay/netplay_common.c @@ -386,3 +386,103 @@ uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta) return 0; return encoding_crc32(0L, (const unsigned char*)delta->state, netplay->state_size); } + +/* + * AD PACKET FORMAT: + * + * Request: + * 1 word: RANQ (RetroArch Netplay Query) + * 1 word: Netplay protocol version + * + * Reply: + * 1 word : RANS (RetroArch Netplay Server) + * 1 word : Netplay protocol version + * 1 word : Port + * 8 words: RetroArch version + * 8 words: Nick + * 8 words: Core name + * 8 words: Core version + * 8 words: Content name (currently always blank) + */ + +#define AD_PACKET_MAX_SIZE 512 +#define AD_PACKET_STRING_SIZE 32 +#define AD_PACKET_STRING_WORDS (AD_PACKET_STRING_SIZE/sizeof(uint32_t)) +static uint32_t *ad_packet_buffer = NULL; + +bool netplay_ad_server(netplay_t *netplay, int ad_fd) +{ + fd_set fds; + struct timeval tmp_tv = {0}; + struct sockaddr their_addr; + socklen_t addr_size; + rarch_system_info_t *info = NULL; + size_t bufloc; + + if (!ad_packet_buffer) + { + ad_packet_buffer = malloc(AD_PACKET_MAX_SIZE); + if (!ad_packet_buffer) + return false; + } + + /* Check for any ad queries */ + while (1) + { + FD_ZERO(&fds); + FD_SET(ad_fd, &fds); + if (socket_select(ad_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) + break; + if (!FD_ISSET(ad_fd, &fds)) + break; + + /* Somebody queried, so check that it's valid */ + if (recvfrom(ad_fd, ad_packet_buffer, AD_PACKET_MAX_SIZE, 0, + &their_addr, &addr_size) >= 2*sizeof(uint32_t)) + { + /* Make sure it's a valid query */ + if (memcmp(ad_packet_buffer, "RANQ", 4)) + continue; + + /* For this version */ + if (ntohl(ad_packet_buffer[1]) != NETPLAY_PROTOCOL_VERSION) + continue; + + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &info); + + /* Now build our response */ + memset(ad_packet_buffer, 0, AD_PACKET_MAX_SIZE); + memcpy(ad_packet_buffer, "RANS", 4); + ad_packet_buffer[1] = htonl(NETPLAY_PROTOCOL_VERSION); + ad_packet_buffer[2] = htonl(netplay->tcp_port); + bufloc = 3; + strncpy((char *) (ad_packet_buffer + bufloc), + PACKAGE_VERSION, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + strncpy((char *) (ad_packet_buffer + bufloc), + netplay->nick, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + if (info) + { + strncpy((char *) (ad_packet_buffer + bufloc), + info->info.library_name, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + strncpy((char *) (ad_packet_buffer + bufloc), + info->info.library_version, AD_PACKET_STRING_SIZE); + bufloc += AD_PACKET_STRING_WORDS; + /* Blank content */ + bufloc += AD_PACKET_STRING_WORDS; + } + else + { + bufloc += 3*AD_PACKET_STRING_WORDS; + } + + /* And send it */ + sendto(ad_fd, ad_packet_buffer, bufloc*sizeof(uint32_t), 0, + &their_addr, addr_size); + } + } + + return true; +} diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index 0a7e38254d..4cfbe9273e 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -234,4 +234,6 @@ bool netplay_cmd_crc(netplay_t *netplay, struct delta_frame *delta); bool netplay_cmd_request_savestate(netplay_t *netplay); +bool netplay_ad_server(netplay_t *netplay, int ad_fd); + #endif From 2e432b005eb7e3d5a6bb5e1d2b41a720ad1b0d51 Mon Sep 17 00:00:00 2001 From: Gregor Richards Date: Mon, 10 Oct 2016 11:56:43 -0400 Subject: [PATCH 2/2] Casts for C++ compatibility. --- network/netplay/netplay_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c index f3c6401645..42a8a279f7 100644 --- a/network/netplay/netplay_common.c +++ b/network/netplay/netplay_common.c @@ -421,7 +421,7 @@ bool netplay_ad_server(netplay_t *netplay, int ad_fd) if (!ad_packet_buffer) { - ad_packet_buffer = malloc(AD_PACKET_MAX_SIZE); + ad_packet_buffer = (uint32_t *) malloc(AD_PACKET_MAX_SIZE); if (!ad_packet_buffer) return false; } @@ -438,7 +438,7 @@ bool netplay_ad_server(netplay_t *netplay, int ad_fd) /* Somebody queried, so check that it's valid */ if (recvfrom(ad_fd, ad_packet_buffer, AD_PACKET_MAX_SIZE, 0, - &their_addr, &addr_size) >= 2*sizeof(uint32_t)) + &their_addr, &addr_size) >= (ssize_t) (2*sizeof(uint32_t))) { /* Make sure it's a valid query */ if (memcmp(ad_packet_buffer, "RANQ", 4))