From 8c48b2f310ab32a1515fa00564f034ad4a33f5fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Mon, 22 Jul 2024 17:24:50 +0200 Subject: [PATCH] relay/api: fix connection to remote using an IPv6 address with square brackets (closes #2156) --- CHANGELOG.md | 1 + .../relay/api/remote/relay-remote-network.c | 6 +- src/plugins/relay/relay-remote.c | 194 ++++++++++-------- .../unit/plugins/relay/test-relay-remote.cpp | 99 ++++++--- 4 files changed, 175 insertions(+), 125 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22cfa576a..7037deacd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ - ruby: fix builtin functions not available ([#2109](https://github.com/weechat/weechat/issues/2109)) - php: fix return value of function hdata_longlong - tcl: fix return value of function hdata_longlong ([#2119](https://github.com/weechat/weechat/issues/2119)) +- relay/api: fix connection to remote using an IPv6 address with square brackets ([#2156](https://github.com/weechat/weechat/issues/2156)) - relay/api: allow clients without authentication when no relay password is defined and option relay.network.allow_empty_password is on ([#2158](https://github.com/weechat/weechat/issues/2158)) - relay/api: fix connection to remote without password ([#2158](https://github.com/weechat/weechat/issues/2158)) - relay/api: fix timezone of dates sent to clients ([#2151](https://github.com/weechat/weechat/issues/2151)) diff --git a/src/plugins/relay/api/remote/relay-remote-network.c b/src/plugins/relay/api/remote/relay-remote-network.c index b7020371a..1fcdd1265 100644 --- a/src/plugins/relay/api/remote/relay-remote-network.c +++ b/src/plugins/relay/api/remote/relay-remote-network.c @@ -57,13 +57,17 @@ relay_remote_network_get_url_resource (struct t_relay_remote *remote, const char *resource) { char *url; + int colon_in_address; if (!remote || !remote->address || !resource || !resource[0]) return NULL; - weechat_asprintf (&url, "%s://%s:%d/api/%s", + colon_in_address = (strchr (remote->address, ':')) ? 1 : 0; + weechat_asprintf (&url, "%s://%s%s%s:%d/api/%s", (remote->tls) ? "https" : "http", + (colon_in_address) ? "[" : "", remote->address, + (colon_in_address) ? "]" : "", remote->port, resource); diff --git a/src/plugins/relay/relay-remote.c b/src/plugins/relay/relay-remote.c index c1e90e918..887306230 100644 --- a/src/plugins/relay/relay-remote.c +++ b/src/plugins/relay/relay-remote.c @@ -185,7 +185,107 @@ relay_remote_name_valid (const char *name) } /* - * Checks if a remote URL is valid; + * Extracts TLS, address and port from remote URL. + * + * If address is an IPv6 like "[::1]", the square brackets are removed. + * + * Returns: + * 1: OK + * 0: error, invalid URL + */ + +int +relay_remote_parse_url (const char *url, + int *tls, char **address, int *port) +{ + const char *ptr_url; + char *pos, *str_port, *error; + long number; + + if (tls) + *tls = 0; + if (address) + *address = NULL; + if (port) + *port = RELAY_REMOTE_DEFAULT_PORT; + + if (!url || !url[0]) + return 0; + + /* check scheme and extract TLS flag */ + if (strncmp (url, "http://", 7) == 0) + { + ptr_url = url + 7; + } + else if (strncmp (url, "https://", 8) == 0) + { + if (tls) + *tls = 1; + ptr_url = url + 8; + } + else + { + return 0; + } + + /* check if there is an IPv6 address with square brackets, like "[::1]" */ + if (ptr_url[0] == '[') + { + /* extract IPv6 address between square brackets */ + pos = strchr (ptr_url, ']'); + if (!pos) + return 0; + if (address) + *address = weechat_strndup (ptr_url + 1, pos - ptr_url - 1); + ptr_url = pos + 1; + } + else + { + /* extract another address */ + pos = strrchr (ptr_url, ':'); + if (!pos) + pos = strchr (ptr_url, '/'); + if (!pos) + pos = strchr (ptr_url, '?'); + if (address) + { + *address = (pos) ? + weechat_strndup (ptr_url, pos - ptr_url) : strdup (ptr_url); + } + } + + /* extract port number */ + pos = strrchr (ptr_url, ':'); + if (pos) + { + ptr_url = pos + 1; + pos = strchr (ptr_url, '/'); + if (!pos) + pos = strchr (ptr_url, '?'); + str_port = (pos) ? + weechat_strndup (ptr_url, pos - ptr_url) : strdup (ptr_url); + if (!str_port) + return 0; + error = NULL; + number = strtol (str_port, &error, 10); + if (error && !error[0] && (number >= 0) && (number <= 65535)) + { + if (port) + *port = number; + free (str_port); + } + else + { + free (str_port); + return 0; + } + } + + return 1; +} + +/* + * Checks if a remote URL is valid. * * Returns: * 1: URL is valid @@ -195,23 +295,7 @@ relay_remote_name_valid (const char *name) int relay_remote_url_valid (const char *url) { - const char *pos; - - if (!url || !url[0]) - return 0; - - /* URL must start with "https://" or "http://" */ - if ((strncmp (url, "https://", 8) != 0) && (strncmp (url, "http://", 7) != 0)) - return 0; - - pos = strchr (url + 7, ':'); - - /* invalid port? */ - if (pos && !isdigit ((unsigned char)pos[1])) - return 0; - - /* URL is valid */ - return 1; + return relay_remote_parse_url (url, NULL, NULL, NULL); } /* @@ -229,76 +313,6 @@ relay_remote_send_signal (struct t_relay_remote *remote) weechat_hook_signal_send (signal, WEECHAT_HOOK_SIGNAL_POINTER, remote); } -/* - * Extracts address from URL. - * - * Note: result must be free after use. - */ - -char * -relay_remote_get_address (const char *url) -{ - const char *ptr_start; - char *pos; - - if (!url) - return NULL; - - if (strncmp (url, "http://", 7) == 0) - ptr_start = url + 7; - else if (strncmp (url, "https://", 8) == 0) - ptr_start = url + 8; - else - return NULL; - - pos = strchr (ptr_start, ':'); - if (!pos) - pos = strchr (ptr_start, '?'); - - return (pos) ? - weechat_strndup (ptr_start, pos - ptr_start) : strdup (ptr_start); -} - -/* - * Extracts port from URL. - */ - -int -relay_remote_get_port (const char *url) -{ - char *pos, *pos2, *str_port, *error; - long port; - - if (!url) - goto error; - - pos = strchr (url + 7, ':'); - if (!pos) - goto error; - - pos++; - - pos2 = strchr (pos, '/'); - if (pos2) - str_port = weechat_strndup (pos, pos2 - pos); - else - str_port = strdup (pos); - if (!str_port) - goto error; - - error = NULL; - port = strtol (str_port, &error, 10); - if (error && !error[0]) - { - free (str_port); - return (int)port; - } - free (str_port); - -error: - return RELAY_REMOTE_DEFAULT_PORT; -} - /* * Allocates and initializes new remote structure. * @@ -415,9 +429,7 @@ void relay_remote_set_url (struct t_relay_remote *remote, const char *url) { free (remote->address); - remote->address = relay_remote_get_address (url); - remote->port = relay_remote_get_port (url); - remote->tls = (weechat_strncmp (url, "https:", 6) == 0) ? 1 : 0; + relay_remote_parse_url (url, &remote->tls, &remote->address, &remote->port); } /* diff --git a/tests/unit/plugins/relay/test-relay-remote.cpp b/tests/unit/plugins/relay/test-relay-remote.cpp index e2c7816a8..ed5968ff3 100644 --- a/tests/unit/plugins/relay/test-relay-remote.cpp +++ b/tests/unit/plugins/relay/test-relay-remote.cpp @@ -21,6 +21,8 @@ #include "CppUTest/TestHarness.h" +#include "tests/tests.h" + extern "C" { #include @@ -28,9 +30,21 @@ extern "C" #include "src/plugins/relay/relay.h" #include "src/plugins/relay/relay-remote.h" -extern char *relay_remote_get_address (const char *url); +extern int relay_remote_parse_url (const char *url, + int *tls, char **address, int *port); } +#define WEE_CHECK_PARSE_URL(__result, __result_tls, __result_address, \ + __result_port, __url) \ + tls = -1; \ + address = NULL; \ + port = -1; \ + relay_remote_parse_url (__url, &tls, &address, &port); \ + LONGS_EQUAL(__result_tls, tls); \ + STRCMP_EQUAL(__result_address, address); \ + LONGS_EQUAL(__result_port, port); \ + free (address); + TEST_GROUP(RelayRemote) { }; @@ -89,6 +103,57 @@ TEST(RelayRemote, NameValid) /* TODO: write tests */ } +/* + * Tests functions: + * relay_remote_parse_url + */ + +TEST(RelayRemote, ParseUrl) +{ + int tls, port; + char *address; + + LONGS_EQUAL(0, relay_remote_parse_url (NULL, NULL, NULL, NULL)); + LONGS_EQUAL(0, relay_remote_parse_url ("", NULL, NULL, NULL)); + LONGS_EQUAL(0, relay_remote_parse_url ("zzz", NULL, NULL, NULL)); + LONGS_EQUAL(0, relay_remote_parse_url ("http://[::1", NULL, NULL, NULL)); + LONGS_EQUAL(0, relay_remote_parse_url ("https://[::1", NULL, NULL, NULL)); + + LONGS_EQUAL(1, relay_remote_parse_url ("http://example.com", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com/", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com?option=1", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com/?option=1", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com:9876", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com:9876/", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com:9876?option=1", NULL, NULL, NULL)); + LONGS_EQUAL(1, relay_remote_parse_url ("https://example.com:9876/?option=1", NULL, NULL, NULL)); + + WEE_CHECK_PARSE_URL(1, 0, "", RELAY_REMOTE_DEFAULT_PORT, "http://"); + WEE_CHECK_PARSE_URL(1, 1, "", RELAY_REMOTE_DEFAULT_PORT, "https://"); + + WEE_CHECK_PARSE_URL(1, 0, "localhost", RELAY_REMOTE_DEFAULT_PORT, "http://localhost"); + WEE_CHECK_PARSE_URL(1, 1, "localhost", RELAY_REMOTE_DEFAULT_PORT, "https://localhost"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", RELAY_REMOTE_DEFAULT_PORT, "https://example.com"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", RELAY_REMOTE_DEFAULT_PORT, "https://example.com/"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", RELAY_REMOTE_DEFAULT_PORT, "https://example.com?option=1"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", RELAY_REMOTE_DEFAULT_PORT, "https://example.com/?option=1"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", 9876, "https://example.com:9876"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", 9876, "https://example.com:9876/"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", 9876, "https://example.com:9876?option=1"); + WEE_CHECK_PARSE_URL(1, 1, "example.com", 9876, "https://example.com:9876/?option=1"); + + WEE_CHECK_PARSE_URL(1, 0, "::1", RELAY_REMOTE_DEFAULT_PORT, "http://[::1]"); + WEE_CHECK_PARSE_URL(1, 1, "::1", RELAY_REMOTE_DEFAULT_PORT, "https://[::1]"); + WEE_CHECK_PARSE_URL(1, 1, "::1", RELAY_REMOTE_DEFAULT_PORT, "https://[::1]/"); + WEE_CHECK_PARSE_URL(1, 1, "::1", RELAY_REMOTE_DEFAULT_PORT, "https://[::1]?option=1"); + WEE_CHECK_PARSE_URL(1, 1, "::1", RELAY_REMOTE_DEFAULT_PORT, "https://[::1]/?option=1"); + WEE_CHECK_PARSE_URL(1, 1, "::1", 9876, "https://[::1]:9876"); + WEE_CHECK_PARSE_URL(1, 1, "::1", 9876, "https://[::1]:9876/"); + WEE_CHECK_PARSE_URL(1, 1, "::1", 9876, "https://[::1]:9876?option=1"); + WEE_CHECK_PARSE_URL(1, 1, "::1", 9876, "https://[::1]:9876/?option=1"); +} + /* * Tests functions: * relay_remote_url_valid @@ -109,38 +174,6 @@ TEST(RelayRemote, SendSignal) /* TODO: write tests */ } -/* - * Tests functions: - * relay_remote_get_address - */ - -TEST(RelayRemote, GetAddress) -{ - POINTERS_EQUAL(NULL, relay_remote_get_address (NULL)); - POINTERS_EQUAL(NULL, relay_remote_get_address ("")); - POINTERS_EQUAL(NULL, relay_remote_get_address ("zzz")); - - STRCMP_EQUAL("", relay_remote_get_address ("http://")); - STRCMP_EQUAL("", relay_remote_get_address ("https://")); - - STRCMP_EQUAL("localhost", relay_remote_get_address ("https://localhost")); - STRCMP_EQUAL("example.com", relay_remote_get_address ("https://example.com")); - STRCMP_EQUAL("example.com", relay_remote_get_address ("https://example.com:8000")); - STRCMP_EQUAL("example.com", relay_remote_get_address ("https://example.com:8000/")); - STRCMP_EQUAL("example.com", relay_remote_get_address ("https://example.com:8000/?option=1")); - STRCMP_EQUAL("example.com", relay_remote_get_address ("https://example.com?option=1")); -} - -/* - * Tests functions: - * relay_remote_get_port - */ - -TEST(RelayRemote, GetPort) -{ - /* TODO: write tests */ -} - /* * Tests functions: * relay_remote_alloc