foxygit / doom Log in
commit 0d002e6ed2289928907a995fcf0a48021cf71da7
Author:     Simon Howard <fraggle@soulsphere.org>
AuthorDate: Sat Sep 7 08:42:02 2024 -0400
Commit:     Simon Howard <fraggle+github@gmail.com>
CommitDate: Sat Sep 7 16:10:40 2024 -0400

    net: Hide public IP addresses for privacy

    labnove19 on the Chocolate Doom Discord expressed safety concerns [1]
    about playing on public servers and pointed out that the lobby screen
    reveals the IP address of every player that is connected to the server.
    There's no actual reason why we need to do this, and an IP address is
    usually considered personally identifiable information that can also be
    used to identify a person's physical location.

    This change therefore changes the server code so that players on the
    public Internet will not be able to see each others' IP addresses.
    However, I've been careful to preserve the behavior for non-public
    addresses:

    1. If a localhost player and a public player are both connected to the
       server, the localhost player can see public IP addresses, since the
       owner of the server ought to be able to see the addresses of players
       who connect to their own server. Conversely, public players can see
       localhost addresses since they're not publicly accessible and reveal
       no private information anyway.

    2. If two LAN players are connected to a server they can see each
       others' IP addresses, since they're presumably physically colocated
       anyway. It could actually be pretty frustrating for players to not
       be able to see each others' IP addresses if they're trying to set
       up a LAN game, having problems and could use the address to debug.

    3. If a LAN player and a public player are both connected to the server,
       situation, the server and the LAN player are presumably physically
       colocated, which effectively is the same case as (1) above, so the
       LAN player can see the public player's IP address. However, the
       public player only sees "[LAN player]" as we should not disclose
       details of the server owner's internal network.

    This change is consistent with the "pet name" change we already made to
    hide people's names unless they make a deliberate choice to reveal them.

    [1] https://discord.com/channels/435469554173476866/829452135078428722/1281374017164677161
---
 src/net_server.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 51 insertions(+), 3 deletions(-)

diff --git a/src/net_server.c b/src/net_server.c
index 42ab0d9f..9374b64e 100644
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -383,11 +383,35 @@ static net_client_t *NET_SV_Controller(void)
     return best;
 }

+typedef enum
+{
+    RANGE_LOCALHOST,   // Same process or 127.x
+    RANGE_PRIVATE,     // RFC 1918
+    RANGE_PUBLIC,      // The public Internet
+} ip_range_t;
+
+static ip_range_t ClientAddressRange(const char *addr)
+{
+    if (!strcmp(addr, "local client")
+     || M_StringStartsWith(addr, "127."))
+    {
+        return RANGE_LOCALHOST;
+    }
+    if (M_StringStartsWith(addr, "10.")
+     || M_StringStartsWith(addr, "192.168."))
+    {
+        return RANGE_PRIVATE;
+    }
+    return RANGE_PUBLIC;
+}
+
 static void NET_SV_SendWaitingData(net_client_t *client)
 {
     net_waitdata_t wait_data;
     net_packet_t *packet;
     net_client_t *controller;
+    ip_range_t client_range, player_range;
+    const char *addr;
     int i;

     NET_SV_AssignPlayers();
@@ -416,6 +440,11 @@ static void NET_SV_SendWaitingData(net_client_t *client)
            sizeof(sha1_digest_t));
     wait_data.is_freedoom = controller->is_freedoom;

+    // We only send IP addresses to locally-connected clients (including
+    // the 127.* loopback range):
+    addr = NET_AddrToString(client->connection.addr);
+    client_range = ClientAddressRange(addr);
+
     // set name and address of each player:

     for (i = 0; i < wait_data.num_players; ++i)
@@ -423,9 +452,28 @@ static void NET_SV_SendWaitingData(net_client_t *client)
         M_StringCopy(wait_data.player_names[i],
                      sv_players[i]->name,
                      MAXPLAYERNAME);
-        M_StringCopy(wait_data.player_addrs[i],
-                     NET_AddrToString(sv_players[i]->addr),
-                     MAXPLAYERNAME);
+
+        // For privacy, only local clients or those on a LAN get to see
+        // addresses. Public clients only get to see their own address,
+        // though we do reveal localhost addresses since they're harmless,
+        // and we do reveal when a client is connected via LAN.
+        addr = NET_AddrToString(sv_players[i]->addr);
+        player_range = ClientAddressRange(addr);
+        if (client_range == RANGE_LOCALHOST || client_range == RANGE_PRIVATE
+         || i == wait_data.consoleplayer || player_range == RANGE_LOCALHOST)
+        {
+            M_StringCopy(wait_data.player_addrs[i], addr, MAXPLAYERNAME);
+        }
+        else if (player_range == RANGE_PRIVATE)
+        {
+            M_snprintf(wait_data.player_addrs[i], MAXPLAYERNAME,
+                       "[LAN player]");
+        }
+        else
+        {
+            M_snprintf(wait_data.player_addrs[i], MAXPLAYERNAME,
+                       "[address hidden]");
+        }
     }

     // Construct packet: