Skip to content

Commit 3596433

Browse files
authored
Merge pull request #208 from denizzzka/net_addr_support
inet and cidr types support added
2 parents 75be9e1 + 3593b06 commit 3596433

File tree

8 files changed

+344
-3
lines changed

8 files changed

+344
-3
lines changed

dub.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
{
4949
"dpq2": { "version": "*", "dflags-dmd": ["-preview=in"] },
5050
"vibe-serialization": { "version": "*", "dflags-dmd": ["-preview=in"] },
51+
"vibe-core": { "version": "*", "dflags-dmd": ["-preview=in"] },
5152
"gfm:math": "~>8.0.6"
5253
},
5354
"configurations": [

src/dpq2/conv/from_d_types.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module dpq2.conv.from_d_types;
55

66
public import dpq2.conv.arrays : isArrayType, toValue, isStaticArrayString;
77
public import dpq2.conv.geometric : isGeometricType, toValue;
8+
public import dpq2.conv.inet: toValue, vibe2pg;
89
import dpq2.conv.time : POSTGRES_EPOCH_DATE, TimeStamp, TimeStampUTC, TimeOfDayWithTZ, Interval;
910
import dpq2.oids : detectOidTypeFromNative, oidConvTo, OidType;
1011
import dpq2.value : Value, ValueFormat;

src/dpq2/conv/inet.d

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
module dpq2.conv.inet;
2+
3+
import dpq2.conv.to_d_types;
4+
import dpq2.oids: OidType;
5+
import dpq2.value;
6+
7+
import std.bitmanip: bigEndianToNative, nativeToBigEndian;
8+
import std.conv: to;
9+
import std.socket;
10+
11+
@safe:
12+
13+
enum PgFamily : ubyte {
14+
PGSQL_AF_INET = AddressFamily.INET,
15+
PGSQL_AF_INET6,
16+
}
17+
18+
alias InetAddress = TInetAddress!false; /// Represents inet PG value
19+
alias CidrAddress = TInetAddress!true; /// Represents cidr PG value
20+
21+
///
22+
package struct TInetAddress (bool isCIDR)
23+
{
24+
PgFamily family;
25+
ubyte netmask;
26+
AddrValue addr;
27+
alias addr this;
28+
29+
///
30+
this(in InternetAddress ia, ubyte mask = 32)
31+
{
32+
addr4 = ia.addr;
33+
family = PgFamily.PGSQL_AF_INET;
34+
netmask = mask;
35+
}
36+
37+
///
38+
this(in Internet6Address ia, ubyte mask = 128)
39+
{
40+
addr6 = ia.addr;
41+
family = PgFamily.PGSQL_AF_INET6;
42+
netmask = mask;
43+
}
44+
45+
///
46+
Address createStdAddr(ushort port = InternetAddress.PORT_ANY) const
47+
{
48+
switch(family)
49+
{
50+
case PgFamily.PGSQL_AF_INET:
51+
return new InternetAddress(addr4, port);
52+
53+
case PgFamily.PGSQL_AF_INET6:
54+
return new Internet6Address(addr6, port);
55+
56+
default:
57+
assert(0, family.unsup);
58+
}
59+
}
60+
61+
///
62+
auto toString() const
63+
{
64+
import std.format: format;
65+
66+
switch (family)
67+
{
68+
case PgFamily.PGSQL_AF_INET:
69+
case PgFamily.PGSQL_AF_INET6:
70+
return format("%s/%d", createStdAddr.toAddrString, this.netmask);
71+
72+
default:
73+
return family.unsup;
74+
}
75+
}
76+
}
77+
78+
unittest
79+
{
80+
auto std_addr = new InternetAddress("127.0.0.1", 123);
81+
auto pg_addr = InetAddress(std_addr);
82+
83+
assert(pg_addr.createStdAddr.toAddrString == "127.0.0.1");
84+
85+
auto v = pg_addr.toValue;
86+
assert(v.binaryValueAs!InetAddress.toString == "127.0.0.1/32");
87+
assert(v.binaryValueAs!InetAddress.createStdAddr.toAddrString == "127.0.0.1");
88+
assert(v.binaryValueAs!InetAddress == pg_addr);
89+
}
90+
91+
unittest
92+
{
93+
auto std_addr = new Internet6Address("::1", 123);
94+
auto pg_addr = InetAddress(std_addr);
95+
96+
assert(pg_addr.createStdAddr.toAddrString == "::1");
97+
98+
auto v = pg_addr.toValue;
99+
assert(v.binaryValueAs!InetAddress.toString == "::1/128");
100+
assert(v.binaryValueAs!InetAddress.createStdAddr.toAddrString == "::1");
101+
assert(v.binaryValueAs!InetAddress == pg_addr);
102+
}
103+
104+
///
105+
InetAddress vibe2pg(VibeNetworkAddress)(VibeNetworkAddress a)
106+
{
107+
InetAddress r;
108+
109+
switch(a.family)
110+
{
111+
case AddressFamily.INET:
112+
r.family = PgFamily.PGSQL_AF_INET;
113+
r.netmask = 32;
114+
r.addr4 = a.sockAddrInet4.sin_addr.s_addr.representAsBytes.bigEndianToNative!uint;
115+
break;
116+
117+
case AddressFamily.INET6:
118+
r.family = PgFamily.PGSQL_AF_INET6;
119+
r.netmask = 128;
120+
r.addr6 = AddrValue(a.sockAddrInet6.sin6_addr.s6_addr).swapEndiannesForBigEndianSystems;
121+
break;
122+
123+
default:
124+
throwUnsup(a.family);
125+
}
126+
127+
return r;
128+
}
129+
130+
private ref ubyte[T.sizeof] representAsBytes(T)(const ref return T s) @trusted
131+
{
132+
return *cast(ubyte[T.sizeof]*) &s;
133+
}
134+
135+
private union Hdr
136+
{
137+
ubyte[4] bytes;
138+
139+
struct
140+
{
141+
PgFamily family;
142+
ubyte netmask;
143+
ubyte always_zero;
144+
ubyte addr_len;
145+
}
146+
}
147+
148+
/// Constructs Value from InetAddress or from CidrAddress
149+
Value toValue(T)(T v)
150+
if(is(T == InetAddress) || is(T == CidrAddress))
151+
{
152+
Hdr hdr;
153+
hdr.family = v.family;
154+
155+
ubyte[] addr_net_byte_order;
156+
157+
switch(v.family)
158+
{
159+
case PgFamily.PGSQL_AF_INET:
160+
addr_net_byte_order ~= v.addr4.nativeToBigEndian;
161+
break;
162+
163+
case PgFamily.PGSQL_AF_INET6:
164+
addr_net_byte_order ~= v.addr.swapEndiannesForBigEndianSystems;
165+
break;
166+
167+
default:
168+
throwUnsup(v.family);
169+
}
170+
171+
hdr.addr_len = addr_net_byte_order.length.to!ubyte;
172+
hdr.netmask = v.netmask;
173+
174+
immutable r = (hdr.bytes ~ addr_net_byte_order).idup;
175+
return Value(r, OidType.HostAddress);
176+
}
177+
178+
package:
179+
180+
/// Convert Value to network address type
181+
T binaryValueAs(T)(in Value v)
182+
if(is(T == InetAddress) || is(T == CidrAddress))
183+
{
184+
enum oidType = is(T == InetAddress) ? OidType.HostAddress : OidType.NetworkAddress;
185+
enum typeName = is(T == InetAddress) ? "inet" : "cidr";
186+
187+
if(v.oidType != oidType)
188+
throwTypeComplaint(v.oidType, typeName);
189+
190+
Hdr hdr;
191+
enum headerLen = hdr.sizeof;
192+
enum ipv4_addr_len = 4;
193+
194+
if(v.data.length < hdr.sizeof + ipv4_addr_len)
195+
throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, "unexpected data ending");
196+
197+
hdr.bytes = v.data[0 .. hdr.bytes.length];
198+
199+
ubyte lenMustBe;
200+
switch(hdr.family)
201+
{
202+
case PgFamily.PGSQL_AF_INET: lenMustBe = ipv4_addr_len; break;
203+
case PgFamily.PGSQL_AF_INET6: lenMustBe = 16; break;
204+
default: throwUnsup(hdr.family);
205+
}
206+
207+
if(hdr.addr_len != lenMustBe && hdr.always_zero == 0)
208+
throw new ValueConvException(
209+
ConvExceptionType.SIZE_MISMATCH,
210+
"Wrong address length, must be "~lenMustBe.to!string
211+
);
212+
213+
if(headerLen + hdr.addr_len != v.data.length)
214+
throw new ValueConvException(
215+
ConvExceptionType.SIZE_MISMATCH,
216+
"Address length not matches to Value data length"
217+
);
218+
219+
import std.bitmanip: bigEndianToNative;
220+
221+
T r;
222+
r.family = hdr.family;
223+
r.netmask = hdr.netmask;
224+
225+
switch(hdr.family)
226+
{
227+
case PgFamily.PGSQL_AF_INET:
228+
const ubyte[4] b = v.data[headerLen..$];
229+
r.addr4 = b.bigEndianToNative!uint;
230+
break;
231+
232+
case PgFamily.PGSQL_AF_INET6:
233+
AddrValue av;
234+
av.addr6 = v.data[headerLen..$];
235+
r.addr6 = av.swapEndiannesForBigEndianSystems;
236+
break;
237+
238+
default: assert(0);
239+
}
240+
241+
return r;
242+
}
243+
244+
private:
245+
246+
//TODO: noreturn?
247+
void throwUnsup(T)(T family)
248+
{
249+
throw new ValueConvException(ConvExceptionType.NOT_IMPLEMENTED, family.unsup);
250+
}
251+
252+
string unsup(T)(in T family)
253+
{
254+
return "Unsupported address family: "~family.to!string;
255+
}
256+
257+
private union AddrValue
258+
{
259+
ubyte[16] addr6; // IPv6 address in native byte order
260+
short[8] addr6_parts; // for endiannes swap purpose
261+
262+
struct
263+
{
264+
ubyte[12] __unused;
265+
uint addr4; // IPv4 address in native byte order
266+
}
267+
}
268+
269+
import std.system: Endian, endian;
270+
271+
static if(endian == Endian.littleEndian)
272+
auto swapEndiannesForBigEndianSystems(in AddrValue s)
273+
{
274+
// do nothing for little endian
275+
return s.addr6;
276+
}
277+
else
278+
{
279+
280+
ubyte[16] swapEndiannesForBigEndianSystems(in AddrValue s)
281+
{
282+
import std.bitmanip: swapEndian;
283+
284+
AddrValue r;
285+
enum len = AddrValue.addr6_parts.length;
286+
287+
foreach(ubyte i; 0 .. len)
288+
r.addr6_parts[i] = s.addr6_parts[i].swapEndian;
289+
290+
return r.addr6;
291+
}
292+
293+
}

src/dpq2/conv/native_tests.d

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ module dpq2.conv.native_tests;
33
import dpq2;
44
import dpq2.conv.arrays : isArrayType;
55
import dpq2.conv.geometric: Line;
6+
import dpq2.conv.inet: InetAddress, CidrAddress;
67
import std.bitmanip : BitArray;
78
import std.datetime;
9+
import std.socket: InternetAddress, Internet6Address;
810
import std.typecons: Nullable;
911
import std.uuid: UUID;
1012
import std.variant: Variant;
@@ -40,6 +42,7 @@ public void _integration_test( string connParam ) @system
4042
{
4143
import std.format: format;
4244
import dpq2.connection: createTestConn;
45+
import vibe.core.net: VibeNetworkAddress = NetworkAddress;
4346

4447
auto conn = createTestConn(connParam);
4548

@@ -258,6 +261,28 @@ public void _integration_test( string connParam ) @system
258261
C!SysTime(SysTime(DateTime(1997, 12, 17, 7, 37, 16), dur!"usecs"(12), testTZ), "timestamptz", "'1997-12-17 07:37:16.000012+02'");
259262
C!(Nullable!SysTime)(Nullable!SysTime(SysTime(DateTime(1997, 12, 17, 7, 37, 16), dur!"usecs"(12), testTZ)), "timestamptz", "'1997-12-17 07:37:16.000012+02'");
260263

264+
// inet
265+
const testInetAddr1 = InetAddress(new InternetAddress("127.0.0.1", InternetAddress.PORT_ANY), 9);
266+
C!InetAddress(testInetAddr1, "inet", `'127.0.0.1/9'`);
267+
const testInetAddr2 = InetAddress(new InternetAddress("127.0.0.1", InternetAddress.PORT_ANY));
268+
C!InetAddress(testInetAddr2, "inet", `'127.0.0.1/32'`);
269+
const testInetAddr3 = VibeNetworkAddress(new InternetAddress("127.0.0.1", InternetAddress.PORT_ANY)).vibe2pg;
270+
C!InetAddress(testInetAddr3, "inet", `'127.0.0.1/32'`);
271+
272+
// inet6
273+
const testInet6Addr1 = InetAddress(new Internet6Address("2::1", InternetAddress.PORT_ANY));
274+
C!InetAddress(testInet6Addr1, "inet", `'2::1/128'`);
275+
const testInet6Addr2 = InetAddress(new Internet6Address("2001:0:130F::9C0:876A:130B", InternetAddress.PORT_ANY),24);
276+
C!InetAddress(testInet6Addr2, "inet", `'2001:0:130f::9c0:876a:130b/24'`);
277+
const testInet6Addr3 = VibeNetworkAddress(new Internet6Address("2001:0:130F::9C0:876A:130B", InternetAddress.PORT_ANY)).vibe2pg;
278+
C!InetAddress(testInet6Addr3, "inet", `'2001:0:130f::9c0:876a:130b/128'`);
279+
280+
// cidr
281+
const testCidrAddr1 = CidrAddress(new InternetAddress("192.168.0.0", InternetAddress.PORT_ANY), 25);
282+
C!CidrAddress(testCidrAddr1, "cidr", `'192.168.0.0/25'`);
283+
const testCidrAddr2 = CidrAddress(new Internet6Address("::", InternetAddress.PORT_ANY), 64);
284+
C!CidrAddress(testCidrAddr2, "cidr", `'::/64'`);
285+
261286
// json
262287
C!PGjson(Json(["float_value": Json(123.456), "text_str": Json("text string")]), "json", `'{"float_value": 123.456,"text_str": "text string"}'`);
263288
C!(Nullable!PGjson)(Nullable!Json(Json(["foo": Json("bar")])), "json", `'{"foo":"bar"}'`);
@@ -289,6 +314,7 @@ public void _integration_test( string connParam ) @system
289314
C!(PGuuid[])([UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640")], "uuid[]", "'{8b9ab33a-96e9-499b-9c36-aad1fe86d640}'");
290315
C!(PGline[])([Line(1,2,3), Line(4,5,6)], "line[]", `'{"{1,2,3}","{4,5,6}"}'`);
291316
C!(PGtimestamp[])([PGtimestamp(DateTime(1997, 12, 17, 7, 37, 16), dur!"usecs"(12))], "timestamp[]", `'{"1997-12-17 07:37:16.000012"}'`);
317+
C!(InetAddress[])([testInetAddr1, testInet6Addr2], "inet[]", `'{127.0.0.1/9,2001:0:130f::9c0:876a:130b/24}'`);
292318
C!(Nullable!(int[]))(Nullable!(int[]).init, "int[]", "NULL");
293319
C!(Nullable!(int[]))(Nullable!(int[])([1,2,3]), "int[]", "'{1,2,3}'");
294320
}

src/dpq2/conv/to_d_types.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import dpq2.conv.from_d_types;
1212
import dpq2.conv.numeric: rawValueToNumeric;
1313
import dpq2.conv.time: binaryValueAs, TimeStamp, TimeStampUTC, TimeOfDayWithTZ, Interval;
1414
import dpq2.conv.geometric: binaryValueAs, Line;
15+
import dpq2.conv.inet: binaryValueAs, InetAddress, CidrAddress;
1516
import dpq2.conv.arrays : binaryValueAs;
1617

1718
import vibe.data.json: Json, parseJsonString;

src/dpq2/conv/to_variant.d

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module dpq2.conv.to_variant;
44
import dpq2.value;
55
import dpq2.oids: OidType;
66
import dpq2.result: ArrayProperties;
7+
import dpq2.conv.inet: InetAddress, CidrAddress;
78
import dpq2.conv.to_d_types;
89
import dpq2.conv.numeric: rawValueToNumeric;
910
import dpq2.conv.time: TimeStampUTC;
@@ -98,6 +99,12 @@ Variant toVariant(bool isNullablePayload = true)(in Value v) @safe
9899
case Date: return retVariant!PGdate;
99100
case DateArray: return retArray__!PGdate;
100101

102+
case HostAddress: return retVariant!InetAddress;
103+
case HostAddressArray: return retArray__!InetAddress;
104+
105+
case NetworkAddress: return retVariant!CidrAddress;
106+
case NetworkAddressArray: return retArray__!CidrAddress;
107+
101108
case Time: return retVariant!PGtime_without_time_zone;
102109
case TimeArray: return retArray__!PGtime_without_time_zone;
103110

0 commit comments

Comments
 (0)