From 1f793d8da8998299b4861a75f9bce3ba5680f7e6 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 4 Jan 2011 17:14:47 +0000 Subject: Import and debitrot Reilly Grant's patch. --- src/rabbit_networking.erl | 61 ++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index d5a9d73c..ebf46aa0 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -31,15 +31,15 @@ -module(rabbit_networking). --export([boot/0, start/0, start_tcp_listener/2, start_ssl_listener/3, - stop_tcp_listener/2, on_node_down/1, active_listeners/0, +-export([boot/0, start/0, start_tcp_listener/3, start_ssl_listener/4, + stop_tcp_listener/3, on_node_down/1, active_listeners/0, node_listeners/1, connections/0, connection_info_keys/0, connection_info/1, connection_info/2, connection_info_all/0, connection_info_all/1, close_connection/2]). %%used by TCP-based transports, e.g. STOMP adapter --export([check_tcp_listener_address/3]). +-export([check_tcp_listener_address/4]). -export([tcp_listener_started/3, tcp_listener_stopped/3, start_client/1, start_ssl_client/2]). @@ -65,11 +65,13 @@ -export_type([ip_port/0, hostname/0]). +-type(family() :: atom()). + -spec(start/0 :: () -> 'ok'). --spec(start_tcp_listener/2 :: (hostname(), ip_port()) -> 'ok'). --spec(start_ssl_listener/3 :: (hostname(), ip_port(), rabbit_types:infos()) - -> 'ok'). --spec(stop_tcp_listener/2 :: (hostname(), ip_port()) -> 'ok'). +-spec(start_tcp_listener/3 :: (hostname(), ip_port(), family()) -> 'ok'). +-spec(start_ssl_listener/4 :: (hostname(), ip_port(), rabbit_types:infos(), + family()) -> 'ok'). +-spec(stop_tcp_listener/3 :: (hostname(), ip_port(), family()) -> 'ok'). -spec(active_listeners/0 :: () -> [rabbit_types:listener()]). -spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]). -spec(connections/0 :: () -> [rabbit_types:connection()]). @@ -84,8 +86,9 @@ (rabbit_types:info_keys()) -> [rabbit_types:infos()]). -spec(close_connection/2 :: (pid(), string()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). --spec(check_tcp_listener_address/3 :: - (atom(), hostname(), ip_port()) -> {inet:ip_address(), atom()}). +-spec(check_tcp_listener_address/4 :: + (atom(), hostname(), ip_port(), family()) + -> {inet:ip_address(), atom()}). -endif. @@ -98,7 +101,9 @@ boot() -> boot_tcp() -> {ok, TcpListeners} = application:get_env(tcp_listeners), - [ok = start_tcp_listener(Host, Port) || {Host, Port} <- TcpListeners], + CheckedListeners = [check_listener(Opts) || Opts <- TcpListeners], + [ok = start_tcp_listener(Host, Port, Family) + || {Host, Port, Family} <- CheckedListeners], ok. boot_ssl() -> @@ -118,7 +123,9 @@ boot_ssl() -> end} | SslOptsConfig] end, - [start_ssl_listener(Host, Port, SslOpts) || {Host, Port} <- SslListeners], + CheckedListeners = [check_listener(Opts) || Opts <- SslListeners], + [start_ssl_listener(Host, Port, Family, SslOpts) + || {Host, Port, Family} <- CheckedListeners], ok end. @@ -132,7 +139,13 @@ start() -> transient, infinity, supervisor, [tcp_client_sup]}), ok. -getaddr(Host) -> +check_listener({Host, Port}) -> + %% Default to IPv4. + {Host, Port, inet}; +check_listener({Host, Port, Family}) -> + {Host, Port, Family}. + +getaddr(Host, Family) -> %% inet_parse:address takes care of ip string, like "0.0.0.0" %% inet:getaddr returns immediately for ip tuple {0,0,0,0}, %% and runs 'inet_gethost' port process for dns lookups. @@ -140,7 +153,7 @@ getaddr(Host) -> case inet_parse:address(Host) of {ok, IPAddress1} -> IPAddress1; {error, _} -> - case inet:getaddr(Host, inet) of + case inet:getaddr(Host, Family) of {ok, IPAddress2} -> IPAddress2; {error, Reason} -> error_logger:error_msg("invalid host ~p - ~p~n", @@ -149,8 +162,8 @@ getaddr(Host) -> end end. -check_tcp_listener_address(NamePrefix, Host, Port) -> - IPAddress = getaddr(Host), +check_tcp_listener_address(NamePrefix, Host, Port, Family) -> + IPAddress = getaddr(Host, Family), if is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) -> ok; true -> error_logger:error_msg("invalid port ~p - not 0..65535~n", [Port]), @@ -159,30 +172,30 @@ check_tcp_listener_address(NamePrefix, Host, Port) -> Name = rabbit_misc:tcp_name(NamePrefix, IPAddress, Port), {IPAddress, Name}. -start_tcp_listener(Host, Port) -> - start_listener(Host, Port, amqp, "TCP Listener", +start_tcp_listener(Host, Port, Family) -> + start_listener(Host, Port, amqp, Family, "TCP Listener", {?MODULE, start_client, []}). -start_ssl_listener(Host, Port, SslOpts) -> - start_listener(Host, Port, 'amqp/ssl', "SSL Listener", +start_ssl_listener(Host, Port, Family, SslOpts) -> + start_listener(Host, Port, 'amqp/ssl', Family, "SSL Listener", {?MODULE, start_ssl_client, [SslOpts]}). -start_listener(Host, Port, Protocol, Label, OnConnect) -> +start_listener(Host, Port, Protocol, Family, Label, OnConnect) -> {IPAddress, Name} = - check_tcp_listener_address(rabbit_tcp_listener_sup, Host, Port), + check_tcp_listener_address(rabbit_tcp_listener_sup, Host, Port, Family), {ok,_} = supervisor:start_child( rabbit_sup, {Name, {tcp_listener_sup, start_link, - [IPAddress, Port, ?RABBIT_TCP_OPTS , + [IPAddress, Port, [Family | ?RABBIT_TCP_OPTS], {?MODULE, tcp_listener_started, [Protocol]}, {?MODULE, tcp_listener_stopped, [Protocol]}, OnConnect, Label]}, transient, infinity, supervisor, [tcp_listener_sup]}), ok. -stop_tcp_listener(Host, Port) -> - IPAddress = getaddr(Host), +stop_tcp_listener(Host, Port, Family) -> + IPAddress = getaddr(Host, Family), Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), ok = supervisor:terminate_child(rabbit_sup, Name), ok = supervisor:delete_child(rabbit_sup, Name), -- cgit v1.2.1 From 92b72c7f3b1a40cf2cb44bbf2fa548b760790aa2 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 10:31:35 +0000 Subject: Special case [::] like we do 0.0.0.0. --- src/rabbit_networking.erl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index ebf46aa0..a1828a98 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -280,15 +280,22 @@ close_connection(Pid, Explanation) -> %%-------------------------------------------------------------------- tcp_host({0,0,0,0}) -> - {ok, Hostname} = inet:gethostname(), - case inet:gethostbyname(Hostname) of - {ok, #hostent{h_name = Name}} -> Name; - {error, _Reason} -> Hostname - end; + hostname(); + +tcp_host({0,0,0,0,0,0,0,0}) -> + hostname(); + tcp_host(IPAddress) -> case inet:gethostbyaddr(IPAddress) of {ok, #hostent{h_name = Name}} -> Name; {error, _Reason} -> inet_parse:ntoa(IPAddress) end. +hostname() -> + {ok, Hostname} = inet:gethostname(), + case inet:gethostbyname(Hostname) of + {ok, #hostent{h_name = Name}} -> Name; + {error, _Reason} -> Hostname + end. + cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). -- cgit v1.2.1 From 8d665d02e58b0139770ed235b38a6f020816c0c7 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 11:13:10 +0000 Subject: Automatically determine ipv4 / ipv6 depending on IP address or hostname provided. --- src/rabbit_networking.erl | 63 +++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index a1828a98..95ab3b03 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -140,37 +140,58 @@ start() -> ok. check_listener({Host, Port}) -> - %% Default to IPv4. - {Host, Port, inet}; + %% auto: determine family IPv4 / IPv6 after converting to IP address + {Host, Port, auto}; check_listener({Host, Port, Family}) -> {Host, Port, Family}. +%% inet_parse:address takes care of ip string, like "0.0.0.0" +%% inet:getaddr returns immediately for ip tuple {0,0,0,0}, +%% and runs 'inet_gethost' port process for dns lookups. +%% On Windows inet:getaddr runs dns resolver for ip string, which may fail. + getaddr(Host, Family) -> - %% inet_parse:address takes care of ip string, like "0.0.0.0" - %% inet:getaddr returns immediately for ip tuple {0,0,0,0}, - %% and runs 'inet_gethost' port process for dns lookups. - %% On Windows inet:getaddr runs dns resolver for ip string, which may fail. case inet_parse:address(Host) of - {ok, IPAddress1} -> IPAddress1; - {error, _} -> - case inet:getaddr(Host, Family) of - {ok, IPAddress2} -> IPAddress2; - {error, Reason} -> - error_logger:error_msg("invalid host ~p - ~p~n", - [Host, Reason]), - throw({error, {invalid_host, Host, Reason}}) + {ok, IPAddress} -> {IPAddress, resolve_family(IPAddress, Family)}; + {error, _} -> gethostaddr(Host, Family) + end. + +gethostaddr(Host, auto) -> + case inet:getaddr(Host, inet6) of + {ok, IPAddress6} -> + {IPAddress6, inet6}; + {error, Reason6} -> + case inet:getaddr(Host, inet) of + {ok, IPAddress4} -> {IPAddress4, inet}; + {error, Reason4} -> host_lookup_error( + Host, {{ipv6, Reason6}, {ipv4, Reason4}}) end + end; + +gethostaddr(Host, Family) -> + case inet:getaddr(Host, Family) of + {ok, IPAddress} -> {IPAddress, Family}; + {error, Reason} -> host_lookup_error(Host, Reason) end. -check_tcp_listener_address(NamePrefix, Host, Port, Family) -> - IPAddress = getaddr(Host, Family), +host_lookup_error(Host, Reason) -> + error_logger:error_msg("invalid host ~p - ~p~n", [Host, Reason]), + throw({error, {invalid_host, Host, Reason}}). + +resolve_family({_,_,_,_}, auto) -> inet; +resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6; +resolve_family(IP, auto) -> throw({error, {strange_family, IP}}); +resolve_family(_, F) -> F. + +check_tcp_listener_address(NamePrefix, Host, Port, Family0) -> + {IPAddress, Family} = getaddr(Host, Family0), if is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) -> ok; true -> error_logger:error_msg("invalid port ~p - not 0..65535~n", [Port]), throw({error, {invalid_port, Port}}) end, Name = rabbit_misc:tcp_name(NamePrefix, IPAddress, Port), - {IPAddress, Name}. + {IPAddress, Name, Family}. start_tcp_listener(Host, Port, Family) -> start_listener(Host, Port, amqp, Family, "TCP Listener", @@ -180,9 +201,9 @@ start_ssl_listener(Host, Port, Family, SslOpts) -> start_listener(Host, Port, 'amqp/ssl', Family, "SSL Listener", {?MODULE, start_ssl_client, [SslOpts]}). -start_listener(Host, Port, Protocol, Family, Label, OnConnect) -> - {IPAddress, Name} = - check_tcp_listener_address(rabbit_tcp_listener_sup, Host, Port, Family), +start_listener(Host, Port, Protocol, Family0, Label, OnConnect) -> + {IPAddress, Name, Family} = check_tcp_listener_address( + rabbit_tcp_listener_sup, Host, Port, Family0), {ok,_} = supervisor:start_child( rabbit_sup, {Name, @@ -195,7 +216,7 @@ start_listener(Host, Port, Protocol, Family, Label, OnConnect) -> ok. stop_tcp_listener(Host, Port, Family) -> - IPAddress = getaddr(Host, Family), + {IPAddress, _} = getaddr(Host, Family), Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), ok = supervisor:terminate_child(rabbit_sup, Name), ok = supervisor:delete_child(rabbit_sup, Name), -- cgit v1.2.1 From fb335bf3d6be6e46285bb9e86c9918440d72bf89 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 11:54:36 +0000 Subject: Make listener spec a bit more of an abstract type so that changes to plugins are minimised. --- src/rabbit_networking.erl | 68 +++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 95ab3b03..ee632f66 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -31,15 +31,15 @@ -module(rabbit_networking). --export([boot/0, start/0, start_tcp_listener/3, start_ssl_listener/4, - stop_tcp_listener/3, on_node_down/1, active_listeners/0, +-export([boot/0, start/0, start_tcp_listener/1, start_ssl_listener/2, + on_node_down/1, active_listeners/0, node_listeners/1, connections/0, connection_info_keys/0, connection_info/1, connection_info/2, connection_info_all/0, connection_info_all/1, close_connection/2]). %%used by TCP-based transports, e.g. STOMP adapter --export([check_tcp_listener_address/4]). +-export([check_tcp_listener_address/2]). -export([tcp_listener_started/3, tcp_listener_stopped/3, start_client/1, start_ssl_client/2]). @@ -66,12 +66,12 @@ -export_type([ip_port/0, hostname/0]). -type(family() :: atom()). +-type(listener_spec() :: {hostname(), ip_port()} | + {hostname(), ip_port(), family()}). -spec(start/0 :: () -> 'ok'). --spec(start_tcp_listener/3 :: (hostname(), ip_port(), family()) -> 'ok'). --spec(start_ssl_listener/4 :: (hostname(), ip_port(), rabbit_types:infos(), - family()) -> 'ok'). --spec(stop_tcp_listener/3 :: (hostname(), ip_port(), family()) -> 'ok'). +-spec(start_tcp_listener/1 :: (listener_spec()) -> 'ok'). +-spec(start_ssl_listener/2 :: (listener_spec(), rabbit_types:infos()) -> 'ok'). -spec(active_listeners/0 :: () -> [rabbit_types:listener()]). -spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]). -spec(connections/0 :: () -> [rabbit_types:connection()]). @@ -86,9 +86,8 @@ (rabbit_types:info_keys()) -> [rabbit_types:infos()]). -spec(close_connection/2 :: (pid(), string()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). --spec(check_tcp_listener_address/4 :: - (atom(), hostname(), ip_port(), family()) - -> {inet:ip_address(), atom()}). +-spec(check_tcp_listener_address/2 :: (atom(), listener_spec()) + -> {inet:ip_address(), ip_port(), family(), atom()}). -endif. @@ -101,9 +100,7 @@ boot() -> boot_tcp() -> {ok, TcpListeners} = application:get_env(tcp_listeners), - CheckedListeners = [check_listener(Opts) || Opts <- TcpListeners], - [ok = start_tcp_listener(Host, Port, Family) - || {Host, Port, Family} <- CheckedListeners], + [ok = start_tcp_listener(Listener) || Listener <- TcpListeners], ok. boot_ssl() -> @@ -123,9 +120,7 @@ boot_ssl() -> end} | SslOptsConfig] end, - CheckedListeners = [check_listener(Opts) || Opts <- SslListeners], - [start_ssl_listener(Host, Port, Family, SslOpts) - || {Host, Port, Family} <- CheckedListeners], + [start_ssl_listener(Listener, SslOpts) || Listener <- SslListeners], ok end. @@ -139,12 +134,6 @@ start() -> transient, infinity, supervisor, [tcp_client_sup]}), ok. -check_listener({Host, Port}) -> - %% auto: determine family IPv4 / IPv6 after converting to IP address - {Host, Port, auto}; -check_listener({Host, Port, Family}) -> - {Host, Port, Family}. - %% inet_parse:address takes care of ip string, like "0.0.0.0" %% inet:getaddr returns immediately for ip tuple {0,0,0,0}, %% and runs 'inet_gethost' port process for dns lookups. @@ -183,7 +172,11 @@ resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6; resolve_family(IP, auto) -> throw({error, {strange_family, IP}}); resolve_family(_, F) -> F. -check_tcp_listener_address(NamePrefix, Host, Port, Family0) -> +check_tcp_listener_address(NamePrefix, {Host, Port}) -> + %% auto: determine family IPv4 / IPv6 after converting to IP address + check_tcp_listener_address(NamePrefix, {Host, Port, auto}); + +check_tcp_listener_address(NamePrefix, {Host, Port, Family0}) -> {IPAddress, Family} = getaddr(Host, Family0), if is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) -> ok; true -> error_logger:error_msg("invalid port ~p - not 0..65535~n", @@ -191,19 +184,19 @@ check_tcp_listener_address(NamePrefix, Host, Port, Family0) -> throw({error, {invalid_port, Port}}) end, Name = rabbit_misc:tcp_name(NamePrefix, IPAddress, Port), - {IPAddress, Name, Family}. + {IPAddress, Port, Family, Name}. -start_tcp_listener(Host, Port, Family) -> - start_listener(Host, Port, amqp, Family, "TCP Listener", +start_tcp_listener(Listener) -> + start_listener(Listener, amqp, "TCP Listener", {?MODULE, start_client, []}). -start_ssl_listener(Host, Port, Family, SslOpts) -> - start_listener(Host, Port, 'amqp/ssl', Family, "SSL Listener", +start_ssl_listener(Listener, SslOpts) -> + start_listener(Listener, 'amqp/ssl', "SSL Listener", {?MODULE, start_ssl_client, [SslOpts]}). -start_listener(Host, Port, Protocol, Family0, Label, OnConnect) -> - {IPAddress, Name, Family} = check_tcp_listener_address( - rabbit_tcp_listener_sup, Host, Port, Family0), +start_listener(Listener, Protocol, Label, OnConnect) -> + {IPAddress, Port, Family, Name} = check_tcp_listener_address( + rabbit_tcp_listener_sup, Listener), {ok,_} = supervisor:start_child( rabbit_sup, {Name, @@ -215,12 +208,13 @@ start_listener(Host, Port, Protocol, Family0, Label, OnConnect) -> transient, infinity, supervisor, [tcp_listener_sup]}), ok. -stop_tcp_listener(Host, Port, Family) -> - {IPAddress, _} = getaddr(Host, Family), - Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), - ok = supervisor:terminate_child(rabbit_sup, Name), - ok = supervisor:delete_child(rabbit_sup, Name), - ok. +%% TODO this appears not to be used by anything in Rabbit or plugins +%% stop_tcp_listener(Host, Port, Family) -> +%% {IPAddress, _} = getaddr(Host, Family), +%% Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), +%% ok = supervisor:terminate_child(rabbit_sup, Name), +%% ok = supervisor:delete_child(rabbit_sup, Name), +%% ok. tcp_listener_started(Protocol, IPAddress, Port) -> %% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1 -- cgit v1.2.1 From e6f1e0022299eccb533d62df86b76fb49c3f624c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 12:18:04 +0000 Subject: Allow check_tcp_listener_address to convert one config into more than one spec (so that in future we can have a simple "bind to port" config that works on dual-stack operating systems). --- src/rabbit_networking.erl | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index ee632f66..769b1bf8 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -66,12 +66,13 @@ -export_type([ip_port/0, hostname/0]). -type(family() :: atom()). --type(listener_spec() :: {hostname(), ip_port()} | - {hostname(), ip_port(), family()}). +-type(listener_config() :: {hostname(), ip_port()} | + {hostname(), ip_port(), family()}). -spec(start/0 :: () -> 'ok'). --spec(start_tcp_listener/1 :: (listener_spec()) -> 'ok'). --spec(start_ssl_listener/2 :: (listener_spec(), rabbit_types:infos()) -> 'ok'). +-spec(start_tcp_listener/1 :: (listener_config()) -> 'ok'). +-spec(start_ssl_listener/2 :: + (listener_config(), rabbit_types:infos()) -> 'ok'). -spec(active_listeners/0 :: () -> [rabbit_types:listener()]). -spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]). -spec(connections/0 :: () -> [rabbit_types:connection()]). @@ -86,8 +87,8 @@ (rabbit_types:info_keys()) -> [rabbit_types:infos()]). -spec(close_connection/2 :: (pid(), string()) -> 'ok'). -spec(on_node_down/1 :: (node()) -> 'ok'). --spec(check_tcp_listener_address/2 :: (atom(), listener_spec()) - -> {inet:ip_address(), ip_port(), family(), atom()}). +-spec(check_tcp_listener_address/2 :: (atom(), listener_config()) + -> [{inet:ip_address(), ip_port(), family(), atom()}]). -endif. @@ -184,7 +185,7 @@ check_tcp_listener_address(NamePrefix, {Host, Port, Family0}) -> throw({error, {invalid_port, Port}}) end, Name = rabbit_misc:tcp_name(NamePrefix, IPAddress, Port), - {IPAddress, Port, Family, Name}. + [{IPAddress, Port, Family, Name}]. start_tcp_listener(Listener) -> start_listener(Listener, amqp, "TCP Listener", @@ -195,8 +196,11 @@ start_ssl_listener(Listener, SslOpts) -> {?MODULE, start_ssl_client, [SslOpts]}). start_listener(Listener, Protocol, Label, OnConnect) -> - {IPAddress, Port, Family, Name} = check_tcp_listener_address( - rabbit_tcp_listener_sup, Listener), + [start_listener0(Spec, Protocol, Label, OnConnect) || + Spec <- check_tcp_listener_address(rabbit_tcp_listener_sup, Listener)], + ok. + +start_listener0({IPAddress, Port, Family, Name}, Protocol, Label, OnConnect) -> {ok,_} = supervisor:start_child( rabbit_sup, {Name, @@ -205,8 +209,7 @@ start_listener(Listener, Protocol, Label, OnConnect) -> {?MODULE, tcp_listener_started, [Protocol]}, {?MODULE, tcp_listener_stopped, [Protocol]}, OnConnect, Label]}, - transient, infinity, supervisor, [tcp_listener_sup]}), - ok. + transient, infinity, supervisor, [tcp_listener_sup]}). %% TODO this appears not to be used by anything in Rabbit or plugins %% stop_tcp_listener(Host, Port, Family) -> -- cgit v1.2.1 From 25454621c7025e41570caa76f60c3170594fe9ff Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 12:28:18 +0000 Subject: Reinstate stop_tcp_listener. --- src/rabbit_networking.erl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 769b1bf8..42eacc58 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -32,7 +32,7 @@ -module(rabbit_networking). -export([boot/0, start/0, start_tcp_listener/1, start_ssl_listener/2, - on_node_down/1, active_listeners/0, + stop_tcp_listener/1, on_node_down/1, active_listeners/0, node_listeners/1, connections/0, connection_info_keys/0, connection_info/1, connection_info/2, connection_info_all/0, connection_info_all/1, @@ -73,6 +73,7 @@ -spec(start_tcp_listener/1 :: (listener_config()) -> 'ok'). -spec(start_ssl_listener/2 :: (listener_config(), rabbit_types:infos()) -> 'ok'). +-spec(stop_tcp_listener/1 :: (listener_config()) -> 'ok'). -spec(active_listeners/0 :: () -> [rabbit_types:listener()]). -spec(node_listeners/1 :: (node()) -> [rabbit_types:listener()]). -spec(connections/0 :: () -> [rabbit_types:connection()]). @@ -211,13 +212,15 @@ start_listener0({IPAddress, Port, Family, Name}, Protocol, Label, OnConnect) -> OnConnect, Label]}, transient, infinity, supervisor, [tcp_listener_sup]}). -%% TODO this appears not to be used by anything in Rabbit or plugins -%% stop_tcp_listener(Host, Port, Family) -> -%% {IPAddress, _} = getaddr(Host, Family), -%% Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), -%% ok = supervisor:terminate_child(rabbit_sup, Name), -%% ok = supervisor:delete_child(rabbit_sup, Name), -%% ok. +stop_tcp_listener(Listener) -> + [stop_tcp_listener0(Spec) || + Spec <- check_tcp_listener_address(rabbit_tcp_listener_sup, Listener)]. + +stop_tcp_listener0({IPAddress, Port, _Family, Name}) -> + Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), + ok = supervisor:terminate_child(rabbit_sup, Name), + ok = supervisor:delete_child(rabbit_sup, Name), + ok. tcp_listener_started(Protocol, IPAddress, Port) -> %% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1 -- cgit v1.2.1 From c0cad6e0d33bbf82accde659c7579e9471439646 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 14:43:22 +0000 Subject: Allow specifying a listener as just a port number, meaning "bind to everything, whatever that means on your OS", and use that by default. --- ebin/rabbit_app.in | 2 +- src/rabbit_networking.erl | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index 25d630c0..45d92a6b 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -13,7 +13,7 @@ %% we also depend on crypto, public_key and ssl but they shouldn't be %% in here as we don't actually want to start it {mod, {rabbit, []}}, - {env, [{tcp_listeners, [{"0.0.0.0", 5672}]}, + {env, [{tcp_listeners, [5672]}, {ssl_listeners, []}, {ssl_options, []}, {vm_memory_high_watermark, 0.4}, diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 42eacc58..f394a1ab 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -174,6 +174,17 @@ resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6; resolve_family(IP, auto) -> throw({error, {strange_family, IP}}); resolve_family(_, F) -> F. +check_tcp_listener_address(NamePrefix, Port) when is_integer(Port) -> + case ipv6_status(Port) of + ipv4_only -> + check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}); + ipv6_single_stack -> + check_tcp_listener_address(NamePrefix, {"::", Port, inet6}); + ipv6_dual_stack -> + check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}) + ++ check_tcp_listener_address(NamePrefix, {"::", Port, inet6}) + end; + check_tcp_listener_address(NamePrefix, {Host, Port}) -> %% auto: determine family IPv4 / IPv6 after converting to IP address check_tcp_listener_address(NamePrefix, {Host, Port, auto}); @@ -320,3 +331,37 @@ hostname() -> end. cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). + +%%-------------------------------------------------------------------- + +%% How to test on Linux: +%% Single stack (default): +%% echo 0 > /proc/sys/net/ipv6/bindv6only +%% Dual stack: +%% echo 1 > /proc/sys/net/ipv6/bindv6only +%% IPv4 only: +%% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then +%% sudo update-grub && sudo reboot + +ipv6_status(Port) -> + %% Unfortunately it seems there is no way to detect single vs dual stack + %% apart from attempting to bind to the socket. + IPv4 = [inet, {ip, {0,0,0,0}}], + IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}], + case gen_tcp:listen(Port, IPv6) of + {ok, LSock6} -> + Status = case gen_tcp:listen(Port, IPv4) of + {ok, LSock4} -> + gen_tcp:close(LSock4), + ipv6_dual_stack; + {error, _} -> + ipv6_single_stack + end, + gen_tcp:close(LSock6), + Status; + {error, _} -> + %% It could be that there's a general problem binding to + %% the port, but plow on; the error will get picked up + %% later when we bind for real. + ipv4_only + end. -- cgit v1.2.1 From ab090a1150b5a72ec0784c764cadbaf9015d6459 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 15:40:42 +0000 Subject: Format IPv4-mapped IPv6 addresses as IPv4, put brackets round IPv6 addresses that are adjacent to colons. --- src/rabbit_control.erl | 4 ++-- src/rabbit_misc.erl | 17 +++++++++++++++++ src/rabbit_networking.erl | 2 +- src/rabbit_reader.erl | 2 +- src/tcp_acceptor.erl | 4 ++-- src/tcp_listener.erl | 9 +++++---- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index df55d961..e214345c 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -338,11 +338,11 @@ format_info_item(#resource{name = Name}) -> escape(Name); format_info_item({N1, N2, N3, N4} = Value) when ?IS_U8(N1), ?IS_U8(N2), ?IS_U8(N3), ?IS_U8(N4) -> - inet_parse:ntoa(Value); + rabbit_misc:ntoa(Value); format_info_item({K1, K2, K3, K4, K5, K6, K7, K8} = Value) when ?IS_U16(K1), ?IS_U16(K2), ?IS_U16(K3), ?IS_U16(K4), ?IS_U16(K5), ?IS_U16(K6), ?IS_U16(K7), ?IS_U16(K8) -> - inet_parse:ntoa(Value); + rabbit_misc:ntoa(Value); format_info_item(Value) when is_pid(Value) -> rabbit_misc:pid_to_string(Value); format_info_item(Value) when is_binary(Value) -> diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index 06ba319b..bdc9965d 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -67,6 +67,7 @@ -export([all_module_attributes/1, build_acyclic_graph/3]). -export([now_ms/0]). -export([lock_file/1]). +-export([ntoa/1, ntoab/1]). %%---------------------------------------------------------------------------- @@ -197,6 +198,8 @@ digraph:vertex(), digraph:vertex()})). -spec(now_ms/0 :: () -> non_neg_integer()). -spec(lock_file/1 :: (file:filename()) -> rabbit_types:ok_or_error('eexist')). +-spec(ntoa/1 :: (inet:ip_address()) -> string()). +-spec(ntoab/1 :: (inet:ip_address()) -> string()). -endif. @@ -817,3 +820,17 @@ lock_file(Path) -> false -> {ok, Lock} = file:open(Path, [write]), ok = file:close(Lock) end. + +%% Format IPv4-mapped IPv6 addresses as IPv4, since they're what we see +%% when IPv6 is enabled but not used (i.e. 99% of the time). +ntoa({0,0,0,0,0,16#ffff,AB,CD} = Foo) -> + inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256}); +ntoa(IP) -> + inet_parse:ntoa(IP). + +ntoab(IP) -> + Str = ntoa(IP), + case string:str(Str, ":") of + 0 -> Str; + _ -> "[" ++ Str ++ "]" + end. diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index f394a1ab..ceb2196d 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -320,7 +320,7 @@ tcp_host({0,0,0,0,0,0,0,0}) -> tcp_host(IPAddress) -> case inet:gethostbyaddr(IPAddress) of {ok, #hostent{h_name = Name}} -> Name; - {error, _Reason} -> inet_parse:ntoa(IPAddress) + {error, _Reason} -> rabbit_misc:ntoa(IPAddress) end. hostname() -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index 92a2f4d7..0429fda9 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -269,7 +269,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, Sock, SockTransform) -> process_flag(trap_exit, true), {PeerAddress, PeerPort} = socket_op(Sock, fun rabbit_net:peername/1), - PeerAddressS = inet_parse:ntoa(PeerAddress), + PeerAddressS = rabbit_misc:ntoab(PeerAddress), rabbit_log:info("starting TCP connection ~p from ~s:~p~n", [self(), PeerAddressS, PeerPort]), ClientSock = socket_op(Sock, SockTransform), diff --git a/src/tcp_acceptor.erl b/src/tcp_acceptor.erl index c9809ace..c59a9d5a 100644 --- a/src/tcp_acceptor.erl +++ b/src/tcp_acceptor.erl @@ -74,8 +74,8 @@ handle_info({inet_async, LSock, Ref, {ok, Sock}}, {Address, Port} = inet_op(fun () -> inet:sockname(LSock) end), {PeerAddress, PeerPort} = inet_op(fun () -> inet:peername(Sock) end), error_logger:info_msg("accepted TCP connection on ~s:~p from ~s:~p~n", - [inet_parse:ntoa(Address), Port, - inet_parse:ntoa(PeerAddress), PeerPort]), + [rabbit_misc:ntoab(Address), Port, + rabbit_misc:ntoab(PeerAddress), PeerPort]), %% In the event that somebody floods us with connections we can spew %% the above message at error_logger faster than it can keep up. %% So error_logger's mailbox grows unbounded until we eat all the diff --git a/src/tcp_listener.erl b/src/tcp_listener.erl index 73ef9586..e5fa65d6 100644 --- a/src/tcp_listener.erl +++ b/src/tcp_listener.erl @@ -65,8 +65,9 @@ init({IPAddress, Port, SocketOpts, end, lists:duplicate(ConcurrentAcceptorCount, dummy)), {ok, {LIPAddress, LPort}} = inet:sockname(LSock), - error_logger:info_msg("started ~s on ~s:~p~n", - [Label, inet_parse:ntoa(LIPAddress), LPort]), + error_logger:info_msg( + "started ~s on ~s:~p~n", + [Label, rabbit_misc:ntoab(LIPAddress), LPort]), apply(M, F, A ++ [IPAddress, Port]), {ok, #state{sock = LSock, on_startup = OnStartup, on_shutdown = OnShutdown, @@ -74,7 +75,7 @@ init({IPAddress, Port, SocketOpts, {error, Reason} -> error_logger:error_msg( "failed to start ~s on ~s:~p - ~p~n", - [Label, inet_parse:ntoa(IPAddress), Port, Reason]), + [Label, rabbit_misc:ntoab(IPAddress), Port, Reason]), {stop, {cannot_listen, IPAddress, Port, Reason}} end. @@ -91,7 +92,7 @@ terminate(_Reason, #state{sock=LSock, on_shutdown = {M,F,A}, label=Label}) -> {ok, {IPAddress, Port}} = inet:sockname(LSock), gen_tcp:close(LSock), error_logger:info_msg("stopped ~s on ~s:~p~n", - [Label, inet_parse:ntoa(IPAddress), Port]), + [Label, rabbit_misc:ntoab(IPAddress), Port]), apply(M, F, A ++ [IPAddress, Port]). code_change(_OldVsn, State, _Extra) -> -- cgit v1.2.1 From 388ed456bd74fc0873e5edb8df6ed366ca293155 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 17:20:48 +0000 Subject: Dialyzer fixes. --- src/rabbit_networking.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index ceb2196d..621343c4 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -66,7 +66,8 @@ -export_type([ip_port/0, hostname/0]). -type(family() :: atom()). --type(listener_config() :: {hostname(), ip_port()} | +-type(listener_config() :: ip_port() | + {hostname(), ip_port()} | {hostname(), ip_port(), family()}). -spec(start/0 :: () -> 'ok'). @@ -225,13 +226,13 @@ start_listener0({IPAddress, Port, Family, Name}, Protocol, Label, OnConnect) -> stop_tcp_listener(Listener) -> [stop_tcp_listener0(Spec) || - Spec <- check_tcp_listener_address(rabbit_tcp_listener_sup, Listener)]. + Spec <- check_tcp_listener_address(rabbit_tcp_listener_sup, Listener)], + ok. stop_tcp_listener0({IPAddress, Port, _Family, Name}) -> Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port), ok = supervisor:terminate_child(rabbit_sup, Name), - ok = supervisor:delete_child(rabbit_sup, Name), - ok. + ok = supervisor:delete_child(rabbit_sup, Name). tcp_listener_started(Protocol, IPAddress, Port) -> %% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1 -- cgit v1.2.1 From 9e757b9c7de2fc97db27da0502b03207d0aee2db Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 5 Jan 2011 17:23:06 +0000 Subject: Oops. --- src/rabbit_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rabbit_misc.erl b/src/rabbit_misc.erl index bdc9965d..b30cfc2e 100644 --- a/src/rabbit_misc.erl +++ b/src/rabbit_misc.erl @@ -823,7 +823,7 @@ lock_file(Path) -> %% Format IPv4-mapped IPv6 addresses as IPv4, since they're what we see %% when IPv6 is enabled but not used (i.e. 99% of the time). -ntoa({0,0,0,0,0,16#ffff,AB,CD} = Foo) -> +ntoa({0,0,0,0,0,16#ffff,AB,CD}) -> inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256}); ntoa(IP) -> inet_parse:ntoa(IP). -- cgit v1.2.1 From bc7d41e765aefb7b22217749be59b05a063e01c3 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 7 Jan 2011 16:03:50 +0000 Subject: Don't have the scripts set bind address of 0.0.0.0 if the port is specified but IP isn't. Instead use the same logic as if it's specified in the configuration file. --- scripts/rabbitmq-multi | 2 +- scripts/rabbitmq-multi.bat | 2 +- scripts/rabbitmq-server | 2 +- scripts/rabbitmq-server.bat | 2 +- scripts/rabbitmq-service.bat | 2 +- src/rabbit_networking.erl | 25 ++++++++++++++++--------- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/scripts/rabbitmq-multi b/scripts/rabbitmq-multi index 33883702..d8a69921 100755 --- a/scripts/rabbitmq-multi +++ b/scripts/rabbitmq-multi @@ -38,7 +38,7 @@ CONFIG_FILE=/etc/rabbitmq/rabbitmq . `dirname $0`/rabbitmq-env -DEFAULT_NODE_IP_ADDRESS=0.0.0.0 +DEFAULT_NODE_IP_ADDRESS=auto DEFAULT_NODE_PORT=5672 [ "x" = "x$RABBITMQ_NODE_IP_ADDRESS" ] && [ "x" != "x$NODE_IP_ADDRESS" ] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS} [ "x" = "x$RABBITMQ_NODE_PORT" ] && [ "x" != "x$NODE_PORT" ] && RABBITMQ_NODE_PORT=${NODE_PORT} diff --git a/scripts/rabbitmq-multi.bat b/scripts/rabbitmq-multi.bat index a4f8c8b4..e1597bc5 100644 --- a/scripts/rabbitmq-multi.bat +++ b/scripts/rabbitmq-multi.bat @@ -52,7 +52,7 @@ if "!RABBITMQ_NODENAME!"=="" ( if "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( if not "!RABBITMQ_NODE_PORT!"=="" ( - set RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 + set RABBITMQ_NODE_IP_ADDRESS=auto ) ) else ( if "!RABBITMQ_NODE_PORT!"=="" ( diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 4155b31d..d1f893cf 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -40,7 +40,7 @@ SERVER_START_ARGS= . `dirname $0`/rabbitmq-env -DEFAULT_NODE_IP_ADDRESS=0.0.0.0 +DEFAULT_NODE_IP_ADDRESS=auto DEFAULT_NODE_PORT=5672 [ "x" = "x$RABBITMQ_NODE_IP_ADDRESS" ] && [ "x" != "x$NODE_IP_ADDRESS" ] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS} [ "x" = "x$RABBITMQ_NODE_PORT" ] && [ "x" != "x$NODE_PORT" ] && RABBITMQ_NODE_PORT=${NODE_PORT} diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 52a250c6..d050d5de 100644 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -52,7 +52,7 @@ if "!RABBITMQ_NODENAME!"=="" ( if "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( if not "!RABBITMQ_NODE_PORT!"=="" ( - set RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 + set RABBITMQ_NODE_IP_ADDRESS=auto ) ) else ( if "!RABBITMQ_NODE_PORT!"=="" ( diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index d2592931..209b2d7a 100644 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -58,7 +58,7 @@ if "!RABBITMQ_NODENAME!"=="" ( if "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( if not "!RABBITMQ_NODE_PORT!"=="" ( - set RABBITMQ_NODE_IP_ADDRESS=0.0.0.0 + set RABBITMQ_NODE_IP_ADDRESS=auto ) ) else ( if "!RABBITMQ_NODE_PORT!"=="" ( diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 621343c4..5243f17a 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -176,15 +176,11 @@ resolve_family(IP, auto) -> throw({error, {strange_family, IP}}); resolve_family(_, F) -> F. check_tcp_listener_address(NamePrefix, Port) when is_integer(Port) -> - case ipv6_status(Port) of - ipv4_only -> - check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}); - ipv6_single_stack -> - check_tcp_listener_address(NamePrefix, {"::", Port, inet6}); - ipv6_dual_stack -> - check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}) - ++ check_tcp_listener_address(NamePrefix, {"::", Port, inet6}) - end; + check_tcp_listener_address_auto(NamePrefix, Port); + +check_tcp_listener_address(NamePrefix, {"auto", Port}) -> + %% Variant to prevent lots of hacking around in bash and batch files + check_tcp_listener_address_auto(NamePrefix, Port); check_tcp_listener_address(NamePrefix, {Host, Port}) -> %% auto: determine family IPv4 / IPv6 after converting to IP address @@ -200,6 +196,17 @@ check_tcp_listener_address(NamePrefix, {Host, Port, Family0}) -> Name = rabbit_misc:tcp_name(NamePrefix, IPAddress, Port), [{IPAddress, Port, Family, Name}]. +check_tcp_listener_address_auto(NamePrefix, Port) -> + case ipv6_status(Port) of + ipv4_only -> + check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}); + ipv6_single_stack -> + check_tcp_listener_address(NamePrefix, {"::", Port, inet6}); + ipv6_dual_stack -> + check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}) + ++ check_tcp_listener_address(NamePrefix, {"::", Port, inet6}) + end. + start_tcp_listener(Listener) -> start_listener(Listener, amqp, "TCP Listener", {?MODULE, start_client, []}). -- cgit v1.2.1 From 5c0a2b1ad002583f99aae6f4f18a8ce8321764db Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Fri, 7 Jan 2011 16:08:16 +0000 Subject: Update documentation. --- docs/rabbitmq-server.1.xml | 5 +++-- docs/rabbitmq-service.xml | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/rabbitmq-server.1.xml b/docs/rabbitmq-server.1.xml index 03e76c79..5041a9c4 100644 --- a/docs/rabbitmq-server.1.xml +++ b/docs/rabbitmq-server.1.xml @@ -83,8 +83,9 @@ machine guide for details. RABBITMQ_NODE_IP_ADDRESS -Defaults to 0.0.0.0. This can be changed if you only want to bind to -one network interface. +By default RabbitMQ will bind to all interfaces, on IPv4 and IPv6 if +available. Set this if you only want to bind to one network interface +or address family. diff --git a/docs/rabbitmq-service.xml b/docs/rabbitmq-service.xml index e95f9889..3368960b 100644 --- a/docs/rabbitmq-service.xml +++ b/docs/rabbitmq-service.xml @@ -165,8 +165,9 @@ machine guide for details. RABBITMQ_NODE_IP_ADDRESS -Defaults to 0.0.0.0. This can be changed if you only want to bind to -one network interface. +By default RabbitMQ will bind to all interfaces, on IPv4 and IPv6 if +available. Set this if you only want to bind to one network interface +or address family. -- cgit v1.2.1 From d3a170133743122dd06d12fa6539df0b05cbaa0d Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Jan 2011 17:31:43 +0000 Subject: Allow gethostaddr(Host, auto) to return more than one IP address in the case where a hostname resolves both A and AAAA. --- src/rabbit_networking.erl | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 5243f17a..3e63b2b6 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -144,25 +144,22 @@ start() -> getaddr(Host, Family) -> case inet_parse:address(Host) of - {ok, IPAddress} -> {IPAddress, resolve_family(IPAddress, Family)}; + {ok, IPAddress} -> [{IPAddress, resolve_family(IPAddress, Family)}]; {error, _} -> gethostaddr(Host, Family) end. gethostaddr(Host, auto) -> - case inet:getaddr(Host, inet6) of - {ok, IPAddress6} -> - {IPAddress6, inet6}; - {error, Reason6} -> - case inet:getaddr(Host, inet) of - {ok, IPAddress4} -> {IPAddress4, inet}; - {error, Reason4} -> host_lookup_error( - Host, {{ipv6, Reason6}, {ipv4, Reason4}}) - end + Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]], + case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of + [] -> + host_lookup_error(Host, Lookups); + IPs -> + IPs end; gethostaddr(Host, Family) -> case inet:getaddr(Host, Family) of - {ok, IPAddress} -> {IPAddress, Family}; + {ok, IPAddress} -> [{IPAddress, Family}]; {error, Reason} -> host_lookup_error(Host, Reason) end. @@ -187,14 +184,14 @@ check_tcp_listener_address(NamePrefix, {Host, Port}) -> check_tcp_listener_address(NamePrefix, {Host, Port, auto}); check_tcp_listener_address(NamePrefix, {Host, Port, Family0}) -> - {IPAddress, Family} = getaddr(Host, Family0), if is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) -> ok; true -> error_logger:error_msg("invalid port ~p - not 0..65535~n", [Port]), throw({error, {invalid_port, Port}}) end, - Name = rabbit_misc:tcp_name(NamePrefix, IPAddress, Port), - [{IPAddress, Port, Family, Name}]. + [{IPAddress, Port, Family, + rabbit_misc:tcp_name(NamePrefix, IPAddress, Port)} || + {IPAddress, Family} <- getaddr(Host, Family0)]. check_tcp_listener_address_auto(NamePrefix, Port) -> case ipv6_status(Port) of -- cgit v1.2.1 From 4fdc11b72c3bab36aaddf59848806087db7f4439 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Jan 2011 18:10:20 +0000 Subject: Rename ipv6_status to port_to_listeners, make it return listeners, explain it more. --- src/rabbit_networking.erl | 55 ++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 3e63b2b6..1941ec32 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -151,10 +151,8 @@ getaddr(Host, Family) -> gethostaddr(Host, auto) -> Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]], case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of - [] -> - host_lookup_error(Host, Lookups); - IPs -> - IPs + [] -> host_lookup_error(Host, Lookups); + IPs -> IPs end; gethostaddr(Host, Family) -> @@ -194,15 +192,8 @@ check_tcp_listener_address(NamePrefix, {Host, Port, Family0}) -> {IPAddress, Family} <- getaddr(Host, Family0)]. check_tcp_listener_address_auto(NamePrefix, Port) -> - case ipv6_status(Port) of - ipv4_only -> - check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}); - ipv6_single_stack -> - check_tcp_listener_address(NamePrefix, {"::", Port, inet6}); - ipv6_dual_stack -> - check_tcp_listener_address(NamePrefix, {"0.0.0.0", Port, inet}) - ++ check_tcp_listener_address(NamePrefix, {"::", Port, inet6}) - end. + lists:append([check_tcp_listener_address(NamePrefix, Listener) || + Listener <- port_to_listeners(Port)]). start_tcp_listener(Listener) -> start_listener(Listener, amqp, "TCP Listener", @@ -339,7 +330,18 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %%-------------------------------------------------------------------- -%% How to test on Linux: +%% There are three kinds of machine (for our purposes). + +%% * Those which treat IPv4 addresses as a special kind of IPv6 address +%% ("Single stack") +%% - Linux by default, Windows Vista and later +%% * Those which consider IPv6 and IPv4 to be completely separate things +%% ("Dual stack") +%% - OpenBSD, Windows XP / 2003, Linux if so configured +%% * Those which do not support IPv6. +%% - Ancient/weird OSes, Linux if so configured + +%% How to reconfigure Linux to test this: %% Single stack (default): %% echo 0 > /proc/sys/net/ipv6/bindv6only %% Dual stack: @@ -348,19 +350,32 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then %% sudo update-grub && sudo reboot -ipv6_status(Port) -> - %% Unfortunately it seems there is no way to detect single vs dual stack - %% apart from attempting to bind to the socket. +%% This matters in (and only in) the case where the sysadmin (or the +%% app descriptor) has only supplied a port and we wish to bind to +%% "all addresses". This means different things depending on whether +%% we're single or dual stack. On single stack binding to "::" +%% implicitly includes all IPv4 addresses, and subsequently attempting +%% to bind to "0.0.0.0" will fail. On dual stack, binding to "::" will +%% only bind to IPv6 addresses, and we need another listener bound to +%% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only +%% want to bind to "0.0.0.0". + +%% Unfortunately it seems there is no way to detect single vs dual stack +%% apart from attempting to bind to the socket. + +port_to_listeners(Port) -> IPv4 = [inet, {ip, {0,0,0,0}}], IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}], + IPv4Listener = {"0.0.0.0", Port, inet}, + IPv6Listener = {"::", Port, inet6}, case gen_tcp:listen(Port, IPv6) of {ok, LSock6} -> Status = case gen_tcp:listen(Port, IPv4) of {ok, LSock4} -> gen_tcp:close(LSock4), - ipv6_dual_stack; + [IPv4Listener, IPv6Listener]; {error, _} -> - ipv6_single_stack + [IPv6Listener] end, gen_tcp:close(LSock6), Status; @@ -368,5 +383,5 @@ ipv6_status(Port) -> %% It could be that there's a general problem binding to %% the port, but plow on; the error will get picked up %% later when we bind for real. - ipv4_only + [IPv4Listener] end. -- cgit v1.2.1 From 425439362c0a9fb8560401fbff0395e4df60ec5b Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Tue, 18 Jan 2011 18:14:36 +0000 Subject: Cosmetics. --- src/rabbit_networking.erl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 1941ec32..be1f4f80 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -331,7 +331,7 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %%-------------------------------------------------------------------- %% There are three kinds of machine (for our purposes). - +%% %% * Those which treat IPv4 addresses as a special kind of IPv6 address %% ("Single stack") %% - Linux by default, Windows Vista and later @@ -340,7 +340,7 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %% - OpenBSD, Windows XP / 2003, Linux if so configured %% * Those which do not support IPv6. %% - Ancient/weird OSes, Linux if so configured - +%% %% How to reconfigure Linux to test this: %% Single stack (default): %% echo 0 > /proc/sys/net/ipv6/bindv6only @@ -349,7 +349,7 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %% IPv4 only: %% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then %% sudo update-grub && sudo reboot - +%% %% This matters in (and only in) the case where the sysadmin (or the %% app descriptor) has only supplied a port and we wish to bind to %% "all addresses". This means different things depending on whether @@ -359,10 +359,9 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %% only bind to IPv6 addresses, and we need another listener bound to %% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only %% want to bind to "0.0.0.0". - +%% %% Unfortunately it seems there is no way to detect single vs dual stack %% apart from attempting to bind to the socket. - port_to_listeners(Port) -> IPv4 = [inet, {ip, {0,0,0,0}}], IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}], @@ -371,11 +370,9 @@ port_to_listeners(Port) -> case gen_tcp:listen(Port, IPv6) of {ok, LSock6} -> Status = case gen_tcp:listen(Port, IPv4) of - {ok, LSock4} -> - gen_tcp:close(LSock4), - [IPv4Listener, IPv6Listener]; - {error, _} -> - [IPv6Listener] + {ok, LSock4} -> gen_tcp:close(LSock4), + [IPv4Listener, IPv6Listener]; + {error, _} -> [IPv6Listener] end, gen_tcp:close(LSock6), Status; -- cgit v1.2.1 From 64dc7c3d98354b569ae2498313c6e560a0094144 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Jan 2011 11:15:35 +0000 Subject: Make port_to_listeners distinguish all the cases now, and write even more comments. --- src/rabbit_networking.erl | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index be1f4f80..bb5e814c 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -335,6 +335,7 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %% * Those which treat IPv4 addresses as a special kind of IPv6 address %% ("Single stack") %% - Linux by default, Windows Vista and later +%% - We also treat any (hypothetical?) IPv6-only machine the same way %% * Those which consider IPv6 and IPv4 to be completely separate things %% ("Dual stack") %% - OpenBSD, Windows XP / 2003, Linux if so configured @@ -361,7 +362,7 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %% want to bind to "0.0.0.0". %% %% Unfortunately it seems there is no way to detect single vs dual stack -%% apart from attempting to bind to the socket. +%% apart from attempting to bind to the port. port_to_listeners(Port) -> IPv4 = [inet, {ip, {0,0,0,0}}], IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}], @@ -369,16 +370,34 @@ port_to_listeners(Port) -> IPv6Listener = {"::", Port, inet6}, case gen_tcp:listen(Port, IPv6) of {ok, LSock6} -> - Status = case gen_tcp:listen(Port, IPv4) of - {ok, LSock4} -> gen_tcp:close(LSock4), - [IPv4Listener, IPv6Listener]; - {error, _} -> [IPv6Listener] - end, - gen_tcp:close(LSock6), - Status; + case gen_tcp:listen(Port, IPv4) of + {ok, LSock4} -> + %% Dual stack + gen_tcp:close(LSock6), + gen_tcp:close(LSock4), + [IPv4Listener, IPv6Listener]; + %% Checking the error here would only let us + %% distinguish single stack IPv6 / IPv4 vs IPv6 only, + %% which we figure out below anyway. + {error, _} -> + gen_tcp:close(LSock6), + case gen_tcp:listen(Port, IPv4) of + %% Single stack + {ok, LSock4} -> gen_tcp:close(LSock4), + [IPv6Listener]; + %% IPv6-only machine. Welcome to the future. + {error, eafnosupport} -> [IPv6Listener]; + %% Dual stack machine with something already + %% on IPv4, return that to force an error later. + {error, _} -> [IPv4Listener] + end + end; + {error, eafnosupport} -> + %% IPv4-only machine. Welcome to the 90s. + [IPv4Listener]; {error, _} -> - %% It could be that there's a general problem binding to - %% the port, but plow on; the error will get picked up - %% later when we bind for real. - [IPv4Listener] + %% There's a general problem binding to the port, but plow + %% on; the error will get picked up later when we bind for + %% real. Return both listeners to ensure this. + [IPv4Listener, IPv6Listener] end. -- cgit v1.2.1 From f9d25b5bb4b43cf9bdeaf45b0d3c89eea802c43c Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Jan 2011 15:34:33 +0000 Subject: Duh. --- src/rabbit_networking.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index bb5e814c..83b47b1f 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -388,8 +388,8 @@ port_to_listeners(Port) -> %% IPv6-only machine. Welcome to the future. {error, eafnosupport} -> [IPv6Listener]; %% Dual stack machine with something already - %% on IPv4, return that to force an error later. - {error, _} -> [IPv4Listener] + %% on IPv4. + {error, _} -> [IPv6Listener, IPv4Listener] end end; {error, eafnosupport} -> -- cgit v1.2.1 From f303fffa1f85639f679055026bc4b2d12d7e3d05 Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Wed, 19 Jan 2011 16:03:19 +0000 Subject: Don't test on the real port. Pick a test port, and retry if we find any evidence of its being in use. --- src/rabbit_networking.erl | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 83b47b1f..be6d1e09 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -59,6 +59,8 @@ -define(SSL_TIMEOUT, 5). %% seconds +-define(FIRST_TEST_BIND_PORT, 10000). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -364,40 +366,46 @@ cmap(F) -> rabbit_misc:filter_exit_map(F, connections()). %% Unfortunately it seems there is no way to detect single vs dual stack %% apart from attempting to bind to the port. port_to_listeners(Port) -> + IPv4 = {"0.0.0.0", Port, inet}, + IPv6 = {"::", Port, inet6}, + case ipv6_status(?FIRST_TEST_BIND_PORT) of + single_stack -> [IPv6]; + ipv6_only -> [IPv6]; + dual_stack -> [IPv6, IPv4]; + ipv4_only -> [IPv4] + end. + +ipv6_status(TestPort) -> IPv4 = [inet, {ip, {0,0,0,0}}], IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}], - IPv4Listener = {"0.0.0.0", Port, inet}, - IPv6Listener = {"::", Port, inet6}, - case gen_tcp:listen(Port, IPv6) of + case gen_tcp:listen(TestPort, IPv6) of {ok, LSock6} -> - case gen_tcp:listen(Port, IPv4) of + case gen_tcp:listen(TestPort, IPv4) of {ok, LSock4} -> %% Dual stack gen_tcp:close(LSock6), gen_tcp:close(LSock4), - [IPv4Listener, IPv6Listener]; + dual_stack; %% Checking the error here would only let us %% distinguish single stack IPv6 / IPv4 vs IPv6 only, %% which we figure out below anyway. {error, _} -> gen_tcp:close(LSock6), - case gen_tcp:listen(Port, IPv4) of + case gen_tcp:listen(TestPort, IPv4) of %% Single stack {ok, LSock4} -> gen_tcp:close(LSock4), - [IPv6Listener]; + single_stack; %% IPv6-only machine. Welcome to the future. - {error, eafnosupport} -> [IPv6Listener]; + {error, eafnosupport} -> ipv6_only; %% Dual stack machine with something already %% on IPv4. - {error, _} -> [IPv6Listener, IPv4Listener] + {error, _} -> ipv6_status(TestPort + 1) end end; {error, eafnosupport} -> %% IPv4-only machine. Welcome to the 90s. - [IPv4Listener]; + ipv4_only; {error, _} -> - %% There's a general problem binding to the port, but plow - %% on; the error will get picked up later when we bind for - %% real. Return both listeners to ensure this. - [IPv4Listener, IPv6Listener] + %% Port in use + ipv6_status(TestPort + 1) end. -- cgit v1.2.1