diff --git a/FU.xs b/FU.xs index 5496c46..f583706 100644 --- a/FU.xs +++ b/FU.xs @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #undef PERL_IMPLICIT_SYS diff --git a/c/pgtypes.c b/c/pgtypes.c index d3eb862..28f9afd 100644 --- a/c/pgtypes.c +++ b/c/pgtypes.c @@ -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 )\ diff --git a/t/pgtypes.t b/t/pgtypes.t index bf57c6c..091797e 100644 --- a/t/pgtypes.t +++ b/t/pgtypes.t @@ -94,6 +94,11 @@ f jsonb => \1; v jsonpath => $_ for ('$."key"', '$."a[*]"?(@ > 2)'); f jsonpath => $_ for ('', 'hello world'); +v inet => $_ for ('0.0.0.0', '0.0.0.0/12', '127.0.0.1', '192.168.0.0/16', '2001:4f8:3:ba::/64', '::ffff:1.2.3.0/120', '::'); +v cidr => '0.0.0.0', '0.0.0.0/32', undef, '0.0.0.0/32'; +v cidr => '::', '::/128', undef, '::/128'; +f inet => $_ for ('', [], '0.0.0.0/a', '0.0.0.0/1a', '[::]', '0.0.0.0/33', '::/129', '/1', ':/1'); + v 'int[]', [], undef, '{}'; v 'int[]', [1], undef, '{1}'; v 'int[]', [1,-3,undef,3], undef, '{1,-3,NULL,3}';