/** * @file sa.c Socket Address * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include #include #include #include #include #include "sa.h" #define DEBUG_MODULE "sa" #define DEBUG_LEVEL 5 #include /** * 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: * *
 *   1.2.3.4:1234
 *   [::1]:1234
 *   [::]:5060
 * 
*/ 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; } }