1%%% @doc 2%%% Codec for `inet', `cidr' 3%%% https://www.postgresql.org/docs/10/static/datatype-net-types.html 4%%% $PG$/src/backend/utils/adt/network.c 5%%% 6%%% TIP: use `inet:ntoa/1' to convert `ip()' to string. 7%%% @end 8%%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru> 9-module(epgsql_codec_net). 10-behaviour(epgsql_codec). 11 12-export([init/2, names/0, encode/3, decode/3, decode_text/3]). 13 14-export_type([data/0]). 15 16-type data() :: ip() | ip_mask() | macaddr() | macaddr8(). 17 18-type ip() :: inet:ip_address(). 19-type mask() :: 0..32. 20-type ip_mask() :: {ip(), mask()}. 21-type macaddr() :: {byte(), byte(), byte(), byte(), byte(), byte()}. 22-type macaddr8() :: {byte(), byte(), byte(), byte(), byte(), byte(), byte(), byte()}. 23 24-define(INET, 2). 25-define(INET6, 3). 26-define(IP_SIZE, 4). 27-define(IP6_SIZE, 16). 28-define(MAX_IP_MASK, 32). 29-define(MAX_IP6_MASK, 128). 30 31init(_, _) -> []. 32 33names() -> 34 [inet, cidr, macaddr, macaddr8]. 35 36encode({B1, B2, B3, B4, B5, B6}, macaddr, _) -> 37 <<B1, B2, B3, B4, B5, B6>>; 38encode({B1, B2, B3, B4, B5, B6, B7, B8}, macaddr8, _) -> 39 <<B1, B2, B3, B4, B5, B6, B7, B8>>; 40encode(IpMask, _, _) -> 41 encode_net(IpMask). 42 43decode(<<B1, B2, B3, B4, B5, B6>>, macaddr, _) -> 44 {B1, B2, B3, B4, B5, B6}; 45decode(<<B1, B2, B3, B4, B5, B6, B7, B8>>, macaddr8, _) -> 46 {B1, B2, B3, B4, B5, B6, B7, B8}; 47decode(Bin, _, _) -> 48 decode_net(Bin). 49 50-spec encode_net(ip() | ip_mask()) -> binary(). 51encode_net({{_, _, _, _} = IP, Mask}) -> 52 Bin = list_to_binary(tuple_to_list(IP)), 53 <<?INET, Mask:8, 1, ?IP_SIZE, Bin/binary>>; 54encode_net({{_, _, _, _, _, _, _, _} = IP, Mask}) -> 55 Bin = << <<X:16>> || X <- tuple_to_list(IP) >>, 56 <<?INET6, Mask:8, 1, ?IP6_SIZE, Bin/binary>>; 57encode_net({_, _, _, _} = IP) -> 58 Bin = list_to_binary(tuple_to_list(IP)), 59 <<?INET, ?MAX_IP_MASK, 0, ?IP_SIZE, Bin/binary>>; 60encode_net({_, _, _, _, _, _, _, _} = IP) -> 61 Bin = << <<X:16>> || X <- tuple_to_list(IP) >>, 62 <<?INET6, ?MAX_IP6_MASK, 0, ?IP6_SIZE, Bin/binary>>. 63 64-spec decode_net(binary()) -> ip() | ip_mask(). 65decode_net(<<?INET, Mask:8, 1, ?IP_SIZE, Bin/binary>>) -> 66 {list_to_tuple(binary_to_list(Bin)), Mask}; 67decode_net(<<?INET6, Mask:8, 1, ?IP6_SIZE, Bin/binary>>) -> 68 {list_to_tuple([X || <<X:16>> <= Bin]), Mask}; 69decode_net(<<?INET, ?MAX_IP_MASK, 0, ?IP_SIZE, Bin/binary>>) -> 70 list_to_tuple(binary_to_list(Bin)); 71decode_net(<<?INET6, ?MAX_IP6_MASK, 0, ?IP6_SIZE, Bin/binary>>) -> 72 list_to_tuple([X || <<X:16>> <= Bin]). 73 74decode_text(V, _, _) -> V. 75