+ * Return true if a string could be an IPv4 address.
+ */
+
+bool is_ipaddress_v4(const char *str)
+{
+ int ret = -1;
+ struct in_addr dest;
+
+ ret = inet_pton(AF_INET, str, &dest);
+ if (ret > 0) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Return true if a string could be a IPv6 address.
+ */
+
+bool is_ipaddress_v6(const char *str)
+{
+#if defined(HAVE_IPV6)
+ int ret = -1;
+
+ if (strchr_m(str, ':')) {
+ char buf[INET6_ADDRSTRLEN] = { 0, };
+ size_t len;
+ const char *addr = str;
+ const char *idxs = NULL;
+ unsigned int idx = 0;
+ struct in6_addr ip6;
+ char *p = strchr_m(str, '%');
+
+ if (p && (p > str)) {
+ len = PTR_DIFF(p, str);
+ idxs = p + 1;
+ } else {
+ len = strlen(str);
+ }
+
+ if (len >= sizeof(buf)) {
+ return false;
+ }
+ if (idxs != NULL) {
+ strncpy(buf, str, len);
+ addr = buf;
+ }
+
+ /*
+ * Cope with link-local.
+ * This is IP:v6:addr%ifidx.
+ */
+ if (idxs != NULL) {
+ char c;
+
+ ret = sscanf(idxs, "%5u%c", &idx, &c);
+ if (ret != 1) {
+ idx = 0;
+ }
+
+ if (idx > 0 && idx < UINT16_MAX) {
+ /* a valid index */
+ idxs = NULL;
+ }
+ }
+
+ /*
+ * Cope with link-local.
+ * This is IP:v6:addr%ifname.
+ */
+ if (idxs != NULL) {
+ idx = if_nametoindex(idxs);
+
+ if (idx > 0) {
+ /* a valid index */
+ idxs = NULL;
+ }
+ }
+
+ if (idxs != NULL) {
+ return false;
+ }
+
+ ret = inet_pton(AF_INET6, addr, &ip6);
+ if (ret <= 0) {
+ return false;
+ }
+
+ return true;
+ }
+#endif
+ return false;
+}
+
+/**
+ * Return true if a string could be an IPv4 or IPv6 address.
+ */
+
+bool is_ipaddress(const char *str)
+{
+ return is_ipaddress_v4(str) || is_ipaddress_v6(str);
+}
+
+/**
+ * Is a sockaddr a broadcast address ?
+ */
+
+bool is_broadcast_addr(const struct sockaddr *pss)
+{
+#if defined(HAVE_IPV6)
+ if (pss->sa_family == AF_INET6) {
+ const struct in6_addr *sin6 =
+ &((const struct sockaddr_in6 *)pss)->sin6_addr;
+ return IN6_IS_ADDR_MULTICAST(sin6);
+ }
+#endif
+ if (pss->sa_family == AF_INET) {
+ uint32_t addr =
+ ntohl(((const struct sockaddr_in *)pss)->sin_addr.s_addr);
+ return addr == INADDR_BROADCAST;
+ }
+ return false;
+}
+
+/**
+ * Check if an IPv7 is 127.0.0.1
+ */
+bool is_loopback_ip_v4(struct in_addr ip)
+{
+ struct in_addr a;
+ a.s_addr = htonl(INADDR_LOOPBACK);
+ return(ip.s_addr == a.s_addr);
+}
+
+/**
+ * Check if a struct sockaddr is the loopback address.
+ */
+bool is_loopback_addr(const struct sockaddr *pss)
+{
+#if defined(HAVE_IPV6)
+ if (pss->sa_family == AF_INET6) {
+ const struct in6_addr *pin6 =
+ &((const struct sockaddr_in6 *)pss)->sin6_addr;
+ return IN6_IS_ADDR_LOOPBACK(pin6);
+ }
+#endif
+ if (pss->sa_family == AF_INET) {
+ const struct in_addr *pin = &((const struct sockaddr_in *)pss)->sin_addr;
+ return is_loopback_ip_v4(*pin);
+ }
+ return false;
+}
+
+/**
+ * Check if a struct sockaddr has an unspecified address.
+ */
+bool is_zero_addr(const struct sockaddr_storage *pss)
+{
+#if defined(HAVE_IPV6)
+ if (pss->ss_family == AF_INET6) {
+ const struct in6_addr *pin6 =
+ &((const struct sockaddr_in6 *)pss)->sin6_addr;
+ return IN6_IS_ADDR_UNSPECIFIED(pin6);
+ }
+#endif
+ if (pss->ss_family == AF_INET) {
+ const struct in_addr *pin = &((const struct sockaddr_in *)pss)->sin_addr;
+ return is_zero_ip_v4(*pin);
+ }
+ return false;
+}
+
+/**
+ * Set an IP to 0.0.0.0.
+ */
+void zero_ip_v4(struct in_addr *ip)
+{
+ ZERO_STRUCTP(ip);
+}
+
+bool is_linklocal_addr(const struct sockaddr_storage *pss)
+{
+#ifdef HAVE_IPV6
+ if (pss->ss_family == AF_INET6) {
+ const struct in6_addr *pin6 =
+ &((const struct sockaddr_in6 *)pss)->sin6_addr;
+ return IN6_IS_ADDR_LINKLOCAL(pin6);
+ }
+#endif
+ if (pss->ss_family == AF_INET) {
+ const struct in_addr *pin =
+ &((const struct sockaddr_in *)pss)->sin_addr;
+ struct in_addr ll_addr;
+ struct in_addr mask_addr;
+
+ /* 169.254.0.0/16, is link local, see RFC 3927 */
+ ll_addr.s_addr = 0xa9fe0000;
+ mask_addr.s_addr = 0xffff0000;
+ return same_net_v4(*pin, ll_addr, mask_addr);
+ }
+ return false;
+}
+
+/**
+ * Convert an IPv4 struct in_addr to a struct sockaddr_storage.
+ */
+void in_addr_to_sockaddr_storage(struct sockaddr_storage *ss,
+ struct in_addr ip)
+{
+ struct sockaddr_in *sa = (struct sockaddr_in *)ss;
+ ZERO_STRUCTP(ss);
+ sa->sin_family = AF_INET;
+ sa->sin_addr = ip;
+}
+
+#if defined(HAVE_IPV6)
+/**
+ * Convert an IPv6 struct in_addr to a struct sockaddr_storage.
+ */
+void in6_addr_to_sockaddr_storage(struct sockaddr_storage *ss,
+ struct in6_addr ip)
+{
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)ss;
+ memset(ss, '\0', sizeof(*ss));
+ sa->sin6_family = AF_INET6;
+ sa->sin6_addr = ip;
+}
+#endif
+
+/**
+ * Are two IPs on the same subnet?
+ */
+bool same_net(const struct sockaddr *ip1,
+ const struct sockaddr *ip2,
+ const struct sockaddr *mask)
+{
+ if (ip1->sa_family != ip2->sa_family) {
+ /* Never on the same net. */
+ return false;
+ }
+
+#if defined(HAVE_IPV6)
+ if (ip1->sa_family == AF_INET6) {
+ struct sockaddr_in6 ip1_6 = *(const struct sockaddr_in6 *)ip1;
+ struct sockaddr_in6 ip2_6 = *(const struct sockaddr_in6 *)ip2;
+ struct sockaddr_in6 mask_6 = *(const struct sockaddr_in6 *)mask;
+ char *p1 = (char *)&ip1_6.sin6_addr;
+ char *p2 = (char *)&ip2_6.sin6_addr;
+ char *m = (char *)&mask_6.sin6_addr;
+ int i;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ *p1++ &= *m;
+ *p2++ &= *m;
+ m++;
+ }
+ return (memcmp(&ip1_6.sin6_addr,
+ &ip2_6.sin6_addr,
+ sizeof(struct in6_addr)) == 0);
+ }
+#endif
+ if (ip1->sa_family == AF_INET) {
+ return same_net_v4(((const struct sockaddr_in *)ip1)->sin_addr,
+ ((const struct sockaddr_in *)ip2)->sin_addr,
+ ((const struct sockaddr_in *)mask)->sin_addr);
+ }
+ return false;
+}
+
+/**
+ * Are two sockaddr 's the same family and address ? Ignore port etc.
+ */
+
+bool sockaddr_equal(const struct sockaddr *ip1,
+ const struct sockaddr *ip2)
+{
+ if (ip1->sa_family != ip2->sa_family) {
+ /* Never the same. */
+ return false;
+ }
+
+#if defined(HAVE_IPV6)
+ if (ip1->sa_family == AF_INET6) {
+ return (memcmp(&((const struct sockaddr_in6 *)ip1)->sin6_addr,
+ &((const struct sockaddr_in6 *)ip2)->sin6_addr,
+ sizeof(struct in6_addr)) == 0);
+ }
+#endif
+ if (ip1->sa_family == AF_INET) {
+ return (memcmp(&((const struct sockaddr_in *)ip1)->sin_addr,
+ &((const struct sockaddr_in *)ip2)->sin_addr,
+ sizeof(struct in_addr)) == 0);
+ }
+ return false;
+}
+
+/**
+ * Is an IP address the INADDR_ANY or in6addr_any value ?
+ */
+bool is_address_any(const struct sockaddr *psa)
+{
+#if defined(HAVE_IPV6)
+ if (psa->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *si6 = (const struct sockaddr_in6 *)psa;
+ if (memcmp(&in6addr_any,
+ &si6->sin6_addr,
+ sizeof(in6addr_any)) == 0) {
+ return true;
+ }
+ return false;
+ }
+#endif
+ if (psa->sa_family == AF_INET) {
+ const struct sockaddr_in *si = (const struct sockaddr_in *)psa;
+ if (si->sin_addr.s_addr == INADDR_ANY) {
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+
+void set_sockaddr_port(struct sockaddr *psa, uint16_t port)
+{
+#if defined(HAVE_IPV6)
+ if (psa->sa_family == AF_INET6) {
+ ((struct sockaddr_in6 *)psa)->sin6_port = htons(port);
+ }
+#endif
+ if (psa->sa_family == AF_INET) {
+ ((struct sockaddr_in *)psa)->sin_port = htons(port);
+ }
+}
+
+
+/****************************************************************************
+ Get a port number in host byte order from a sockaddr_storage.
+****************************************************************************/
+
+uint16_t get_sockaddr_port(const struct sockaddr_storage *pss)
+{
+ uint16_t port = 0;
+
+ if (pss->ss_family != AF_INET) {
+#if defined(HAVE_IPV6)
+ /* IPv6 */
+ const struct sockaddr_in6 *sa6 =
+ (const struct sockaddr_in6 *)pss;
+ port = ntohs(sa6->sin6_port);
+#endif
+ } else {
+ const struct sockaddr_in *sa =
+ (const struct sockaddr_in *)pss;
+ port = ntohs(sa->sin_port);
+ }
+ return port;
+}
+
+/****************************************************************************
+ Print out an IPv4 or IPv6 address from a struct sockaddr_storage.
+****************************************************************************/
+
+char *print_sockaddr_len(char *dest,
+ size_t destlen,
+ const struct sockaddr *psa,
+ socklen_t psalen)
+{
+ if (destlen > 0) {
+ dest[0] = '\0';
+ }
+ (void)sys_getnameinfo(psa,
+ psalen,
+ dest, destlen,
+ NULL, 0,
+ NI_NUMERICHOST);
+ return dest;
+}
+
+/****************************************************************************
+ Print out an IPv4 or IPv6 address from a struct sockaddr_storage.
+****************************************************************************/
+
+char *print_sockaddr(char *dest,
+ size_t destlen,
+ const struct sockaddr_storage *psa)
+{
+ return print_sockaddr_len(dest, destlen, (const struct sockaddr *)psa,
+ sizeof(struct sockaddr_storage));
+}
+
+/****************************************************************************
+ Print out a canonical IPv4 or IPv6 address from a struct sockaddr_storage.
+****************************************************************************/