re/src/sa/sa.c
2015-12-17 14:07:00 +00:00

593 lines
9.8 KiB
C

/**
* @file sa.c Socket Address
*
* Copyright (C) 2010 Creytiv.com
*/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#include <string.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_list.h>
#include <re_sa.h>
#include "sa.h"
#define DEBUG_MODULE "sa"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
/**
* Initialize a Socket Address
*
* @param sa Socket Address
* @param af Address Family
*/
void sa_init(struct sa *sa, int af)
{
if (!sa)
return;
memset(sa, 0, sizeof(*sa));
sa->u.sa.sa_family = af;
sa->len = sizeof(sa->u);
}
/**
* Set a Socket Address from a PL string
*
* @param sa Socket Address
* @param addr IP-address
* @param port Port number
*
* @return 0 if success, otherwise errorcode
*/
int sa_set(struct sa *sa, const struct pl *addr, uint16_t port)
{
char buf[64];
(void)pl_strcpy(addr, buf, sizeof(buf));
return sa_set_str(sa, buf, port);
}
/**
* Set a Socket Address from a string
*
* @param sa Socket Address
* @param addr IP-address
* @param port Port number
*
* @return 0 if success, otherwise errorcode
*/
int sa_set_str(struct sa *sa, const char *addr, uint16_t port)
{
int err;
if (!sa || !addr)
return EINVAL;
err = net_inet_pton(addr, sa);
if (err)
return err;
switch (sa->u.sa.sa_family) {
case AF_INET:
sa->u.in.sin_port = htons(port);
sa->len = sizeof(struct sockaddr_in);
break;
#ifdef HAVE_INET6
case AF_INET6:
sa->u.in6.sin6_port = htons(port);
sa->len = sizeof(struct sockaddr_in6);
break;
#endif
default:
return EAFNOSUPPORT;
}
return 0;
}
/**
* Set a Socket Address from an IPv4 address
*
* @param sa Socket Address
* @param addr IPv4 address in host order
* @param port Port number
*
* @return 0 if success, otherwise errorcode
*/
void sa_set_in(struct sa *sa, uint32_t addr, uint16_t port)
{
if (!sa)
return;
sa->u.in.sin_family = AF_INET;
sa->u.in.sin_addr.s_addr = htonl(addr);
sa->u.in.sin_port = htons(port);
sa->len = sizeof(struct sockaddr_in);
}
/**
* Set a Socket Address from an IPv6 address
*
* @param sa Socket Address
* @param addr IPv6 address
* @param port Port number
*
* @return 0 if success, otherwise errorcode
*/
void sa_set_in6(struct sa *sa, const uint8_t *addr, uint16_t port)
{
if (!sa)
return;
#ifdef HAVE_INET6
sa->u.in6.sin6_family = AF_INET6;
memcpy(&sa->u.in6.sin6_addr, addr, 16);
sa->u.in6.sin6_port = htons(port);
sa->len = sizeof(struct sockaddr_in6);
#else
(void)addr;
(void)port;
#endif
}
/**
* Set a Socket Address from a sockaddr
*
* @param sa Socket Address
* @param s Sockaddr
*
* @return 0 if success, otherwise errorcode
*/
int sa_set_sa(struct sa *sa, const struct sockaddr *s)
{
if (!sa || !s)
return EINVAL;
switch (s->sa_family) {
case AF_INET:
memcpy(&sa->u.in, s, sizeof(struct sockaddr_in));
sa->len = sizeof(struct sockaddr_in);
break;
#ifdef HAVE_INET6
case AF_INET6:
memcpy(&sa->u.in6, s, sizeof(struct sockaddr_in6));
sa->len = sizeof(struct sockaddr_in6);
break;
#endif
default:
return EAFNOSUPPORT;
}
sa->u.sa.sa_family = s->sa_family;
return 0;
}
/**
* Set the port number on a Socket Address
*
* @param sa Socket Address
* @param port Port number
*/
void sa_set_port(struct sa *sa, uint16_t port)
{
if (!sa)
return;
switch (sa->u.sa.sa_family) {
case AF_INET:
sa->u.in.sin_port = htons(port);
break;
#ifdef HAVE_INET6
case AF_INET6:
sa->u.in6.sin6_port = htons(port);
break;
#endif
default:
DEBUG_WARNING("sa_set_port: no af %d (port %u)\n",
sa->u.sa.sa_family, port);
break;
}
}
/**
* Set a socket address from a string of type "address:port"
* IPv6 addresses must be encapsulated in square brackets.
*
* @param sa Socket Address
* @param str Address and port string
* @param len Length of string
*
* @return 0 if success, otherwise errorcode
*
* Example strings:
*
* <pre>
* 1.2.3.4:1234
* [::1]:1234
* [::]:5060
* </pre>
*/
int sa_decode(struct sa *sa, const char *str, size_t len)
{
struct pl addr, port, pl;
const char *c;
if (!sa || !str || !len)
return EINVAL;
pl.p = str;
pl.l = len;
if ('[' == str[0] && (c = pl_strchr(&pl, ']'))) {
addr.p = str + 1;
addr.l = c - str - 1;
++c;
}
else if (NULL != (c = pl_strchr(&pl, ':'))) {
addr.p = str;
addr.l = c - str;
}
else {
return EINVAL;
}
if (len < (size_t)(c - str + 2))
return EINVAL;
if (':' != *c)
return EINVAL;
port.p = ++c;
port.l = len + str - c;
return sa_set(sa, &addr, pl_u32(&port));
}
/**
* Get the Address Family of a Socket Address
*
* @param sa Socket Address
*
* @return Address Family
*/
int sa_af(const struct sa *sa)
{
return sa ? sa->u.sa.sa_family : AF_UNSPEC;
}
/**
* Get the IPv4-address of a Socket Address
*
* @param sa Socket Address
*
* @return IPv4 address in host order
*/
uint32_t sa_in(const struct sa *sa)
{
return sa ? ntohl(sa->u.in.sin_addr.s_addr) : 0;
}
/**
* Get the IPv6-address of a Socket Address
*
* @param sa Socket Address
* @param addr On return, contains the IPv6-address
*/
void sa_in6(const struct sa *sa, uint8_t *addr)
{
if (!sa || !addr)
return;
#ifdef HAVE_INET6
memcpy(addr, &sa->u.in6.sin6_addr, 16);
#endif
}
/**
* Convert a Socket Address to Presentation format
*
* @param sa Socket Address
* @param buf Buffer to store presentation format
* @param size Buffer size
*
* @return 0 if success, otherwise errorcode
*/
int sa_ntop(const struct sa *sa, char *buf, int size)
{
return net_inet_ntop(sa, buf, size);
}
/**
* Get the port number from a Socket Address
*
* @param sa Socket Address
*
* @return Port number in host order
*/
uint16_t sa_port(const struct sa *sa)
{
if (!sa)
return 0;
switch (sa->u.sa.sa_family) {
case AF_INET:
return ntohs(sa->u.in.sin_port);
#ifdef HAVE_INET6
case AF_INET6:
return ntohs(sa->u.in6.sin6_port);
#endif
default:
return 0;
}
}
/**
* Check if a Socket Address is set
*
* @param sa Socket Address
* @param flag Flags specifying which fields to check
*
* @return true if set, false if not set
*/
bool sa_isset(const struct sa *sa, int flag)
{
if (!sa)
return false;
switch (sa->u.sa.sa_family) {
case AF_INET:
if (flag & SA_ADDR)
if (INADDR_ANY == sa->u.in.sin_addr.s_addr)
return false;
if (flag & SA_PORT)
if (0 == sa->u.in.sin_port)
return false;
break;
#ifdef HAVE_INET6
case AF_INET6:
if (flag & SA_ADDR)
if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.in6.sin6_addr))
return false;
if (flag & SA_PORT)
if (0 == sa->u.in6.sin6_port)
return false;
break;
#endif
default:
return false;
}
return true;
}
/**
* Calculate the hash value of a Socket Address
*
* @param sa Socket Address
* @param flag Flags specifying which fields to use
*
* @return Hash value
*/
uint32_t sa_hash(const struct sa *sa, int flag)
{
uint32_t v = 0;
if (!sa)
return 0;
switch (sa->u.sa.sa_family) {
case AF_INET:
if (flag & SA_ADDR)
v += ntohl(sa->u.in.sin_addr.s_addr);
if (flag & SA_PORT)
v += ntohs(sa->u.in.sin_port);
break;
#ifdef HAVE_INET6
case AF_INET6:
if (flag & SA_ADDR) {
uint32_t *a = (uint32_t *)&sa->u.in6.sin6_addr;
v += a[0] ^ a[1] ^ a[2] ^ a[3];
}
if (flag & SA_PORT)
v += ntohs(sa->u.in6.sin6_port);
break;
#endif
default:
DEBUG_WARNING("sa_hash: unknown af %d\n", sa->u.sa.sa_family);
return 0;
}
return v;
}
/**
* Copy a Socket Address
*
* @param dst Socket Address to be written
* @param src Socket Address to be copied
*/
void sa_cpy(struct sa *dst, const struct sa *src)
{
if (!dst || !src)
return;
memcpy(dst, src, sizeof(*dst));
}
/**
* Compare two Socket Address objects
*
* @param l Socket Address number one
* @param r Socket Address number two
* @param flag Flags specifying which fields to use
*
* @return true if match, false if no match
*/
bool sa_cmp(const struct sa *l, const struct sa *r, int flag)
{
if (!l || !r)
return false;
if (l == r)
return true;
if (l->u.sa.sa_family != r->u.sa.sa_family)
return false;
switch (l->u.sa.sa_family) {
case AF_INET:
if (flag & SA_ADDR)
if (l->u.in.sin_addr.s_addr != r->u.in.sin_addr.s_addr)
return false;
if (flag & SA_PORT)
if (l->u.in.sin_port != r->u.in.sin_port)
return false;
break;
#ifdef HAVE_INET6
case AF_INET6:
if (flag & SA_ADDR)
if (memcmp(&l->u.in6.sin6_addr,
&r->u.in6.sin6_addr, 16))
return false;
if (flag & SA_PORT)
if (l->u.in6.sin6_port != r->u.in6.sin6_port)
return false;
break;
#endif
default:
return false;
}
return true;
}
/** IPv4 Link-local test */
#define IN_IS_ADDR_LINKLOCAL(a) \
(((a) & htonl(0xffff0000)) == htonl (0xa9fe0000))
/**
* Check if socket address is a link-local address
*
* @param sa Socket address
*
* @return true if link-local address, otherwise false
*/
bool sa_is_linklocal(const struct sa *sa)
{
if (!sa)
return false;
switch (sa_af(sa)) {
case AF_INET:
return IN_IS_ADDR_LINKLOCAL(sa->u.in.sin_addr.s_addr);
#ifdef HAVE_INET6
case AF_INET6:
return IN6_IS_ADDR_LINKLOCAL(&sa->u.in6.sin6_addr);
#endif
default:
return false;
}
}
/**
* Check if socket address is a loopback address
*
* @param sa Socket address
*
* @return true if loopback address, otherwise false
*/
bool sa_is_loopback(const struct sa *sa)
{
if (!sa)
return false;
switch (sa_af(sa)) {
case AF_INET:
return INADDR_LOOPBACK == ntohl(sa->u.in.sin_addr.s_addr);
#ifdef HAVE_INET6
case AF_INET6:
return IN6_IS_ADDR_LOOPBACK(&sa->u.in6.sin6_addr);
#endif
default:
return false;
}
}
/**
* Check if socket address is any/unspecified address
*
* @param sa Socket address
*
* @return true if any address, otherwise false
*/
bool sa_is_any(const struct sa *sa)
{
if (!sa)
return false;
switch (sa_af(sa)) {
case AF_INET:
return INADDR_ANY == ntohl(sa->u.in.sin_addr.s_addr);
#ifdef HAVE_INET6
case AF_INET6:
return IN6_IS_ADDR_UNSPECIFIED(&sa->u.in6.sin6_addr);
#endif
default:
return false;
}
}