(Netplay) Support for banning clients (#14146)

This commit is contained in:
Cthulhu-throwaway 2022-07-07 08:40:38 -03:00 committed by GitHub
parent 6318e5da5e
commit c6e0e7e198
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 404 additions and 118 deletions

View File

@ -12964,6 +12964,10 @@ MSG_HASH(
MSG_NETPLAY_HOST_FULL, MSG_NETPLAY_HOST_FULL,
"Netplay host full" "Netplay host full"
) )
MSG_HASH(
MSG_NETPLAY_BANNED,
"You are banned from this host"
)
MSG_HASH( MSG_HASH(
MSG_FAILED_TO_RECEIVE_HEADER_FROM_HOST, MSG_FAILED_TO_RECEIVE_HEADER_FROM_HOST,
"Failed to receive header from host" "Failed to receive header from host"

View File

@ -182,6 +182,7 @@ enum msg_hash_enums
MSG_FAILED_TO_CONNECT_TO_CLIENT, MSG_FAILED_TO_CONNECT_TO_CLIENT,
MSG_FAILED_TO_CONNECT_TO_HOST, MSG_FAILED_TO_CONNECT_TO_HOST,
MSG_NETPLAY_HOST_FULL, MSG_NETPLAY_HOST_FULL,
MSG_NETPLAY_BANNED,
MSG_FAILED_TO_RECEIVE_HEADER_FROM_HOST, MSG_FAILED_TO_RECEIVE_HEADER_FROM_HOST,
MSG_NETPLAY_FAILED, MSG_NETPLAY_FAILED,
MSG_NETPLAY_UNSUPPORTED, MSG_NETPLAY_UNSUPPORTED,

View File

@ -73,7 +73,8 @@ enum rarch_netplay_ctl_state
RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL, RARCH_NETPLAY_CTL_FINISHED_NAT_TRAVERSAL,
RARCH_NETPLAY_CTL_DESYNC_PUSH, RARCH_NETPLAY_CTL_DESYNC_PUSH,
RARCH_NETPLAY_CTL_DESYNC_POP, RARCH_NETPLAY_CTL_DESYNC_POP,
RARCH_NETPLAY_CTL_KICK_CLIENT RARCH_NETPLAY_CTL_KICK_CLIENT,
RARCH_NETPLAY_CTL_BAN_CLIENT
}; };
/* The current status of a connection */ /* The current status of a connection */

View File

@ -127,6 +127,7 @@
#define NETPLAY_MAGIC 0x52414E50 /* RANP */ #define NETPLAY_MAGIC 0x52414E50 /* RANP */
#define FULL_MAGIC 0x46554C4C /* FULL */ #define FULL_MAGIC 0x46554C4C /* FULL */
#define POKE_MAGIC 0x504F4B45 /* POKE */ #define POKE_MAGIC 0x504F4B45 /* POKE */
#define BANNED_MAGIC 0x44454E59 /* DENY */
/* Discovery magics */ /* Discovery magics */
#define DISCOVERY_QUERY_MAGIC 0x52414E51 /* RANQ */ #define DISCOVERY_QUERY_MAGIC 0x52414E51 /* RANQ */
@ -135,6 +136,7 @@
/* MITM magics */ /* MITM magics */
#define MITM_SESSION_MAGIC 0x52415453 /* RATS */ #define MITM_SESSION_MAGIC 0x52415453 /* RATS */
#define MITM_LINK_MAGIC 0x5241544C /* RATL */ #define MITM_LINK_MAGIC 0x5241544C /* RATL */
#define MITM_ADDR_MAGIC 0x52415441 /* RATA */
#define MITM_PING_MAGIC 0x52415450 /* RATP */ #define MITM_PING_MAGIC 0x52415450 /* RATP */
#define ANNOUNCE_FRAMES 1200 #define ANNOUNCE_FRAMES 1200
@ -934,15 +936,16 @@ bool netplay_handshake_init(netplay_t *netplay,
if (netplay->is_server) if (netplay->is_server)
{ {
/* Poking the server for information? Just disconnect */ switch (netplay_magic)
if (netplay_magic == POKE_MAGIC)
{ {
send_info_and_disconnect(netplay, connection); case NETPLAY_MAGIC:
return true; break;
} case POKE_MAGIC:
else if (netplay_magic != NETPLAY_MAGIC) /* Poking the server for information? Just disconnect */
{ send_info_and_disconnect(netplay, connection);
return false; return true;
default:
return false;
} }
} }
else else
@ -950,17 +953,22 @@ bool netplay_handshake_init(netplay_t *netplay,
/* Only the client is able to estimate latency at this point. */ /* Only the client is able to estimate latency at this point. */
SET_PING(connection) SET_PING(connection)
if (netplay_magic == FULL_MAGIC) switch (netplay_magic)
{ {
dmsg = msg_hash_to_str(MSG_NETPLAY_HOST_FULL); case NETPLAY_MAGIC:
RARCH_ERR("[Netplay] %s\n", dmsg); break;
goto error; case FULL_MAGIC:
} dmsg = msg_hash_to_str(MSG_NETPLAY_HOST_FULL);
else if (netplay_magic != NETPLAY_MAGIC) RARCH_ERR("[Netplay] %s\n", dmsg);
{ goto error;
dmsg = msg_hash_to_str(MSG_NETPLAY_NOT_RETROARCH); case BANNED_MAGIC:
RARCH_ERR("[Netplay] %s\n", dmsg); dmsg = msg_hash_to_str(MSG_NETPLAY_BANNED);
goto error; RARCH_ERR("[Netplay] %s\n", dmsg);
goto error;
default:
dmsg = msg_hash_to_str(MSG_NETPLAY_NOT_RETROARCH);
RARCH_ERR("[Netplay] %s\n", dmsg);
goto error;
} }
} }
@ -2483,7 +2491,7 @@ void netplay_recv_flush(struct socket_buffer *sbuf)
sbuf->start = sbuf->read; sbuf->start = sbuf->read;
} }
static bool netplay_full(netplay_t *netplay, int sockfd) static bool netplay_full(netplay_t *netplay, int fd)
{ {
size_t i; size_t i;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
@ -2504,23 +2512,19 @@ static bool netplay_full(netplay_t *netplay, int sockfd)
if (total >= max_connections) if (total >= max_connections)
{ {
/* We send a header to let the client /* We send a header to let the client know we are full. */
know we are full */
uint32_t header[6]; uint32_t header[6];
/* The only parameter that we need to set is /* The only parameter that we need to set is netplay magic;
netplay magic; we set the protocol version parameter too
we set the protocol version parameter for backwards compatibility. */
too for backwards compatibility */
memset(header, 0, sizeof(header)); memset(header, 0, sizeof(header));
header[0] = htonl(FULL_MAGIC); header[0] = htonl(FULL_MAGIC);
header[4] = htonl(HIGH_NETPLAY_PROTOCOL_VERSION); header[4] = htonl(HIGH_NETPLAY_PROTOCOL_VERSION);
/* The kernel might close the socket before /* The kernel might close the socket before sending our data.
sending our data. This is fine; the header is just a warning for the client. */
This is fine; the header is just a warning socket_send_all_nonblocking(fd, header, sizeof(header), true);
for the client. */
socket_send_all_nonblocking(sockfd, header, sizeof(header), true);
return true; return true;
} }
@ -2528,6 +2532,35 @@ static bool netplay_full(netplay_t *netplay, int sockfd)
return false; return false;
} }
static bool netplay_banned(netplay_t *netplay, int fd, netplay_address_t *addr)
{
size_t i;
for (i = 0; i < netplay->ban_list.size; i++)
{
if (!memcmp(addr, &netplay->ban_list.list[i], sizeof(*addr)))
{
/* We send a header to let the client know it's banned. */
uint32_t header[6];
/* The only parameter that we need to set is netplay magic;
we set the protocol version parameter too
for backwards compatibility. */
memset(header, 0, sizeof(header));
header[0] = htonl(BANNED_MAGIC);
header[4] = htonl(HIGH_NETPLAY_PROTOCOL_VERSION);
/* The kernel might close the socket before sending our data.
This is fine; the header is just a warning for the client. */
socket_send_all_nonblocking(fd, header, sizeof(header), true);
return true;
}
}
return false;
}
/** /**
* netplay_cmd_crc * netplay_cmd_crc
* *
@ -3107,13 +3140,15 @@ static void netplay_handle_frame_hash(netplay_t *netplay,
/** /**
* handle_connection * handle_connection
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
* @addr : value of pointer is set to the address of the peer on a completed connection
* @error : value of pointer is set to true if a critical error occurs * @error : value of pointer is set to true if a critical error occurs
* *
* Accepts a new client connection. * Accepts a new client connection.
* *
* Returns: fd of a new connection or -1 if there was no new connection. * Returns: fd of a new connection or -1 if there was no new connection.
*/ */
static int handle_connection(netplay_t *netplay, bool *error) static int handle_connection(netplay_t *netplay, netplay_address_t *addr,
bool *error)
{ {
int new_fd; int new_fd;
struct sockaddr_storage their_addr; struct sockaddr_storage their_addr;
@ -3129,6 +3164,40 @@ static int handle_connection(netplay_t *netplay, bool *error)
return -1; return -1;
} }
#define INET_TO_NETPLAY(in_addr, out_addr) \
{ \
uint16_t *preffix = (uint16_t*)&(out_addr)->addr[10]; \
uint32_t *addr4 = (uint32_t*)&(out_addr)->addr[12]; \
memset(&(out_addr)->addr[0], 0, 10); \
*preffix = 0xffff; \
memcpy(addr4, &((struct sockaddr_in*)(in_addr))->sin_addr, \
sizeof(*addr4)); \
}
#ifdef HAVE_INET6
switch (their_addr.ss_family)
{
case AF_INET:
/* For IPv4, we need to write the address as:
::ffff:a.b.c.d */
INET_TO_NETPLAY(&their_addr, addr)
break;
case AF_INET6:
/* For IPv6, we just copy the address data. */
memcpy(addr, &((struct sockaddr_in6*)&their_addr)->sin6_addr,
sizeof(*addr));
break;
default:
/* We can't handle this. Not a critical error though. */
socket_close(new_fd);
return -1;
}
#else
INET_TO_NETPLAY(&their_addr, addr)
#endif
#undef INET_TO_NETPLAY
/* Set the socket nonblocking */ /* Set the socket nonblocking */
if (!socket_nonblock(new_fd)) if (!socket_nonblock(new_fd))
{ {
@ -3164,6 +3233,7 @@ static bool netplay_tunnel_connect(int fd, const struct addrinfo *addr)
/** /**
* handle_mitm_connection * handle_mitm_connection
* @netplay : pointer to netplay object * @netplay : pointer to netplay object
* @addr : value of pointer is set to the address of the peer on a completed connection
* @error : value of pointer is set to true if a critical error occurs * @error : value of pointer is set to true if a critical error occurs
* *
* Do three things here. * Do three things here.
@ -3175,18 +3245,16 @@ static bool netplay_tunnel_connect(int fd, const struct addrinfo *addr)
* *
* Returns: fd of a new completed connection or -1 if no connection was completed. * Returns: fd of a new completed connection or -1 if no connection was completed.
*/ */
static int handle_mitm_connection(netplay_t *netplay, bool *error) static int handle_mitm_connection(netplay_t *netplay, netplay_address_t *addr,
bool *error)
{ {
size_t i; size_t i;
void* recv_buf;
size_t recv_len;
ssize_t recvd;
int new_fd = -1; int new_fd = -1;
retro_time_t ctime = cpu_features_get_time_usec(); retro_time_t ctime = cpu_features_get_time_usec();
for (i = 0; i < NETPLAY_MITM_MAX_PENDING; i++) for (i = 0; i < ARRAY_SIZE(netplay->mitm_handler->pending); i++)
{ {
int fd = netplay->mitm_pending->fds[i]; int fd = netplay->mitm_handler->pending[i].fd;
if (fd >= 0) if (fd >= 0)
{ {
@ -3199,29 +3267,37 @@ static int handle_mitm_connection(netplay_t *netplay, bool *error)
} }
else if (ready) else if (ready)
{ {
/* Connection is ready. /* Connection is ready. */
Send the linking id. */ mitm_id_t *id = &netplay->mitm_handler->pending[i].id;
mitm_id_t *lid = &netplay->mitm_pending->ids[i];
if (socket_send_all_nonblocking(fd, lid, sizeof(*lid), true) == /* Check to see if the tunnel server has already reported
sizeof(*lid)) the peer's address. */
if (!netplay->mitm_handler->pending[i].has_addr)
continue;
/* Now send the linking id. */
if (socket_send_all_nonblocking(fd, id, sizeof(*id), true) ==
sizeof(*id))
{ {
new_fd = fd; new_fd = fd;
memcpy(addr, &netplay->mitm_handler->pending[i].addr,
sizeof(*addr));
RARCH_LOG("[Netplay] Tunnel link connection completed.\n"); RARCH_LOG("[Netplay] Tunnel link connection completed.\n");
} }
else else
{ {
/* We couldn't send our id in one call. Assume error. */ /* We couldn't send the peer id in one call. Assume error. */
socket_close(fd); socket_close(fd);
RARCH_ERR("[Netplay] Tunnel link connection failed after handshake.\n"); RARCH_ERR("[Netplay] Tunnel link connection failed after handshake.\n");
} }
netplay->mitm_pending->fds[i] = -1;
netplay->mitm_handler->pending[i].fd = -1;
break; break;
} }
else else
{ {
/* Check if the connection timeouted. */ /* Check if the connection timeouted. */
retro_time_t timeout = netplay->mitm_pending->timeouts[i]; retro_time_t timeout = netplay->mitm_handler->pending[i].timeout;
if (ctime < timeout) if (ctime < timeout)
continue; continue;
@ -3230,65 +3306,121 @@ static int handle_mitm_connection(netplay_t *netplay, bool *error)
} }
socket_close(fd); socket_close(fd);
netplay->mitm_pending->fds[i] = -1; netplay->mitm_handler->pending[i].fd = -1;
} }
} }
recv_buf = ((uint8_t*)&netplay->mitm_pending->id_buf) + if (netplay->mitm_handler->id_recvd < sizeof(netplay->mitm_handler->id_buf))
netplay->mitm_pending->id_recvd;
if (netplay->mitm_pending->id_recvd <
sizeof(netplay->mitm_pending->id_buf.magic))
recv_len = sizeof(netplay->mitm_pending->id_buf.magic) -
netplay->mitm_pending->id_recvd;
else
recv_len = sizeof(netplay->mitm_pending->id_buf) -
netplay->mitm_pending->id_recvd;
recvd = socket_receive_all_nonblocking(netplay->listen_fd,
error, recv_buf, recv_len);
if (recvd < 0 || (size_t)recvd > recv_len)
{ {
RARCH_ERR("[Netplay] Tunnel server error.\n"); /* We haven't received a full id yet. */
goto critical_failure; ssize_t recvd;
size_t len;
/* Size depends on whether we've received the magic or not. */
if (netplay->mitm_handler->id_recvd <
sizeof(netplay->mitm_handler->id_buf.magic))
len = sizeof(netplay->mitm_handler->id_buf.magic) -
netplay->mitm_handler->id_recvd;
else
len = sizeof(netplay->mitm_handler->id_buf) -
netplay->mitm_handler->id_recvd;
recvd = socket_receive_all_nonblocking(netplay->listen_fd, error,
(((uint8_t*)&netplay->mitm_handler->id_buf) +
netplay->mitm_handler->id_recvd),
len);
if (recvd < 0 || (size_t)recvd > len)
{
RARCH_ERR("[Netplay] Tunnel server error.\n");
goto critical_failure;
}
netplay->mitm_handler->id_recvd += recvd;
}
else
{
/* We've received a full id, receive any additional data now. */
switch (ntohl(netplay->mitm_handler->id_buf.magic))
{
case MITM_ADDR_MAGIC:
{
ssize_t recvd;
size_t len = sizeof(netplay->mitm_handler->addr_buf) -
netplay->mitm_handler->addr_recvd;
recvd = socket_receive_all_nonblocking(netplay->listen_fd, error,
(((uint8_t*)&netplay->mitm_handler->addr_buf) +
netplay->mitm_handler->addr_recvd),
len);
if (recvd < 0 || (size_t)recvd > len)
{
RARCH_ERR("[Netplay] Tunnel server error.\n");
goto critical_failure;
}
netplay->mitm_handler->addr_recvd += recvd;
break;
}
default:
RARCH_ERR("[Netplay] Received unknown additional data from tunnel server.\n");
goto critical_failure;
}
} }
netplay->mitm_pending->id_recvd += recvd; if (netplay->mitm_handler->id_recvd >=
if (netplay->mitm_pending->id_recvd >= sizeof(netplay->mitm_handler->id_buf.magic))
sizeof(netplay->mitm_pending->id_buf.magic))
{ {
switch (ntohl(netplay->mitm_pending->id_buf.magic)) switch (ntohl(netplay->mitm_handler->id_buf.magic))
{ {
case MITM_LINK_MAGIC: case MITM_LINK_MAGIC:
{ {
if (netplay->mitm_pending->id_recvd < if (netplay->mitm_handler->id_recvd <
sizeof(netplay->mitm_pending->id_buf)) sizeof(netplay->mitm_handler->id_buf))
break; break;
netplay->mitm_pending->id_recvd = 0; netplay->mitm_handler->id_recvd = 0;
/* Find a free spot to allocate this connection. */ /* Find a free spot to allocate this connection. */
for (i = 0; i < NETPLAY_MITM_MAX_PENDING; i++) for (i = 0; i < ARRAY_SIZE(netplay->mitm_handler->pending); i++)
if (netplay->mitm_pending->fds[i] < 0) if (netplay->mitm_handler->pending[i].fd < 0)
break; break;
if (i < NETPLAY_MITM_MAX_PENDING) if (i < ARRAY_SIZE(netplay->mitm_handler->pending))
{ {
int fd = socket( int fd = socket(
netplay->mitm_pending->addr->ai_family, netplay->mitm_handler->addr->ai_family,
netplay->mitm_pending->addr->ai_socktype, netplay->mitm_handler->addr->ai_socktype,
netplay->mitm_pending->addr->ai_protocol netplay->mitm_handler->addr->ai_protocol
); );
if (fd >= 0) if (fd >= 0)
{ {
if (netplay_tunnel_connect(fd, netplay->mitm_pending->addr)) if (netplay_tunnel_connect(fd, netplay->mitm_handler->addr))
{ {
netplay->mitm_pending->fds[i] = fd; mitm_id_t req_addr;
memcpy(&netplay->mitm_pending->ids[i],
&netplay->mitm_pending->id_buf, /* Make sure to request the address of this peer. */
sizeof(*netplay->mitm_pending->ids)); req_addr.magic = htonl(MITM_ADDR_MAGIC);
/* 30 seconds */ memcpy(req_addr.unique,
netplay->mitm_pending->timeouts[i] = ctime + 30000000; netplay->mitm_handler->id_buf.unique,
sizeof(req_addr.unique));
if (socket_send_all_nonblocking(netplay->listen_fd,
&req_addr, sizeof(req_addr), true) !=
sizeof(req_addr))
{
socket_close(fd);
RARCH_ERR("[Netplay] Tunnel peer address request failed.\n");
goto critical_failure;
}
/* Now queue the connection. */
netplay->mitm_handler->pending[i].fd = fd;
netplay->mitm_handler->pending[i].has_addr = false;
memcpy(&netplay->mitm_handler->pending[i].id,
&netplay->mitm_handler->id_buf,
sizeof(netplay->mitm_handler->pending[i].id));
netplay->mitm_handler->pending[i].timeout =
ctime + 15000000; /* 15 seconds */
RARCH_LOG("[Netplay] Queued tunnel link connection.\n"); RARCH_LOG("[Netplay] Queued tunnel link connection.\n");
} }
else else
@ -3309,16 +3441,53 @@ static int handle_mitm_connection(netplay_t *netplay, bool *error)
break; break;
} }
case MITM_ADDR_MAGIC:
{
if (netplay->mitm_handler->id_recvd <
sizeof(netplay->mitm_handler->id_buf))
break;
if (netplay->mitm_handler->addr_recvd <
sizeof(netplay->mitm_handler->addr_buf))
break;
netplay->mitm_handler->id_recvd = 0;
netplay->mitm_handler->addr_recvd = 0;
/* Find the pending connection this address belongs to.
If we can't find a pending connection, just ignore this data. */
for (i = 0; i < ARRAY_SIZE(netplay->mitm_handler->pending); i++)
{
if (netplay->mitm_handler->pending[i].fd < 0)
continue;
if (netplay->mitm_handler->pending[i].has_addr)
continue;
if (!memcmp(netplay->mitm_handler->pending[i].id.unique,
netplay->mitm_handler->id_buf.unique,
sizeof(netplay->mitm_handler->pending[i].id.unique)))
{
/* Now copy the received address into the
correct pending connection. */
memcpy(&netplay->mitm_handler->pending[i].addr,
&netplay->mitm_handler->addr_buf,
sizeof(netplay->mitm_handler->pending[i].addr));
netplay->mitm_handler->pending[i].has_addr = true;
break;
}
}
break;
}
case MITM_PING_MAGIC: case MITM_PING_MAGIC:
{ {
/* Tunnel server requested for us to reply to a ping request. */ /* Tunnel server requested for us to reply to a ping request. */
void *ping = &netplay->mitm_pending->id_buf.magic; void *ping = &netplay->mitm_handler->id_buf.magic;
size_t len = sizeof(netplay->mitm_pending->id_buf.magic); size_t len = sizeof(netplay->mitm_handler->id_buf.magic);
netplay->mitm_pending->id_recvd = 0; netplay->mitm_handler->id_recvd = 0;
if (socket_send_all_nonblocking(netplay->listen_fd, if (socket_send_all_nonblocking(netplay->listen_fd,
ping, len, true) != len) ping, len, true) != len)
{ {
/* We couldn't send our ping reply in one call. Assume error. */ /* We couldn't send our ping reply in one call. Assume error. */
RARCH_ERR("[Netplay] Tunnel ping reply failed.\n"); RARCH_ERR("[Netplay] Tunnel ping reply failed.\n");
@ -3487,13 +3656,14 @@ static bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect)
if (netplay->is_server) if (netplay->is_server)
{ {
int new_fd = -1; int new_fd = -1;
bool server_error = false; netplay_address_t new_addr = {0};
bool server_error = false;
if (netplay->mitm_pending) if (netplay->mitm_handler)
new_fd = handle_mitm_connection(netplay, &server_error); new_fd = handle_mitm_connection(netplay, &new_addr, &server_error);
else else
new_fd = handle_connection(netplay, &server_error); new_fd = handle_connection(netplay, &new_addr, &server_error);
if (server_error) if (server_error)
{ {
@ -3503,6 +3673,13 @@ static bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect)
{ {
struct netplay_connection *connection; struct netplay_connection *connection;
if (netplay_banned(netplay, new_fd, &new_addr))
{
/* Client is banned. */
socket_close(new_fd);
goto process;
}
if (netplay_full(netplay, new_fd)) if (netplay_full(netplay, new_fd))
{ {
/* Not accepting any more connections. */ /* Not accepting any more connections. */
@ -3533,6 +3710,8 @@ static bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect)
connection->active = true; connection->active = true;
connection->fd = new_fd; connection->fd = new_fd;
connection->mode = NETPLAY_CONNECTION_INIT; connection->mode = NETPLAY_CONNECTION_INIT;
memcpy(&connection->addr, &new_addr, sizeof(connection->addr));
} }
} }
@ -6472,13 +6651,16 @@ static int init_tcp_connection(netplay_t *netplay, const struct addrinfo *addr,
sizeof(netplay->mitm_session_id.unique))) sizeof(netplay->mitm_session_id.unique)))
{ {
/* Initialize data for handling tunneled client connections. */ /* Initialize data for handling tunneled client connections. */
netplay->mitm_pending = (struct netplay_mitm_pending*) netplay->mitm_handler = (struct netplay_mitm_handler*)
calloc(1, sizeof(*netplay->mitm_pending)); calloc(1, sizeof(*netplay->mitm_handler));
if (netplay->mitm_pending) if (netplay->mitm_handler)
{ {
memset(netplay->mitm_pending->fds, -1, size_t i;
sizeof(netplay->mitm_pending->fds));
netplay->mitm_pending->addr = addr; netplay->mitm_handler->addr = addr;
for (i = 0; i < ARRAY_SIZE(netplay->mitm_handler->pending); i++)
netplay->mitm_handler->pending[i].fd = -1;
return fd; return fd;
} }
@ -6635,8 +6817,8 @@ try_ipv4:
break; break;
} while ((tmp_info = tmp_info->ai_next)); } while ((tmp_info = tmp_info->ai_next));
if (netplay->mitm_pending && netplay->mitm_pending->addr) if (netplay->mitm_handler && netplay->mitm_handler->addr)
netplay->mitm_pending->base_addr = addr; netplay->mitm_handler->base_addr = addr;
else else
freeaddrinfo_retro(addr); freeaddrinfo_retro(addr);
addr = NULL; addr = NULL;
@ -6849,18 +7031,18 @@ static void netplay_free(netplay_t *netplay)
if (netplay->listen_fd >= 0) if (netplay->listen_fd >= 0)
socket_close(netplay->listen_fd); socket_close(netplay->listen_fd);
if (netplay->mitm_pending) if (netplay->mitm_handler)
{ {
for (i = 0; i < NETPLAY_MITM_MAX_PENDING; i++) for (i = 0; i < ARRAY_SIZE(netplay->mitm_handler->pending); i++)
{ {
int fd = netplay->mitm_pending->fds[i]; int fd = netplay->mitm_handler->pending[i].fd;
if (fd >= 0) if (fd >= 0)
socket_close(fd); socket_close(fd);
} }
freeaddrinfo_retro(netplay->mitm_pending->base_addr); freeaddrinfo_retro(netplay->mitm_handler->base_addr);
free(netplay->mitm_pending); free(netplay->mitm_handler);
} }
for (i = 0; i < netplay->connections_size; i++) for (i = 0; i < netplay->connections_size; i++)
@ -8335,7 +8517,7 @@ static bool netplay_pre_frame(netplay_t *netplay)
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
#ifdef HAVE_NETPLAYDISCOVERY #ifdef HAVE_NETPLAYDISCOVERY
if (!netplay->mitm_pending) if (!netplay->mitm_handler)
{ {
net_driver_state_t *net_st = &networking_driver_st; net_driver_state_t *net_st = &networking_driver_st;
@ -8607,7 +8789,40 @@ static size_t retrieve_client_info(netplay_t *netplay, netplay_client_info_t *bu
return j; return j;
} }
static bool kick_client_by_id(netplay_t *netplay, int client_id) static bool ban_client(netplay_t *netplay,
struct netplay_connection *connection)
{
if (netplay->ban_list.size >= netplay->ban_list.allocated)
{
if (!netplay->ban_list.size)
{
netplay->ban_list.list =
(netplay_address_t*)malloc(2 * sizeof(*netplay->ban_list.list));
if (!netplay->ban_list.list)
return false;
netplay->ban_list.allocated = 2;
}
else
{
size_t new_allocated = netplay->ban_list.allocated + 4;
netplay_address_t *new_list = (netplay_address_t*)realloc(
netplay->ban_list.list, new_allocated * sizeof(*new_list));
if (!new_list)
return false;
netplay->ban_list.allocated = new_allocated;
netplay->ban_list.list = new_list;
}
}
memcpy(&netplay->ban_list.list[netplay->ban_list.size++], &connection->addr,
sizeof(*netplay->ban_list.list));
return true;
}
static bool kick_client_by_id(netplay_t *netplay, int client_id, bool ban)
{ {
struct netplay_connection *connection = NULL; struct netplay_connection *connection = NULL;
@ -8620,12 +8835,16 @@ static bool kick_client_by_id(netplay_t *netplay, int client_id)
if (!connection->active || connection->mode < NETPLAY_CONNECTION_CONNECTED) if (!connection->active || connection->mode < NETPLAY_CONNECTION_CONNECTED)
return false; return false;
if (ban && !ban_client(netplay, connection))
return false;
netplay_hangup(netplay, connection); netplay_hangup(netplay, connection);
return true; return true;
} }
static bool kick_client_by_name(netplay_t *netplay, const char *client_name) static bool kick_client_by_name(netplay_t *netplay, const char *client_name,
bool ban)
{ {
size_t i; size_t i;
@ -8641,6 +8860,9 @@ static bool kick_client_by_name(netplay_t *netplay, const char *client_name)
/* Kick the first client with a matched name. */ /* Kick the first client with a matched name. */
if (string_is_equal(client_name, connection->nick)) if (string_is_equal(client_name, connection->nick))
{ {
if (ban && !ban_client(netplay, connection))
return false;
netplay_hangup(netplay, connection); netplay_hangup(netplay, connection);
return true; return true;
@ -8651,7 +8873,7 @@ static bool kick_client_by_name(netplay_t *netplay, const char *client_name)
} }
static bool kick_client_by_id_and_name(netplay_t *netplay, static bool kick_client_by_id_and_name(netplay_t *netplay,
int client_id, const char *client_name) int client_id, const char *client_name, bool ban)
{ {
struct netplay_connection *connection = NULL; struct netplay_connection *connection = NULL;
@ -8668,6 +8890,9 @@ static bool kick_client_by_id_and_name(netplay_t *netplay,
if (!string_is_equal(client_name, connection->nick)) if (!string_is_equal(client_name, connection->nick))
return false; return false;
if (ban && !ban_client(netplay, connection))
return false;
netplay_hangup(netplay, connection); netplay_hangup(netplay, connection);
return true; return true;
@ -8866,11 +9091,35 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
} }
if (client->id >= 0 && !string_is_empty(client->name)) if (client->id >= 0 && !string_is_empty(client->name))
ret = kick_client_by_id_and_name(netplay, ret = kick_client_by_id_and_name(netplay,
client->id, client->name); client->id, client->name, false);
else if (client->id >= 0) else if (client->id >= 0)
ret = kick_client_by_id(netplay, client->id); ret = kick_client_by_id(netplay, client->id, false);
else if (!string_is_empty(client->name)) else if (!string_is_empty(client->name))
ret = kick_client_by_name(netplay, client->name); ret = kick_client_by_name(netplay, client->name, false);
else
ret = false;
}
else
ret = false;
break;
case RARCH_NETPLAY_CTL_BAN_CLIENT:
/* Only the server should be able to ban others. */
if (netplay && netplay->is_server)
{
netplay_client_info_t *client = (netplay_client_info_t*)data;
if (!client)
{
ret = false;
break;
}
if (client->id >= 0 && !string_is_empty(client->name))
ret = kick_client_by_id_and_name(netplay,
client->id, client->name, true);
else if (client->id >= 0)
ret = kick_client_by_id(netplay, client->id, true);
else if (!string_is_empty(client->name))
ret = kick_client_by_name(netplay, client->name, true);
else else
ret = false; ret = false;
} }

View File

@ -348,6 +348,16 @@ struct socket_buffer
size_t read; size_t read;
}; };
/* We do it like this instead of using sockaddr_storage
in order to have relay server IPv6 support on platforms
that do not support IPv6. */
typedef struct netplay_address
{
/* Can hold an IPv6 address aswell as an IPv4 address in the
::ffff:a.b.c.d format. */
uint8_t addr[16];
} netplay_address_t;
/* Each connection gets a connection struct */ /* Each connection gets a connection struct */
struct netplay_connection struct netplay_connection
{ {
@ -357,6 +367,9 @@ struct netplay_connection
/* Timer used to estimate a connection's latency */ /* Timer used to estimate a connection's latency */
retro_time_t ping_timer; retro_time_t ping_timer;
/* Connection's address */
netplay_address_t addr;
/* Buffers for sending and receiving data */ /* Buffers for sending and receiving data */
struct socket_buffer send_packet_buffer; struct socket_buffer send_packet_buffer;
struct socket_buffer recv_packet_buffer; struct socket_buffer recv_packet_buffer;
@ -428,15 +441,30 @@ typedef struct mitm_id
} mitm_id_t; } mitm_id_t;
#define NETPLAY_MITM_MAX_PENDING 8 #define NETPLAY_MITM_MAX_PENDING 8
struct netplay_mitm_pending struct netplay_mitm_handler
{ {
retro_time_t timeouts[NETPLAY_MITM_MAX_PENDING]; struct
mitm_id_t ids[NETPLAY_MITM_MAX_PENDING]; {
retro_time_t timeout;
mitm_id_t id;
netplay_address_t addr;
int fd;
bool has_addr;
} pending[NETPLAY_MITM_MAX_PENDING];
mitm_id_t id_buf; mitm_id_t id_buf;
netplay_address_t addr_buf;
struct addrinfo *base_addr; struct addrinfo *base_addr;
const struct addrinfo *addr; const struct addrinfo *addr;
size_t id_recvd; size_t id_recvd;
int fds[NETPLAY_MITM_MAX_PENDING]; size_t addr_recvd;
};
struct netplay_ban_list
{
netplay_address_t *list;
size_t size;
size_t allocated;
}; };
struct netplay_chat struct netplay_chat
@ -477,11 +505,14 @@ struct netplay
/* MITM session id */ /* MITM session id */
mitm_id_t mitm_session_id; mitm_id_t mitm_session_id;
/* Banned addresses */
struct netplay_ban_list ban_list;
/* Chat messages */ /* Chat messages */
struct netplay_chat chat; struct netplay_chat chat;
/* MITM connection handler */ /* MITM connection handler */
struct netplay_mitm_pending *mitm_pending; struct netplay_mitm_handler *mitm_handler;
/* All of our connections */ /* All of our connections */
struct netplay_connection *connections; struct netplay_connection *connections;