efl/src/tests/ecore_con/ecore_con_test_efl_net_ip_a...

1207 lines
36 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#include <Ecore.h>
#include <Ecore_Con.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#include <ctype.h>
#include "ecore_con_suite.h"
#include "ecore_con_private.h"
struct log_ctx {
const char *dom;
const char *msg;
int level;
unsigned int did;
};
/* tests should not output on success, just uncomment this for debugging */
//#define SHOW_LOG 1
static void
_eina_test_safety_print_cb(const Eina_Log_Domain *d, Eina_Log_Level level, const char *file, const char *fnc, int line, const char *fmt, void *data, va_list args EINA_UNUSED)
{
struct log_ctx *ctx = data;
const char *str;
if (ctx->level != level) goto log;
if (strcmp(fmt, "%s") != 0)
str = fmt;
else
{
va_list cp_args;
va_copy(cp_args, args);
str = va_arg(cp_args, const char *);
va_end(cp_args);
}
if (ctx->dom)
{
if ((!d->name) || (strcmp(ctx->dom, d->name) != 0))
goto log;
}
if (ctx->msg)
{
if (strcmp(ctx->msg, str) != 0)
goto log;
}
ctx->did++;
#ifndef SHOW_LOG
return;
#endif
log:
eina_log_print_cb_stderr(d, level, file, fnc, line, fmt, NULL, args);
}
#define TRAP_ERRORS_BEGIN(_dom, _level, _msg) \
do \
{ \
struct log_ctx _log_ctx = { \
.dom = #_dom, \
.level = EINA_LOG_LEVEL_ ## _level, \
.msg = _msg, \
}; \
eina_log_print_cb_set(_eina_test_safety_print_cb, &_log_ctx)
#define TRAP_ERRORS_FINISH(cnt) \
eina_log_print_cb_set(eina_log_print_cb_stderr, NULL); \
if (_log_ctx.did != cnt) \
{ \
ck_abort_msg("Expected error %u (did: %u) messages to be logged to domain=%s, level=%d, mesage='%s'", cnt, _log_ctx.did, _log_ctx.dom, _log_ctx.level, _log_ctx.msg); \
} \
} \
while (0)
static Eina_Value
_timeout(void *data,
const Eina_Value t,
const Eina_Future *dead EINA_UNUSED)
{
Eina_Bool *did = data;
if (t.type == EINA_VALUE_TYPE_ERROR)
return t;
*did = EINA_TRUE;
ck_abort_msg("timed out!");
return t;
}
#define LOOP_WITH_TIMEOUT(t) \
do \
{ \
Eina_Bool _did_timeout = EINA_FALSE; \
Eina_Future *_timeout_future = efl_loop_timeout(efl_main_loop_get(), t); \
eina_future_then(_timeout_future, _timeout, &_did_timeout, NULL); \
mark_point(); \
ecore_main_loop_begin(); \
if (!_did_timeout) eina_future_cancel(_timeout_future); \
else ck_abort_msg("Timed out!"); \
} \
while (0)
struct resolve_ctx {
Eina_Value *results;
Eina_Stringshare *canonical_name;
Eina_Stringshare *request_address;
Eina_Future *future;
Eina_Error err;
};
static void
_resolve_cleanup(struct resolve_ctx *ctx)
{
mark_point();
if (ctx->results) eina_value_free(ctx->results);
ctx->results = NULL;
ctx->err = 0;
eina_stringshare_replace(&ctx->canonical_name, NULL);
eina_stringshare_replace(&ctx->request_address, NULL);
if (ctx->future) eina_future_cancel(ctx->future);
ctx->future = NULL;
}
static Eina_Bool
_resolve_found(const struct resolve_ctx *ctx, const char *string)
{
const Efl_Net_Ip_Address *o;
unsigned int i, len;
ck_assert_ptr_ne(ctx->results, NULL);
EINA_VALUE_ARRAY_FOREACH(ctx->results, len, i, o)
{
if (strcmp(string, efl_net_ip_address_string_get(o)) == 0)
return EINA_TRUE;
}
return EINA_FALSE;
}
#define _assert_found(...) _assert_found_internal(__FILE__, __LINE__, __VA_ARGS__)
static void
_assert_found_internal(const char *file, int line, const struct resolve_ctx *ctx, const char *string, Eina_Bool expected, Eina_Error err)
{
Eina_Bool found;
unsigned int i, len;
const Efl_Net_Ip_Address *o;
if (ctx->err != err)
_ck_assert_failed(file, line, "Failed",
"Expected error=%d (%s), got %d (%s) resolving=%s",
err, err ? eina_error_msg_get(err) : "success",
ctx->err, ctx->err ? eina_error_msg_get(ctx->err) : "success",
string,
NULL);
if (err) return;
found = _resolve_found(ctx, string);
if (found == expected) return;
fprintf(stderr, "ERROR: did%s expect '%s' in results:\n",
expected ? "" : " NOT", string);
EINA_VALUE_ARRAY_FOREACH(ctx->results, len, i, o)
fprintf(stderr, "result %u: %s\n", i, efl_net_ip_address_string_get(o));
_ck_assert_failed(file, line, "Failed",
"Expected found=%hhu, got %hhu resolving=%s",
expected, found,
string,
NULL);
}
static Eina_Value
_resolve_done(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
{
struct resolve_ctx *ctx = data;
Eina_Value_Array desc = { 0 };
mark_point();
if (eina_value_type_get(&v) == EINA_VALUE_TYPE_ERROR)
{
eina_value_error_get(&v, &ctx->err);
goto end;
}
eina_value_struct_get(&v, "canonical_name", &ctx->canonical_name);
eina_value_struct_get(&v, "request_address", &ctx->request_address);
eina_value_struct_get(&v, "results", &desc);
ctx->results = eina_value_new(EINA_VALUE_TYPE_ARRAY);
eina_value_pset(ctx->results, &desc);
end:
ecore_main_loop_quit();
ctx->future = NULL;
mark_point();
return v;
}
static void
_resolve(struct resolve_ctx *ctx, const char *address, int family, int flags)
{
ctx->future = efl_net_ip_address_resolve(
address, family, flags);
ck_assert_ptr_ne(ctx->future, NULL);
ctx->future = eina_future_then(ctx->future, _resolve_done, ctx, NULL);
LOOP_WITH_TIMEOUT(10);
}
/* IPv4 *****************************************************************/
static void
_ipv4_check(Eo *o, const struct sockaddr_in *addr)
{
Eina_Slice slice = { .mem = &addr->sin_addr, .len = sizeof(addr->sin_addr) };
Eina_Slice rs;
char buf[INET_ADDRSTRLEN + sizeof(":65536")] = "";
ck_assert_ptr_ne(o, NULL);
ck_assert_int_eq(efl_net_ip_address_family_get(o), AF_INET);
ck_assert_int_eq(efl_net_ip_address_port_get(o), eina_ntohs(addr->sin_port));
rs = efl_net_ip_address_get(o);
ck_assert_int_eq(eina_slice_compare(rs, slice), 0);
inet_ntop(AF_INET, slice.mem, buf, INET_ADDRSTRLEN);
if (addr->sin_port)
{
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
":%hu", eina_htons(addr->sin_port));
}
ck_assert_ptr_ne(efl_net_ip_address_string_get(o), NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), buf);
}
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_manual_ok)
{
struct sockaddr_in addr = {
.sin_family = AF_INET,
};
Eina_Slice slice = { .mem = &addr.sin_addr, .len = sizeof(addr.sin_addr) };
Eo *o;
addr.sin_port = eina_htons(12345);
addr.sin_addr.s_addr = eina_htonl(0xabcdefafU);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET),
efl_net_ip_address_port_set(efl_added, eina_ntohs(addr.sin_port)),
efl_net_ip_address_set(efl_added, slice));
_ipv4_check(o, &addr);
efl_unref(o);
addr.sin_port = eina_htons(8081);
addr.sin_addr.s_addr = eina_htonl(0);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET),
efl_net_ip_address_port_set(efl_added, eina_ntohs(addr.sin_port)),
efl_net_ip_address_set(efl_added, slice));
_ipv4_check(o, &addr);
efl_unref(o);
addr.sin_port = eina_htons(0);
addr.sin_addr.s_addr = eina_htonl(0x12345678);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET),
efl_net_ip_address_set(efl_added, slice));
_ipv4_check(o, &addr);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_manual_fail)
{
uint8_t c = 123;
Eina_Slice wrong_slice = { .mem = &c, .len = 1 };
struct sockaddr_in addr = {
.sin_family = AF_INET,
};
Eina_Slice slice = { .mem = &addr.sin_addr, .len = sizeof(addr.sin_addr) };
Eo *o;
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: pd->addr.sa_family == 0 is true");
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL);
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(1);
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, 12345));
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(2);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: pd->addr.sa_family == 0 is true");
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_set(efl_added, wrong_slice));
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(2);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: pd->addr.sa_family == 0 is true");
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_port_set(efl_added, 1234));
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(2);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: pd->addr.sa_family == 0 is true");
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_port_set(efl_added, 1234),
efl_net_ip_address_set(efl_added, wrong_slice));
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(3);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: rw_slice.len != address.len is true");
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET),
efl_net_ip_address_set(efl_added, wrong_slice));
_ipv4_check(o, &addr);
efl_unref(o);
TRAP_ERRORS_FINISH(1);
addr.sin_port = eina_htons(12345);
addr.sin_addr.s_addr = eina_htonl(0xabcdefafU);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET),
efl_net_ip_address_port_set(efl_added, eina_ntohs(addr.sin_port)),
efl_net_ip_address_set(efl_added, slice));
_ipv4_check(o, &addr);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "port already set to %hu, new %hu");
efl_net_ip_address_port_set(o, eina_ntohs(addr.sin_port));
TRAP_ERRORS_FINISH(0);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "port already set to %hu, new %hu");
efl_net_ip_address_port_set(o, 999);
TRAP_ERRORS_FINISH(1);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: rw_slice.len != address.len is true");
slice.len = 1;
efl_net_ip_address_set(o, slice);
TRAP_ERRORS_FINISH(1);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "address already set to %s, new %s");
slice.len = sizeof(addr.sin_addr.s_addr);
efl_net_ip_address_set(o, slice);
TRAP_ERRORS_FINISH(0);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "address already set to %s, new %s");
addr.sin_addr.s_addr = eina_htonl(0x12345678);
slice.len = sizeof(addr.sin_addr.s_addr);
efl_net_ip_address_set(o, slice);
TRAP_ERRORS_FINISH(1);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_create_ok)
{
struct sockaddr_in addr = {
.sin_family = AF_INET,
};
Eina_Slice slice = { .mem = &addr.sin_addr, .len = sizeof(addr.sin_addr) };
Eo *o;
addr.sin_port = eina_htons(12345);
addr.sin_addr.s_addr = eina_htonl(0xabcdefafU);
o = efl_net_ip_address_create(
eina_ntohs(addr.sin_port),
slice);
_ipv4_check(o, &addr);
efl_unref(o);
addr.sin_port = eina_htons(8081);
addr.sin_addr.s_addr = eina_htonl(0);
o = efl_net_ip_address_create(
eina_ntohs(addr.sin_port),
slice);
_ipv4_check(o, &addr);
efl_unref(o);
addr.sin_port = eina_htons(0);
addr.sin_addr.s_addr = eina_htonl(0x12345678);
o = efl_net_ip_address_create(
eina_ntohs(addr.sin_port),
slice);
_ipv4_check(o, &addr);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_create_fail)
{
uint8_t c = 123;
Eina_Slice wrong_slice = { .mem = &c, .len = 1 };
Eo *o;
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: address.len != 4 && address.len != 16 is true");
o = efl_net_ip_address_create(
1234,
wrong_slice);
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(1);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_create_sockaddr_ok)
{
struct sockaddr_in addr = {
.sin_family = AF_INET,
};
Eo *o;
addr.sin_port = eina_htons(12345);
addr.sin_addr.s_addr = eina_htonl(0xabcdefafU);
o = efl_net_ip_address_create_sockaddr(&addr);
ck_assert_ptr_ne(&addr, efl_net_ip_address_sockaddr_get(o));
_ipv4_check(o, &addr);
efl_unref(o);
addr.sin_port = eina_htons(0);
addr.sin_addr.s_addr = eina_htonl(0);
o = efl_net_ip_address_create_sockaddr(&addr);
ck_assert_ptr_ne(&addr, efl_net_ip_address_sockaddr_get(o));
_ipv4_check(o, &addr);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_create_sockaddr_fail)
{
struct sockaddr_in addr = {
.sin_family = AF_UNSPEC,
};
Eo *o;
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: sockaddr == NULL");
o = efl_net_ip_address_create_sockaddr(NULL);
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(1);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: (sockaddr->sa_family != AF_INET) && (sockaddr->sa_family != AF_INET6) is true");
o = efl_net_ip_address_create_sockaddr(&addr);
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(1);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_parse_ok)
{
Eo *o;
o = efl_net_ip_address_parse( "127.0.0.1:12345");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "127.0.0.1:12345");
efl_unref(o);
o = efl_net_ip_address_parse( "127.0.0.1:0");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "127.0.0.1");
efl_unref(o);
o = efl_net_ip_address_parse( "127.0.0.1");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "127.0.0.1");
efl_unref(o);
o = efl_net_ip_address_parse( "192.168.0.123:80");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "192.168.0.123:80");
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_parse_fail)
{
Eo *o;
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: numeric_address == NULL");
o = efl_net_ip_address_parse( NULL);
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(1);
/* incomplete numbers */
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_net_ip_address_parse( "127.");
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(0); /* no error messages! */
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_net_ip_address_parse( "127.0.0.");
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(0); /* no error messages! */
/* hostnames are not numeric, shouldn't return an object */
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_net_ip_address_parse( "google.com");
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(0); /* no error messages! */
/* port names are not numeric, shouldn't return an object */
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_net_ip_address_parse( "127.0.0.1:http");
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(0); /* no error messages! */
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_resolve_ok)
{
struct resolve_ctx ctx = { };
_resolve(&ctx, "localhost:http", 0, 0);
_assert_found(&ctx, "127.0.0.1:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, "localhost", 0, 0);
_assert_found(&ctx, "127.0.0.1", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, "127.0.0.1", 0, 0);
_assert_found(&ctx, "127.0.0.1", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, "127.0.0.1:http", 0, 0);
_assert_found(&ctx, "127.0.0.1:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, "127.0.0.1:80", 0, 0);
_assert_found(&ctx, "127.0.0.1:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, "localhost:80", 0, 0);
_assert_found(&ctx, "127.0.0.1:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, "localhost:http", AF_INET, 0);
_assert_found(&ctx, "[::1]:80", EINA_FALSE, 0);
_resolve_cleanup(&ctx);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_resolve_fail)
{
struct resolve_ctx ctx = { };
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: address == NULL");
ctx.future = efl_net_ip_address_resolve(
NULL, 0, 0);
ck_assert_ptr_eq(ctx.future, NULL);
TRAP_ERRORS_FINISH(1);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: (family != AF_UNSPEC) && (family != AF_INET) && (family != AF_INET6) is true");
ctx.future = efl_net_ip_address_resolve(
"localhost", 1234, 0);
ck_assert_ptr_eq(ctx.future, NULL);
TRAP_ERRORS_FINISH(1);
_resolve(&ctx, "xxlocalhost:xxhttp", 0, 0);
_assert_found(&ctx, "xxlocalhost:xxhttp", EINA_FALSE, EFL_NET_ERROR_COULDNT_RESOLVE_HOST);
_resolve_cleanup(&ctx);
_resolve(&ctx, "[::1]:http", AF_INET, 0);
_assert_found(&ctx, "[::1]:http", EINA_FALSE, EFL_NET_ERROR_COULDNT_RESOLVE_HOST);
_resolve_cleanup(&ctx);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv4_checks)
{
const struct test {
uint32_t addr;
Eina_Bool is_a, is_b, is_c, is_d, is_multicast, is_loopback, is_any;
} *itr, tests[] = {
#define TEST(_addr) \
{ .addr = _addr, \
.is_a = IN_CLASSA(_addr), \
.is_b = IN_CLASSB(_addr), \
.is_c = IN_CLASSC(_addr), \
.is_d = IN_CLASSD(_addr), \
.is_multicast = IN_MULTICAST(_addr) , \
.is_loopback = _addr == INADDR_LOOPBACK, \
.is_any = _addr == INADDR_ANY, \
}
TEST(INADDR_LOOPBACK),
TEST(INADDR_ANY),
TEST(0x0a000001),
TEST(0x80000002),
TEST(0xc0000003),
TEST(0xe0000004),
#undef TEST
};
for (itr = tests; itr < tests + sizeof(tests)/sizeof(tests[0]); itr++)
{
struct sockaddr_in a = {
.sin_family = AF_INET,
.sin_port = 0,
.sin_addr.s_addr = eina_htonl(itr->addr),
};
Eo *o = efl_net_ip_address_create_sockaddr(&a);
ck_assert_ptr_ne(o, NULL);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_a_check(o), itr->is_a);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_b_check(o), itr->is_b);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_c_check(o), itr->is_c);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_d_check(o), itr->is_d);
ck_assert_int_eq(efl_net_ip_address_multicast_check(o), itr->is_multicast);
ck_assert_int_eq(efl_net_ip_address_loopback_check(o), itr->is_loopback);
ck_assert_int_eq(efl_net_ip_address_any_check(o), itr->is_any);
ck_assert_int_eq(efl_net_ip_address_ipv6_v4mapped_check(o), EINA_FALSE);
ck_assert_int_eq(efl_net_ip_address_ipv6_v4compat_check(o), EINA_FALSE);
ck_assert_int_eq(efl_net_ip_address_ipv6_local_link_check(o), EINA_FALSE);
ck_assert_int_eq(efl_net_ip_address_ipv6_local_site_check(o), EINA_FALSE);
efl_unref(o);
}
}
EFL_END_TEST
/* IPv6 *****************************************************************/
static void
_ipv6_check(Eo *o, const struct sockaddr_in6 *addr)
{
Eina_Slice slice = { .mem = &addr->sin6_addr, .len = sizeof(addr->sin6_addr) };
Eina_Slice rs;
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")] = "";
ck_assert_ptr_ne(o, NULL);
ck_assert_int_eq(efl_net_ip_address_family_get(o), AF_INET6);
ck_assert_int_eq(efl_net_ip_address_port_get(o), eina_ntohs(addr->sin6_port));
rs = efl_net_ip_address_get(o);
ck_assert_int_eq(eina_slice_compare(rs, slice), 0);
buf[0] = '[';
inet_ntop(AF_INET6, slice.mem, buf + 1, INET6_ADDRSTRLEN);
buf[strlen(buf)] = ']';
if (addr->sin6_port)
{
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
":%hu", eina_htons(addr->sin6_port));
}
ck_assert_ptr_ne(efl_net_ip_address_string_get(o), NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), buf);
}
static void
_ipv6_set(struct sockaddr_in6 *addr, uint16_t s1, uint16_t s2, uint16_t s3, uint16_t s4, uint16_t s5, uint16_t s6, uint16_t s7, uint16_t s8)
{
uint16_t s[8] = { s1, s2, s3, s4, s5, s6, s7, s8 };
memcpy(&addr->sin6_addr, s, 16);
}
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_manual_ok)
{
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
};
Eina_Slice slice = { .mem = &addr.sin6_addr, .len = sizeof(addr.sin6_addr) };
Eo *o;
addr.sin6_port = eina_htons(12345);
_ipv6_set(&addr, 1, 2, 3, 4, 5, 6, 7, 8);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET6),
efl_net_ip_address_port_set(efl_added, eina_ntohs(addr.sin6_port)),
efl_net_ip_address_set(efl_added, slice));
_ipv6_check(o, &addr);
efl_unref(o);
addr.sin6_port = eina_htons(8081);
_ipv6_set(&addr, 0, 0, 0, 0, 0, 0, 0, 0);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET6),
efl_net_ip_address_port_set(efl_added, eina_ntohs(addr.sin6_port)),
efl_net_ip_address_set(efl_added, slice));
_ipv6_check(o, &addr);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_manual_fail)
{
uint8_t c = 123;
Eina_Slice wrong_slice = { .mem = &c, .len = 1 };
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
};
Eina_Slice slice = { .mem = &addr.sin6_addr, .len = sizeof(addr.sin6_addr) };
Eo *o;
/* generic errors checked at ecore_test_efl_net_ip_address_ipv4_manual_fail */
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: rw_slice.len != address.len is true");
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET6),
efl_net_ip_address_set(efl_added, wrong_slice));
_ipv6_check(o, &addr);
efl_unref(o);
TRAP_ERRORS_FINISH(1);
addr.sin6_port = eina_htons(12345);
_ipv6_set(&addr, 0, 0, 0, 0, 0, 0, 0, 1);
o = efl_add_ref(EFL_NET_IP_ADDRESS_CLASS, NULL,
efl_net_ip_address_family_set(efl_added, AF_INET6),
efl_net_ip_address_port_set(efl_added, eina_ntohs(addr.sin6_port)),
efl_net_ip_address_set(efl_added, slice));
_ipv6_check(o, &addr);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "port already set to %hu, new %hu");
efl_net_ip_address_port_set(o, eina_ntohs(addr.sin6_port));
TRAP_ERRORS_FINISH(0);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "port already set to %hu, new %hu");
efl_net_ip_address_port_set(o, 999);
TRAP_ERRORS_FINISH(1);
TRAP_ERRORS_BEGIN(eina_safety, ERR, "safety check failed: rw_slice.len != address.len is true");
slice.len = 1;
efl_net_ip_address_set(o, slice);
TRAP_ERRORS_FINISH(1);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "address already set to %s, new %s");
slice.len = sizeof(addr.sin6_addr);
efl_net_ip_address_set(o, slice);
TRAP_ERRORS_FINISH(0);
TRAP_ERRORS_BEGIN(ecore_con, ERR, "address already set to %s, new %s");
_ipv6_set(&addr, 1, 2, 3, 4, 5, 6, 7, 8);
slice.len = sizeof(addr.sin6_addr);
efl_net_ip_address_set(o, slice);
TRAP_ERRORS_FINISH(1);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_create_ok)
{
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
};
Eina_Slice slice = { .mem = &addr.sin6_addr, .len = sizeof(addr.sin6_addr) };
Eo *o;
addr.sin6_port = eina_htons(12365);
_ipv6_set(&addr, 1, 2, 3, 4, 5, 6, 7, 8);
o = efl_net_ip_address_create(
eina_ntohs(addr.sin6_port),
slice);
_ipv6_check(o, &addr);
efl_unref(o);
addr.sin6_port = eina_htons(8081);
_ipv6_set(&addr, 0, 0, 0, 0, 0, 0, 0, 0);
o = efl_net_ip_address_create(
eina_ntohs(addr.sin6_port),
slice);
_ipv6_check(o, &addr);
efl_unref(o);
addr.sin6_port = eina_htons(0);
_ipv6_set(&addr, 0, 0, 0, 0, 0, 0, 0, 1);
o = efl_net_ip_address_create(
eina_ntohs(addr.sin6_port),
slice);
_ipv6_check(o, &addr);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_create_sockaddr_ok)
{
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
};
Eo *o;
addr.sin6_port = eina_htons(12345);
_ipv6_set(&addr, 1, 2, 3, 4, 5, 6, 7, 8);
o = efl_net_ip_address_create_sockaddr(&addr);
ck_assert_ptr_ne(&addr, efl_net_ip_address_sockaddr_get(o));
_ipv6_check(o, &addr);
efl_unref(o);
addr.sin6_port = eina_htons(0);
_ipv6_set(&addr, 0, 0, 0, 0, 0, 0, 0, 0);
o = efl_net_ip_address_create_sockaddr(&addr);
ck_assert_ptr_ne(&addr, efl_net_ip_address_sockaddr_get(o));
_ipv6_check(o, &addr);
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_parse_ok)
{
Eo *o;
o = efl_net_ip_address_parse( "[::1]:12345");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::1]:12345");
efl_unref(o);
o = efl_net_ip_address_parse( "[::1]:0");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::1]");
efl_unref(o);
o = efl_net_ip_address_parse( "[::1]");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::1]");
efl_unref(o);
o = efl_net_ip_address_parse( "::1");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::1]");
efl_unref(o);
o = efl_net_ip_address_parse( "[::]:12345");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::]:12345");
efl_unref(o);
o = efl_net_ip_address_parse( "[::]:0");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::]");
efl_unref(o);
o = efl_net_ip_address_parse( "[::]");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::]");
efl_unref(o);
o = efl_net_ip_address_parse( "::");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::]");
efl_unref(o);
/* IPv4 Mapped */
o = efl_net_ip_address_parse( "[::ffff:192.168.0.1]:12345");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::ffff:192.168.0.1]:12345");
efl_unref(o);
o = efl_net_ip_address_parse( "[::ffff:192.168.0.1]");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::ffff:192.168.0.1]");
efl_unref(o);
/* IPv4 Compatible */
o = efl_net_ip_address_parse( "[::192.168.0.1]:12345");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::192.168.0.1]:12345");
efl_unref(o);
o = efl_net_ip_address_parse( "[::192.168.0.1]");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[::192.168.0.1]");
efl_unref(o);
/* Link Local */
o = efl_net_ip_address_parse( "[fe80::1]:12345");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[fe80::1]:12345");
efl_unref(o);
o = efl_net_ip_address_parse( "[fe80::1]");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[fe80::1]");
efl_unref(o);
/* Site Local */
o = efl_net_ip_address_parse( "[fc00::1]:12345");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[fc00::1]:12345");
efl_unref(o);
o = efl_net_ip_address_parse( "[fc00::1]");
ck_assert_ptr_ne(o, NULL);
ck_assert_str_eq(efl_net_ip_address_string_get(o), "[fc00::1]");
efl_unref(o);
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_parse_fail)
{
Eo *o;
/* generic error (null ptr) checked in ipv4_parse_fail */
/* incomplete numbers */
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_net_ip_address_parse( "::9999999");
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(0); /* no error messages! */
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_net_ip_address_parse( "ab:cd:ef:gh");
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(0); /* no error messages! */
/* port names are not numeric, shouldn't return an object */
TRAP_ERRORS_BEGIN(eina_safety, ERR, NULL);
o = efl_net_ip_address_parse( "[::1]:http");
ck_assert_ptr_eq(o, NULL);
TRAP_ERRORS_FINISH(0); /* no error messages! */
}
EFL_END_TEST
static const char *
_ipv6_localhost_check(void)
{
#ifndef ETC_HOSTS
#ifdef _WIN32
#define ETC_HOSTS "C:\\Windows\\System32\\Drivers\\etc\\hosts"
#else
#define ETC_HOSTS "/etc/hosts"
#endif
#endif
Eina_File *f;
Eina_Iterator *it;
Eina_File_Line *line;
static const char localhost[] = "localhost";
static const char localhost6[] = "localhost6";
const char *found = NULL;
f = eina_file_open(ETC_HOSTS, EINA_FALSE);
if (!f)
{
fprintf(stderr, "WARNING: your system misses %s: %s\n", ETC_HOSTS, eina_error_msg_get(eina_error_get() ? : errno));
return NULL;
}
it = eina_file_map_lines(f);
if (!it)
{
fprintf(stderr, "ERROR: could not map lines of %s\n", ETC_HOSTS);
goto end;
}
EINA_ITERATOR_FOREACH(it, line)
{
const char *p;
if (line->length < strlen("::1 localhost")) continue;
for (p = line->start; p < line->end; p++)
if (!isspace(p[0])) break;
if (p >= line->end) continue;
if (p[0] == '#') continue;
if ((size_t)(line->end - p) < strlen("::1 localhost")) continue;
if (memcmp(p, "::1", strlen("::1")) != 0) continue;
p += strlen("::1");
while (p < line->end)
{
const char *e;
if (!isspace(p[0]))
{
p = line->end;
break;
}
p++;
for (; p < line->end; p++)
if (!isspace(p[0])) break;
if (p >= line->end) break;
if (p[0] == '#')
{
p = line->end;
break;
}
for (e = p; e < line->end; e++)
if (isspace(e[0])) break;
if (e > p)
{
size_t len = e - p;
if ((len == strlen(localhost)) && (memcmp(p, localhost, strlen(localhost)) == 0))
found = localhost;
else if ((len == strlen(localhost6)) && (memcmp(p, localhost6, strlen(localhost6)) == 0))
found = localhost6;
}
p = e;
}
if (found) break;
}
eina_iterator_free(it);
end:
if (!found) fprintf(stderr, "WARNING: your system miss '::1 localhost' or '::1 localhost6' in %s\n", ETC_HOSTS);
eina_file_close(f);
return found;
}
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_resolve_ok)
{
struct resolve_ctx ctx = { };
char buf[4096];
const char *localhost_str;
localhost_str = _ipv6_localhost_check();
if (localhost_str)
{
snprintf(buf, sizeof(buf), "%s:http", localhost_str);
_resolve(&ctx, buf, 0, 0);
_assert_found(&ctx, "[::1]:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, localhost_str, 0, 0);
_assert_found(&ctx, "[::1]", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
}
_resolve(&ctx, "::1", 0, 0);
_assert_found(&ctx, "[::1]", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
#if defined(AI_V4MAPPED) && (AI_V4MAPPED > 0)
_resolve(&ctx, "127.0.0.1", AF_INET6, AI_V4MAPPED);
_assert_found(&ctx, "[::ffff:127.0.0.1]", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
#endif
_resolve(&ctx, "[::1]:http", 0, 0);
_assert_found(&ctx, "[::1]:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
_resolve(&ctx, "[::1]:80", 0, 0);
_assert_found(&ctx, "[::1]:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
if (localhost_str)
{
snprintf(buf, sizeof(buf), "%s:80", localhost_str);
_resolve(&ctx, buf, 0, 0);
_assert_found(&ctx, "[::1]:80", EINA_TRUE, 0);
_resolve_cleanup(&ctx);
snprintf(buf, sizeof(buf), "%s:http", localhost_str);
_resolve(&ctx, buf, AF_INET6, 0);
_assert_found(&ctx, "127.0.0.1:80", EINA_FALSE, 0);
_resolve_cleanup(&ctx);
}
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_resolve_fail)
{
struct resolve_ctx ctx = { };
/* generic checks at ipv4_resolve_fail */
#if defined(AI_V4MAPPED) && (AI_V4MAPPED > 0)
_resolve(&ctx, "127.0.0.1:http", AF_INET6, AI_CANONNAME); /* do NOT set V4MAPPED, but use non-zero */
_assert_found(&ctx, "127.0.0.1:http", EINA_FALSE, EFL_NET_ERROR_COULDNT_RESOLVE_HOST);
_resolve_cleanup(&ctx);
#endif
}
EFL_END_TEST
EFL_START_TEST(ecore_test_efl_net_ip_address_ipv6_checks)
{
const struct test {
const char *str;
} *itr, tests[] = {
#define TEST(_addr) { .str = _addr }
TEST("::1"),
TEST("::"),
TEST("1:2:3:4:5:6:7:8"),
TEST("::ffff:192.168.0.1"),
TEST("::192.168.0.1"),
TEST("fe80::1"),
TEST("fc00::2"),
#undef TEST
};
for (itr = tests; itr < tests + sizeof(tests)/sizeof(tests[0]); itr++)
{
struct sockaddr_in6 a = {
.sin6_family = AF_INET6,
.sin6_port = 0,
};
const struct in6_addr any = { };
struct in6_addr *ia = &a.sin6_addr;
int r;
r = inet_pton(AF_INET6, itr->str, ia);
ck_assert_int_eq(r, 1);
Eo *o = efl_net_ip_address_create_sockaddr(&a);
ck_assert_ptr_ne(o, NULL);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_a_check(o), EINA_FALSE);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_b_check(o), EINA_FALSE);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_c_check(o), EINA_FALSE);
ck_assert_int_eq(efl_net_ip_address_ipv4_class_d_check(o), EINA_FALSE);
ck_assert_int_eq(efl_net_ip_address_multicast_check(o), IN6_IS_ADDR_MULTICAST(ia));
ck_assert_int_eq(efl_net_ip_address_loopback_check(o), IN6_IS_ADDR_LOOPBACK(ia));
ck_assert_int_eq(efl_net_ip_address_any_check(o), memcmp(ia, &any, 16) == 0);
ck_assert_int_eq(efl_net_ip_address_ipv6_v4mapped_check(o), IN6_IS_ADDR_V4MAPPED(ia));
ck_assert_int_eq(efl_net_ip_address_ipv6_v4compat_check(o), IN6_IS_ADDR_V4COMPAT(ia));
ck_assert_int_eq(efl_net_ip_address_ipv6_local_link_check(o), IN6_IS_ADDR_LINKLOCAL(ia));
ck_assert_int_eq(efl_net_ip_address_ipv6_local_site_check(o), IN6_IS_ADDR_SITELOCAL(ia));
efl_unref(o);
}
}
EFL_END_TEST
void ecore_con_test_efl_net_ip_address(TCase *tc)
{
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_manual_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_manual_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_create_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_create_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_create_sockaddr_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_create_sockaddr_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_parse_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_parse_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_resolve_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_resolve_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv4_checks);
if (!getenv("EFL_TEST_ECORE_CON_IPV6"))
{
fputs("\nWARNING: your system has IPv6 disabled. Skipping IPv6 tests!\n\n"
"\nexport EFL_TEST_ECORE_CON_IPV6=1\n"
"\nTo enable it\n",
stderr);
return;
}
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_manual_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_manual_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_create_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_create_sockaddr_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_parse_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_parse_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_resolve_ok);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_resolve_fail);
tcase_add_test(tc, ecore_test_efl_net_ip_address_ipv6_checks);
}