shadow_server: allow specifying IP addresses to listen on (#6050)
authorLinus Heckemann <git@sphalerite.org>
Tue, 5 May 2020 06:35:19 +0000 (08:35 +0200)
committerakallabeth <akallabeth@posteo.net>
Fri, 8 May 2020 09:06:02 +0000 (11:06 +0200)
* shadow_server: allow specifying IP addresses to listen on

This allows using IPv6 as well as listening only on specific
interfaces. Additionally, it enables listening on local and TCP
sockets simultaneously.

* listener: log address with square brackets

This disambiguates IPv6 addresses.

* shadow_server: check error on each socket binding

* Refactored shadow /bind-address for 2.0 compiatibility.

* Made /ipc-socket and /bind-address incompatible arguments.

* Fixed shadow /bind-address handling and description

* Allow multiple bind addresses for shadow server.

Co-authored-by: akallabeth <akallabeth@posteo.net>
libfreerdp/core/listener.c
server/shadow/shadow_client.c
server/shadow/shadow_server.c

index 68b0c02..3d0a1b1 100644 (file)
@@ -138,7 +138,7 @@ static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_a
                WSAEventSelect(sockfd, listener->events[listener->num_sockfds],
                               FD_READ | FD_ACCEPT | FD_CLOSE);
                listener->num_sockfds++;
-               WLog_INFO(TAG, "Listening on %s:%d", addr, port);
+               WLog_INFO(TAG, "Listening on [%s]:%d", addr, port);
        }
 
        freeaddrinfo(res);
index 3dded01..f32f649 100644 (file)
@@ -128,6 +128,7 @@ static INLINE void shadow_client_free_queued_message(void* obj)
 
 static BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
 {
+       const char bind_address[] = "bind-address,";
        rdpSettings* settings;
        rdpShadowServer* server;
        const wObject cb = { NULL, NULL, NULL, shadow_client_free_queued_message, NULL };
@@ -157,7 +158,8 @@ static BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* clien
        if (!(settings->RdpKeyFile = _strdup(settings->PrivateKeyFile)))
                goto fail_rdpkey_file;
 
-       if (server->ipcSocket)
+       if (server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
+                                         strnlen(bind_address, sizeof(bind_address))) != 0))
        {
                settings->LyncRdpMode = TRUE;
                settings->CompressionEnabled = FALSE;
index d7a7536..30a63c9 100644 (file)
 
 #define TAG SERVER_TAG("shadow")
 
+static const char bind_address[] = "bind-address,";
+
 static const COMMAND_LINE_ARGUMENT_A shadow_args[] = {
        { "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
        { "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "<ipc-socket>", NULL, NULL, -1, NULL,
          "Server IPC socket" },
+       { "bind-address", COMMAND_LINE_VALUE_REQUIRED, "<bind-address>[,<another address>, ...]", NULL,
+         NULL, -1, NULL,
+         "An address to bind to. Use '[<ipv6>]' for IPv6 addresses, e.g. '[::1]' for "
+         "localhost" },
        { "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", NULL, NULL, -1, NULL,
          "Select or list monitors" },
        { "rect", COMMAND_LINE_VALUE_REQUIRED, "<x,y,w,h>", NULL, NULL, -1, NULL,
@@ -220,11 +226,31 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
                }
                CommandLineSwitchCase(arg, "ipc-socket")
                {
+                       /* /bind-address is incompatible */
+                       if (server->ipcSocket)
+                               return -1;
+
                        server->ipcSocket = _strdup(arg->Value);
 
                        if (!server->ipcSocket)
                                return -1;
                }
+               CommandLineSwitchCase(arg, "bind-address")
+               {
+                       int rc;
+                       size_t len = strlen(arg->Value) + sizeof(bind_address);
+                       /* /ipc-socket is incompatible */
+                       if (server->ipcSocket)
+                               return -1;
+                       server->ipcSocket = calloc(len, sizeof(CHAR));
+
+                       if (!server->ipcSocket)
+                               return -1;
+
+                       rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
+                       if ((rc < 0) || ((size_t)rc != len - 1))
+                               return -1;
+               }
                CommandLineSwitchCase(arg, "may-view")
                {
                        server->mayView = arg->Value ? TRUE : FALSE;
@@ -480,8 +506,44 @@ static DWORD WINAPI shadow_server_thread(LPVOID arg)
        return 0;
 }
 
+static BOOL open_port(rdpShadowServer* server, char* address)
+{
+       BOOL status;
+       char* modaddr = address;
+
+       if (modaddr)
+       {
+               if (modaddr[0] == '[')
+               {
+                       char* end = strchr(address, ']');
+                       if (!end)
+                       {
+                               WLog_ERR(TAG, "Could not parse bind-address %s", address);
+                               return -1;
+                       }
+                       *end++ = '\0';
+                       if (strlen(end) > 0)
+                       {
+                               WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
+                               return -1;
+                       }
+                       modaddr++;
+               }
+       }
+       status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
+
+       if (!status)
+       {
+               WLog_ERR(TAG,
+                        "Problem creating TCP listener. (Port already used or insufficient permissions?)");
+       }
+
+       return status;
+}
+
 int shadow_server_start(rdpShadowServer* server)
 {
+       BOOL ipc;
        BOOL status;
        WSADATA wsaData;
 
@@ -510,16 +572,50 @@ int shadow_server_start(rdpShadowServer* server)
                return -1;
        }
 
-       if (!server->ipcSocket)
-               status = server->listener->Open(server->listener, NULL, (UINT16)server->port);
-       else
-               status = server->listener->OpenLocal(server->listener, server->ipcSocket);
+       /* Bind magic:
+        *
+        * emtpy                 ... bind TCP all
+        * <local path>          ... bind local (IPC)
+        * bind-socket,<address> ... bind TCP to specified interface
+        */
+       ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
+                                           strnlen(bind_address, sizeof(bind_address))) != 0);
+       if (!ipc)
+       {
+               size_t x, count;
+               char** list = CommandLineParseCommaSeparatedValuesEx(NULL, server->ipcSocket, &count);
+               if (!list || (count <= 1))
+               {
+                       free(list);
+                       if (server->ipcSocket == NULL)
+                       {
+                               if (!open_port(server, NULL))
+                                       return -1;
+                       }
+                       else
+                               return -1;
+               }
 
-       if (!status)
+               for (x = 1; x < count; x++)
+               {
+                       BOOL success = open_port(server, list[x]);
+                       if (!success)
+                       {
+                               free(list);
+                               return -1;
+                       }
+               }
+               free(list);
+       }
+       else
        {
-               WLog_ERR(TAG,
-                        "Problem creating listener. (Port already used or insufficient permissions?)");
-               return -1;
+               status = server->listener->OpenLocal(server->listener, server->ipcSocket);
+               if (!status)
+               {
+                       WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
+                                     "insufficient permissions?)");
+                       return -1;
+               }
        }
 
        if (!(server->thread = CreateThread(NULL, 0, shadow_server_thread, (void*)server, 0, NULL)))