1 // Written in D programming language
2 /**
3 *   PostgreSQL network types binary format.
4 *
5 *   Copyright: © 2014 DSoftOut
6 *   License: Subject to the terms of the MIT license, as written in the included LICENSE file.
7 *   Authors: NCrashed <ncrashed@gmail.com>
8 */
9 module pgator.db.pq.types.inet;
10 
11 import pgator.db.pq.types.oids;
12 import pgator.util.hexconv;
13 import std.algorithm;
14 import std.array;
15 import std.bitmanip;
16 import std.socket;
17 import std.conv;
18 import std.format;
19 import vibe.data.bson;
20 import core.sys.posix.sys.socket;
21 
22 /**
23 *   MAC address. Struct holds 6 octets of the address.
24 *
25 *   Serializes to bson as string like 'xx:xx:xx:xx:xx:xx'
26 */
27 struct PQMacAddress
28 {
29     ubyte a,b,c,d,e,f;
30     
31     /**
32     *   Creating from already separated octets
33     */
34     this(ubyte a, ubyte b, ubyte c, ubyte d, ubyte e, ubyte f)
35     {
36         this.a = a;
37         this.b = b;
38         this.c = c;
39         this.d = d;
40         this.e = e;
41         this.f = f;
42     }
43     
44     /**
45     *   Creating form raw buffer
46     */
47     this(ubyte[6] data)
48     {
49         a = data[0];
50         b = data[1];
51         c = data[2];
52         d = data[3];
53         e = data[4];
54         f = data[5];
55     } 
56     
57     /**
58     *   Parsing from string 'xx:xx:xx:xx:xx:xx'
59     */
60     this(string s)
61     {
62         auto data = s.splitter(':').array;
63         enforce(data.length == 6, "Failed to parse MacAddress");
64         ubyte[6] ret;
65         foreach(i, bs; data)
66         {
67             enforce(bs.length == 2, "Failed to parse MacAddress");
68             ret[i] = cast(ubyte)xtoul(bs);
69         }
70         this(ret);
71     }
72     
73     /**
74     *   Converting to string 'xx:xx:xx:xx:xx:xx'
75     */
76     string toString() const
77     {
78         string hex(const ubyte f)
79         {
80             auto builder = appender!string;
81             formattedWrite(builder, "%02x", f);
82             return builder.data;
83         }
84         return text(hex(a),":",hex(b),":",hex(c),":",hex(d),":",hex(e),":",hex(f));
85     }
86     
87     /**
88     *   Serializing to bson as string 'xx:xx:xx:xx:xx:xx'
89     */
90     Bson toBson() const
91     {
92         return Bson(toString);
93     }
94     
95     /**
96     *   Deserializing from bson. Expecting string format
97     */
98     static PQMacAddress fromBson(Bson bson)
99     {
100         return PQMacAddress(bson.get!string);
101     }
102 }
103 
104 /**
105 *   Struct holds PostgreSQL 'cidr' and 'inet' data types.
106 *   Supports IPv4 and IPv6 with explicit mask designation (CIDR model).
107 */
108 struct PQInetAddress
109 {
110 	version(Windows)
111 	{
112 		import std.socket:AddressFamily;
113 		
114 		enum Family:int
115 		{
116 			AFInet = AddressFamily.INET,
117 			
118 			AFInet6 = AddressFamily.INET6
119 		}
120 	}
121 	else version(Posix)
122 	{
123 	    enum Family : ubyte
124 	    {
125 	        AFInet = AF_INET,
126 	        AFInet6 = AF_INET+1,
127 	    }
128     }
129 	
130 	/// Address version
131     Family family;
132     /// Mask bits
133     ubyte bits;
134     /// Address body, not all buffer is used for IPv4
135     ubyte[16] ipaddr;
136     
137     /**
138     *   Parsing from string and network mask bits.
139     */
140     this(string addr, ubyte maskBits)
141     {
142         bits = maskBits;
143         auto ipv6sep = addr.find(':');
144         if(ipv6sep.empty)
145         {
146             family = Family.AFInet;
147             uint val = InternetAddress.parse(addr.dup);
148             (cast(ubyte[])ipaddr).write(val, 0);
149         } else
150         {
151             family = Family.AFInet6;
152             ipaddr = Internet6Address.parse(addr.dup);
153         }
154     }
155     
156     /**
157     *   Creating from raw data
158     */
159     this(ubyte[16] adrr, ubyte maskBits, Family family)
160     {
161         this.family = family;
162         bits = maskBits;
163         ipaddr = adrr.dup;
164     }
165     
166     /**
167     *   Returns address without mask
168     */
169     string address() const @property
170     {
171         if (family == Family.AFInet)
172         {
173             return InternetAddress.addrToString((cast(ubyte[])ipaddr).peek!uint);
174         } else
175         {
176             return new Internet6Address(ipaddr, Internet6Address.PORT_ANY).toAddrString;
177         }
178     }
179     
180     /**
181     *   Converts to string like 'address/mast'
182     */
183     string toString()
184     {
185         if(bits != 0)
186         {
187             return address ~ "/" ~ bits.to!string;
188         } else
189         {
190             return address;
191         }
192     }
193     
194     /**
195     *   Casting to native D type, but mask is thrown away.
196     */
197     T opCast(T)() if(is(T==Address))
198     {
199         if (family == Family.AFInet)
200         {
201             return new InternetAddress((cast(uint[])ipaddr)[0], InternetAddress.PORT_ANY);
202         } else
203         {
204             return new Internet6Address(ipaddr, Internet6Address.PORT_ANY);
205         }
206     }
207     
208     /**
209     *   Serializing to BSON. Address and mask are holded separatly.
210     *   Example:
211     *   -------
212     *   {
213     *       "address": "address without mask",
214     *       "mask": "bits count"
215     *   }
216     *   -------
217     */
218     Bson toBson() const
219     {
220         Bson[string] map;
221         map["address"] = Bson(address);
222         map["mask"] = Bson(cast(int)bits);
223         return Bson(map);
224     }
225     
226     /**
227     *   Deserializing from BSON.
228     */
229     static PQInetAddress fromBson(Bson bson)
230     {
231         return PQInetAddress(bson.address.get!string, cast(ubyte)bson.mask.get!int);
232     }
233 }
234 
235 PQMacAddress convert(PQType type)(ubyte[] val)
236     if(type == PQType.MacAddress)
237 {
238     assert(val.length == 6);
239     ubyte[6] buff;
240     buff[] = val[0..6];
241     return PQMacAddress(buff);
242 }
243 
244 PQInetAddress convert(PQType type)(ubyte[] val)
245     if((type == PQType.HostAddress || type == PQType.NetworkAddress))
246 {
247     assert(val.length >= 4);
248     ubyte family = val.read!ubyte;
249     ubyte bits = val.read!ubyte;
250 
251     val.read!ubyte; // flag for cidr or inet recognize
252     ubyte n = val.read!ubyte;
253     ubyte[16] addrBytes;
254     if(n == 4)
255     {
256         assert(val.length == 4, text("Expected 4 bytes, but got ", val.length));
257         addrBytes[0..4] = val[0..4]; 
258     } else if(n == 16)
259     {
260         assert(val.length == 16, text("Expected 16 bytes, but got ", val.length));
261         addrBytes[] = val[0..16]; 
262     } else
263     {
264         assert(false, text("Got invalid address size: ", n));
265     }
266 
267     return PQInetAddress(addrBytes, bits, cast(PQInetAddress.Family)family);
268 }
269 
270 version(IntegrationTest2)
271 {
272     import pgator.db.pq.types.test;
273     import pgator.db.pool;
274     import std.random;
275     import std.algorithm;
276     import std.encoding;
277     import std.math;
278     import dlogg.log;
279     import dlogg.buffered;
280     
281     void test(PQType type)(shared ILogger strictLogger, shared IConnectionPool pool)
282         if(type == PQType.MacAddress)
283     {
284         strictLogger.logInfo("Testing MacAddress...");
285 
286         auto logger = new shared BufferedLogger(strictLogger);
287         scope(failure) logger.minOutputLevel = LoggingLevel.Notice;
288         scope(exit) logger.finalize;
289         
290         assert(queryValue(logger, pool, "'08:00:2b:01:02:03'::macaddr").deserializeBson!PQMacAddress == PQMacAddress("08:00:2b:01:02:03"));
291         assert(queryValue(logger, pool, "'08-00-2b-01-02-03'::macaddr").deserializeBson!PQMacAddress == PQMacAddress("08:00:2b:01:02:03"));
292         assert(queryValue(logger, pool, "'08002b:010203'::macaddr").deserializeBson!PQMacAddress == PQMacAddress("08:00:2b:01:02:03"));
293         assert(queryValue(logger, pool, "'08002b-010203'::macaddr").deserializeBson!PQMacAddress == PQMacAddress("08:00:2b:01:02:03"));
294         assert(queryValue(logger, pool, "'0800.2b01.0203'::macaddr").deserializeBson!PQMacAddress == PQMacAddress("08:00:2b:01:02:03"));
295         assert(queryValue(logger, pool, "'08002b010203'::macaddr").deserializeBson!PQMacAddress == PQMacAddress("08:00:2b:01:02:03"));
296     }
297     
298     void test(PQType type)(shared ILogger strictLogger, shared IConnectionPool pool)
299         if(type == PQType.HostAddress)
300     {
301         strictLogger.logInfo("Testing HostAddress...");
302 
303         auto logger = new shared BufferedLogger(strictLogger);
304         scope(failure) logger.minOutputLevel = LoggingLevel.Notice;
305         scope(exit) logger.finalize;
306         
307         assert(queryValue(logger, pool, "'192.168.100.128/25'::inet").deserializeBson!PQInetAddress == PQInetAddress("192.168.100.128", 25));
308         assert(queryValue(logger, pool, "'10.1.2.3/32'::inet").deserializeBson!PQInetAddress == PQInetAddress("10.1.2.3", 32));
309         assert(queryValue(logger, pool, "'2001:4f8:3:ba::/64'::inet").deserializeBson!PQInetAddress == PQInetAddress("2001:4f8:3:ba::", 64));
310         assert(queryValue(logger, pool, "'2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128'::inet").deserializeBson!PQInetAddress == PQInetAddress("2001:4f8:3:ba:2e0:81ff:fe22:d1f1", 128));
311         assert(queryValue(logger, pool, "'::ffff:1.2.3.0/120'::inet").deserializeBson!PQInetAddress == PQInetAddress("::ffff:1.2.3.0", 120));
312         assert(queryValue(logger, pool, "'::ffff:1.2.3.0/128'::inet").deserializeBson!PQInetAddress == PQInetAddress("::ffff:1.2.3.0", 128));
313     }
314     
315     void test(PQType type)(shared ILogger strictLogger, shared IConnectionPool pool)
316         if(type == PQType.NetworkAddress)
317     {
318         strictLogger.logInfo("Testing NetworkAddress...");
319 
320         auto logger = new shared BufferedLogger(strictLogger);
321         scope(failure) logger.minOutputLevel = LoggingLevel.Notice;
322         scope(exit) logger.finalize;
323 
324         assert(queryValue(logger, pool, "'192.168.100.128/25'::cidr").deserializeBson!PQInetAddress == PQInetAddress("192.168.100.128", 25));
325         assert(queryValue(logger, pool, "'192.168/24'::cidr").deserializeBson!PQInetAddress == PQInetAddress("192.168.0.0", 24));
326         assert(queryValue(logger, pool, "'192.168/25'::cidr").deserializeBson!PQInetAddress == PQInetAddress("192.168.0.0", 25));
327         assert(queryValue(logger, pool, "'192.168.1'::cidr").deserializeBson!PQInetAddress == PQInetAddress("192.168.1.0", 24));
328         assert(queryValue(logger, pool, "'192.168'::cidr").deserializeBson!PQInetAddress == PQInetAddress("192.168.0.0", 24));
329         assert(queryValue(logger, pool, "'128.1'::cidr").deserializeBson!PQInetAddress == PQInetAddress("128.1.0.0", 16));
330         assert(queryValue(logger, pool, "'128'::cidr").deserializeBson!PQInetAddress == PQInetAddress("128.0.0.0", 16));
331         assert(queryValue(logger, pool, "'128.1.2'::cidr").deserializeBson!PQInetAddress == PQInetAddress("128.1.2.0", 24));
332         assert(queryValue(logger, pool, "'10.1.2'::cidr").deserializeBson!PQInetAddress == PQInetAddress("10.1.2.0", 24));
333         assert(queryValue(logger, pool, "'10.1'::cidr").deserializeBson!PQInetAddress == PQInetAddress("10.1.0.0", 16));
334         assert(queryValue(logger, pool, "'10'::cidr").deserializeBson!PQInetAddress == PQInetAddress("10.0.0.0", 8));
335         assert(queryValue(logger, pool, "'10.1.2.3/32'::cidr").deserializeBson!PQInetAddress == PQInetAddress("10.1.2.3", 32));
336         assert(queryValue(logger, pool, "'2001:4f8:3:ba::/64'::cidr").deserializeBson!PQInetAddress == PQInetAddress("2001:4f8:3:ba::", 64));
337         assert(queryValue(logger, pool, "'2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128'::cidr").deserializeBson!PQInetAddress == PQInetAddress("2001:4f8:3:ba:2e0:81ff:fe22:d1f1", 128));
338         assert(queryValue(logger, pool, "'::ffff:1.2.3.0/120'::cidr").deserializeBson!PQInetAddress == PQInetAddress("::ffff:1.2.3.0", 120));
339         assert(queryValue(logger, pool, "'::ffff:1.2.3.0/128'::cidr").deserializeBson!PQInetAddress == PQInetAddress("::ffff:1.2.3.0", 128));
340     }
341 }