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 }