pss->ss_family = AF_INET;
}
+static char *normalize_ipv6_literal(const char *str, char *buf, size_t *_len)
+{
+#define IPv6_LITERAL_NET ".ipv6-literal.net"
+ const size_t llen = sizeof(IPv6_LITERAL_NET) - 1;
+ size_t len = *_len;
+ int cmp;
+ size_t i;
+ size_t idx_chars = 0;
+ size_t cnt_delimiter = 0;
+ size_t cnt_chars = 0;
+
+ if (len <= llen) {
+ return NULL;
+ }
+
+ /* ignore a trailing '.' */
+ if (str[len - 1] == '.') {
+ len -= 1;
+ }
+
+ len -= llen;
+ if (len >= INET6_ADDRSTRLEN) {
+ return NULL;
+ }
+ if (len < 2) {
+ return NULL;
+ }
+
+ cmp = strncasecmp(&str[len], IPv6_LITERAL_NET, llen);
+ if (cmp != 0) {
+ return NULL;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (idx_chars != 0) {
+ break;
+ }
+
+ switch (str[i]) {
+ case '-':
+ buf[i] = ':';
+ cnt_chars = 0;
+ cnt_delimiter += 1;
+ break;
+ case 's':
+ buf[i] = SCOPE_DELIMITER;
+ idx_chars += 1;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'A':
+ case 'b':
+ case 'B':
+ case 'c':
+ case 'C':
+ case 'd':
+ case 'D':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ buf[i] = str[i];
+ cnt_chars += 1;
+ break;
+ default:
+ return NULL;
+ }
+ if (cnt_chars > 4) {
+ return NULL;
+ }
+ if (cnt_delimiter > 7) {
+ return NULL;
+ }
+ }
+
+ if (cnt_delimiter < 2) {
+ return NULL;
+ }
+
+ for (; idx_chars != 0 && i < len; i++) {
+ switch (str[i]) {
+ case SCOPE_DELIMITER:
+ case ':':
+ return NULL;
+ default:
+ buf[i] = str[i];
+ idx_chars += 1;
+ break;
+ }
+ }
+
+ if (idx_chars == 1) {
+ return NULL;
+ }
+
+ buf[i] = '\0';
+ *_len = len;
+ return buf;
+}
+
/**
* Wrap getaddrinfo...
*/
{
int ret;
struct addrinfo hints;
+#if defined(HAVE_IPV6)
+ char addr[INET6_ADDRSTRLEN*2] = { 0, };
+ unsigned int scope_id = 0;
+ size_t len = strlen(str);
+#endif
ZERO_STRUCT(hints);
/* always try as a numeric host first. This prevents unnecessary name
* lookups, and also ensures we accept IPv6 addresses */
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+
+#if defined(HAVE_IPV6)
+ if (len < sizeof(addr)) {
+ char *p = NULL;
+
+ p = normalize_ipv6_literal(str, addr, &len);
+ if (p != NULL) {
+ hints.ai_family = AF_INET6;
+ str = p;
+ }
+ }
+
+ if (strchr_m(str, ':')) {
+ char *p = strchr_m(str, SCOPE_DELIMITER);
+
+ /*
+ * Cope with link-local.
+ * This is IP:v6:addr%ifname.
+ */
+
+ if (p && (p > str) && ((scope_id = if_nametoindex(p+1)) != 0)) {
+ /* Length of string we want to copy.
+ This is IP:v6:addr (removing the %ifname).
+ */
+ len = PTR_DIFF(p,str);
+
+ if (len+1 > sizeof(addr)) {
+ /* string+nul too long for array. */
+ return false;
+ }
+ if (str != addr) {
+ memcpy(addr, str, len);
+ }
+ addr[len] = '\0';
+
+ str = addr;
+ }
+ }
+#endif
+
ret = getaddrinfo(str, NULL, &hints, ppres);
if (ret == 0) {
+#if defined(HAVE_IPV6)
+ struct sockaddr_in6 *ps6 = NULL;
+
+ if (scope_id == 0) {
+ return true;
+ }
+ if (ppres == NULL) {
+ return true;
+ }
+ if ((*ppres) == NULL) {
+ return true;
+ }
+ if ((*ppres)->ai_addr->sa_family != AF_INET6) {
+ return true;
+ }
+
+ ps6 = (struct sockaddr_in6 *)(*ppres)->ai_addr;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&ps6->sin6_addr) &&
+ ps6->sin6_scope_id == 0) {
+ ps6->sin6_scope_id = scope_id;
+ }
+#endif
+
return true;
}
bool prefer_ipv4)
{
struct addrinfo *res = NULL;
-#if defined(HAVE_IPV6)
- char addr[INET6_ADDRSTRLEN];
- unsigned int scope_id = 0;
int int_flags;
- if (strchr_m(str, ':')) {
- char *p = strchr_m(str, '%');
-
- /*
- * Cope with link-local.
- * This is IP:v6:addr%ifname.
- */
-
- if (p && (p > str) && ((scope_id = if_nametoindex(p+1)) != 0)) {
- strlcpy(addr, str,
- MIN(PTR_DIFF(p,str)+1,
- sizeof(addr)));
- str = addr;
- }
- }
-#endif
-
zero_sockaddr(pss);
if (flags & AI_NUMERICHOST) {
memcpy(pss, res->ai_addr, res->ai_addrlen);
}
-#if defined(HAVE_IPV6)
- if (pss->ss_family == AF_INET6 && scope_id) {
- struct sockaddr_in6 *ps6 = (struct sockaddr_in6 *)pss;
- if (IN6_IS_ADDR_LINKLOCAL(&ps6->sin6_addr) &&
- ps6->sin6_scope_id == 0) {
- ps6->sin6_scope_id = scope_id;
- }
- }
-#endif
-
freeaddrinfo(res);
return true;
}
return false;
}
+bool is_ipv6_literal(const char *str)
+{
+#if defined(HAVE_IPV6)
+ char buf[INET6_ADDRSTRLEN*2] = { 0, };
+ size_t len = strlen(str);
+ char *p = NULL;
+
+ if (len >= sizeof(buf)) {
+ return false;
+ }
+
+ p = normalize_ipv6_literal(str, buf, &len);
+ if (p == NULL) {
+ return false;
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
+
/**
* Return true if a string could be a IPv6 address.
*/
{
#if defined(HAVE_IPV6)
int ret = -1;
+ char *p = NULL;
+ char buf[INET6_ADDRSTRLEN] = { 0, };
+ size_t len;
+ const char *addr = str;
+ const char *idxs = NULL;
+ unsigned int idx = 0;
+ struct in6_addr ip6;
+
+ p = strchr_m(str, ':');
+ if (p == NULL) {
+ return is_ipv6_literal(str);
+ }
- if (strchr_m(str, ':')) {
- char addr[INET6_ADDRSTRLEN];
- struct in6_addr dest6;
- const char *sp = str;
- char *p = strchr_m(str, '%');
+ p = strchr_m(str, SCOPE_DELIMITER);
+ if (p && (p > str)) {
+ len = PTR_DIFF(p, str);
+ idxs = p + 1;
+ } else {
+ len = strlen(str);
+ }
- /*
- * Cope with link-local.
- * This is IP:v6:addr%ifname.
- */
+ if (len >= sizeof(buf)) {
+ return false;
+ }
+ if (idxs != NULL) {
+ strncpy(buf, str, len);
+ addr = buf;
+ }
- if (p && (p > str) && (if_nametoindex(p+1) != 0)) {
- strlcpy(addr, str,
- MIN(PTR_DIFF(p,str)+1,
- sizeof(addr)));
- sp = addr;
+ /*
+ * 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;
}
- ret = inet_pton(AF_INET6, sp, &dest6);
- if (ret > 0) {
- return true;
+
+ 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;
}
#if defined(HAVE_IPV6)
if (sa.ss_family == AF_INET6) {
- return ntohs(((struct sockaddr_in6 *)&sa)->sin6_port);
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)&sa;
+ return ntohs(sa_in6->sin6_port);
}
#endif
if (sa.ss_family == AF_INET) {
- return ntohs(((struct sockaddr_in *)&sa)->sin_port);
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)&sa;
+ return ntohs(sa_in->sin_port);
}
return -1;
}
* zero IPv6 address. No good choice here.
*/
- strlcpy(addr_buf, "0.0.0.0", addr_len);
+ if (strlcpy(addr_buf, "0.0.0.0", addr_len) >= addr_len) {
+ /* Truncate ! */
+ return NULL;
+ }
if (fd == -1) {
return addr_buf;