summaryrefslogtreecommitdiff
path: root/3rdparty/plibsys/src/psocketaddress.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/plibsys/src/psocketaddress.c')
-rw-r--r--3rdparty/plibsys/src/psocketaddress.c619
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);
+}