#define EFL_NET_IP_ADDRESS_PROTECTED 1 #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #ifdef _WIN32 # include #endif #include "Ecore.h" #include "Ecore_Con.h" #include "ecore_con_private.h" typedef struct _Efl_Net_Ip_Address_Data { char string[INET6_ADDRSTRLEN + sizeof("[]:65536")]; union { struct sockaddr addr; struct sockaddr_in ipv4; struct sockaddr_in6 ipv6; }; Eina_Slice addr_slice; } Efl_Net_Ip_Address_Data; #define MY_CLASS EFL_NET_IP_ADDRESS_CLASS EOLIAN static Eo * _efl_net_ip_address_efl_object_finalize(Eo *o, Efl_Net_Ip_Address_Data *pd) { const uint16_t *pport; o = efl_finalize(efl_super(o, MY_CLASS)); if (!o) return NULL; EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->addr.sa_family == 0, NULL); if (!efl_net_ip_port_fmt(pd->string, sizeof(pd->string), &pd->addr)) { ERR("Could not format address!"); return NULL; } if (pd->addr.sa_family == AF_INET6) pport = &pd->ipv6.sin6_port; else pport = &pd->ipv4.sin_port; if (*pport == 0) /* port == 0, no ":0" in the string */ { char *d = strrchr(pd->string, ':'); EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL); *d = '\0'; } return o; } EOLIAN static const char * _efl_net_ip_address_string_get(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return pd->string; } EOLIAN static void _efl_net_ip_address_family_set(Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd, int family) { if (pd->addr.sa_family == family) return; EINA_SAFETY_ON_TRUE_RETURN(pd->addr.sa_family != 0); EINA_SAFETY_ON_TRUE_RETURN((family != AF_INET) && (family != AF_INET6)); pd->addr.sa_family = family; if (family == AF_INET6) { pd->addr_slice.mem = &pd->ipv6.sin6_addr; pd->addr_slice.len = sizeof(pd->ipv6.sin6_addr); } else { pd->addr_slice.mem = &pd->ipv4.sin_addr; pd->addr_slice.len = sizeof(pd->ipv4.sin_addr); } } EOLIAN static int _efl_net_ip_address_family_get(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return pd->addr.sa_family; } EOLIAN static void _efl_net_ip_address_port_set(Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd, uint16_t port) { uint16_t *pport, nport = htons(port); EINA_SAFETY_ON_TRUE_RETURN(pd->addr.sa_family == 0); if (pd->addr.sa_family == AF_INET6) pport = &pd->ipv6.sin6_port; else pport = &pd->ipv4.sin_port; if (*pport == nport) return; if (*pport) { ERR("port already set to %hu, new %hu", ntohs(*pport), port); return; } *pport = nport; } EOLIAN static uint16_t _efl_net_ip_address_port_get(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { const uint16_t *pport; EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->addr.sa_family == 0, 0); if (pd->addr.sa_family == AF_INET6) pport = &pd->ipv6.sin6_port; else pport = &pd->ipv4.sin_port; return ntohs(*pport); } EOLIAN static void _efl_net_ip_address_address_set(Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd, Eina_Slice address) { Eina_Rw_Slice rw_slice; size_t i; EINA_SAFETY_ON_TRUE_RETURN(pd->addr.sa_family == 0); rw_slice.mem = (void *)pd->addr_slice.mem; rw_slice.len = pd->addr_slice.len; EINA_SAFETY_ON_TRUE_RETURN(rw_slice.len != address.len); if (eina_slice_compare(eina_rw_slice_slice_get(rw_slice), address) == 0) return; for (i = 0; i < rw_slice.len; i++) { if (rw_slice.bytes[i]) { char old_str[INET6_ADDRSTRLEN] = ""; char new_str[INET6_ADDRSTRLEN] = ""; if (!inet_ntop(pd->addr.sa_family, rw_slice.mem, old_str, sizeof(old_str))) { old_str[0] = '?'; old_str[1] = '\0'; } if (!inet_ntop(pd->addr.sa_family, address.mem, new_str, sizeof(new_str))) { new_str[0] = '?'; new_str[1] = '\0'; } ERR("address already set to %s, new %s", old_str, new_str); return; } } eina_rw_slice_copy(rw_slice, address); } EOLIAN static Eina_Slice _efl_net_ip_address_address_get(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return pd->addr_slice; } EOLIAN static void _efl_net_ip_address_sockaddr_set(Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd, const void *ptr) { const struct sockaddr *sockaddr = ptr; EINA_SAFETY_ON_TRUE_RETURN(pd->addr.sa_family != 0); EINA_SAFETY_ON_NULL_RETURN(sockaddr); EINA_SAFETY_ON_TRUE_RETURN((sockaddr->sa_family != AF_INET) && (sockaddr->sa_family != AF_INET6)); if (sockaddr->sa_family == AF_INET6) { memcpy(&pd->ipv6, sockaddr, sizeof(pd->ipv6)); pd->addr_slice.mem = &pd->ipv6.sin6_addr; pd->addr_slice.len = sizeof(pd->ipv6.sin6_addr); } else { memcpy(&pd->ipv4, sockaddr, sizeof(pd->ipv4)); pd->addr_slice.mem = &pd->ipv4.sin_addr; pd->addr_slice.len = sizeof(pd->ipv4.sin_addr); } } EOLIAN static const void * _efl_net_ip_address_sockaddr_get(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return &pd->addr; } #define IPV4_ADDR_GET(pd) ntohl(pd->ipv4.sin_addr.s_addr) EOLIAN static Eina_Bool _efl_net_ip_address_ipv4_class_a_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET) && IN_CLASSA(IPV4_ADDR_GET(pd)); } EOLIAN static Eina_Bool _efl_net_ip_address_ipv4_class_b_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET) && IN_CLASSB(IPV4_ADDR_GET(pd)); } EOLIAN static Eina_Bool _efl_net_ip_address_ipv4_class_c_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET) && IN_CLASSC(IPV4_ADDR_GET(pd)); } EOLIAN static Eina_Bool _efl_net_ip_address_ipv4_class_d_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET) && IN_CLASSD(IPV4_ADDR_GET(pd)); } EOLIAN static Eina_Bool _efl_net_ip_address_ipv6_v4mapped_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET6) && IN6_IS_ADDR_V4MAPPED(&pd->ipv6.sin6_addr); } EOLIAN static Eina_Bool _efl_net_ip_address_ipv6_v4compat_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET6) && IN6_IS_ADDR_V4COMPAT(&pd->ipv6.sin6_addr); } EOLIAN static Eina_Bool _efl_net_ip_address_ipv6_local_link_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET6) && IN6_IS_ADDR_LINKLOCAL(&pd->ipv6.sin6_addr); } EOLIAN static Eina_Bool _efl_net_ip_address_ipv6_local_site_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { return (pd->addr.sa_family == AF_INET6) && IN6_IS_ADDR_SITELOCAL(&pd->ipv6.sin6_addr); } EOLIAN static Eina_Bool _efl_net_ip_address_multicast_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { if (pd->addr.sa_family == AF_INET6) return IN6_IS_ADDR_MULTICAST(&pd->ipv6.sin6_addr); else return IN_MULTICAST(IPV4_ADDR_GET(pd)); } EOLIAN static Eina_Bool _efl_net_ip_address_loopback_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { if (pd->addr.sa_family == AF_INET6) return IN6_IS_ADDR_LOOPBACK(&pd->ipv6.sin6_addr); else return IPV4_ADDR_GET(pd) == INADDR_LOOPBACK; } EOLIAN static Eina_Bool _efl_net_ip_address_any_check(const Eo *o EINA_UNUSED, Efl_Net_Ip_Address_Data *pd) { size_t i; for (i = 0; i < pd->addr_slice.len; i++) { if (pd->addr_slice.bytes[i]) return EINA_FALSE; } return i > 0; } EOLIAN static Efl_Net_Ip_Address * _efl_net_ip_address_create(Eo *cls, void *pd EINA_UNUSED, uint16_t port, const Eina_Slice address) { int family; EINA_SAFETY_ON_TRUE_RETURN_VAL(address.len != 4 && address.len != 16, NULL); if (address.len == 16) family = AF_INET6; else family = AF_INET; return efl_add_ref(cls, NULL, efl_net_ip_address_family_set(efl_added, family), efl_net_ip_address_port_set(efl_added, port), efl_net_ip_address_set(efl_added, address)); } EOLIAN static Efl_Net_Ip_Address * _efl_net_ip_address_create_sockaddr(Eo *cls, void *pd EINA_UNUSED, const void *ptr) { const struct sockaddr *sockaddr = ptr; EINA_SAFETY_ON_NULL_RETURN_VAL(sockaddr, NULL); EINA_SAFETY_ON_TRUE_RETURN_VAL((sockaddr->sa_family != AF_INET) && (sockaddr->sa_family != AF_INET6), NULL); return efl_add_ref(cls, NULL, efl_net_ip_address_sockaddr_set(efl_added, sockaddr)); } EOLIAN static Efl_Net_Ip_Address * _efl_net_ip_address_parse(Eo *cls, void *pd EINA_UNUSED, const char *numeric_address) { struct sockaddr_storage ss; Eina_Bool r; const char *address = numeric_address; char *tmp = NULL; EINA_SAFETY_ON_NULL_RETURN_VAL(numeric_address, NULL); if (numeric_address[0] != '[') { const char *p = strchr(numeric_address, ':'); if (p) { p = strchr(p + 1, ':'); if (p) { size_t len = strlen(numeric_address); /* IPv6 no braces: ::1, etc... no port, add braces */ tmp = malloc(len + sizeof("[]")); EINA_SAFETY_ON_NULL_RETURN_VAL(tmp, NULL); tmp[0] = '['; memcpy(tmp + 1, numeric_address, len); tmp[1 + len] = ']'; tmp[1 + len + 1] = '\0'; address = tmp; } } } r = efl_net_ip_port_parse(address, &ss); free(tmp); if (!r) { DBG("could not parse numeric address: %s", numeric_address); return NULL; } return efl_add_ref(cls, NULL, efl_net_ip_address_sockaddr_set(efl_added, &ss)); } typedef struct _Efl_Net_Ip_Address_Resolve_Context { Efl_Net_Ip_Address_Resolve_Results *result; Ecore_Thread *thread; Efl_Promise *promise; } Efl_Net_Ip_Address_Resolve_Context; static void _efl_net_ip_address_resolve_results_free(void *data) { Efl_Net_Ip_Address_Resolve_Results *r = data; if (r->results) { Eina_Array_Iterator it; unsigned int i; Efl_Net_Ip_Address *o; EINA_ARRAY_ITER_NEXT(r->results, i, o, it) efl_unref(o); eina_array_free(r->results); r->results = NULL; } eina_stringshare_replace(&r->canonical_name, NULL); eina_stringshare_replace(&r->request_address, NULL); free(r); } static void _efl_net_ip_address_resolve_del(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Net_Ip_Address_Resolve_Context *ctx = data; ctx->promise = NULL; if (ctx->thread) { ecore_thread_cancel(ctx->thread); ctx->thread = NULL; } if (ctx->result) { _efl_net_ip_address_resolve_results_free(ctx->result); ctx->result = NULL; } free(ctx); } static inline int _efl_net_ip_address_find(const Eina_Array *array, const struct sockaddr *addr) { Eina_Array_Iterator it; unsigned int i; const Efl_Net_Ip_Address *o; if (addr->sa_family == AF_INET6) { EINA_ARRAY_ITER_NEXT(array, i, o, it) { const struct sockaddr *other = efl_net_ip_address_sockaddr_get(o); if (other->sa_family == AF_INET6) { if (memcmp(other, addr, sizeof(struct sockaddr_in6)) == 0) return (int)i; } } } else { EINA_ARRAY_ITER_NEXT(array, i, o, it) { const struct sockaddr *other = efl_net_ip_address_sockaddr_get(o); if (other->sa_family == AF_INET) { if (memcmp(other, addr, sizeof(struct sockaddr_in)) == 0) return (int)i; } } } return -1; } static void _efl_net_ip_address_resolve_done(void *data, const char *host, const char *port, const struct addrinfo *hints EINA_UNUSED, struct addrinfo *result, int gai_error) { Efl_Net_Ip_Address_Resolve_Context *ctx = data; Efl_Net_Ip_Address_Resolve_Results *r; const struct addrinfo *a; DBG("done resolving '%s' (host='%s', port='%s'): %s", ctx->result->request_address, host, port, gai_error ? gai_strerror(gai_error) : "success"); ctx->thread = NULL; if (gai_error) { Eina_Error err = EFL_NET_ERROR_COULDNT_RESOLVE_HOST; if (gai_error == EAI_SYSTEM) err = errno; efl_promise_failed_set(ctx->promise, err); return; } ctx->result->results = eina_array_new(16); if (!ctx->result->results) { efl_promise_failed_set(ctx->promise, ENOMEM); return; } r = ctx->result; ctx->result = NULL; /* steal for efl_promise_value_set() */ for (a = result; a != NULL; a = a->ai_next) { Eo *o; if (EINA_UNLIKELY((r->canonical_name == NULL) && (a->ai_canonname != NULL))) r->canonical_name = eina_stringshare_add(a->ai_canonname); /* some addresses get duplicated with different options that we * do not care, so check for duplicates. */ if (EINA_UNLIKELY(_efl_net_ip_address_find(r->results, a->ai_addr) >= 0)) continue; o = efl_net_ip_address_create_sockaddr(EFL_NET_IP_ADDRESS_CLASS, a->ai_addr); if (o) { if (!eina_array_push(r->results, o)) efl_unref(o); } } freeaddrinfo(result); efl_promise_value_set(ctx->promise, r, _efl_net_ip_address_resolve_results_free); } EOLIAN static Efl_Future * _efl_net_ip_address_resolve(Eo *cls EINA_UNUSED, void *pd EINA_UNUSED, const char *address, int family, int flags) { Efl_Net_Ip_Address_Resolve_Context *ctx; struct addrinfo hints = { }; const char *host = NULL, *port = NULL; Eina_Bool r; char *str; EINA_SAFETY_ON_NULL_RETURN_VAL(address, NULL); if (family == 0) family = AF_UNSPEC; EINA_SAFETY_ON_TRUE_RETURN_VAL((family != AF_UNSPEC) && (family != AF_INET) && (family != AF_INET6), NULL); if (flags == 0) flags = AI_ADDRCONFIG | AI_V4MAPPED | AI_CANONNAME; hints.ai_family = family; hints.ai_flags = flags; str = strdup(address); EINA_SAFETY_ON_NULL_RETURN_VAL(str, NULL); r = efl_net_ip_port_split(str, &host, &port); if ((!r) || (!host) || (host[0] == '\0')) { host = address; port = "0"; } if (!port) port = "0"; ctx = calloc(1, sizeof(Efl_Net_Ip_Address_Resolve_Context)); EINA_SAFETY_ON_NULL_GOTO(ctx, error_ctx); ctx->result = calloc(1, sizeof(Efl_Net_Ip_Address_Resolve_Results)); EINA_SAFETY_ON_NULL_GOTO(ctx->result, error_result); ctx->result->request_address = eina_stringshare_add(address); EINA_SAFETY_ON_NULL_GOTO(ctx->result->request_address, error_result_address); ctx->thread = efl_net_ip_resolve_async_new(host, port, &hints, _efl_net_ip_address_resolve_done, ctx); EINA_SAFETY_ON_NULL_GOTO(ctx->thread, error_thread); ctx->promise = efl_add(EFL_PROMISE_CLASS, efl_main_loop_get(), efl_event_callback_add(efl_added, EFL_EVENT_DEL, _efl_net_ip_address_resolve_del, ctx)); EINA_SAFETY_ON_NULL_GOTO(ctx->promise, error_promise); free(str); return efl_promise_future_get(ctx->promise); error_promise: ecore_thread_cancel(ctx->thread); error_thread: eina_stringshare_del(ctx->result->request_address); error_result_address: free(ctx->result); error_result: free(ctx); error_ctx: free(str); return NULL; } #include "efl_net_ip_address.eo.c"