pgtypes: Support cidr and inet

This commit is contained in:
Yorhel 2025-02-10 10:54:25 +01:00
parent b66610e25a
commit 7076714296
3 changed files with 59 additions and 2 deletions

View file

@ -326,6 +326,55 @@ SENDFN(array) {
#undef ARRAY_MAXDIM
RECVFN(inet) { /* Also works for cidr */
char tmp[128];
if (len < 8) RERR("input data too short");
// 0: ip_family, 1: mask_bits, 2: is_cidr, 3: addrsize, 4: address
if (buf[0] == 2) { /* INET */
RLEN(8);
if (!inet_ntop(AF_INET, buf+4, tmp, sizeof(tmp)-1)) RERR("%s", strerror(errno));
} else if (buf[0] == 3) { /* INET6 */
RLEN(20);
if (!inet_ntop(AF_INET6, buf+4, tmp, sizeof(tmp)-1)) RERR("%s", strerror(errno));
} else RERR("unknown address type");
if (buf[2] || buf[1] != (buf[0] == 2 ? 32 : (char)128))
return newSVpvf("%s/%d", tmp, (unsigned char)buf[1]);
return newSVpv(tmp, 0);
}
SENDFN(inet) {
char tmp[128];
STRLEN len;
const char *in = SvPV(val, len);
if (len >= sizeof(tmp)) SERR("input too long");
char family = strchr(in, ':') ? 3 : 2;
char *wr = fustr_write_buf(out, family == 2 ? 8 : 20);
unsigned char *mask = (unsigned char*)wr+1;
wr[0] = family;
*mask = family == 2 ? 32 : 128;
wr[2] = ctx->oid == 650;
wr[3] = family == 2 ? 4 : 16;
char *slash = strchr(in, '/');
if (slash && slash - in < 100) {
memcpy(tmp, in, slash - in);
tmp[slash - in] = 0;
in = tmp;
}
if (inet_pton(family == 2 ? AF_INET : AF_INET6, in, wr+4) != 1)
SERR("invalid address");
if (slash) {
UV uv = 129;
if (!grok_atoUV(slash+1, &uv, NULL) || uv > *mask)
SERR("invalid mask");
*mask = uv;
}
}
#undef SIV
#undef RLEN
#undef RECVFN
@ -385,7 +434,7 @@ SENDFN(array) {
/* 603 box */\
/* 604 polygon */\
/* 628 line */\
/* 650 cidr */\
B( 650, "cidr", inet )\
A( 629, "_line", 628 )\
A( 651, "_cidr", 650 )\
B( 700, "float4", float4)\
@ -397,7 +446,7 @@ SENDFN(array) {
/* 790 money */\
A( 791, "_money", 790 )\
/* 829 macaddr */\
/* 869 inet */\
B( 869, "inet", inet )\
A( 1000, "_bool", 16 )\
A( 1001, "_bytea", 17 )\
A( 1002, "_char", 18 )\