file containing a SSL certificate.
</DD><DT CLASS="dt-description"><B><TT>{domain_certfile, Domain, Path}</TT></B></DT><DD CLASS="dd-description">
Full path to the file containing the SSL certificate for a specific domain.
+</DD><DT CLASS="dt-description"><B><TT>{outgoing_s2s_options, Methods, Timeout}</TT></B></DT><DD CLASS="dd-description">
+Specify which address families to try, in what order, and connect timeout in milliseconds.
+By default it first tries connecting with IPv4, if that fails it tries using IPv6,
+with a timeout of 10000 milliseconds.
</DD><DT CLASS="dt-description"><B><TT>{s2s_default_policy, allow|deny}</TT></B></DT><DD CLASS="dd-description">
The default policy for incoming and outgoing s2s connections to other Jabber servers.
The default value is <TT>allow</TT>.
</PRE></DD><DT CLASS="dt-description"><B><TT>{resource, <resource>}</TT></B></DT><DD CLASS="dd-description"> Matches any JID with a resource
<TT><resource></TT>. Example:
<PRE CLASS="verbatim">{acl, mucklres, {resource, "muckl"}}.
+</PRE></DD><DT CLASS="dt-description"><B><TT>{shared_group, <groupname>}</TT></B></DT><DD CLASS="dd-description"> Matches any member of a Shared Roster Group with name <TT><groupname></TT> in the virtual host. Example:
+<PRE CLASS="verbatim">{acl, techgroupmembers, {shared_group, "techteam"}}.
+</PRE></DD><DT CLASS="dt-description"><B><TT>{shared_group, <groupname>, <server>}</TT></B></DT><DD CLASS="dd-description"> Matches any member of a Shared Roster Group with name <TT><groupname></TT> in the virtual host <TT><server></TT>. Example:
+<PRE CLASS="verbatim">{acl, techgroupmembers, {shared_group, "techteam", "example.org"}}.
</PRE></DD><DT CLASS="dt-description"><B><TT>{user_regexp, <regexp>}</TT></B></DT><DD CLASS="dd-description"> Matches any local user with a name that
matches <TT><regexp></TT> on local virtual hosts. Example:
<PRE CLASS="verbatim">{acl, tests, {user_regexp, "^test[0-9]*$"}}.
-define(INVALID_XML_ERR,
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
+-define(SOCKET_DEFAULT_RESULT, {error, badarg}).
+
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
_ ->
open_socket1(Addr, Port)
end
- end, {error, badarg}, AddrList) of
+ end, ?SOCKET_DEFAULT_RESULT, AddrList) of
{ok, Socket} ->
Version = if
StateData#state.use_v10 ->
{next_state, open_socket, StateData}.
%%----------------------------------------------------------------------
-open_socket1(Addr, Port) ->
- ?DEBUG("s2s_out: connecting to ~s:~p~n", [Addr, Port]),
- Res = case catch ejabberd_socket:connect(
- Addr, Port,
- [binary, {packet, 0},
- {active, false}]) of
- {ok, _Socket} = R -> R;
- {error, Reason1} ->
- ?DEBUG("s2s_out: connect return ~p~n", [Reason1]),
- catch ejabberd_socket:connect(
- Addr, Port,
- [binary, {packet, 0},
- {active, false}, inet6]);
- {'EXIT', Reason1} ->
- ?DEBUG("s2s_out: connect crashed ~p~n", [Reason1]),
- catch ejabberd_socket:connect(
- Addr, Port,
- [binary, {packet, 0},
- {active, false}, inet6])
- end,
- case Res of
- {ok, Socket} ->
- {ok, Socket};
- {error, Reason} ->
- ?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
- {error, Reason};
+%% IPv4
+open_socket1({_,_,_,_} = Addr, Port) ->
+ open_socket2(inet, Addr, Port);
+
+%% IPv6
+open_socket1({_,_,_,_,_,_,_,_} = Addr, Port) ->
+ open_socket2(inet6, Addr, Port);
+
+%% Hostname
+open_socket1(Host, Port) ->
+ lists:foldl(fun(_Family, {ok, _Socket} = R) ->
+ R;
+ (Family, _) ->
+ Addrs = get_addrs(Host, Family),
+ lists:foldl(fun(_Addr, {ok, _Socket} = R) ->
+ R;
+ (Addr, _) ->
+ open_socket1(Addr, Port)
+ end, ?SOCKET_DEFAULT_RESULT, Addrs)
+ end, ?SOCKET_DEFAULT_RESULT, outgoing_s2s_families()).
+
+open_socket2(Type, Addr, Port) ->
+ ?DEBUG("s2s_out: connecting to ~p:~p~n", [Addr, Port]),
+ Timeout = outgoing_s2s_timeout(),
+ case (catch ejabberd_socket:connect(Addr, Port,
+ [binary, {packet, 0},
+ {active, false}, Type],
+ Timeout)) of
+ {ok, _Socket} = R -> R;
+ {error, Reason} = R ->
+ ?DEBUG("s2s_out: connect return ~p~n", [Reason]),
+ R;
{'EXIT', Reason} ->
- ?DEBUG("s2s_out: inet6 connect crashed ~p~n", [Reason]),
+ ?DEBUG("s2s_out: connect crashed ~p~n", [Reason]),
{error, Reason}
end.
end
end, [], lists:seq(1, 100000)).
+get_addrs(Host, Family) ->
+ Type = case Family of
+ inet4 -> a;
+ ipv4 -> a;
+ inet6 -> aaaa;
+ ipv6 -> aaaa
+ end,
+ case inet_res:getbyname(Host, Type) of
+ {ok, #hostent{h_addr_list = Addrs}} ->
+ ?DEBUG("~s of ~s resolved to: ~p~n", [Type, Host, Addrs]),
+ Addrs;
+ {error, Reason} ->
+ ?DEBUG("~s lookup of '~s' failed: ~p~n", [Type, Host, Reason]),
+ []
+ end.
+
+
outgoing_s2s_port() ->
case ejabberd_config:get_local_option(outgoing_s2s_port) of
Port when is_integer(Port) ->
5269
end.
+outgoing_s2s_families() ->
+ case ejabberd_config:get_local_option(outgoing_s2s_options) of
+ {Families, _} when is_list(Families) ->
+ Families;
+ undefined ->
+ %% DISCUSSION: Why prefer IPv4 first?
+ %%
+ %% IPv4 connectivity will be available for everyone for
+ %% many years to come. So, there's absolutely no benefit
+ %% in preferring IPv6 connections which are flaky at best
+ %% nowadays.
+ %%
+ %% On the other hand content providers hesitate putting up
+ %% AAAA records for their sites due to the mentioned
+ %% quality of current IPv6 connectivity. Making IPv6 the a
+ %% `fallback' may avoid these problems elegantly.
+ [ipv4, ipv6]
+ end.
+
+outgoing_s2s_timeout() ->
+ case ejabberd_config:get_local_option(outgoing_s2s_options) of
+ {_, Timeout} when is_integer(Timeout) ->
+ Timeout;
+ {_, infinity} ->
+ infinity;
+ undefined ->
+ %% 10 seconds
+ 10000
+ end.
+
%% Human readable S2S logging: Log only new outgoing connections as INFO
%% Do not log dialback
log_s2s_out(false, _, _) -> ok;