diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c index 9064eaf..dcb527f 100644 --- a/src/backend/utils/adt/inet_net_pton.c +++ b/src/backend/utils/adt/inet_net_pton.c @@ -435,6 +435,7 @@ inet_net_pton_ipv6(const char *src, u_char *dst) #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 #define NS_INADDRSZ 4 +#define NS_IN6SCOPESZ 256 static int inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) @@ -442,13 +443,16 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[NS_IN6ADDRSZ], + scopeid[NS_IN6SCOPESZ], *tp, *endp, - *colonp; + *colonp, + *sp; const char *xdigits, *curtok; int ch, - saw_xdigit; + saw_xdigit, + saw_scopeid; u_int val; int digits; int bits; @@ -458,6 +462,8 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) memset((tp = tmp), '\0', NS_IN6ADDRSZ); endp = tp + NS_IN6ADDRSZ; + + memset((sp = scopeid), '\0', NS_IN6SCOPESZ); colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') @@ -465,6 +471,7 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) goto enoent; curtok = src; saw_xdigit = 0; + saw_scopeid = 0; val = 0; digits = 0; bits = -1; @@ -513,6 +520,28 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) } if (ch == '/' && getbits(src, &bits) > 0) break; + if (ch == '%') + { + if (saw_scopeid) + goto enoent; + + do + { + ch = *src++; + /* consider all printable characters */ + if (32 <= ch && ch <= 127) + *sp++ = ch; + else + goto enoent; + + if (sp - scopeid >= NS_IN6SCOPESZ) + goto enoent; + } while (*src != '\0' && *src != '/'); + + saw_scopeid = 1; + continue; + } + goto enoent; } if (saw_xdigit) @@ -552,6 +581,7 @@ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) * Copy out the result. */ memcpy(dst, tmp, NS_IN6ADDRSZ); + memcpy(dst + NS_IN6ADDRSZ, scopeid, NS_IN6SCOPESZ); return (bits); diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c index 1f8469a..d3c4594 100644 --- a/src/backend/utils/adt/network.c +++ b/src/backend/utils/adt/network.c @@ -117,6 +117,24 @@ network_out(inet *src, bool is_cidr) snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src)); } + if (ip_scopeid_size(src)) + { + char *ch = strchr(tmp, '/'); + + if (ch) + { + len = ch - tmp; + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_scopeid(src)); + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src)); + } + else + { + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_scopeid(src)); + } + } + return pstrdup(tmp); } @@ -399,7 +417,11 @@ network_cmp_internal(inet *a1, inet *a2) order = ((int) ip_bits(a1)) - ((int) ip_bits(a2)); if (order != 0) return order; - return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1)); + order = bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1)); + if (order != 0) + return order; + + return pg_strcasecmp((const char *)ip_scopeid(a1), (const char *)ip_scopeid(a2)); } return ip_family(a1) - ip_family(a2); diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h index 2fe3ca8..7b52983 100644 --- a/src/include/utils/inet.h +++ b/src/include/utils/inet.h @@ -25,6 +25,7 @@ typedef struct unsigned char family; /* PGSQL_AF_INET or PGSQL_AF_INET6 */ unsigned char bits; /* number of bits in netmask */ unsigned char ipaddr[16]; /* up to 128 bits of address */ + unsigned char ipscopeid[256]; /* up to 255 bytes of interface name */ } inet_struct; /* @@ -75,15 +76,22 @@ typedef struct #define ip_addr(inetptr) \ (((inet_struct *) VARDATA_ANY(inetptr))->ipaddr) +#define ip_scopeid(inetptr) \ + (((inet_struct *) VARDATA_ANY(inetptr))->ipscopeid) + #define ip_addrsize(inetptr) \ (ip_family(inetptr) == PGSQL_AF_INET ? 4 : 16) +#define ip_scopeid_size(inetptr) \ + (ip_family(inetptr) == PGSQL_AF_INET ? 0 \ + : (strlen((const char *)((inet_struct *) VARDATA_ANY(inetptr))->ipscopeid))) + #define ip_maxbits(inetptr) \ (ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128) #define SET_INET_VARSIZE(dst) \ SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \ - ip_addrsize(dst)) + ip_addrsize(dst) + ip_scopeid_size(dst)) /*