diff --git a/src/backend/utils/adt/inet_net_pton.c b/src/backend/utils/adt/inet_net_pton.c index b8fa7d2..6a732d7 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_IN6ZONESZ 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], + zoneid[NS_IN6ZONESZ], *tp, *endp, - *colonp; + *colonp, + *zp; const char *xdigits, *curtok; int ch, - saw_xdigit; + saw_xdigit, + saw_zoneid; 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((zp = zoneid), '\0', NS_IN6ZONESZ); 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_zoneid = 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_zoneid) + goto enoent; + + do + { + ch = *src++; + /* consider all printable characters */ + if (32 <= ch && ch <= 127) + *zp++ = ch; + else + goto enoent; + + if (zp - zoneid >= NS_IN6ZONESZ) + goto enoent; + } while (*src != '\0' && *src != '/'); + + saw_zoneid = 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, zoneid, NS_IN6ZONESZ); return (bits); diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c index 1f8469a..2c666d3 100644 --- a/src/backend/utils/adt/network.c +++ b/src/backend/utils/adt/network.c @@ -99,7 +99,7 @@ cidr_in(PG_FUNCTION_ARGS) static char * network_out(inet *src, bool is_cidr) { - char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN]; char *dst; int len; @@ -117,6 +117,24 @@ network_out(inet *src, bool is_cidr) snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src)); } + if (ip_contains_zoneid(src)) + { + char *ch = strchr(tmp, '/'); + + if (ch) + { + len = ch - tmp; + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(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_zoneid(src)); + } + } + return pstrdup(tmp); } @@ -152,6 +170,7 @@ network_recv(StringInfo buf, bool is_cidr) { inet *addr; char *addrptr; + char *zoneidptr; int bits; int nb, i; @@ -188,6 +207,18 @@ network_recv(StringInfo buf, bool is_cidr) for (i = 0; i < nb; i++) addrptr[i] = pq_getmsgbyte(buf); + /* zoneid */ + nb = pq_getmsgbyte(buf); + if (nb >= MAX_ZONEID_LEN) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid length in external zoneid value"), + errhint("Max supported zone id length :%d", (MAX_ZONEID_LEN - 1)))); + + zoneidptr = (char *) ip_zoneid(addr); + for (i = 0; i < nb; i++) + zoneidptr[i] = pq_getmsgbyte(buf); + /* * Error check: CIDR values must not have any bits set beyond the masklen. */ @@ -230,6 +261,7 @@ network_send(inet *addr, bool is_cidr) { StringInfoData buf; char *addrptr; + char *zoneidptr; int nb, i; @@ -244,6 +276,16 @@ network_send(inet *addr, bool is_cidr) addrptr = (char *) ip_addr(addr); for (i = 0; i < nb; i++) pq_sendbyte(&buf, addrptr[i]); + + /* zoneid */ + nb = ip_zoneid_size(addr); + if (nb < 0) + nb = 0; + pq_sendbyte(&buf, nb); + zoneidptr = (char *) ip_zoneid(addr); + for (i = 0; i < nb; i++) + pq_sendbyte(&buf, zoneidptr[i]); + return pq_endtypsend(&buf); } @@ -399,7 +441,15 @@ 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; + + if ((ip_family(a1) == PGSQL_AF_INET6) + && (ip_contains_zoneid(a1) || ip_contains_zoneid(a2))) + return strcmp((const char *) ip_zoneid(a1), (const char *) ip_zoneid(a2)); + + return 0; } return ip_family(a1) - ip_family(a2); @@ -597,7 +647,7 @@ network_host(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); char *ptr; - char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN]; /* force display of max bits, regardless of masklen... */ if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip), @@ -610,6 +660,14 @@ network_host(PG_FUNCTION_ARGS) if ((ptr = strchr(tmp, '/')) != NULL) *ptr = '\0'; + if (ip_contains_zoneid(ip)) + { + int len; + + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip)); + } + PG_RETURN_TEXT_P(cstring_to_text(tmp)); } @@ -623,7 +681,7 @@ network_show(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); int len; - char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN]; if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip), tmp, sizeof(tmp)) == NULL) @@ -638,6 +696,24 @@ network_show(PG_FUNCTION_ARGS) snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); } + if (ip_contains_zoneid(ip)) + { + char *ch = strchr(tmp, '/'); + + if (ch) + { + len = ch - tmp; + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip)); + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); + } + else + { + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip)); + } + } + PG_RETURN_TEXT_P(cstring_to_text(tmp)); } @@ -646,7 +722,7 @@ inet_abbrev(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); char *dst; - char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN]; dst = inet_net_ntop(ip_family(ip), ip_addr(ip), ip_bits(ip), tmp, sizeof(tmp)); @@ -656,6 +732,25 @@ inet_abbrev(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("could not format inet value: %m"))); + if (ip_contains_zoneid(ip)) + { + int len; + char *ch = strchr(tmp, '/'); + + if (ch) + { + len = ch - tmp; + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip)); + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); + } + else + { + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip)); + } + } + PG_RETURN_TEXT_P(cstring_to_text(tmp)); } @@ -664,7 +759,7 @@ cidr_abbrev(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); char *dst; - char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128") + MAX_ZONEID_LEN]; dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip), ip_bits(ip), tmp, sizeof(tmp)); @@ -674,6 +769,25 @@ cidr_abbrev(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("could not format cidr value: %m"))); + if (ip_contains_zoneid(ip)) + { + int len; + char *ch = strchr(tmp, '/'); + + if (ch) + { + len = ch - tmp; + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip)); + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); + } + else + { + len = strlen(tmp); + snprintf(tmp + len, sizeof(tmp) - len, "%%%s", ip_zoneid(ip)); + } + } + PG_RETURN_TEXT_P(cstring_to_text(tmp)); } diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h index 2fe3ca8..464fb0c 100644 --- a/src/include/utils/inet.h +++ b/src/include/utils/inet.h @@ -16,6 +16,8 @@ #include "fmgr.h" +#define MAX_ZONEID_LEN 256 + /* * This is the internal storage format for IP addresses * (both INET and CIDR datatypes): @@ -25,6 +27,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 ipzoneid[MAX_ZONEID_LEN]; /* up to 255 bytes of zone id name */ } inet_struct; /* @@ -75,15 +78,28 @@ typedef struct #define ip_addr(inetptr) \ (((inet_struct *) VARDATA_ANY(inetptr))->ipaddr) +#define ip_zoneid(inetptr) \ + (((inet_struct *) VARDATA_ANY(inetptr))->ipzoneid) + #define ip_addrsize(inetptr) \ (ip_family(inetptr) == PGSQL_AF_INET ? 4 : 16) +#define inet_addrsize(dst) \ + (VARHDRSZ + offsetof(inet_struct, ipaddr) + ip_addrsize(dst)) + +#define ip_zoneid_size(inetptr) \ + (ip_family(inetptr) == PGSQL_AF_INET ? false \ + : (strlen((const char *)((inet_struct *) VARDATA_ANY(inetptr))->ipzoneid))) + +#define ip_contains_zoneid(inetptr) \ + (ip_family(inetptr) == PGSQL_AF_INET ? false \ + : (VARSIZE_ANY(inetptr) > inet_addrsize(inetptr) ? true : false)) + #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)) + SET_VARSIZE(dst, inet_addrsize(dst) + ip_zoneid_size(dst)) /*