
nice_address_to_string expects an already-allocated string, which is not great for introspection. Adds nice_address_to_string_dup, which does the same thing, but returns a newly allocated string.
449 lines
10 KiB
C
449 lines
10 KiB
C
/*
|
|
* This file is part of the Nice GLib ICE library.
|
|
*
|
|
* (C) 2006-2009 Collabora Ltd.
|
|
* Contact: Youness Alaoui
|
|
* (C) 2006-2009 Nokia Corporation. All rights reserved.
|
|
* Contact: Kai Vehmanen
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Nice GLib ICE library.
|
|
*
|
|
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
|
|
* Corporation. All Rights Reserved.
|
|
*
|
|
* Contributors:
|
|
* Dafydd Harries, Collabora Ltd.
|
|
* Youness Alaoui, Collabora Ltd.
|
|
* Kai Vehmanen, Nokia
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of the
|
|
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
|
|
* case the provisions of LGPL are applicable instead of those above. If you
|
|
* wish to allow use of your version of this file only under the terms of the
|
|
* LGPL and not to allow others to use your version of this file under the
|
|
* MPL, indicate your decision by deleting the provisions above and replace
|
|
* them with the notice and other provisions required by the LGPL. If you do
|
|
* not delete the provisions above, a recipient may use your version of this
|
|
* file under either the MPL or the LGPL.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#else
|
|
#define NICEAPI_EXPORT
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_NETDB_H
|
|
#include <netdb.h>
|
|
#endif
|
|
|
|
#include "address.h"
|
|
|
|
#ifdef G_OS_WIN32
|
|
#define inet_ntop inet_ntop_win32
|
|
|
|
/* Defined in recent versions of mingw:
|
|
* https://github.com/mirror/mingw-w64/commit/0f4899473c4ba2e34fa447b1931a04e38c1f105e
|
|
*/
|
|
#ifndef IN6_ARE_ADDR_EQUAL
|
|
#define IN6_ARE_ADDR_EQUAL(a, b) \
|
|
(memcmp ((const void *) (a), (const void *) (b), sizeof (struct in6_addr)) == 0)
|
|
#endif
|
|
|
|
|
|
static const char *
|
|
inet_ntop_win32 (int af, const void *src, char *dst, socklen_t cnt)
|
|
{
|
|
if (af == AF_INET) {
|
|
struct sockaddr_in in;
|
|
memset(&in, 0, sizeof(in));
|
|
in.sin_family = AF_INET;
|
|
memcpy(&in.sin_addr, src, sizeof(struct in_addr));
|
|
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in),
|
|
dst, cnt, NULL, 0, NI_NUMERICHOST);
|
|
return dst;
|
|
} else if (af == AF_INET6) {
|
|
struct sockaddr_in6 in;
|
|
memset(&in, 0, sizeof(in));
|
|
in.sin6_family = AF_INET6;
|
|
memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
|
|
getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6),
|
|
dst, cnt, NULL, 0, NI_NUMERICHOST);
|
|
return dst;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
NICEAPI_EXPORT
|
|
G_DEFINE_BOXED_TYPE (NiceAddress, nice_address, nice_address_dup,
|
|
nice_address_free);
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_init (NiceAddress *addr)
|
|
{
|
|
addr->s.addr.sa_family = AF_UNSPEC;
|
|
memset (&addr->s, 0, sizeof(addr->s));
|
|
}
|
|
|
|
NICEAPI_EXPORT NiceAddress *
|
|
nice_address_new (void)
|
|
{
|
|
NiceAddress *addr = g_slice_new0 (NiceAddress);
|
|
nice_address_init (addr);
|
|
return addr;
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_set_ipv4 (NiceAddress *addr, guint32 addr_ipv4)
|
|
{
|
|
addr->s.ip4.sin_family = AF_INET;
|
|
#ifdef HAVE_SA_LEN
|
|
addr->s.ip4.sin_len = sizeof (addr->sa.ip4);
|
|
#endif
|
|
addr->s.ip4.sin_addr.s_addr = addr_ipv4 ? htonl (addr_ipv4) : INADDR_ANY;
|
|
addr->s.ip4.sin_port = 0;
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_set_ipv6 (NiceAddress *addr, const guchar *addr_ipv6)
|
|
{
|
|
addr->s.ip6.sin6_family = AF_INET6;
|
|
#ifdef HAVE_SA_LEN
|
|
addr->s.ip6.sin6_len = sizeof (addr->sa.ip6);
|
|
#endif
|
|
memcpy (addr->s.ip6.sin6_addr.s6_addr, addr_ipv6, 16);
|
|
addr->s.ip6.sin6_port = 0;
|
|
addr->s.ip6.sin6_scope_id = 0;
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_set_port (NiceAddress *addr, guint port)
|
|
{
|
|
g_assert (addr);
|
|
|
|
switch (addr->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
addr->s.ip4.sin_port = htons (port);
|
|
break;
|
|
case AF_INET6:
|
|
addr->s.ip6.sin6_port = htons (port);
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
guint
|
|
nice_address_get_port (const NiceAddress *addr)
|
|
{
|
|
if (!addr)
|
|
return 0;
|
|
|
|
switch (addr->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return ntohs (addr->s.ip4.sin_port);
|
|
case AF_INET6:
|
|
return ntohs (addr->s.ip6.sin6_port);
|
|
default:
|
|
g_return_val_if_reached (0);
|
|
}
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT gboolean
|
|
nice_address_set_from_string (NiceAddress *addr, const gchar *str)
|
|
{
|
|
struct addrinfo hints;
|
|
struct addrinfo *res;
|
|
|
|
memset (&hints, 0, sizeof (hints));
|
|
|
|
/* AI_NUMERICHOST prevents getaddrinfo() from doing DNS resolution. */
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
if (getaddrinfo (str, NULL, &hints, &res) != 0)
|
|
return FALSE; /* invalid address */
|
|
|
|
nice_address_set_from_sockaddr (addr, res->ai_addr);
|
|
|
|
freeaddrinfo (res);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_set_from_sockaddr (NiceAddress *addr,
|
|
const struct sockaddr *sa)
|
|
{
|
|
union {
|
|
const struct sockaddr *sa;
|
|
const struct sockaddr_in *in;
|
|
const struct sockaddr_in6 *in6;
|
|
} s;
|
|
|
|
s.sa = sa;
|
|
|
|
switch (sa->sa_family)
|
|
{
|
|
case AF_INET:
|
|
memcpy(&addr->s.ip4, s.in, sizeof (addr->s.ip4));
|
|
break;
|
|
case AF_INET6:
|
|
memcpy(&addr->s.ip6, s.in6, sizeof (addr->s.ip6));
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_copy_to_sockaddr (const NiceAddress *addr,
|
|
struct sockaddr *_sa)
|
|
{
|
|
union {
|
|
struct sockaddr *addr;
|
|
struct sockaddr_in *in;
|
|
struct sockaddr_in6 *in6;
|
|
} sa;
|
|
|
|
sa.addr = _sa;
|
|
|
|
g_assert (_sa);
|
|
|
|
switch (addr->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
memcpy (sa.in, &addr->s.ip4, sizeof (*sa.in));
|
|
break;
|
|
case AF_INET6:
|
|
memcpy (sa.in6, &addr->s.ip6, sizeof (*sa.in6));
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_to_string (const NiceAddress *addr, gchar *dst)
|
|
{
|
|
switch (addr->s.addr.sa_family) {
|
|
case AF_INET:
|
|
inet_ntop (AF_INET, &addr->s.ip4.sin_addr, dst, INET_ADDRSTRLEN);
|
|
break;
|
|
case AF_INET6:
|
|
inet_ntop (AF_INET6, &addr->s.ip6.sin6_addr, dst, INET6_ADDRSTRLEN);
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
NICEAPI_EXPORT gchar *
|
|
nice_address_dup_string (const NiceAddress *addr)
|
|
{
|
|
char dst[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)] = "";
|
|
|
|
nice_address_to_string (addr, dst);
|
|
|
|
return g_strdup (dst);
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT gboolean
|
|
nice_address_equal (const NiceAddress *a, const NiceAddress *b)
|
|
{
|
|
if (a->s.addr.sa_family != b->s.addr.sa_family)
|
|
return FALSE;
|
|
|
|
switch (a->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return (a->s.ip4.sin_addr.s_addr == b->s.ip4.sin_addr.s_addr)
|
|
&& (a->s.ip4.sin_port == b->s.ip4.sin_port);
|
|
|
|
case AF_INET6:
|
|
return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr)
|
|
&& (a->s.ip6.sin6_port == b->s.ip6.sin6_port)
|
|
&& (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 ||
|
|
(a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id));
|
|
|
|
default:
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT NiceAddress *
|
|
nice_address_dup (const NiceAddress *a)
|
|
{
|
|
NiceAddress *dup = g_slice_new0 (NiceAddress);
|
|
|
|
*dup = *a;
|
|
return dup;
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT void
|
|
nice_address_free (NiceAddress *addr)
|
|
{
|
|
g_slice_free (NiceAddress, addr);
|
|
}
|
|
|
|
|
|
/* "private" in the sense of "not routable on the Internet" */
|
|
static gboolean
|
|
ipv4_address_is_private (guint32 addr)
|
|
{
|
|
addr = ntohl (addr);
|
|
|
|
/* http://tools.ietf.org/html/rfc3330
|
|
* https://tools.ietf.org/html/rfc3927
|
|
*/
|
|
return (
|
|
/* 10.0.0.0/8 */
|
|
((addr & 0xff000000) == 0x0a000000) ||
|
|
/* 172.16.0.0 - 172.31.255.255 = 172.16.0.0/12 */
|
|
((addr & 0xfff00000) == 0xac100000) ||
|
|
/* 192.168.0.0/16 */
|
|
((addr & 0xffff0000) == 0xc0a80000) ||
|
|
/* 169.254.x.x/16 (for APIPA) */
|
|
((addr & 0xffff0000) == 0xa9fe0000) ||
|
|
/* 127.0.0.0/8 */
|
|
((addr & 0xff000000) == 0x7f000000));
|
|
}
|
|
|
|
|
|
static gboolean
|
|
ipv6_address_is_private (const guchar *addr)
|
|
{
|
|
return (
|
|
/* fe80::/10 (link local addresses, needs scope) */
|
|
((addr[0] == 0xfe) && ((addr[1] & 0xc0) == 0x80)) ||
|
|
/* fd00::/8 (official private IP block) */
|
|
(addr[0] == 0xfd) ||
|
|
/* fc00::/7 (those are ULA) */
|
|
((addr[0] & 0xfe) == 0xfc) ||
|
|
/* ::1 loopback */
|
|
((memcmp (addr, "\x00\x00\x00\x00"
|
|
"\x00\x00\x00\x00"
|
|
"\x00\x00\x00\x00"
|
|
"\x00\x00\x00\x01", 16) == 0)));
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT gboolean
|
|
nice_address_is_private (const NiceAddress *a)
|
|
{
|
|
switch (a->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return ipv4_address_is_private (a->s.ip4.sin_addr.s_addr);
|
|
case AF_INET6:
|
|
return ipv6_address_is_private (a->s.ip6.sin6_addr.s6_addr);
|
|
default:
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
ipv4_address_is_linklocal (guint32 addr)
|
|
{
|
|
addr = ntohl (addr);
|
|
|
|
return (addr & 0xffff0000) == 0xa9fe0000;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
ipv6_address_is_linklocal (const guchar *addr)
|
|
{
|
|
return (addr[0] == 0xfe) && ((addr[1] & 0xc0) == 0x80);
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT gboolean
|
|
nice_address_is_linklocal (const NiceAddress *a)
|
|
{
|
|
switch (a->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return ipv4_address_is_linklocal (a->s.ip4.sin_addr.s_addr);
|
|
case AF_INET6:
|
|
return ipv6_address_is_linklocal (a->s.ip6.sin6_addr.s6_addr);
|
|
default:
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
NICEAPI_EXPORT gboolean
|
|
nice_address_is_valid (const NiceAddress *a)
|
|
{
|
|
switch (a->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
case AF_INET6:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
NICEAPI_EXPORT int
|
|
nice_address_ip_version (const NiceAddress *addr)
|
|
{
|
|
switch (addr->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return 4;
|
|
case AF_INET6:
|
|
return 6;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
NICEAPI_EXPORT gboolean
|
|
nice_address_equal_no_port (const NiceAddress *a, const NiceAddress *b)
|
|
{
|
|
if (a->s.addr.sa_family != b->s.addr.sa_family)
|
|
return FALSE;
|
|
|
|
switch (a->s.addr.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return (a->s.ip4.sin_addr.s_addr == b->s.ip4.sin_addr.s_addr);
|
|
|
|
case AF_INET6:
|
|
return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr)
|
|
&& (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 ||
|
|
(a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id));
|
|
|
|
default:
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
}
|