glib/gio/tests/network-address.c
Philip Withnall 128c413261 gsocketconnectable: Add a to_string() virtual method
Add string serialisation functions for GNetworkAddress, GSocketAddress,
GUnixSocketAddress, GInetSocketAddress, GNetworkService and
GSocketConnectable. These are intended for use in debug output, not for
serialisation in network or disc protocols.

They are implemented as a new virtual method on GSocketConnectable:
g_socket_connectable_to_string().

GInetSocketAddress and GUnixSocketAddress now implement
GSocketConnectable directly to implement to_string(). Previously they
implemented it via their abstract parent class, GSocketAddress.

https://bugzilla.gnome.org/show_bug.cgi?id=737116
2015-10-13 15:42:14 +01:00

564 lines
16 KiB
C

#include "config.h"
#include <gio/gio.h>
#include <gio/gnetworking.h>
static void
test_basic (void)
{
GNetworkAddress *address;
guint port;
gchar *hostname;
gchar *scheme;
address = (GNetworkAddress*)g_network_address_new ("www.gnome.org", 8080);
g_assert_cmpstr (g_network_address_get_hostname (address), ==, "www.gnome.org");
g_assert_cmpint (g_network_address_get_port (address), ==, 8080);
g_object_get (address, "hostname", &hostname, "port", &port, "scheme", &scheme, NULL);
g_assert_cmpstr (hostname, ==, "www.gnome.org");
g_assert_cmpint (port, ==, 8080);
g_assert (scheme == NULL);
g_free (hostname);
g_object_unref (address);
}
typedef struct {
const gchar *input;
const gchar *scheme;
const gchar *hostname;
guint16 port;
gint error_code;
} ParseTest;
static ParseTest uri_tests[] = {
{ "http://www.gnome.org:2020/start", "http", "www.gnome.org", 2020, -1 },
{ "ftp://joe~:(*)%46@ftp.gnome.org:2020/start", "ftp", "ftp.gnome.org", 2020, -1 },
{ "ftp://[fec0::abcd]/start", "ftp", "fec0::abcd", 8080, -1 },
{ "ftp://[fec0::abcd]:999/start", "ftp", "fec0::abcd", 999, -1 },
{ "ftp://joe%x-@ftp.gnome.org:2020/start", NULL, NULL, 0, G_IO_ERROR_INVALID_ARGUMENT },
{ "http://[fec0::abcd%em1]/start", "http", "fec0::abcd%em1", 8080, -1 },
{ "http://[fec0::abcd%25em1]/start", "http", "fec0::abcd%em1", 8080, -1 },
{ "http://[fec0::abcd%10]/start", "http", "fec0::abcd%10", 8080, -1 },
{ "http://[fec0::abcd%25em%31]/start", NULL, NULL, 0, G_IO_ERROR_INVALID_ARGUMENT },
{ "ftp://ftp.gnome.org/start?foo=bar@baz", "ftp", "ftp.gnome.org", 8080, -1 }
};
static void
test_parse_uri (gconstpointer d)
{
const ParseTest *test = d;
GNetworkAddress *address;
GError *error;
error = NULL;
address = (GNetworkAddress*)g_network_address_parse_uri (test->input, 8080, &error);
if (address)
{
g_assert_cmpstr (g_network_address_get_scheme (address), ==, test->scheme);
g_assert_cmpstr (g_network_address_get_hostname (address), ==, test->hostname);
g_assert_cmpint (g_network_address_get_port (address), ==, test->port);
g_assert_no_error (error);
}
else
g_assert_error (error, G_IO_ERROR, test->error_code);
if (address)
g_object_unref (address);
if (error)
g_error_free (error);
}
static ParseTest host_tests[] =
{
{ "www.gnome.org", NULL, "www.gnome.org", 1234, -1 },
{ "www.gnome.org:8080", NULL, "www.gnome.org", 8080, -1 },
{ "[2001:db8::1]", NULL, "2001:db8::1", 1234, -1 },
{ "[2001:db8::1]:888", NULL, "2001:db8::1", 888, -1 },
{ "[2001:db8::1%em1]", NULL, "2001:db8::1%em1", 1234, -1 },
{ "[hostname", NULL, NULL, 0, G_IO_ERROR_INVALID_ARGUMENT },
{ "[hostnam]e", NULL, NULL, 0, G_IO_ERROR_INVALID_ARGUMENT },
{ "hostname:", NULL, NULL, 0, G_IO_ERROR_INVALID_ARGUMENT },
{ "hostname:-1", NULL, NULL, 0, G_IO_ERROR_INVALID_ARGUMENT },
{ "hostname:9999999", NULL, NULL, 0, G_IO_ERROR_INVALID_ARGUMENT }
};
static void
test_parse_host (gconstpointer d)
{
const ParseTest *test = d;
GNetworkAddress *address;
GError *error;
error = NULL;
address = (GNetworkAddress*)g_network_address_parse (test->input, 1234, &error);
if (address)
{
g_assert_null (g_network_address_get_scheme (address));
g_assert_cmpstr (g_network_address_get_hostname (address), ==, test->hostname);
g_assert_cmpint (g_network_address_get_port (address), ==, test->port);
g_assert_no_error (error);
}
else
{
g_assert_error (error, G_IO_ERROR, test->error_code);
}
if (address)
g_object_unref (address);
if (error)
g_error_free (error);
}
typedef struct {
const gchar *input;
gboolean valid_parse, valid_resolve, valid_ip;
} ResolveTest;
static ResolveTest address_tests[] = {
{ "192.168.1.2", TRUE, TRUE, TRUE },
{ "fe80::42", TRUE, TRUE, TRUE },
/* GResolver accepts this by ignoring the scope ID. This was not
* intentional, but it's best to not "fix" it at this point.
*/
{ "fe80::42%1", TRUE, TRUE, FALSE },
/* g_network_address_parse() accepts these, but they are not
* (just) IP addresses.
*/
{ "192.168.1.2:80", TRUE, FALSE, FALSE },
{ "[fe80::42]", TRUE, FALSE, FALSE },
{ "[fe80::42]:80", TRUE, FALSE, FALSE },
/* These should not be considered IP addresses by anyone. */
{ "192.168.258", FALSE, FALSE, FALSE },
{ "192.11010306", FALSE, FALSE, FALSE },
{ "3232235778", FALSE, FALSE, FALSE },
{ "0300.0250.0001.0001", FALSE, FALSE, FALSE },
{ "0xC0.0xA8.0x01.0x02", FALSE, FALSE, FALSE },
{ "0xc0.0xa8.0x01.0x02", FALSE, FALSE, FALSE },
{ "0xc0a80102", FALSE, FALSE, FALSE }
};
static void
test_resolve_address (gconstpointer d)
{
const ResolveTest *test = d;
GSocketConnectable *connectable;
GSocketAddressEnumerator *addr_enum;
GSocketAddress *addr;
GError *error = NULL;
g_assert_cmpint (test->valid_ip, ==, g_hostname_is_ip_address (test->input));
connectable = g_network_address_parse (test->input, 1234, &error);
g_assert_no_error (error);
addr_enum = g_socket_connectable_enumerate (connectable);
addr = g_socket_address_enumerator_next (addr_enum, NULL, &error);
g_object_unref (addr_enum);
g_object_unref (connectable);
if (addr)
{
g_assert_true (test->valid_parse);
g_assert_true (G_IS_INET_SOCKET_ADDRESS (addr));
g_object_unref (addr);
}
else
{
g_assert_false (test->valid_parse);
g_assert_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
g_error_free (error);
return;
}
}
/* Technically this should be in a GResolver test program, but we don't
* have one of those since it's mostly impossible to test programmatically.
* So it goes here so it can share the tests.
*/
static void
test_resolve_address_gresolver (gconstpointer d)
{
const ResolveTest *test = d;
GResolver *resolver;
GList *addrs;
GInetAddress *iaddr;
GError *error = NULL;
resolver = g_resolver_get_default ();
addrs = g_resolver_lookup_by_name (resolver, test->input, NULL, &error);
g_object_unref (resolver);
if (addrs)
{
g_assert_true (test->valid_resolve);
g_assert_cmpint (g_list_length (addrs), ==, 1);
iaddr = addrs->data;
g_assert_true (G_IS_INET_ADDRESS (iaddr));
g_object_unref (iaddr);
g_list_free (addrs);
}
else
{
g_assert_false (test->valid_resolve);
if (!test->valid_parse)
{
/* GResolver should have rejected the address internally, in
* which case we're guaranteed to get G_RESOLVER_ERROR_NOT_FOUND.
*/
g_assert_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
}
else
{
/* If GResolver didn't reject the string itself, then we
* might have attempted to send it over the network. If that
* attempt succeeded, we'd get back NOT_FOUND, but if
* there's no network available we might have gotten some
* other error instead.
*/
}
g_error_free (error);
return;
}
}
#define SCOPE_ID_TEST_ADDR "fe80::42"
#define SCOPE_ID_TEST_PORT 99
#if defined (HAVE_IF_INDEXTONAME) && defined (HAVE_IF_NAMETOINDEX)
static char SCOPE_ID_TEST_IFNAME[IF_NAMESIZE];
static int SCOPE_ID_TEST_INDEX;
#else
#define SCOPE_ID_TEST_IFNAME "1"
#define SCOPE_ID_TEST_INDEX 1
#endif
static void
find_ifname_and_index (void)
{
if (SCOPE_ID_TEST_INDEX != 0)
return;
#if defined (HAVE_IF_INDEXTONAME) && defined (HAVE_IF_NAMETOINDEX)
SCOPE_ID_TEST_INDEX = if_nametoindex ("lo");
if (SCOPE_ID_TEST_INDEX != 0)
{
g_strlcpy (SCOPE_ID_TEST_IFNAME, "lo", sizeof (SCOPE_ID_TEST_IFNAME));
return;
}
for (SCOPE_ID_TEST_INDEX = 1; SCOPE_ID_TEST_INDEX < 1024; SCOPE_ID_TEST_INDEX++) {
if (if_indextoname (SCOPE_ID_TEST_INDEX, SCOPE_ID_TEST_IFNAME))
break;
}
g_assert_cmpstr (SCOPE_ID_TEST_IFNAME, !=, "");
#endif
}
static void
test_scope_id (GSocketConnectable *addr)
{
GSocketAddressEnumerator *addr_enum;
GSocketAddress *saddr;
GInetSocketAddress *isaddr;
GInetAddress *iaddr;
char *tostring;
GError *error = NULL;
addr_enum = g_socket_connectable_enumerate (addr);
saddr = g_socket_address_enumerator_next (addr_enum, NULL, &error);
g_assert_no_error (error);
g_assert (saddr != NULL);
g_assert (G_IS_INET_SOCKET_ADDRESS (saddr));
isaddr = G_INET_SOCKET_ADDRESS (saddr);
g_assert_cmpint (g_inet_socket_address_get_scope_id (isaddr), ==, SCOPE_ID_TEST_INDEX);
g_assert_cmpint (g_inet_socket_address_get_port (isaddr), ==, SCOPE_ID_TEST_PORT);
iaddr = g_inet_socket_address_get_address (isaddr);
tostring = g_inet_address_to_string (iaddr);
g_assert_cmpstr (tostring, ==, SCOPE_ID_TEST_ADDR);
g_free (tostring);
g_object_unref (saddr);
saddr = g_socket_address_enumerator_next (addr_enum, NULL, &error);
g_assert_no_error (error);
g_assert (saddr == NULL);
g_object_unref (addr_enum);
}
static void
test_host_scope_id (void)
{
GSocketConnectable *addr;
char *str;
find_ifname_and_index ();
str = g_strdup_printf ("%s%%%s", SCOPE_ID_TEST_ADDR, SCOPE_ID_TEST_IFNAME);
addr = g_network_address_new (str, SCOPE_ID_TEST_PORT);
g_free (str);
test_scope_id (addr);
g_object_unref (addr);
}
static void
test_uri_scope_id (void)
{
GSocketConnectable *addr;
char *uri;
GError *error = NULL;
find_ifname_and_index ();
uri = g_strdup_printf ("http://[%s%%%s]:%d/foo",
SCOPE_ID_TEST_ADDR,
SCOPE_ID_TEST_IFNAME,
SCOPE_ID_TEST_PORT);
addr = g_network_address_parse_uri (uri, 0, &error);
g_free (uri);
g_assert_no_error (error);
test_scope_id (addr);
g_object_unref (addr);
uri = g_strdup_printf ("http://[%s%%25%s]:%d/foo",
SCOPE_ID_TEST_ADDR,
SCOPE_ID_TEST_IFNAME,
SCOPE_ID_TEST_PORT);
addr = g_network_address_parse_uri (uri, 0, &error);
g_free (uri);
g_assert_no_error (error);
test_scope_id (addr);
g_object_unref (addr);
}
static void
test_loopback_basic (void)
{
GNetworkAddress *addr; /* owned */
addr = G_NETWORK_ADDRESS (g_network_address_new_loopback (666));
/* Test basic properties. */
g_assert_cmpstr (g_network_address_get_hostname (addr), ==, "localhost");
g_assert_cmpuint (g_network_address_get_port (addr), ==, 666);
g_assert_null (g_network_address_get_scheme (addr));
g_object_unref (addr);
}
static void
assert_socket_address_matches (GSocketAddress *a,
const gchar *expected_address,
guint16 expected_port)
{
GInetSocketAddress *sa;
gchar *str; /* owned */
g_assert (G_IS_INET_SOCKET_ADDRESS (a));
sa = G_INET_SOCKET_ADDRESS (a);
g_assert_cmpint (g_inet_socket_address_get_port (sa), ==, expected_port);
str = g_inet_address_to_string (g_inet_socket_address_get_address (sa));
g_assert_cmpstr (str, ==, expected_address);
g_free (str);
}
static void
test_loopback_sync (void)
{
GSocketConnectable *addr; /* owned */
GSocketAddressEnumerator *enumerator; /* owned */
GSocketAddress *a; /* owned */
GError *error = NULL;
addr = g_network_address_new_loopback (616);
enumerator = g_socket_connectable_enumerate (addr);
/* IPv6 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "::1", 616);
g_object_unref (a);
/* IPv4 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "127.0.0.1", 616);
g_object_unref (a);
/* End of results. */
g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
g_assert_no_error (error);
g_object_unref (enumerator);
g_object_unref (addr);
}
typedef struct {
GList/*<owned GSocketAddress> */ *addrs; /* owned */
GMainLoop *loop; /* owned */
} AsyncData;
static void
got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data)
{
GSocketAddressEnumerator *enumerator;
AsyncData *data;
GSocketAddress *a; /* owned */
GError *error = NULL;
enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source_object);
data = user_data;
a = g_socket_address_enumerator_next_finish (enumerator, result, &error);
g_assert_no_error (error);
if (a == NULL)
{
/* End of results. */
data->addrs = g_list_reverse (data->addrs);
g_main_loop_quit (data->loop);
}
else
{
g_assert (G_IS_INET_SOCKET_ADDRESS (a));
data->addrs = g_list_prepend (data->addrs, a);
g_socket_address_enumerator_next_async (enumerator, NULL,
got_addr, user_data);
}
}
static void
test_loopback_async (void)
{
GSocketConnectable *addr; /* owned */
GSocketAddressEnumerator *enumerator; /* owned */
AsyncData data = { 0, };
addr = g_network_address_new_loopback (610);
enumerator = g_socket_connectable_enumerate (addr);
/* Get all the addresses. */
data.addrs = NULL;
data.loop = g_main_loop_new (NULL, FALSE);
g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
g_main_loop_run (data.loop);
g_main_loop_unref (data.loop);
/* Check results. */
g_assert_cmpuint (g_list_length (data.addrs), ==, 2);
assert_socket_address_matches (data.addrs->data, "::1", 610);
assert_socket_address_matches (data.addrs->next->data, "127.0.0.1", 610);
g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
g_object_unref (enumerator);
g_object_unref (addr);
}
static void
test_to_string (void)
{
GSocketConnectable *addr = NULL;
gchar *str = NULL;
GError *error = NULL;
/* Without port. */
addr = g_network_address_new ("some-hostname", 0);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "some-hostname");
g_free (str);
g_object_unref (addr);
/* With port. */
addr = g_network_address_new ("some-hostname", 123);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "some-hostname:123");
g_free (str);
g_object_unref (addr);
/* With scheme and port. */
addr = g_network_address_parse_uri ("http://some-hostname:123", 80, &error);
g_assert_no_error (error);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "http:some-hostname:123");
g_free (str);
g_object_unref (addr);
/* Loopback. */
addr = g_network_address_new ("localhost", 456);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "localhost:456");
g_free (str);
g_object_unref (addr);
}
int
main (int argc, char *argv[])
{
gint i;
gchar *path;
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/network-address/basic", test_basic);
for (i = 0; i < G_N_ELEMENTS (host_tests); i++)
{
path = g_strdup_printf ("/network-address/parse-host/%d", i);
g_test_add_data_func (path, &host_tests[i], test_parse_host);
g_free (path);
}
for (i = 0; i < G_N_ELEMENTS (uri_tests); i++)
{
path = g_strdup_printf ("/network-address/parse-uri/%d", i);
g_test_add_data_func (path, &uri_tests[i], test_parse_uri);
g_free (path);
}
for (i = 0; i < G_N_ELEMENTS (address_tests); i++)
{
path = g_strdup_printf ("/network-address/resolve-address/%d", i);
g_test_add_data_func (path, &address_tests[i], test_resolve_address);
g_free (path);
}
for (i = 0; i < G_N_ELEMENTS (address_tests); i++)
{
path = g_strdup_printf ("/gresolver/resolve-address/%d", i);
g_test_add_data_func (path, &address_tests[i], test_resolve_address_gresolver);
g_free (path);
}
g_test_add_func ("/network-address/scope-id", test_host_scope_id);
g_test_add_func ("/network-address/uri-scope-id", test_uri_scope_id);
g_test_add_func ("/network-address/loopback/basic", test_loopback_basic);
g_test_add_func ("/network-address/loopback/sync", test_loopback_sync);
g_test_add_func ("/network-address/loopback/async", test_loopback_async);
g_test_add_func ("/network-address/to-string", test_to_string);
return g_test_run ();
}