shadow_server: allow specifying IP addresses to listen on (#6050)

* 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>
This commit is contained in:
Linus Heckemann 2020-05-05 08:35:19 +02:00 committed by GitHub
parent 5b98aa7515
commit 5ce0ab909f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 11 deletions

View 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);

View 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;

View File

@ -45,10 +45,16 @@
#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);
if (!status)
/* 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)
{
WLog_ERR(TAG,
"Problem creating listener. (Port already used or insufficient permissions?)");
return -1;
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;
}
for (x = 1; x < count; x++)
{
BOOL success = open_port(server, list[x]);
if (!success)
{
free(list);
return -1;
}
}
free(list);
}
else
{
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)))