aboutsummaryrefslogtreecommitdiff
path: root/src/eldap.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/eldap.erl')
-rw-r--r--src/eldap.erl257
1 files changed, 154 insertions, 103 deletions
diff --git a/src/eldap.erl b/src/eldap.erl
index f48b2d840..34c20228f 100644
--- a/src/eldap.erl
+++ b/src/eldap.erl
@@ -6,7 +6,7 @@
%%% draft-ietf-asid-ldap-c-api-00.txt
%%%
%%% Copyright (C) 2000 Torbjorn Tornkvist, tnt@home.se
-%%%
+%%%
%%%
%%% This program is free software; you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by
@@ -63,9 +63,8 @@
%%% active_bind - sent bind() request and waiting for response
%%%----------------------------------------------------------------------
--behaviour(gen_fsm).
+-behaviour(p1_fsm).
--include("ejabberd.hrl").
-include("logger.hrl").
%% External exports
@@ -88,7 +87,7 @@
-export_type([filter/0]).
-include("ELDAPv3.hrl").
-
+-include_lib("kernel/include/inet.hrl").
-include("eldap.hrl").
-define(LDAP_VERSION, 3).
@@ -126,36 +125,37 @@
-record(eldap,
{version = ?LDAP_VERSION :: non_neg_integer(),
hosts = [] :: [binary()],
- host :: binary(),
+ host = undefined :: binary() | undefined,
port = 389 :: inet:port_number(),
sockmod = gen_tcp :: ssl | gen_tcp,
tls = none :: none | tls,
- tls_options = [] :: [{cacertfile, string()} |
+ tls_options = [] :: [{certfile, string()} |
+ {cacertfile, string()} |
{depth, non_neg_integer()} |
{verify, non_neg_integer()}],
- fd,
+ fd :: gen_tcp:socket() | undefined,
rootdn = <<"">> :: binary(),
passwd = <<"">> :: binary(),
id = 0 :: non_neg_integer(),
bind_timer = make_ref() :: reference(),
- dict = dict:new() :: ?TDICT,
- req_q = queue:new() :: ?TQUEUE}).
+ dict = dict:new() :: dict:dict(),
+ req_q = queue:new() :: queue:queue()}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start_link(Name) ->
- Reg_name = jlib:binary_to_atom(<<"eldap_",
+ Reg_name = misc:binary_to_atom(<<"eldap_",
Name/binary>>),
- gen_fsm:start_link({local, Reg_name}, ?MODULE, [], []).
+ p1_fsm:start_link({local, Reg_name}, ?MODULE, [], []).
-spec start_link(binary(), [binary()], inet:port_number(), binary(),
binary(), tlsopts()) -> any().
start_link(Name, Hosts, Port, Rootdn, Passwd, Opts) ->
- Reg_name = jlib:binary_to_atom(<<"eldap_",
+ Reg_name = misc:binary_to_atom(<<"eldap_",
Name/binary>>),
- gen_fsm:start_link({local, Reg_name}, ?MODULE,
+ p1_fsm:start_link({local, Reg_name}, ?MODULE,
[Hosts, Port, Rootdn, Passwd, Opts], []).
-spec get_status(handle()) -> any().
@@ -165,7 +165,7 @@ start_link(Name, Hosts, Port, Rootdn, Passwd, Opts) ->
%%% --------------------------------------------------------------------
get_status(Handle) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_all_state_event(Handle1, get_status).
+ p1_fsm:sync_send_all_state_event(Handle1, get_status).
%%% --------------------------------------------------------------------
%%% Shutdown connection (and process) asynchronous.
@@ -174,14 +174,14 @@ get_status(Handle) ->
close(Handle) ->
Handle1 = get_handle(Handle),
- gen_fsm:send_all_state_event(Handle1, close).
+ p1_fsm:send_all_state_event(Handle1, close).
%%% --------------------------------------------------------------------
%%% Add an entry. The entry field MUST NOT exist for the AddRequest
%%% to succeed. The parent of the entry MUST exist.
%%% Example:
%%%
-%%% add(Handle,
+%%% add(Handle,
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
%%% [{"objectclass", ["person"]},
%%% {"cn", ["Bill Valentine"]},
@@ -191,7 +191,7 @@ close(Handle) ->
%%% --------------------------------------------------------------------
add(Handle, Entry, Attributes) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_event(Handle1,
+ p1_fsm:sync_send_event(Handle1,
{add, Entry, add_attrs(Attributes)}, ?CALL_TIMEOUT).
%%% Do sanity check !
@@ -205,17 +205,17 @@ add_attrs(Attrs) ->
end.
%%% --------------------------------------------------------------------
-%%% Delete an entry. The entry consists of the DN of
+%%% Delete an entry. The entry consists of the DN of
%%% the entry to be deleted.
%%% Example:
%%%
-%%% delete(Handle,
+%%% delete(Handle,
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com"
%%% )
%%% --------------------------------------------------------------------
delete(Handle, Entry) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_event(Handle1, {delete, Entry},
+ p1_fsm:sync_send_event(Handle1, {delete, Entry},
?CALL_TIMEOUT).
%%% --------------------------------------------------------------------
@@ -223,21 +223,21 @@ delete(Handle, Entry) ->
%%% operations can be performed as one atomic operation.
%%% Example:
%%%
-%%% modify(Handle,
+%%% modify(Handle,
%%% "cn=Torbjorn Tornkvist, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
%%% [replace("telephoneNumber", ["555 555 00"]),
-%%% add("description", ["LDAP hacker"])]
+%%% add("description", ["LDAP hacker"])]
%%% )
%%% --------------------------------------------------------------------
-spec modify(handle(), any(), [add | delete | replace]) -> any().
modify(Handle, Object, Mods) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_event(Handle1, {modify, Object, Mods},
+ p1_fsm:sync_send_event(Handle1, {modify, Object, Mods},
?CALL_TIMEOUT).
%%%
-%%% Modification operations.
+%%% Modification operations.
%%% Example:
%%% replace("telephoneNumber", ["555 555 00"])
%%%
@@ -252,7 +252,7 @@ mod_delete(Type, Values) ->
%%% operations can be performed as one atomic operation.
%%% Example:
%%%
-%%% modify_dn(Handle,
+%%% modify_dn(Handle,
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
%%% "cn=Ben Emerson",
%%% true,
@@ -273,7 +273,7 @@ m(Operation, Type, Values) ->
modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_event(Handle1,
+ p1_fsm:sync_send_event(Handle1,
{modify_dn, Entry, NewRDN, bool_p(DelOldRDN),
optional(NewSup)},
?CALL_TIMEOUT).
@@ -282,22 +282,22 @@ modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup) ->
modify_passwd(Handle, DN, Passwd) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_event(Handle1,
+ p1_fsm:sync_send_event(Handle1,
{modify_passwd, DN, Passwd}, ?CALL_TIMEOUT).
%%% --------------------------------------------------------------------
%%% Bind.
%%% Example:
%%%
-%%% bind(Handle,
+%%% bind(Handle,
%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
%%% "secret")
%%% --------------------------------------------------------------------
-spec bind(handle(), binary(), binary()) -> any().
-
+
bind(Handle, RootDN, Passwd) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_event(Handle1, {bind, RootDN, Passwd},
+ p1_fsm:sync_send_event(Handle1, {bind, RootDN, Passwd},
?CALL_TIMEOUT).
%%% Sanity checks !
@@ -308,7 +308,7 @@ optional([]) -> asn1_NOVALUE;
optional(Value) -> Value.
%%% --------------------------------------------------------------------
-%%% Synchronous search of the Directory returning a
+%%% Synchronous search of the Directory returning a
%%% requested set of attributes.
%%%
%%% Example:
@@ -355,7 +355,7 @@ search(Handle, L) when is_list(L) ->
call_search(Handle, A) ->
Handle1 = get_handle(Handle),
- gen_fsm:sync_send_event(Handle1, {search, A},
+ p1_fsm:sync_send_event(Handle1, {search, A},
?CALL_TIMEOUT).
-spec parse_search_args(search_args()) -> eldap_search().
@@ -548,7 +548,7 @@ extensibleMatch_opts([], MRA) -> MRA.
get_handle(Pid) when is_pid(Pid) -> Pid;
get_handle(Atom) when is_atom(Atom) -> Atom;
get_handle(Name) when is_binary(Name) ->
- jlib:binary_to_atom(<<"eldap_",
+ misc:binary_to_atom(<<"eldap_",
Name/binary>>).
%%%----------------------------------------------------------------------
@@ -560,16 +560,12 @@ get_handle(Name) when is_binary(Name) ->
%% Returns: {ok, StateName, StateData} |
%% {ok, StateName, StateData, Timeout} |
%% ignore |
-%% {stop, StopReason}
+%% {stop, StopReason}
%% I use the trick of setting a timeout of 0 to pass control into the
-%% process.
+%% process.
%%----------------------------------------------------------------------
init([Hosts, Port, Rootdn, Passwd, Opts]) ->
- Encrypt = case gen_mod:get_opt(encrypt, Opts,
- fun(tls) -> tls;
- (starttls) -> starttls;
- (none) -> none
- end) of
+ Encrypt = case proplists:get_value(encrypt, Opts) of
tls -> tls;
_ -> none
end,
@@ -581,46 +577,36 @@ init([Hosts, Port, Rootdn, Passwd, Opts]) ->
end;
PT -> PT
end,
- CacertOpts = case gen_mod:get_opt(
- tls_cacertfile, Opts,
- fun(S) when is_binary(S) ->
- binary_to_list(S);
- (undefined) ->
- undefined
- end) of
+ CertOpts = case proplists:get_value(tls_certfile, Opts) of
+ undefined ->
+ [];
+ Path1 ->
+ [{certfile, Path1}]
+ end,
+ CacertOpts = case proplists:get_value(tls_cacertfile, Opts) of
undefined ->
[];
- Path ->
- [{cacertfile, Path}]
+ Path2 ->
+ [{cacertfile, Path2}]
end,
- DepthOpts = case gen_mod:get_opt(
- tls_depth, Opts,
- fun(I) when is_integer(I), I>=0 ->
- I;
- (undefined) ->
- undefined
- end) of
+ DepthOpts = case proplists:get_value(tls_depth, Opts) of
undefined ->
[];
Depth ->
[{depth, Depth}]
end,
- Verify = gen_mod:get_opt(tls_verify, Opts,
- fun(hard) -> hard;
- (soft) -> soft;
- (false) -> false
- end, false),
+ Verify = proplists:get_value(tls_verify, Opts, false),
TLSOpts = if (Verify == hard orelse Verify == soft)
andalso CacertOpts == [] ->
?WARNING_MSG("TLS verification is enabled but no CA "
"certfiles configured, so verification "
"is disabled.",
[]),
- [];
+ CertOpts;
Verify == soft ->
- [{verify, 1}] ++ CacertOpts ++ DepthOpts;
+ [{verify, 1}] ++ CertOpts ++ CacertOpts ++ DepthOpts;
Verify == hard ->
- [{verify, 2}] ++ CacertOpts ++ DepthOpts;
+ [{verify, 2}] ++ CertOpts ++ CacertOpts ++ DepthOpts;
true -> []
end,
{ok, connecting,
@@ -650,10 +636,10 @@ active(Event, From, S) ->
%%----------------------------------------------------------------------
%% Func: handle_event/3
-%% Called when gen_fsm:send_all_state_event/2 is invoked.
+%% Called when p1_fsm:send_all_state_event/2 is invoked.
%% Returns: {next_state, NextStateName, NextStateData} |
%% {next_state, NextStateName, NextStateData, Timeout} |
-%% {stop, Reason, NewStateData}
+%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
handle_event(close, _StateName, S) ->
catch (S#eldap.sockmod):close(S#eldap.fd),
@@ -669,11 +655,11 @@ handle_sync_event(_Event, _From, StateName, S) ->
%%
handle_info({Tag, _Socket, Data}, connecting, S)
when Tag == tcp; Tag == ssl ->
- ?DEBUG("tcp packet received when disconnected!~n~p", [Data]),
+ ?DEBUG("TCP packet received when disconnected!~n~p", [Data]),
{next_state, connecting, S};
handle_info({Tag, _Socket, Data}, wait_bind_response, S)
when Tag == tcp; Tag == ssl ->
- cancel_timer(S#eldap.bind_timer),
+ misc:cancel_timer(S#eldap.bind_timer),
case catch recvd_wait_bind_response(Data, S) of
bound -> dequeue_commands(S);
{fail_bind, Reason} ->
@@ -693,7 +679,7 @@ handle_info({Tag, _Socket, Data}, StateName, S)
case catch recvd_packet(Data, S) of
{response, Response, RequestType} ->
NewS = case Response of
- {reply, Reply, To, S1} -> gen_fsm:reply(To, Reply), S1;
+ {reply, Reply, To, S1} -> p1_fsm:reply(To, Reply), S1;
{ok, S1} -> S1
end,
if StateName == active_bind andalso
@@ -706,7 +692,7 @@ handle_info({Tag, _Socket, Data}, StateName, S)
end;
handle_info({Tag, _Socket}, Fsm_state, S)
when Tag == tcp_closed; Tag == ssl_closed ->
- ?WARNING_MSG("LDAP server closed the connection: ~s:~p~nIn "
+ ?WARNING_MSG("LDAP server closed the connection: ~ts:~p~nIn "
"State: ~p",
[S#eldap.host, S#eldap.port, Fsm_state]),
{next_state, connecting, close_and_retry(S)};
@@ -722,7 +708,7 @@ handle_info({timeout, Timer, {cmd_timeout, Id}},
StateName, S) ->
case cmd_timeout(Timer, Id, S) of
{reply, To, Reason, NewS} ->
- gen_fsm:reply(To, Reason),
+ p1_fsm:reply(To, Reason),
{next_state, StateName, NewS};
{error, _Reason} -> {next_state, StateName, S}
end;
@@ -735,7 +721,7 @@ handle_info({timeout, _Timer, bind_timeout}, wait_bind_response, S) ->
%% Make sure we don't fill the message queue with rubbish
%%
handle_info(Info, StateName, S) ->
- ?DEBUG("eldap. Unexpected Info: ~p~nIn state: "
+ ?DEBUG("Unexpected Info: ~p~nIn state: "
"~p~n when StateData is: ~p",
[Info, StateName, S]),
{next_state, StateName, S}.
@@ -836,7 +822,7 @@ gen_req({bind, RootDN, Passwd}) ->
%% recvd_packet
%% Deals with incoming packets in the active state
%% Will return one of:
-%% {ok, NewS} - Don't reply to client yet as this is part of a search
+%% {ok, NewS} - Don't reply to client yet as this is part of a search
%% result and we haven't got all the answers yet.
%% {reply, Result, From, NewS} - Reply with result to client From
%% {error, Reason}
@@ -861,14 +847,14 @@ recvd_packet(Pkt, S) ->
if Reason == success; Reason == sizeLimitExceeded ->
{Res, Ref} = polish(Result_so_far),
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
{reply,
#eldap_search_result{entries = Res,
referrals = Ref},
From, S#eldap{dict = New_dict}};
true ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
{reply, {error, Reason}, From,
S#eldap{dict = New_dict}}
end;
@@ -877,37 +863,37 @@ recvd_packet(Pkt, S) ->
{ok, S#eldap{dict = New_dict}};
{addRequest, {addResponse, Result}} ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
Reply = check_reply(Result, From),
{reply, Reply, From, S#eldap{dict = New_dict}};
{delRequest, {delResponse, Result}} ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
Reply = check_reply(Result, From),
{reply, Reply, From, S#eldap{dict = New_dict}};
{modifyRequest, {modifyResponse, Result}} ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
Reply = check_reply(Result, From),
{reply, Reply, From, S#eldap{dict = New_dict}};
{modDNRequest, {modDNResponse, Result}} ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
Reply = check_reply(Result, From),
{reply, Reply, From, S#eldap{dict = New_dict}};
{bindRequest, {bindResponse, Result}} ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
Reply = check_bind_reply(Result, From),
{reply, Reply, From, S#eldap{dict = New_dict}};
{extendedReq, {extendedResp, Result}} ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
Reply = check_extended_reply(Result, From),
{reply, Reply, From, S#eldap{dict = New_dict}};
{OtherName, OtherResult} ->
New_dict = dict:erase(Id, Dict),
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
{reply,
{error, {invalid_result, OtherName, OtherResult}},
From, S#eldap{dict = New_dict}}
@@ -982,16 +968,11 @@ check_id(_, _) -> throw({error, wrong_bind_id}).
%% General Helpers
%%-----------------------------------------------------------------------
-cancel_timer(Timer) ->
- erlang:cancel_timer(Timer),
- receive {timeout, Timer, _} -> ok after 0 -> ok end.
-
-
close_and_retry(S, Timeout) ->
catch (S#eldap.sockmod):close(S#eldap.fd),
Queue = dict:fold(fun (_Id,
[{Timer, Command, From, _Name} | _], Q) ->
- cancel_timer(Timer),
+ misc:cancel_timer(Timer),
queue:in_r({Command, From}, Q);
(_, _, Q) -> Q
end,
@@ -1004,7 +985,7 @@ close_and_retry(S) ->
close_and_retry(S, ?RETRY_TIMEOUT).
report_bind_failure(Host, Port, Reason) ->
- ?WARNING_MSG("LDAP bind failed on ~s:~p~nReason: ~p",
+ ?WARNING_MSG("LDAP bind failed on ~ts:~p~nReason: ~p",
[Host, Port, Reason]).
%%-----------------------------------------------------------------------
@@ -1059,8 +1040,6 @@ polish([], Res, Ref) -> {Res, Ref}.
%%-----------------------------------------------------------------------
connect_bind(S) ->
Host = next_host(S#eldap.host, S#eldap.hosts),
- ?INFO_MSG("LDAP connection on ~s:~p",
- [Host, S#eldap.port]),
Opts = if S#eldap.tls == tls ->
[{packet, asn1}, {active, true}, {keepalive, true},
binary
@@ -1069,16 +1048,14 @@ connect_bind(S) ->
[{packet, asn1}, {active, true}, {keepalive, true},
{send_timeout, ?SEND_TIMEOUT}, binary]
end,
+ ?DEBUG("Connecting to LDAP server at ~ts:~p with options ~p",
+ [Host, S#eldap.port, Opts]),
HostS = binary_to_list(Host),
- SocketData = case S#eldap.tls of
- tls ->
- SockMod = ssl, ssl:connect(HostS, S#eldap.port, Opts);
- %% starttls -> %% TODO: Implement STARTTLS;
- _ ->
- SockMod = gen_tcp,
- gen_tcp:connect(HostS, S#eldap.port, Opts)
- end,
- case SocketData of
+ SockMod = case S#eldap.tls of
+ tls -> ssl;
+ _ -> gen_tcp
+ end,
+ case connect(HostS, S#eldap.port, SockMod, Opts) of
{ok, Socket} ->
case bind_request(Socket, S#eldap{sockmod = SockMod}) of
{ok, NewS} ->
@@ -1093,9 +1070,8 @@ connect_bind(S) ->
{ok, connecting, NewS#eldap{host = Host}}
end;
{error, Reason} ->
- ?ERROR_MSG("LDAP connection failed:~n** Server: "
- "~s:~p~n** Reason: ~p~n** Socket options: ~p",
- [Host, S#eldap.port, Reason, Opts]),
+ ?ERROR_MSG("LDAP connection to ~ts:~b failed: ~ts",
+ [Host, S#eldap.port, format_error(SockMod, Reason)]),
NewS = close_and_retry(S),
{ok, connecting, NewS#eldap{host = Host}}
end.
@@ -1134,3 +1110,78 @@ bump_id(#eldap{id = Id})
when Id > (?MAX_TRANSACTION_ID) ->
?MIN_TRANSACTION_ID;
bump_id(#eldap{id = Id}) -> Id + 1.
+
+format_error(SockMod, Reason) ->
+ Txt = case SockMod of
+ ssl -> ssl:format_error(Reason);
+ gen_tcp -> inet:format_error(Reason)
+ end,
+ case Txt of
+ "unknown POSIX error" ->
+ lists:flatten(io_lib:format("~p", [Reason]));
+ _ ->
+ Txt
+ end.
+
+%%--------------------------------------------------------------------
+%% Connecting stuff
+%%--------------------------------------------------------------------
+-define(CONNECT_TIMEOUT, timer:seconds(15)).
+-define(DNS_TIMEOUT, timer:seconds(5)).
+
+connect(Host, Port, Mod, Opts) ->
+ case lookup(Host) of
+ {ok, AddrsFamilies} ->
+ do_connect(AddrsFamilies, Port, Mod, Opts, {error, nxdomain});
+ {error, _} = Err ->
+ Err
+ end.
+
+do_connect([{IP, Family}|AddrsFamilies], Port, Mod, Opts, _Err) ->
+ case Mod:connect(IP, Port, [Family|Opts], ?CONNECT_TIMEOUT) of
+ {ok, Sock} ->
+ {ok, Sock};
+ {error, _} = Err ->
+ do_connect(AddrsFamilies, Port, Mod, Opts, Err)
+ end;
+do_connect([], _Port, _Mod, _Opts, Err) ->
+ Err.
+
+lookup(Host) ->
+ case inet:parse_address(Host) of
+ {ok, IP} ->
+ {ok, [{IP, get_addr_type(IP)}]};
+ {error, _} ->
+ do_lookup([{Host, Family} || Family <- [inet6, inet]],
+ [], {error, nxdomain})
+ end.
+
+do_lookup([{Host, Family}|HostFamilies], AddrFamilies, Err) ->
+ case inet:gethostbyname(Host, Family, ?DNS_TIMEOUT) of
+ {ok, HostEntry} ->
+ Addrs = host_entry_to_addrs(HostEntry),
+ AddrFamilies1 = [{Addr, Family} || Addr <- Addrs],
+ do_lookup(HostFamilies,
+ AddrFamilies ++ AddrFamilies1,
+ Err);
+ {error, _} = Err1 ->
+ do_lookup(HostFamilies, AddrFamilies, Err1)
+ end;
+do_lookup([], [], Err) ->
+ Err;
+do_lookup([], AddrFamilies, _Err) ->
+ {ok, AddrFamilies}.
+
+host_entry_to_addrs(#hostent{h_addr_list = AddrList}) ->
+ lists:filter(
+ fun(Addr) ->
+ try get_addr_type(Addr) of
+ _ -> true
+ catch _:badarg ->
+ false
+ end
+ end, AddrList).
+
+get_addr_type({_, _, _, _}) -> inet;
+get_addr_type({_, _, _, _, _, _, _, _}) -> inet6;
+get_addr_type(_) -> erlang:error(badarg).