diff options
Diffstat (limited to '3rdparty/plibsys/src/psocketaddress.c')
-rw-r--r-- | 3rdparty/plibsys/src/psocketaddress.c | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/3rdparty/plibsys/src/psocketaddress.c b/3rdparty/plibsys/src/psocketaddress.c new file mode 100644 index 0000000..2338c83 --- /dev/null +++ b/3rdparty/plibsys/src/psocketaddress.c @@ -0,0 +1,619 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pstring.h" +#include "psocketaddress.h" +#include "plibsys-private.h" + +#include <stdlib.h> +#include <string.h> + +#ifndef P_OS_WIN +# include <arpa/inet.h> +#endif + +#if defined (PLIBSYS_HAS_GETADDRINFO) && !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) +# undef PLIBSYS_HAS_GETADDRINFO +#endif + +#ifdef PLIBSYS_HAS_GETADDRINFO +# include <netdb.h> +#endif + +/* According to Open Group specifications */ +#ifndef INET_ADDRSTRLEN +# ifdef P_OS_WIN + /* On Windows it includes port number */ +# define INET_ADDRSTRLEN 22 +# else +# define INET_ADDRSTRLEN 16 +# endif +#endif + +#ifdef AF_INET6 +# ifndef INET6_ADDRSTRLEN +# ifdef P_OS_WIN + /* On Windows it includes port number */ +# define INET6_ADDRSTRLEN 65 +# else +# define INET6_ADDRSTRLEN 46 +# endif +# endif +#endif + +#ifdef P_OS_VMS +# if PLIBSYS_SIZEOF_VOID_P == 8 +# define addrinfo __addrinfo64 +# endif +#endif + +#if defined (P_OS_BEOS) || defined (P_OS_OS2) +# ifdef AF_INET6 +# undef AF_INET6 +# endif +#endif + +struct PSocketAddress_ { + PSocketFamily family; + union addr_ { + struct in_addr sin_addr; +#ifdef AF_INET6 + struct in6_addr sin6_addr; +#endif + } addr; + puint16 port; + puint32 flowinfo; + puint32 scope_id; +}; + +P_LIB_API PSocketAddress * +p_socket_address_new_from_native (pconstpointer native, + psize len) +{ + PSocketAddress *ret; + puint16 family; + + if (P_UNLIKELY (native == NULL || len == 0)) + return NULL; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) + return NULL; + + family = ((struct sockaddr *) native)->sa_family; + + if (family == AF_INET) { + if (len < sizeof (struct sockaddr_in)) { + P_WARNING ("PSocketAddress::p_socket_address_new_from_native: invalid IPv4 native size"); + p_free (ret); + return NULL; + } + + memcpy (&ret->addr.sin_addr, &((struct sockaddr_in *) native)->sin_addr, sizeof (struct in_addr)); + ret->family = P_SOCKET_FAMILY_INET; + ret->port = p_ntohs (((struct sockaddr_in *) native)->sin_port); + return ret; + } +#ifdef AF_INET6 + else if (family == AF_INET6) { + if (len < sizeof (struct sockaddr_in6)) { + P_WARNING ("PSocketAddress::p_socket_address_new_from_native: invalid IPv6 native size"); + p_free (ret); + return NULL; + } + + memcpy (&ret->addr.sin6_addr, + &((struct sockaddr_in6 *) native)->sin6_addr, + sizeof (struct in6_addr)); + + ret->family = P_SOCKET_FAMILY_INET6; + ret->port = p_ntohs (((struct sockaddr_in *) native)->sin_port); +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO + ret->flowinfo = ((struct sockaddr_in6 *) native)->sin6_flowinfo; +#endif +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID + ret->scope_id = ((struct sockaddr_in6 *) native)->sin6_scope_id; +#endif + return ret; + } +#endif + else { + p_free (ret); + return NULL; + } +} + +P_LIB_API PSocketAddress * +p_socket_address_new (const pchar *address, + puint16 port) +{ + PSocketAddress *ret; +#if defined (P_OS_WIN) || defined (PLIBSYS_HAS_GETADDRINFO) + struct addrinfo hints; + struct addrinfo *res; +#endif + +#ifdef P_OS_WIN + struct sockaddr_storage sa; + struct sockaddr_in *sin = (struct sockaddr_in *) &sa; +# ifdef AF_INET6 + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &sa; +# endif /* AF_INET6 */ + pint len; +#endif /* P_OS_WIN */ + + if (P_UNLIKELY (address == NULL)) + return NULL; + +#if (defined (P_OS_WIN) || defined (PLIBSYS_HAS_GETADDRINFO)) && defined (AF_INET6) + if (strchr (address, ':') != NULL) { + memset (&hints, 0, sizeof (hints)); + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; +# ifndef P_OS_UNIXWARE + hints.ai_flags = AI_NUMERICHOST; +# endif + + if (P_UNLIKELY (getaddrinfo (address, NULL, &hints, &res) != 0)) + return NULL; + + if (P_LIKELY (res->ai_family == AF_INET6 && + res->ai_addrlen == sizeof (struct sockaddr_in6))) { + ((struct sockaddr_in6 *) res->ai_addr)->sin6_port = p_htons (port); + ret = p_socket_address_new_from_native (res->ai_addr, res->ai_addrlen); + } else + ret = NULL; + + freeaddrinfo (res); + + return ret; + } +#endif + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) { + P_ERROR ("PSocketAddress::p_socket_address_new: failed to allocate memory"); + return NULL; + } + + ret->port = port; + +#ifdef P_OS_WIN + memset (&sa, 0, sizeof (sa)); + len = sizeof (sa); + sin->sin_family = AF_INET; + + if (WSAStringToAddressA ((LPSTR) address, AF_INET, NULL, (LPSOCKADDR) &sa, &len) == 0) { + memcpy (&ret->addr.sin_addr, &sin->sin_addr, sizeof (struct in_addr)); + ret->family = P_SOCKET_FAMILY_INET; + return ret; + } +# ifdef AF_INET6 + else { + sin6->sin6_family = AF_INET6; + + if (WSAStringToAddressA ((LPSTR) address, AF_INET6, NULL, (LPSOCKADDR) &sa, &len) == 0) { + memcpy (&ret->addr.sin6_addr, &sin6->sin6_addr, sizeof (struct in6_addr)); + ret->family = P_SOCKET_FAMILY_INET6; + return ret; + } + } +# endif /* AF_INET6 */ +#else /* P_OS_WIN */ + if (inet_pton (AF_INET, address, &ret->addr.sin_addr) > 0) { + ret->family = P_SOCKET_FAMILY_INET; + return ret; + } +# ifdef AF_INET6 + else if (inet_pton (AF_INET6, address, &ret->addr.sin6_addr) > 0) { + ret->family = P_SOCKET_FAMILY_INET6; + return ret; + } +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + + p_free (ret); + return NULL; +} + +P_LIB_API PSocketAddress * +p_socket_address_new_any (PSocketFamily family, + puint16 port) +{ + PSocketAddress *ret; + puchar any_addr[] = {0, 0, 0, 0}; +#ifdef AF_INET6 + struct in6_addr any6_addr = IN6ADDR_ANY_INIT; +#endif + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) { + P_ERROR ("PSocketAddress::p_socket_address_new_any: failed to allocate memory"); + return NULL; + } + + if (family == P_SOCKET_FAMILY_INET) + memcpy (&ret->addr.sin_addr, any_addr, sizeof (any_addr)); +#ifdef AF_INET6 + else if (family == P_SOCKET_FAMILY_INET6) + memcpy (&ret->addr.sin6_addr, &any6_addr.s6_addr, sizeof (any6_addr.s6_addr)); +#endif + else { + p_free (ret); + return NULL; + } + + ret->family = family; + ret->port = port; + + return ret; +} + +P_LIB_API PSocketAddress * +p_socket_address_new_loopback (PSocketFamily family, + puint16 port) +{ + PSocketAddress *ret; + puchar loop_addr[] = {127, 0, 0, 0}; +#ifdef AF_INET6 + struct in6_addr loop6_addr = IN6ADDR_LOOPBACK_INIT; +#endif + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) { + P_ERROR ("PSocketAddress::p_socket_address_new_loopback: failed to allocate memory"); + return NULL; + } + + if (family == P_SOCKET_FAMILY_INET) + memcpy (&ret->addr.sin_addr, loop_addr, sizeof (loop_addr)); +#ifdef AF_INET6 + else if (family == P_SOCKET_FAMILY_INET6) + memcpy (&ret->addr.sin6_addr, &loop6_addr.s6_addr, sizeof (loop6_addr.s6_addr)); +#endif + else { + p_free (ret); + return NULL; + } + + ret->family = family; + ret->port = port; + + return ret; +} + +P_LIB_API pboolean +p_socket_address_to_native (const PSocketAddress *addr, + ppointer dest, + psize destlen) +{ + struct sockaddr_in *sin; +#ifdef AF_INET6 + struct sockaddr_in6 *sin6; +#endif + + if (P_UNLIKELY (addr == NULL || dest == NULL || destlen == 0)) + return FALSE; + + sin = (struct sockaddr_in *) dest; +#ifdef AF_INET6 + sin6 = (struct sockaddr_in6 *) dest; +#endif + + if (addr->family == P_SOCKET_FAMILY_INET) { + if (P_UNLIKELY (destlen < sizeof (struct sockaddr_in))) { + P_WARNING ("PSocketAddress::p_socket_address_to_native: invalid buffer size for IPv4"); + return FALSE; + } + + memcpy (&sin->sin_addr, &addr->addr.sin_addr, sizeof (struct in_addr)); + sin->sin_family = AF_INET; + sin->sin_port = p_htons (addr->port); + memset (sin->sin_zero, 0, sizeof (sin->sin_zero)); + return TRUE; + } +#ifdef AF_INET6 + else if (addr->family == P_SOCKET_FAMILY_INET6) { + if (P_UNLIKELY (destlen < sizeof (struct sockaddr_in6))) { + P_ERROR ("PSocketAddress::p_socket_address_to_native: invalid buffer size for IPv6"); + return FALSE; + } + + memcpy (&sin6->sin6_addr, &addr->addr.sin6_addr, sizeof (struct in6_addr)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = p_htons (addr->port); +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO + sin6->sin6_flowinfo = addr->flowinfo; +#endif +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID + sin6->sin6_scope_id = addr->scope_id; +#endif + return TRUE; + } +#endif + else { + P_WARNING ("PSocketAddress::p_socket_address_to_native: unsupported socket address"); + return FALSE; + } +} + +P_LIB_API psize +p_socket_address_get_native_size (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + + if (addr->family == P_SOCKET_FAMILY_INET) + return sizeof (struct sockaddr_in); +#ifdef AF_INET6 + else if (addr->family == P_SOCKET_FAMILY_INET6) + return sizeof (struct sockaddr_in6); +#endif + else { + P_WARNING ("PSocketAddress::p_socket_address_get_native_size: unsupported socket family"); + return 0; + } +} + +P_LIB_API PSocketFamily +p_socket_address_get_family (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return P_SOCKET_FAMILY_UNKNOWN; + + return addr->family; +} + +P_LIB_API pchar * +p_socket_address_get_address (const PSocketAddress *addr) +{ +#ifdef AF_INET6 + pchar buffer[INET6_ADDRSTRLEN]; +#else + pchar buffer[INET_ADDRSTRLEN]; +#endif + +#ifdef P_OS_WIN + DWORD buflen = sizeof (buffer); + DWORD addrlen; + struct sockaddr_storage sa; + struct sockaddr_in *sin; +# ifdef AF_INET6 + struct sockaddr_in6 *sin6; +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + + if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN)) + return NULL; +#ifdef P_OS_WIN + sin = (struct sockaddr_in *) &sa; +# ifdef AF_INET6 + sin6 = (struct sockaddr_in6 *) &sa; +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + +#ifdef P_OS_WIN + memset (&sa, 0, sizeof (sa)); +#endif + +#ifdef P_OS_WIN + sa.ss_family = addr->family; + + if (addr->family == P_SOCKET_FAMILY_INET) { + addrlen = sizeof (struct sockaddr_in); + memcpy (&sin->sin_addr, &addr->addr.sin_addr, sizeof (struct in_addr)); + } +# ifdef AF_INET6 + else { + addrlen = sizeof (struct sockaddr_in6); + memcpy (&sin6->sin6_addr, &addr->addr.sin6_addr, sizeof (struct in6_addr)); + } +# endif /* AF_INET6 */ + + if (P_UNLIKELY (WSAAddressToStringA ((LPSOCKADDR) &sa, + addrlen, + NULL, + (LPSTR) buffer, + &buflen) != 0)) + return NULL; +#else /* !P_OS_WIN */ + if (addr->family == P_SOCKET_FAMILY_INET) + inet_ntop (AF_INET, &addr->addr.sin_addr, buffer, sizeof (buffer)); +# ifdef AF_INET6 + else + inet_ntop (AF_INET6, &addr->addr.sin6_addr, buffer, sizeof (buffer)); +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + + return p_strdup (buffer); +} + +P_LIB_API puint16 +p_socket_address_get_port (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + + return addr->port; +} + +P_LIB_API puint32 +p_socket_address_get_flow_info (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO) + return 0; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return 0; + + return addr->flowinfo; +#endif +} + +P_LIB_API puint32 +p_socket_address_get_scope_id (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) + return 0; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return 0; + + return addr->scope_id; +#endif +} + +P_LIB_API void +p_socket_address_set_flow_info (PSocketAddress *addr, + puint32 flowinfo) +{ + if (P_UNLIKELY (addr == NULL)) + return; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO) + P_UNUSED (flowinfo); + return; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return; + + addr->flowinfo = flowinfo; +#endif +} + +P_LIB_API void +p_socket_address_set_scope_id (PSocketAddress *addr, + puint32 scope_id) +{ + if (P_UNLIKELY (addr == NULL)) + return; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) + P_UNUSED (scope_id); + return; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return; + + addr->scope_id = scope_id; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_flow_info_supported (void) +{ +#ifdef AF_INET6 +# ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO + return TRUE; +# else + return FALSE; +# endif +#else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_scope_id_supported (void) +{ +#ifdef AF_INET6 +# ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID + return TRUE; +# else + return FALSE; +# endif +#else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_ipv6_supported (void) +{ +#ifdef AF_INET6 + return TRUE; +#else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_any (const PSocketAddress *addr) +{ + puint32 addr4; + + if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN)) + return FALSE; + + if (addr->family == P_SOCKET_FAMILY_INET) { + addr4 = p_ntohl (* ((puint32 *) &addr->addr.sin_addr)); + + return (addr4 == INADDR_ANY); + } +#ifdef AF_INET6 + else + return IN6_IS_ADDR_UNSPECIFIED (&addr->addr.sin6_addr); +#else + else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_loopback (const PSocketAddress *addr) +{ + puint32 addr4; + + if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN)) + return FALSE; + + if (addr->family == P_SOCKET_FAMILY_INET) { + addr4 = p_ntohl (* ((puint32 *) &addr->addr.sin_addr)); + + /* 127.0.0.0/8 */ + return ((addr4 & 0xff000000) == 0x7f000000); + } +#ifdef AF_INET6 + else + return IN6_IS_ADDR_LOOPBACK (&addr->addr.sin6_addr); +#else + else + return FALSE; +#endif +} + +P_LIB_API void +p_socket_address_free (PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return; + + p_free (addr); +} |