Skip to content

Commit 014d29f

Browse files
authored
Merge pull request #1418 from AyanKoche/cidr-change
CIDR Support in vertx-pg-client
2 parents acb02de + f107d9a commit 014d29f

File tree

5 files changed

+251
-2
lines changed

5 files changed

+251
-2
lines changed

vertx-pg-client/README.adoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ The *Reactive Postgres Client* currently supports the following data types
221221
|`io.vertx.pgclient.data.Money[]`
222222
|✔
223223

224+
|`CIDR`
225+
|`io.vertx.pgclient.data.Cidr`
226+
|✔
227+
224228
|`PATH`
225229
|`i.r.p.data.Path`
226230
|✔
@@ -270,7 +274,7 @@ Note: PostgreSQL JSON and JSONB types are represented by the following Java type
270274

271275
The following types
272276

273-
_MONEY_, _BIT_, _VARBIT_, _MACADDR_, _CIDR_, _MACADDR8_,
277+
_MONEY_, _BIT_, _VARBIT_, _MACADDR_, _MACADDR8_,
274278
_XML_, _HSTORE_, _OID_,
275279
_VOID_
276280

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.vertx.pgclient.data;
2+
3+
import java.net.Inet4Address;
4+
import java.net.Inet6Address;
5+
import java.net.InetAddress;
6+
7+
/**
8+
* A PostgreSQL <a href="https://www.postgresql.org/docs/current/datatype-net-types.html#DATATYPE-CIDR">classless internet domain routing</a>.
9+
*/
10+
public class Cidr {
11+
private InetAddress address;
12+
private Integer netmask;
13+
14+
public InetAddress getAddress(){
15+
return address;
16+
}
17+
public Cidr setAddress(InetAddress address) {
18+
if (address instanceof Inet4Address || address instanceof Inet6Address) {
19+
this.address = address;
20+
} else {
21+
throw new IllegalArgumentException("Invalid IP address type");
22+
}
23+
return this;
24+
}
25+
26+
public Integer getNetmask(){
27+
return netmask;
28+
}
29+
30+
public Cidr setNetmask(Integer netmask) {
31+
if (netmask != null && ((getAddress() instanceof Inet4Address && (netmask < 0 || netmask > 32)) ||
32+
(getAddress() instanceof Inet6Address && (netmask < 0 || netmask > 128)))) {
33+
throw new IllegalArgumentException("Invalid netmask: " + netmask);
34+
}
35+
this.netmask = netmask;
36+
return this;
37+
}
38+
39+
40+
}

vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import io.vertx.pgclient.data.Line;
2929
import io.vertx.pgclient.data.LineSegment;
3030
import io.vertx.pgclient.data.Money;
31+
import io.vertx.pgclient.data.Cidr;
3132
import io.vertx.sqlclient.Tuple;
3233
import io.vertx.sqlclient.data.Numeric;
3334
import io.vertx.pgclient.data.Interval;
@@ -97,7 +98,7 @@ public enum DataType {
9798
MACADDR(829, true, Object.class, JDBCType.OTHER),
9899
INET(869, true, Inet.class, JDBCType.OTHER),
99100
INET_ARRAY(1041, true, Inet[].class, JDBCType.OTHER),
100-
CIDR(650, true, Object.class, JDBCType.OTHER),
101+
CIDR(650, true, Cidr.class, JDBCType.OTHER),
101102
MACADDR8(774, true, Object[].class, JDBCType.OTHER),
102103
UUID(2950, true, UUID.class, JDBCType.OTHER, Tuple::getUUID),
103104
UUID_ARRAY(2951, true, UUID[].class, JDBCType.OTHER, Tuple::getArrayOfUUIDs),

vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,9 @@ public static void encodeBinary(DataType id, Object value, ByteBuf buff) {
364364
case MONEY_ARRAY:
365365
binaryEncodeArray((Money[]) value, DataType.MONEY, buff);
366366
break;
367+
case CIDR:
368+
binaryEncodeCidr((Cidr) value, buff);
369+
break;
367370
default:
368371
logger.debug("Data type " + id + " does not support binary encoding");
369372
defaultEncodeBinary(value, buff);
@@ -501,6 +504,8 @@ public static Object decodeBinary(DataType id, int index, int len, ByteBuf buff)
501504
return binaryDecodeMoney(index, len, buff);
502505
case MONEY_ARRAY:
503506
return binaryDecodeArray(MONEY_ARRAY_FACTORY, DataType.MONEY, index, len, buff);
507+
case CIDR:
508+
return binaryDecodeCidr(index, len, buff);
504509
default:
505510
logger.debug("Data type " + id + " does not support binary decoding");
506511
return defaultDecodeBinary(index, len, buff);
@@ -641,6 +646,8 @@ public static Object decodeText(DataType id, int index, int len, ByteBuf buff) {
641646
return textDecodeMoney(index, len, buff);
642647
case MONEY_ARRAY:
643648
return textDecodeArray(MONEY_ARRAY_FACTORY, DataType.MONEY, index, len, buff);
649+
case CIDR:
650+
return textDecodeCidr(index, len, buff);
644651
default:
645652
return defaultDecodeText(index, len, buff);
646653
}
@@ -1713,4 +1720,85 @@ private static <T> void textEncodeArray(T[] values, DataType type, ByteBuf buff)
17131720
}
17141721
buff.writeByte('}');
17151722
}
1723+
1724+
private static Cidr binaryDecodeCidr(int index, int len, ByteBuf buff){
1725+
byte family = buff.getByte(index);
1726+
byte netmask = buff.getByte(index+1);
1727+
Integer val;
1728+
int size = buff.getByte(index+3);
1729+
byte[] data = new byte[size];
1730+
buff.getBytes(index+4,data);
1731+
InetAddress address;
1732+
1733+
switch (family){
1734+
case 2:
1735+
case 3:
1736+
// IPV4 and IPV6
1737+
try {
1738+
address = InetAddress.getByAddress(data);
1739+
}catch (UnknownHostException e){
1740+
throw new DecoderException(e);
1741+
}
1742+
break;
1743+
default:
1744+
throw new DecoderException("Invalid IP family: " + family);
1745+
}
1746+
val = Byte.toUnsignedInt(netmask);
1747+
return new Cidr().setAddress(address).setNetmask(val);
1748+
}
1749+
1750+
private static void binaryEncodeCidr(Cidr value, ByteBuf buff) {
1751+
InetAddress address = value.getAddress();
1752+
byte family;
1753+
byte[] data;
1754+
int netmask;
1755+
1756+
if (address instanceof Inet6Address) {
1757+
family = 3;
1758+
Inet6Address inet6Address = (Inet6Address) address;
1759+
data = inet6Address.getAddress();
1760+
netmask = (value.getNetmask() == null) ? 128 : value.getNetmask();
1761+
} else if (address instanceof Inet4Address) {
1762+
family = 2;
1763+
Inet4Address inet4Address = (Inet4Address) address;
1764+
data = inet4Address.getAddress();
1765+
netmask = (value.getNetmask() == null) ? 32 : value.getNetmask();
1766+
} else {
1767+
throw new DecoderException("Invalid inet address");
1768+
}
1769+
1770+
buff.writeByte(family);
1771+
buff.writeByte(netmask);
1772+
buff.writeByte(0); // INET
1773+
buff.writeByte(data.length);
1774+
buff.writeBytes(data);
1775+
}
1776+
1777+
private static Cidr textDecodeCidr(int index, int len, ByteBuf buff) {
1778+
Cidr cidr = new Cidr();
1779+
int sepIdx = buff.indexOf(index, index + len, (byte) '/');
1780+
String s;
1781+
1782+
if (sepIdx == -1) {
1783+
s = textdecodeTEXT(index, len, buff);
1784+
} else {
1785+
s = textdecodeTEXT(index, sepIdx - index, buff);
1786+
String t = textdecodeTEXT(sepIdx + 1, len - (sepIdx + 1 - index), buff);
1787+
try {
1788+
int netmask = Integer.parseInt(t);
1789+
cidr.setNetmask(netmask);
1790+
} catch (NumberFormatException e) {
1791+
throw new DecoderException(e);
1792+
}
1793+
}
1794+
1795+
try {
1796+
InetAddress v = InetAddress.getByName(s);
1797+
cidr.setAddress(v);
1798+
} catch (UnknownHostException e) {
1799+
throw new DecoderException(e);
1800+
}
1801+
1802+
return cidr;
1803+
}
17161804
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package io.vertx.pgclient.data;
2+
3+
import io.vertx.ext.unit.TestContext;
4+
import io.vertx.pgclient.PgConnection;
5+
import io.vertx.sqlclient.*;
6+
import org.junit.Test;
7+
8+
import java.net.Inet4Address;
9+
import java.net.Inet6Address;
10+
import java.net.InetAddress;
11+
import java.util.function.BiFunction;
12+
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertThrows;
15+
16+
public class CidrCodecTest extends DataTypeTestBase{
17+
18+
@Test
19+
public void testValidIPv4() throws Exception {
20+
InetAddress address = InetAddress.getByName("192.168.1.1");
21+
Cidr cidr = new Cidr();
22+
cidr.setAddress(address);
23+
cidr.setNetmask(24);
24+
assertEquals(address, cidr.getAddress());
25+
assertEquals(Integer.valueOf(24), cidr.getNetmask());
26+
}
27+
28+
@Test
29+
public void testValidIPv6() throws Exception {
30+
InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944");
31+
Cidr cidr = new Cidr();
32+
cidr.setAddress(address);
33+
cidr.setNetmask(64);
34+
assertEquals(address, cidr.getAddress());
35+
assertEquals(Integer.valueOf(64), cidr.getNetmask());
36+
}
37+
38+
@Test
39+
public void testInvalidNetmaskIPv4() throws Exception {
40+
InetAddress address = InetAddress.getByName("192.168.1.1");
41+
Cidr cidr = new Cidr();
42+
cidr.setAddress(address);
43+
assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(33));
44+
}
45+
46+
@Test
47+
public void testInvalidNetmaskIPv6() throws Exception {
48+
InetAddress address = InetAddress.getByName("fe80::f03c:91ff:feae:e944");
49+
Cidr cidr = new Cidr();
50+
cidr.setAddress(address);
51+
assertThrows(IllegalArgumentException.class, () -> cidr.setNetmask(129));
52+
}
53+
54+
@Test
55+
public void testBinaryDecodeCIDR(TestContext ctx) throws Exception {
56+
testDecodeCIDR(ctx, SqlClient::preparedQuery);
57+
}
58+
59+
private void testDecodeCIDR(TestContext ctx, BiFunction<SqlClient, String, Query<RowSet<Row>>> a) throws Exception {
60+
InetAddress addr1 = Inet4Address.getByName("128.0.0.0");
61+
InetAddress addr2 = Inet6Address.getByName("2001:0db8:1234:0000:0000:0000:0000:0000");
62+
PgConnection.connect(vertx, options).onComplete(ctx.asyncAssertSuccess(conn -> {
63+
a.apply(conn, "SELECT " +
64+
"'128.0.0.0'::CIDR," +
65+
"'128.0.0.0/4'::CIDR," +
66+
"'2001:0db8:1234:0000:0000:0000:0000:0000'::CIDR," +
67+
"'2001:0db8:1234:0000:0000:0000:0000:0000/56'::CIDR")
68+
.execute()
69+
.onComplete(ctx.asyncAssertSuccess(rows -> {
70+
ctx.assertEquals(1, rows.size());
71+
Row row = rows.iterator().next();
72+
Cidr v1 = (Cidr) row.getValue(0);
73+
Cidr v2 = (Cidr) row.getValue(1);
74+
Cidr v3 = (Cidr) row.getValue(2);
75+
Cidr v4 = (Cidr) row.getValue(3);
76+
ctx.assertEquals(addr1, v1.getAddress());
77+
ctx.assertEquals(32,v1.getNetmask());
78+
ctx.assertEquals(addr1, v2.getAddress());
79+
ctx.assertEquals(4, v2.getNetmask());
80+
ctx.assertEquals(addr2, v3.getAddress());
81+
ctx.assertEquals(128, v3.getNetmask());
82+
ctx.assertEquals(addr2, v4.getAddress());
83+
ctx.assertEquals(56, v4.getNetmask());
84+
}));
85+
}));
86+
}
87+
88+
@Test
89+
public void testBinaryEncodeCIDR(TestContext ctx) throws Exception {
90+
InetAddress addr1 = Inet4Address.getByName("128.0.0.0");
91+
InetAddress addr2 = Inet6Address.getByName("2001:0db8:1234:0000:0000:0000:0000:0000");
92+
PgConnection.connect(vertx, options).onComplete(ctx.asyncAssertSuccess(conn -> {
93+
conn
94+
.preparedQuery("SELECT ($1::CIDR)::VARCHAR, ($2::CIDR)::VARCHAR, ($3::CIDR)::VARCHAR, ($4::CIDR)::VARCHAR")
95+
.execute(Tuple.of(
96+
new Cidr().setAddress(addr1),
97+
new Cidr().setAddress(addr1).setNetmask(4),
98+
new Cidr().setAddress(addr2),
99+
new Cidr().setAddress(addr2).setNetmask(56)
100+
))
101+
.onComplete(ctx.asyncAssertSuccess(rows -> {
102+
ctx.assertEquals(1, rows.size());
103+
Row row = rows.iterator().next();
104+
String v1 = row.getString(0);
105+
String v2 = row.getString(1);
106+
String v3 = row.getString(2);
107+
String v4 = row.getString(3);
108+
ctx.assertEquals("128.0.0.0/32", v1);
109+
ctx.assertEquals("128.0.0.0/4", v2);
110+
ctx.assertEquals("2001:db8:1234::/128", v3);
111+
ctx.assertEquals("2001:db8:1234::/56", v4);
112+
}));
113+
}));
114+
}
115+
116+
}

0 commit comments

Comments
 (0)