aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--doc/guide.html8
-rw-r--r--doc/guide.tex4
-rw-r--r--src/ejabberd.cfg.example7
-rw-r--r--src/ejabberd_config.erl2
-rw-r--r--src/ejabberd_s2s_out.erl111
-rw-r--r--src/ejabberd_socket.erl6
7 files changed, 121 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index 4688874c9..fe1367479 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2008-12-26 Badlop <badlop@process-one.net>
+
+ * src/ejabberd_config.erl: Option outgoing_s2s_options to define
+ s2s outgoing behaviour: IPv4, IPv6 and timeout (thanks to Stephan
+ Maka)(EJAB-665)
+ * src/ejabberd_s2s_out.erl: Likewise
+ * src/ejabberd_socket.erl: Likewise
+ * src/ejabberd.cfg.example: Likewise
+ * doc/guide.tex: Likewise
+ * doc/guide.html: Likewise
+
2008-12-26 Evgeniy Khramtsov <ekhramtsov@process-one.net>
* src/odbc/ejabberd_odbc.erl: get rid of SERIALIZABLE isolation
@@ -10,7 +21,7 @@
2008-12-24 Badlop <badlop@process-one.net>
* src/aclocal.m4: Fixes in configure script: fix
- disable-disable_zlib and disable-pam; in case of problems, PAM
+ disable-ejabberd_zlib and disable-pam; in case of problems, PAM
verification aborts with error instead of warning. (EJAB-787)
* src/configure.ac: Likewise
* src/configure: Likewise
diff --git a/doc/guide.html b/doc/guide.html
index 2982f355d..6d143c335 100644
--- a/doc/guide.html
+++ b/doc/guide.html
@@ -745,6 +745,10 @@ use STARTTLS for s2s connections.
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>.
@@ -1032,6 +1036,10 @@ declarations of ACLs in the configuration file have the following syntax:
</PRE></DD><DT CLASS="dt-description"><B><TT>{resource, &lt;resource&gt;}</TT></B></DT><DD CLASS="dd-description"> Matches any JID with a resource
<TT>&lt;resource&gt;</TT>. Example:
<PRE CLASS="verbatim">{acl, mucklres, {resource, "muckl"}}.
+</PRE></DD><DT CLASS="dt-description"><B><TT>{shared_group, &lt;groupname&gt;}</TT></B></DT><DD CLASS="dd-description"> Matches any member of a Shared Roster Group with name <TT>&lt;groupname&gt;</TT> in the virtual host. Example:
+<PRE CLASS="verbatim">{acl, techgroupmembers, {shared_group, "techteam"}}.
+</PRE></DD><DT CLASS="dt-description"><B><TT>{shared_group, &lt;groupname&gt;, &lt;server&gt;}</TT></B></DT><DD CLASS="dd-description"> Matches any member of a Shared Roster Group with name <TT>&lt;groupname&gt;</TT> in the virtual host <TT>&lt;server&gt;</TT>. Example:
+<PRE CLASS="verbatim">{acl, techgroupmembers, {shared_group, "techteam", "example.org"}}.
</PRE></DD><DT CLASS="dt-description"><B><TT>{user_regexp, &lt;regexp&gt;}</TT></B></DT><DD CLASS="dd-description"> Matches any local user with a name that
matches <TT>&lt;regexp&gt;</TT> on local virtual hosts. Example:
<PRE CLASS="verbatim">{acl, tests, {user_regexp, "^test[0-9]*$"}}.
diff --git a/doc/guide.tex b/doc/guide.tex
index 9501c0cc5..afc63a3ff 100644
--- a/doc/guide.tex
+++ b/doc/guide.tex
@@ -870,6 +870,10 @@ There are some additional global options:
file containing a SSL certificate.
\titem{\{domain\_certfile, Domain, Path\}} \ind{options!domain\_certfile}
Full path to the file containing the SSL certificate for a specific domain.
+ \titem{\{outgoing\_s2s\_options, Methods, Timeout\}} \ind{options!outgoing\_s2s\_options}
+ 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.
\titem{\{s2s\_default\_policy, allow|deny\}}
The default policy for incoming and outgoing s2s connections to other Jabber servers.
The default value is \term{allow}.
diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example
index 6a6b52494..046db942b 100644
--- a/src/ejabberd.cfg.example
+++ b/src/ejabberd.cfg.example
@@ -186,6 +186,13 @@
%%{{s2s_host, "goodhost.org"}, allow}.
%%{{s2s_host, "badhost.org"}, deny}.
+%%
+%% Outgoing S2S options
+%%
+%% Preferred address families (which to try first) and connect timeout
+%% in milliseconds.
+%%
+%%{outgoing_s2s_options, [ipv4, ipv6], 10000}.
%%% ==============
%%% AUTHENTICATION
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 6ee0fa528..a5083424b 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -330,6 +330,8 @@ process_term(Term, State) ->
add_option(language, Val, State);
{outgoing_s2s_port, Port} ->
add_option(outgoing_s2s_port, Port, State);
+ {outgoing_s2s_options, Methods, Timeout} ->
+ add_option(outgoing_s2s_options, {Methods, Timeout}, State);
{s2s_use_starttls, Port} ->
add_option(s2s_use_starttls, Port, State);
{s2s_certfile, CertFile} ->
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index aa4c7ed96..5f5d905f8 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -119,6 +119,8 @@
-define(INVALID_XML_ERR,
xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
+-define(SOCKET_DEFAULT_RESULT, {error, badarg}).
+
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
@@ -206,7 +208,7 @@ open_socket(init, StateData) ->
_ ->
open_socket1(Addr, Port)
end
- end, {error, badarg}, AddrList) of
+ end, ?SOCKET_DEFAULT_RESULT, AddrList) of
{ok, Socket} ->
Version = if
StateData#state.use_v10 ->
@@ -239,34 +241,40 @@ open_socket(_, StateData) ->
{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.
@@ -1000,6 +1008,23 @@ test_get_addr_port(Server) ->
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) ->
@@ -1008,6 +1033,36 @@ outgoing_s2s_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;
diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl
index ba706e4e8..01e1eddcb 100644
--- a/src/ejabberd_socket.erl
+++ b/src/ejabberd_socket.erl
@@ -30,6 +30,7 @@
%% API
-export([start/4,
connect/3,
+ connect/4,
starttls/2,
starttls/3,
compress/1,
@@ -94,7 +95,10 @@ start(Module, SockMod, Socket, Opts) ->
end.
connect(Addr, Port, Opts) ->
- case gen_tcp:connect(Addr, Port, Opts) of
+ connect(Addr, Port, Opts, infinity).
+
+connect(Addr, Port, Opts, Timeout) ->
+ case gen_tcp:connect(Addr, Port, Opts, Timeout) of
{ok, Socket} ->
Receiver = ejabberd_receiver:start(Socket, gen_tcp, none),
SocketData = #socket_state{sockmod = gen_tcp,