diff --git a/command.c b/command.c index abff46c982..304e13ffca 100644 --- a/command.c +++ b/command.c @@ -2363,7 +2363,8 @@ bool command_event(enum event_command cmd, void *data) #ifdef HAVE_NETWORKING if (!init_netplay( data, settings->netplay.server, - settings->netplay.port)) + settings->netplay.port, + settings->netplay.password)) return false; #endif break; diff --git a/configuration.c b/configuration.c index b869a84f66..8d3c52aaef 100644 --- a/configuration.c +++ b/configuration.c @@ -607,6 +607,7 @@ static int populate_settings_path(settings_t *settings, struct config_path_setti SETTING_PATH("core_updater_buildbot_assets_url", settings->network.buildbot_assets_url, false, NULL, true); #ifdef HAVE_NETWORKING SETTING_PATH("netplay_ip_address", settings->netplay.server, false, NULL, true); + SETTING_PATH("netplay_password", settings->netplay.password, false, NULL, true); #endif SETTING_PATH("recording_output_directory", global->record.output_dir, false, NULL, true); diff --git a/configuration.h b/configuration.h index 85093933a1..993a987968 100644 --- a/configuration.h +++ b/configuration.h @@ -405,6 +405,7 @@ typedef struct settings unsigned check_frames; bool swap_input; bool nat_traversal; + char password[127]; } netplay; #endif diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index a8830bab89..d13357c100 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -667,6 +667,14 @@ static bool netplay_get_cmd(netplay_t *netplay, return false; return ret; } + case NETPLAY_CONNECTION_PRE_PASSWORD: + { + bool ret = netplay_handshake_pre_password(netplay, connection, had_input); + if (connection->mode >= NETPLAY_CONNECTION_CONNECTED && + !send_cur_input(netplay, connection)) + return false; + return ret; + } case NETPLAY_CONNECTION_PRE_SYNC: { bool ret = netplay_handshake_pre_sync(netplay, connection, had_input); @@ -1920,6 +1928,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) * @direct_host : Netplay host discovered from scanning. * @server : IP address of server. * @port : Port of server. + * @password : Password required to connect. * @delay_frames : Amount of delay frames. * @check_frames : Frequency with which to check CRCs. * @cb : Libretro callbacks. @@ -1933,9 +1942,9 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) * Returns: new netplay handle. **/ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, - unsigned delay_frames, unsigned check_frames, - const struct retro_callbacks *cb, bool nat_traversal, - const char *nick, uint64_t quirks) + const char *password, unsigned delay_frames, unsigned check_frames, + const struct retro_callbacks *cb, bool nat_traversal, const char *nick, + uint64_t quirks) { netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); if (!netplay) @@ -1967,6 +1976,7 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, } strlcpy(netplay->nick, nick[0] ? nick : RARCH_DEFAULT_NICK, sizeof(netplay->nick)); + strlcpy(netplay->password, password ? password : "", sizeof(netplay->password)); if (!init_socket(netplay, direct_host, server, port)) { @@ -2452,7 +2462,11 @@ void deinit_netplay(void) } /** - * init_netplay: + * init_netplay + * @direct_host : Host to connect to directly, if applicable (client only) + * @server : server address to connect to (client only) + * @port : TCP port to host on/connect to + * @password : Password required to connect (server only) * * Initializes netplay. * @@ -2460,8 +2474,8 @@ void deinit_netplay(void) * * Returns: true (1) if successful, otherwise false (0). **/ - -bool init_netplay(void *direct_host, const char *server, unsigned port) +bool init_netplay(void *direct_host, const char *server, unsigned port, + const char *password) { struct retro_callbacks cbs = {0}; settings_t *settings = config_get_ptr(); @@ -2516,6 +2530,7 @@ bool init_netplay(void *direct_host, const char *server, unsigned port) netplay_is_client ? direct_host : NULL, netplay_is_client ? server : NULL, port ? port : RARCH_DEFAULT_PORT, + password, settings->netplay.delay_frames, settings->netplay.check_frames, &cbs, settings->netplay.nat_traversal, settings->username, quirks); diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 61e63a24a5..ea377ccc65 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -59,8 +59,10 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames); /** * init_netplay + * @direct_host : Host to connect to directly, if applicable (client only) * @server : server address to connect to (client only) * @port : TCP port to host on/connect to + * @password : Password required to connect (server only) * * Initializes netplay. * @@ -68,7 +70,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames); * * Returns: true (1) if successful, otherwise false (0). **/ -bool init_netplay(void *direct_host, const char *server, unsigned port); +bool init_netplay(void *direct_host, const char *server, unsigned port, const char *password); void deinit_netplay(void); diff --git a/network/netplay/netplay_common.c b/network/netplay/netplay_common.c index 0811e0f3b6..48058a4d51 100644 --- a/network/netplay/netplay_common.c +++ b/network/netplay/netplay_common.c @@ -27,8 +27,10 @@ #include "../../msg_hash.h" #include "../../configuration.h" #include "../../content.h" +#include "../../retroarch.h" #include "../../runloop.h" #include "../../version.h" +#include "../../menu/widgets/menu_input_dialog.h" /** * netplay_impl_magic: @@ -117,7 +119,7 @@ static bool netplay_endian_mismatch(uint32_t pma, uint32_t pmb) bool netplay_handshake_init_send(netplay_t *netplay, struct netplay_connection *connection) { uint32_t *content_crc_ptr = NULL; - uint32_t header[4] = {0}; + uint32_t header[5] = {0}; content_get_crc(&content_crc_ptr); @@ -125,6 +127,8 @@ bool netplay_handshake_init_send(netplay_t *netplay, struct netplay_connection * header[1] = htonl(*content_crc_ptr); header[2] = htonl(netplay_platform_magic()); header[3] = htonl(NETPLAY_COMPRESSION_SUPPORTED); + header[4] = htonl((netplay->is_server && netplay->password[0]) ? + NETPLAY_FLAG_PASSWORD_REQUIRED : 0); if (!netplay_send(&connection->send_packet_buffer, connection->fd, header, sizeof(header)) || @@ -140,6 +144,12 @@ struct nick_buf_s char nick[32]; }; +struct password_buf_s +{ + uint32_t cmd[2]; + char password[128]; +}; + #define RECV(buf, sz) \ recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), (sz), false); \ if (recvd >= 0 && recvd < (sz)) \ @@ -149,9 +159,30 @@ struct nick_buf_s } \ else if (recvd < 0) +static netplay_t *handshake_password_netplay; + +static void handshake_password(void *ignore, const char *line) +{ + uint32_t cmd[2]; + netplay_t *netplay = handshake_password_netplay; + struct netplay_connection *connection = &netplay->connections[0]; + + memset(netplay->password, 0, sizeof(netplay->password)); + strlcpy(netplay->password, line, sizeof(netplay->password)); + cmd[0] = ntohl(NETPLAY_CMD_PASSWORD); + cmd[1] = ntohl(sizeof(netplay->password)); + + netplay_send(&connection->send_packet_buffer, connection->fd, cmd, sizeof(cmd)) && + netplay_send(&connection->send_packet_buffer, connection->fd, netplay->password, sizeof(netplay->password)) && + netplay_send_flush(&connection->send_packet_buffer, connection->fd, false); + + menu_input_dialog_end(); + rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); +} + bool netplay_handshake_init(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { - uint32_t header[4] = {0}; + uint32_t header[5] = {0}; ssize_t recvd; char msg[512]; struct nick_buf_s nick_buf; @@ -231,6 +262,19 @@ bool netplay_handshake_init(netplay_t *netplay, struct netplay_connection *conne return false; } + /* If a password is demanded, ask for it */ + if (!netplay->is_server && (ntohl(header[4]) & NETPLAY_FLAG_PASSWORD_REQUIRED)) + { + menu_input_ctx_line_t line; + rarch_ctl(RARCH_CTL_MENU_RUNNING, NULL); + memset(&line, 0, sizeof(line)); + handshake_password_netplay = netplay; + line.label = "Enter Netplay server password:"; + line.label_setting = "no_setting"; + line.cb = handshake_password; + menu_input_dialog_start(&line); + } + /* Send our nick */ nick_buf.cmd[0] = htonl(NETPLAY_CMD_NICK); nick_buf.cmd[1] = htonl(sizeof(nick_buf.nick)); @@ -285,6 +329,59 @@ static void netplay_handshake_ready(netplay_t *netplay, struct netplay_connectio netplay->stall = 0; } +bool netplay_handshake_sync(netplay_t *netplay, struct netplay_connection *connection) +{ + /* If we're the server, now we send sync info */ + uint32_t cmd[5]; + uint32_t connected_players; + settings_t *settings = config_get_ptr(); + size_t i; + uint32_t device; + retro_ctx_memory_info_t mem_info; + + mem_info.id = RETRO_MEMORY_SAVE_RAM; + core_get_memory(&mem_info); + + /* Send basic sync info */ + cmd[0] = htonl(NETPLAY_CMD_SYNC); + cmd[1] = htonl(3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + mem_info.size); + cmd[2] = htonl(netplay->self_frame_count); + connected_players = netplay->connected_players; + if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) + connected_players |= 1<self_player; + cmd[3] = htonl(connected_players); + if (netplay->flip) + cmd[4] = htonl(netplay->flip_frame); + else + cmd[4] = htonl(0); + + if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, + sizeof(cmd))) + return false; + + /* Now send the device info */ + for (i = 0; i < MAX_USERS; i++) + { + device = htonl(settings->input.libretro_device[i]); + if (!netplay_send(&connection->send_packet_buffer, connection->fd, + &device, sizeof(device))) + return false; + } + + /* And finally, the SRAM */ + if (!netplay_send(&connection->send_packet_buffer, connection->fd, + mem_info.data, mem_info.size) || + !netplay_send_flush(&connection->send_packet_buffer, connection->fd, + false)) + return false; + + /* Now we're ready! */ + connection->mode = NETPLAY_CONNECTION_SPECTATING; + netplay_handshake_ready(netplay, connection); + + return true; +} + bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { struct nick_buf_s nick_buf; @@ -318,54 +415,17 @@ bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *c if (netplay->is_server) { - /* If we're the server, now we send sync info */ - uint32_t cmd[5]; - uint32_t connected_players; - settings_t *settings = config_get_ptr(); - size_t i; - uint32_t device; - retro_ctx_memory_info_t mem_info; - - mem_info.id = RETRO_MEMORY_SAVE_RAM; - core_get_memory(&mem_info); - - /* Send basic sync info */ - cmd[0] = htonl(NETPLAY_CMD_SYNC); - cmd[1] = htonl(3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + mem_info.size); - cmd[2] = htonl(netplay->self_frame_count); - connected_players = netplay->connected_players; - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) - connected_players |= 1<self_player; - cmd[3] = htonl(connected_players); - if (netplay->flip) - cmd[4] = htonl(netplay->flip_frame); - else - cmd[4] = htonl(0); - - if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, - sizeof(cmd))) - return false; - - /* Now send the device info */ - for (i = 0; i < MAX_USERS; i++) + if (netplay->password[0]) { - device = htonl(settings->input.libretro_device[i]); - if (!netplay_send(&connection->send_packet_buffer, connection->fd, - &device, sizeof(device))) + /* There's a password, so just put them in PRE_PASSWORD mode */ + connection->mode = NETPLAY_CONNECTION_PRE_PASSWORD; + } + else + { + if (!netplay_handshake_sync(netplay, connection)) return false; } - /* And finally, the SRAM */ - if (!netplay_send(&connection->send_packet_buffer, connection->fd, - mem_info.data, mem_info.size) || - !netplay_send_flush(&connection->send_packet_buffer, connection->fd, - false)) - return false; - - /* Now we're ready! */ - connection->mode = NETPLAY_CONNECTION_SPECTATING; - netplay_handshake_ready(netplay, connection); - } else { @@ -379,6 +439,44 @@ bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *c return true; } +bool netplay_handshake_pre_password(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) +{ + struct password_buf_s password_buf; + ssize_t recvd; + char msg[512]; + + msg[0] = '\0'; + + RECV(&password_buf, sizeof(password_buf)); + + /* Expecting only a nick command */ + if (recvd < 0 || + ntohl(password_buf.cmd[0]) != NETPLAY_CMD_PASSWORD || + ntohl(password_buf.cmd[1]) != sizeof(password_buf.password)) + { + if (netplay->is_server) + strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT), + sizeof(msg)); + else + strlcpy(msg, + msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST), + sizeof(msg)); + RARCH_ERR("%s\n", msg); + runloop_msg_queue_push(msg, 1, 180, false); + return false; + } + + if (strncmp(netplay->password, password_buf.password, sizeof(password_buf.password))) + return false; + + if (!netplay_handshake_sync(netplay, connection)) + return false; + + *had_input = true; + netplay_recv_flush(&connection->recv_packet_buffer); + return true; +} + bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { uint32_t cmd[2]; diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index bb8371307d..23f236fca2 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -79,6 +79,9 @@ #define NETPLAY_COMPRESSION_SUPPORTED 0 #endif +/* Netplay connection flags */ +#define NETPLAY_FLAG_PASSWORD_REQUIRED (1<<0) + enum netplay_cmd { /* Basic commands */ @@ -103,17 +106,20 @@ enum netplay_cmd /* Inform the other side of our nick (must be first command) */ NETPLAY_CMD_NICK = 0x0020, + /* Give the connection password */ + NETPLAY_CMD_PASSWORD = 0x0021, + /* Initial synchronization info (frame, sram, player info) */ - NETPLAY_CMD_SYNC = 0x0021, + NETPLAY_CMD_SYNC = 0x0022, /* Join spectator mode */ - NETPLAY_CMD_SPECTATE = 0x0022, + NETPLAY_CMD_SPECTATE = 0x0023, /* Join play mode */ - NETPLAY_CMD_PLAY = 0x0023, + NETPLAY_CMD_PLAY = 0x0024, /* Report player mode */ - NETPLAY_CMD_MODE = 0x0024, + NETPLAY_CMD_MODE = 0x0025, /* Loading and synchronization */ @@ -177,6 +183,7 @@ enum rarch_netplay_connection_mode /* Initialization: */ NETPLAY_CONNECTION_INIT, /* Waiting for header */ NETPLAY_CONNECTION_PRE_NICK, /* Waiting for nick */ + NETPLAY_CONNECTION_PRE_PASSWORD, /* Waiting for password */ NETPLAY_CONNECTION_PRE_SYNC, /* Waiting for sync */ /* Ready: */ @@ -268,6 +275,9 @@ struct netplay /* TCP connection for listening (server only) */ int listen_fd; + /* Password required to connect (server only) */ + char password[128]; + /* Our player number */ uint32_t self_player; @@ -394,6 +404,7 @@ void input_poll_net(void); * @direct_host : Netplay host discovered from scanning. * @server : IP address of server. * @port : Port of server. + * @password : Password required to connect. * @delay_frames : Amount of delay frames. * @check_frames : Frequency with which to check CRCs. * @cb : Libretro callbacks. @@ -407,9 +418,9 @@ void input_poll_net(void); * Returns: new netplay handle. **/ netplay_t *netplay_new(void *direct_host, const char *server, - uint16_t port, unsigned delay_frames, unsigned check_frames, - const struct retro_callbacks *cb, bool nat_traversal, - const char *nick, uint64_t quirks); + uint16_t port, const char *password, unsigned delay_frames, + unsigned check_frames, const struct retro_callbacks *cb, + bool nat_traversal, const char *nick, uint64_t quirks); /** * netplay_free: @@ -493,6 +504,7 @@ bool netplay_send_nickname(netplay_t *netplay, int fd); bool netplay_handshake_init_send(netplay_t *netplay, struct netplay_connection *connection); bool netplay_handshake_init(netplay_t *netplay, struct netplay_connection *connection, bool *had_input); bool netplay_handshake_pre_nick(netplay_t *netplay, struct netplay_connection *connection, bool *had_input); +bool netplay_handshake_pre_password(netplay_t *netplay, struct netplay_connection *connection, bool *had_input); bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *connection, bool *had_input); uint32_t netplay_impl_magic(void);