|
| 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 | +} |
0 commit comments