1%% This Source Code Form is subject to the terms of the Mozilla Public
2%% License, v. 2.0. If a copy of the MPL was not distributed with this
3%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4%%
5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates.  All rights reserved.
6%%
7
8-module(rabbit_networking).
9
10%% This module contains various functions that deal with networking,
11%% TCP and TLS listeners, and connection information.
12%%
13%% It also contains a boot step — boot/0 — that starts networking machinery.
14%% This module primarily covers AMQP 0-9-1 but some bits are reused in
15%% plugins that provide protocol support, e.g. STOMP or MQTT.
16%%
17%% Functions in this module take care of normalising TCP listener options,
18%% including dual IP stack cases, and starting the AMQP 0-9-1 listener(s).
19%%
20%% See also tcp_listener_sup and tcp_listener.
21
22-export([boot/0, start_tcp_listener/2, start_tcp_listener/3,
23         start_ssl_listener/3, start_ssl_listener/4,
24         stop_tcp_listener/1, on_node_down/1, active_listeners/0,
25         node_listeners/1, node_client_listeners/1,
26         register_connection/1, unregister_connection/1,
27         register_non_amqp_connection/1, unregister_non_amqp_connection/1,
28         connections/0, non_amqp_connections/0, connection_info_keys/0,
29         connection_info/1, connection_info/2,
30         connection_info_all/0, connection_info_all/1,
31         emit_connection_info_all/4, emit_connection_info_local/3,
32         close_connection/2, close_connections/2, close_all_connections/1,
33         close_all_user_connections/2,
34         force_connection_event_refresh/1, force_non_amqp_connection_event_refresh/1,
35         handshake/2, tcp_host/1,
36         ranch_ref/1, ranch_ref/2, ranch_ref_of_protocol/1,
37         listener_of_protocol/1, stop_ranch_listener_of_protocol/1]).
38
39%% Used by TCP-based transports, e.g. STOMP adapter
40-export([tcp_listener_addresses/1,
41         tcp_listener_spec/9, tcp_listener_spec/10,
42         ensure_ssl/0, fix_ssl_options/1, poodle_check/1]).
43
44-export([tcp_listener_started/4, tcp_listener_stopped/4]).
45
46-deprecated([{force_connection_event_refresh, 1, eventually}]).
47
48-export([
49    local_connections/0,
50    local_non_amqp_connections/0,
51    %% prefer local_connections/0
52    connections_local/0
53]).
54
55-include_lib("rabbit_common/include/rabbit.hrl").
56-include_lib("rabbit_common/include/rabbit_misc.hrl").
57
58%% IANA-suggested ephemeral port range is 49152 to 65535
59-define(FIRST_TEST_BIND_PORT, 49152).
60
61%%----------------------------------------------------------------------------
62
63-export_type([ip_port/0, hostname/0]).
64
65-type hostname() :: rabbit_net:hostname().
66-type ip_port() :: rabbit_net:ip_port().
67
68-type family() :: atom().
69-type listener_config() :: ip_port() |
70                           {hostname(), ip_port()} |
71                           {hostname(), ip_port(), family()}.
72-type address() :: {inet:ip_address(), ip_port(), family()}.
73-type name_prefix() :: atom().
74-type protocol() :: atom().
75-type label() :: string().
76
77-spec boot() -> 'ok' | no_return().
78
79boot() ->
80    ok = record_distribution_listener(),
81    _ = application:start(ranch),
82    rabbit_log:debug("Started Ranch"),
83    %% Failures will throw exceptions
84    _ = boot_listeners(fun boot_tcp/2, application:get_env(rabbit, num_tcp_acceptors, 10),
85                       application:get_env(rabbit, num_conns_sups, 1), "TCP"),
86    _ = boot_listeners(fun boot_tls/2, application:get_env(rabbit, num_ssl_acceptors, 10),
87                       application:get_env(rabbit, num_conns_sups, 1), "TLS"),
88    ok.
89
90boot_listeners(Fun, NumAcceptors, ConcurrentConnsSupsCount, Type) ->
91    case Fun(NumAcceptors, ConcurrentConnsSupsCount) of
92        ok                                                                  ->
93            ok;
94        {error, {could_not_start_listener, Address, Port, Details}} = Error ->
95            rabbit_log:error("Failed to start ~s listener [~s]:~p, error: ~p",
96                             [Type, Address, Port, Details]),
97            throw(Error)
98    end.
99
100boot_tcp(NumAcceptors, ConcurrentConnsSupsCount) ->
101    {ok, TcpListeners} = application:get_env(tcp_listeners),
102    case lists:foldl(fun(Listener, ok) ->
103                             start_tcp_listener(Listener, NumAcceptors, ConcurrentConnsSupsCount);
104                        (_Listener, Error) ->
105                             Error
106                     end,
107                     ok, TcpListeners) of
108        ok                 -> ok;
109        {error, _} = Error -> Error
110    end.
111
112boot_tls(NumAcceptors, ConcurrentConnsSupsCount) ->
113    case application:get_env(ssl_listeners) of
114        {ok, []} ->
115            ok;
116        {ok, SslListeners} ->
117            SslOpts = ensure_ssl(),
118            case poodle_check('AMQP') of
119                ok     -> [start_ssl_listener(L, SslOpts, NumAcceptors, ConcurrentConnsSupsCount)
120                           || L <- SslListeners];
121                danger -> ok
122            end,
123            ok
124    end.
125
126-spec ensure_ssl() -> rabbit_types:infos().
127
128ensure_ssl() ->
129    {ok, SslAppsConfig} = application:get_env(rabbit, ssl_apps),
130    ok = app_utils:start_applications(SslAppsConfig),
131    {ok, SslOptsConfig0} = application:get_env(rabbit, ssl_options),
132    rabbit_ssl_options:fix(SslOptsConfig0).
133
134-spec poodle_check(atom()) -> 'ok' | 'danger'.
135
136poodle_check(Context) ->
137    {ok, Vsn} = application:get_key(ssl, vsn),
138    case rabbit_misc:version_compare(Vsn, "5.3", gte) of %% R16B01
139        true  -> ok;
140        false -> case application:get_env(rabbit, ssl_allow_poodle_attack) of
141                     {ok, true}  -> ok;
142                     _           -> log_poodle_fail(Context),
143                                    danger
144                 end
145    end.
146
147log_poodle_fail(Context) ->
148    rabbit_log:error(
149      "The installed version of Erlang (~s) contains the bug OTP-10905,~n"
150      "which makes it impossible to disable SSLv3. This makes the system~n"
151      "vulnerable to the POODLE attack. SSL listeners for ~s have therefore~n"
152      "been disabled.~n~n"
153      "You are advised to upgrade to a recent Erlang version; R16B01 is the~n"
154      "first version in which this bug is fixed, but later is usually~n"
155      "better.~n~n"
156      "If you cannot upgrade now and want to re-enable SSL listeners, you can~n"
157      "set the config item 'ssl_allow_poodle_attack' to 'true' in the~n"
158      "'rabbit' section of your configuration file.",
159      [rabbit_misc:otp_release(), Context]).
160
161fix_ssl_options(Config) ->
162    rabbit_ssl_options:fix(Config).
163
164-spec tcp_listener_addresses(listener_config()) -> [address()].
165
166tcp_listener_addresses(Port) when is_integer(Port) ->
167    tcp_listener_addresses_auto(Port);
168tcp_listener_addresses({"auto", Port}) ->
169    %% Variant to prevent lots of hacking around in bash and batch files
170    tcp_listener_addresses_auto(Port);
171tcp_listener_addresses({Host, Port}) ->
172    %% auto: determine family IPv4 / IPv6 after converting to IP address
173    tcp_listener_addresses({Host, Port, auto});
174tcp_listener_addresses({Host, Port, Family0})
175  when is_integer(Port) andalso (Port >= 0) andalso (Port =< 65535) ->
176    [{IPAddress, Port, Family} ||
177        {IPAddress, Family} <- getaddr(Host, Family0)];
178tcp_listener_addresses({_Host, Port, _Family0}) ->
179    rabbit_log:error("invalid port ~p - not 0..65535", [Port]),
180    throw({error, {invalid_port, Port}}).
181
182tcp_listener_addresses_auto(Port) ->
183    lists:append([tcp_listener_addresses(Listener) ||
184                     Listener <- port_to_listeners(Port)]).
185
186tcp_listener_spec(NamePrefix, Address, SocketOpts, Transport, ProtoSup, ProtoOpts,
187                  Protocol, NumAcceptors, Label) ->
188    tcp_listener_spec(NamePrefix, Address, SocketOpts, Transport, ProtoSup, ProtoOpts,
189                      Protocol, NumAcceptors, 1, Label).
190
191-spec tcp_listener_spec
192        (name_prefix(), address(), [gen_tcp:listen_option()], module(), module(),
193         any(), protocol(), non_neg_integer(), non_neg_integer(), label()) ->
194            supervisor:child_spec().
195
196tcp_listener_spec(NamePrefix, {IPAddress, Port, Family}, SocketOpts,
197                  Transport, ProtoSup, ProtoOpts, Protocol, NumAcceptors,
198                  ConcurrentConnsSupsCount, Label) ->
199    Args = [IPAddress, Port, Transport, [Family | SocketOpts], ProtoSup, ProtoOpts,
200            {?MODULE, tcp_listener_started, [Protocol, SocketOpts]},
201            {?MODULE, tcp_listener_stopped, [Protocol, SocketOpts]},
202            NumAcceptors, ConcurrentConnsSupsCount, Label],
203    {rabbit_misc:tcp_name(NamePrefix, IPAddress, Port),
204     {tcp_listener_sup, start_link, Args},
205     transient, infinity, supervisor, [tcp_listener_sup]}.
206
207-spec ranch_ref(#listener{} | [{atom(), any()}] | 'undefined') -> ranch:ref() | undefined.
208ranch_ref(#listener{port = Port}) ->
209    [{IPAddress, Port, _Family} | _] = tcp_listener_addresses(Port),
210    {acceptor, IPAddress, Port};
211ranch_ref(Listener) when is_list(Listener) ->
212    Port = rabbit_misc:pget(port, Listener),
213    [{IPAddress, Port, _Family} | _] = tcp_listener_addresses(Port),
214    {acceptor, IPAddress, Port};
215ranch_ref(undefined) ->
216    undefined.
217
218-spec ranch_ref(inet:ip_address(), ip_port()) -> ranch:ref().
219
220%% Returns a reference that identifies a TCP listener in Ranch.
221ranch_ref(IPAddress, Port) ->
222    {acceptor, IPAddress, Port}.
223
224-spec ranch_ref_of_protocol(atom()) -> ranch:ref() | undefined.
225ranch_ref_of_protocol(Protocol) ->
226    ranch_ref(listener_of_protocol(Protocol)).
227
228-spec listener_of_protocol(atom()) -> #listener{}.
229listener_of_protocol(Protocol) ->
230    rabbit_misc:execute_mnesia_transaction(
231        fun() ->
232            MatchSpec = #listener{
233                node = node(),
234                protocol = Protocol,
235                _ = '_'
236            },
237            case mnesia:match_object(rabbit_listener, MatchSpec, read) of
238                []    -> undefined;
239                [Row] -> Row
240            end
241        end).
242
243-spec stop_ranch_listener_of_protocol(atom()) -> ok | {error, not_found}.
244stop_ranch_listener_of_protocol(Protocol) ->
245    case rabbit_networking:ranch_ref_of_protocol(Protocol) of
246        undefined -> ok;
247        Ref       ->
248            rabbit_log:debug("Stopping Ranch listener for protocol ~s", [Protocol]),
249            ranch:stop_listener(Ref)
250    end.
251
252-spec start_tcp_listener(
253        listener_config(), integer()) -> 'ok' | {'error', term()}.
254
255start_tcp_listener(Listener, NumAcceptors) ->
256    start_tcp_listener(Listener, NumAcceptors, 1).
257
258-spec start_tcp_listener(
259        listener_config(), integer(), integer()) -> 'ok' | {'error', term()}.
260
261start_tcp_listener(Listener, NumAcceptors, ConcurrentConnsSupsCount) ->
262    start_listener(Listener, NumAcceptors, ConcurrentConnsSupsCount, amqp,
263                   "TCP listener", tcp_opts()).
264
265-spec start_ssl_listener(
266        listener_config(), rabbit_types:infos(), integer()) -> 'ok' | {'error', term()}.
267
268start_ssl_listener(Listener, SslOpts, NumAcceptors) ->
269    start_ssl_listener(Listener, SslOpts, NumAcceptors, 1).
270
271-spec start_ssl_listener(
272        listener_config(), rabbit_types:infos(), integer(), integer()) -> 'ok' | {'error', term()}.
273
274start_ssl_listener(Listener, SslOpts, NumAcceptors, ConcurrentConnsSupsCount) ->
275    start_listener(Listener, NumAcceptors, ConcurrentConnsSupsCount, 'amqp/ssl',
276                   "TLS (SSL) listener", tcp_opts() ++ SslOpts).
277
278-spec start_listener(
279        listener_config(), integer(), integer(), protocol(), label(), list()) ->
280          'ok' | {'error', term()}.
281start_listener(Listener, NumAcceptors, ConcurrentConnsSupsCount, Protocol, Label, Opts) ->
282    lists:foldl(fun (Address, ok) ->
283                        start_listener0(Address, NumAcceptors, ConcurrentConnsSupsCount, Protocol,
284                                        Label, Opts);
285                    (_Address, {error, _} = Error) ->
286                        Error
287                end, ok, tcp_listener_addresses(Listener)).
288
289start_listener0(Address, NumAcceptors, ConcurrentConnsSupsCount, Protocol, Label, Opts) ->
290    Transport = transport(Protocol),
291    Spec = tcp_listener_spec(rabbit_tcp_listener_sup, Address, Opts,
292                             Transport, rabbit_connection_sup, [], Protocol,
293                             NumAcceptors, ConcurrentConnsSupsCount, Label),
294    case supervisor:start_child(rabbit_sup, Spec) of
295        {ok, _}          -> ok;
296        {error, {{shutdown, {failed_to_start_child, _,
297                             {shutdown, {failed_to_start_child, _,
298                                         {listen_error, _, PosixError}}}}}, _}} ->
299            {IPAddress, Port, _Family} = Address,
300            {error, {could_not_start_listener, rabbit_misc:ntoa(IPAddress), Port, PosixError}};
301        {error, Other} ->
302            {IPAddress, Port, _Family} = Address,
303            {error, {could_not_start_listener, rabbit_misc:ntoa(IPAddress), Port, Other}}
304    end.
305
306transport(Protocol) ->
307    case Protocol of
308        amqp       -> ranch_tcp;
309        'amqp/ssl' -> ranch_ssl
310    end.
311
312-spec stop_tcp_listener(listener_config()) -> 'ok'.
313
314stop_tcp_listener(Listener) ->
315    [stop_tcp_listener0(Address) ||
316        Address <- tcp_listener_addresses(Listener)],
317    ok.
318
319stop_tcp_listener0({IPAddress, Port, _Family}) ->
320    Name = rabbit_misc:tcp_name(rabbit_tcp_listener_sup, IPAddress, Port),
321    ok = supervisor:terminate_child(rabbit_sup, Name),
322    ok = supervisor:delete_child(rabbit_sup, Name).
323
324-spec tcp_listener_started
325        (_, _,
326         string() |
327         {byte(),byte(),byte(),byte()} |
328         {char(),char(),char(),char(),char(),char(),char(),char()}, _) ->
329            'ok'.
330
331tcp_listener_started(Protocol, Opts, IPAddress, Port) ->
332    %% We need the ip to distinguish e.g. 0.0.0.0 and 127.0.0.1
333    %% We need the host so we can distinguish multiple instances of the above
334    %% in a cluster.
335    ok = mnesia:dirty_write(
336           rabbit_listener,
337           #listener{node = node(),
338                     protocol = Protocol,
339                     host = tcp_host(IPAddress),
340                     ip_address = IPAddress,
341                     port = Port,
342                     opts = Opts}).
343
344-spec tcp_listener_stopped
345        (_, _,
346         string() |
347         {byte(),byte(),byte(),byte()} |
348         {char(),char(),char(),char(),char(),char(),char(),char()},
349         _) ->
350            'ok'.
351
352tcp_listener_stopped(Protocol, Opts, IPAddress, Port) ->
353    ok = mnesia:dirty_delete_object(
354           rabbit_listener,
355           #listener{node = node(),
356                     protocol = Protocol,
357                     host = tcp_host(IPAddress),
358                     ip_address = IPAddress,
359                     port = Port,
360                     opts = Opts}).
361
362-spec record_distribution_listener() -> ok | no_return().
363
364record_distribution_listener() ->
365    {Name, Host} = rabbit_nodes:parts(node()),
366    case erl_epmd:port_please(list_to_atom(Name), Host, infinity) of
367        {port, Port, _Version} ->
368            tcp_listener_started(clustering, [], {0,0,0,0,0,0,0,0}, Port);
369        noport ->
370            throw({error, no_epmd_port})
371    end.
372
373-spec active_listeners() -> [rabbit_types:listener()].
374
375active_listeners() ->
376    rabbit_misc:dirty_read_all(rabbit_listener).
377
378-spec node_listeners(node()) -> [rabbit_types:listener()].
379
380node_listeners(Node) ->
381    mnesia:dirty_read(rabbit_listener, Node).
382
383-spec node_client_listeners(node()) -> [rabbit_types:listener()].
384
385node_client_listeners(Node) ->
386    case node_listeners(Node) of
387        [] -> [];
388        Xs ->
389            lists:filter(fun (#listener{protocol = clustering}) -> false;
390                             (_) -> true
391                         end, Xs)
392    end.
393
394-spec on_node_down(node()) -> 'ok'.
395
396on_node_down(Node) ->
397    case lists:member(Node, nodes()) of
398        false ->
399            rabbit_log:info(
400                   "Node ~s is down, deleting its listeners", [Node]),
401            ok = mnesia:dirty_delete(rabbit_listener, Node);
402        true  ->
403            rabbit_log:info(
404                   "Keeping ~s listeners: the node is already back", [Node])
405    end.
406
407-spec register_connection(pid()) -> ok.
408
409register_connection(Pid) -> pg_local:join(rabbit_connections, Pid).
410
411-spec unregister_connection(pid()) -> ok.
412
413unregister_connection(Pid) -> pg_local:leave(rabbit_connections, Pid).
414
415-spec connections() -> [rabbit_types:connection()].
416
417connections() ->
418    Nodes = rabbit_nodes:all_running(),
419    rabbit_misc:append_rpc_all_nodes(Nodes, rabbit_networking, connections_local, [], ?RPC_TIMEOUT).
420
421-spec local_connections() -> [rabbit_types:connection()].
422%% @doc Returns pids of AMQP 0-9-1 and AMQP 1.0 connections local to this node.
423local_connections() ->
424    connections_local().
425
426-spec connections_local() -> [rabbit_types:connection()].
427%% @deprecated Prefer {@link local_connections}
428connections_local() -> pg_local:get_members(rabbit_connections).
429
430-spec register_non_amqp_connection(pid()) -> ok.
431
432register_non_amqp_connection(Pid) -> pg_local:join(rabbit_non_amqp_connections, Pid).
433
434-spec unregister_non_amqp_connection(pid()) -> ok.
435
436unregister_non_amqp_connection(Pid) -> pg_local:leave(rabbit_non_amqp_connections, Pid).
437
438-spec non_amqp_connections() -> [rabbit_types:connection()].
439
440non_amqp_connections() ->
441  Nodes = rabbit_nodes:all_running(),
442  rabbit_misc:append_rpc_all_nodes(Nodes, rabbit_networking, local_non_amqp_connections, [], ?RPC_TIMEOUT).
443
444-spec local_non_amqp_connections() -> [rabbit_types:connection()].
445local_non_amqp_connections() ->
446  pg_local:get_members(rabbit_non_amqp_connections).
447
448-spec connection_info_keys() -> rabbit_types:info_keys().
449
450connection_info_keys() -> rabbit_reader:info_keys().
451
452-spec connection_info(rabbit_types:connection()) -> rabbit_types:infos().
453
454connection_info(Pid) -> rabbit_reader:info(Pid).
455
456-spec connection_info(rabbit_types:connection(), rabbit_types:info_keys()) ->
457          rabbit_types:infos().
458
459connection_info(Pid, Items) -> rabbit_reader:info(Pid, Items).
460
461-spec connection_info_all() -> [rabbit_types:infos()].
462
463connection_info_all() -> cmap(fun (Q) -> connection_info(Q) end).
464
465-spec connection_info_all(rabbit_types:info_keys()) ->
466          [rabbit_types:infos()].
467
468connection_info_all(Items) -> cmap(fun (Q) -> connection_info(Q, Items) end).
469
470emit_connection_info_all(Nodes, Items, Ref, AggregatorPid) ->
471    Pids = [ spawn_link(Node, rabbit_networking, emit_connection_info_local, [Items, Ref, AggregatorPid]) || Node <- Nodes ],
472    rabbit_control_misc:await_emitters_termination(Pids),
473    ok.
474
475emit_connection_info_local(Items, Ref, AggregatorPid) ->
476    rabbit_control_misc:emitting_map_with_exit_handler(
477      AggregatorPid, Ref, fun(Q) -> connection_info(Q, Items) end,
478      connections_local()).
479
480-spec close_connection(pid(), string()) -> 'ok'.
481
482close_connection(Pid, Explanation) ->
483    case lists:member(Pid, connections()) of
484        true  ->
485            Res = rabbit_reader:shutdown(Pid, Explanation),
486            rabbit_log:info("Closing connection ~p because ~p", [Pid, Explanation]),
487            Res;
488        false ->
489            rabbit_log:warning("Asked to close connection ~p (reason: ~p) "
490                               "but no running cluster node reported it as an active connection. Was it already closed? ",
491                               [Pid, Explanation]),
492            ok
493    end.
494
495-spec close_connections([pid()], string()) -> 'ok'.
496close_connections(Pids, Explanation) ->
497    [close_connection(Pid, Explanation) || Pid <- Pids],
498    ok.
499
500-spec close_all_user_connections(rabbit_types:username(), string()) -> 'ok'.
501close_all_user_connections(Username, Explanation) ->
502    Pids = [Pid || #tracked_connection{pid = Pid} <- rabbit_connection_tracking:list_of_user(Username)],
503    [close_connection(Pid, Explanation) || Pid <- Pids],
504    ok.
505
506%% Meant to be used by tests only
507-spec close_all_connections(string()) -> 'ok'.
508close_all_connections(Explanation) ->
509    Pids = connections(),
510    [close_connection(Pid, Explanation) || Pid <- Pids],
511    ok.
512
513-spec force_connection_event_refresh(reference()) -> 'ok'.
514force_connection_event_refresh(Ref) ->
515    [rabbit_reader:force_event_refresh(C, Ref) || C <- connections()],
516    ok.
517
518-spec force_non_amqp_connection_event_refresh(reference()) -> 'ok'.
519force_non_amqp_connection_event_refresh(Ref) ->
520  [gen_server:cast(Pid, {force_event_refresh, Ref}) || Pid <- non_amqp_connections()],
521  ok.
522
523-spec failed_to_recv_proxy_header(_, _) -> no_return().
524failed_to_recv_proxy_header(Ref, Error) ->
525    Msg = case Error of
526        closed -> "error when receiving proxy header: TCP socket was ~p prematurely";
527        _Other -> "error when receiving proxy header: ~p"
528    end,
529    rabbit_log:debug(Msg, [Error]),
530    % The following call will clean up resources then exit
531    _ = ranch:handshake(Ref),
532    exit({shutdown, failed_to_recv_proxy_header}).
533
534handshake(Ref, ProxyProtocolEnabled) ->
535    case ProxyProtocolEnabled of
536        true ->
537            case ranch:recv_proxy_header(Ref, 3000) of
538                {error, Error} ->
539                    failed_to_recv_proxy_header(Ref, Error);
540                {error, protocol_error, Error} ->
541                    failed_to_recv_proxy_header(Ref, Error);
542                {ok, ProxyInfo} ->
543                    {ok, Sock} = ranch:handshake(Ref),
544                    setup_socket(Sock),
545                    {ok, {rabbit_proxy_socket, Sock, ProxyInfo}}
546            end;
547        false ->
548            {ok, Sock} = ranch:handshake(Ref),
549            setup_socket(Sock),
550            {ok, Sock}
551    end.
552
553setup_socket(Sock) ->
554    ok = tune_buffer_size(Sock),
555    ok = file_handle_cache:obtain().
556
557tune_buffer_size(Sock) ->
558    case tune_buffer_size1(Sock) of
559        ok         -> ok;
560        {error, _} -> rabbit_net:fast_close(Sock),
561                      exit(normal)
562    end.
563
564tune_buffer_size1(Sock) ->
565    case rabbit_net:getopts(Sock, [sndbuf, recbuf, buffer]) of
566        {ok, BufSizes} -> BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]),
567                          rabbit_net:setopts(Sock, [{buffer, BufSz}]);
568        Error          -> Error
569    end.
570
571%%--------------------------------------------------------------------
572
573tcp_host(IPAddress) ->
574    rabbit_net:tcp_host(IPAddress).
575
576cmap(F) -> rabbit_misc:filter_exit_map(F, connections()).
577
578tcp_opts() ->
579    {ok, ConfigOpts} = application:get_env(rabbit, tcp_listen_options),
580    ConfigOpts.
581
582%% inet_parse:address takes care of ip string, like "0.0.0.0"
583%% inet:getaddr returns immediately for ip tuple {0,0,0,0},
584%%  and runs 'inet_gethost' port process for dns lookups.
585%% On Windows inet:getaddr runs dns resolver for ip string, which may fail.
586getaddr(Host, Family) ->
587    case inet_parse:address(Host) of
588        {ok, IPAddress} -> [{IPAddress, resolve_family(IPAddress, Family)}];
589        {error, _}      -> gethostaddr(Host, Family)
590    end.
591
592gethostaddr(Host, auto) ->
593    Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]],
594    case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of
595        []  -> host_lookup_error(Host, Lookups);
596        IPs -> IPs
597    end;
598
599gethostaddr(Host, Family) ->
600    case inet:getaddr(Host, Family) of
601        {ok, IPAddress} -> [{IPAddress, Family}];
602        {error, Reason} -> host_lookup_error(Host, Reason)
603    end.
604
605-spec host_lookup_error(_, _) -> no_return().
606host_lookup_error(Host, Reason) ->
607    rabbit_log:error("invalid host ~p - ~p", [Host, Reason]),
608    throw({error, {invalid_host, Host, Reason}}).
609
610resolve_family({_,_,_,_},         auto) -> inet;
611resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6;
612resolve_family(IP,                auto) -> throw({error, {strange_family, IP}});
613resolve_family(_,                 F)    -> F.
614
615%%--------------------------------------------------------------------
616
617%% There are three kinds of machine (for our purposes).
618%%
619%% * Those which treat IPv4 addresses as a special kind of IPv6 address
620%%   ("Single stack")
621%%   - Linux by default, Windows Vista and later
622%%   - We also treat any (hypothetical?) IPv6-only machine the same way
623%% * Those which consider IPv6 and IPv4 to be completely separate things
624%%   ("Dual stack")
625%%   - OpenBSD, Windows XP / 2003, Linux if so configured
626%% * Those which do not support IPv6.
627%%   - Ancient/weird OSes, Linux if so configured
628%%
629%% How to reconfigure Linux to test this:
630%% Single stack (default):
631%% echo 0 > /proc/sys/net/ipv6/bindv6only
632%% Dual stack:
633%% echo 1 > /proc/sys/net/ipv6/bindv6only
634%% IPv4 only:
635%% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then
636%% sudo update-grub && sudo reboot
637%%
638%% This matters in (and only in) the case where the sysadmin (or the
639%% app descriptor) has only supplied a port and we wish to bind to
640%% "all addresses". This means different things depending on whether
641%% we're single or dual stack. On single stack binding to "::"
642%% implicitly includes all IPv4 addresses, and subsequently attempting
643%% to bind to "0.0.0.0" will fail. On dual stack, binding to "::" will
644%% only bind to IPv6 addresses, and we need another listener bound to
645%% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only
646%% want to bind to "0.0.0.0".
647%%
648%% Unfortunately it seems there is no way to detect single vs dual stack
649%% apart from attempting to bind to the port.
650port_to_listeners(Port) ->
651    IPv4 = {"0.0.0.0", Port, inet},
652    IPv6 = {"::",      Port, inet6},
653    case ipv6_status(?FIRST_TEST_BIND_PORT) of
654        single_stack -> [IPv6];
655        ipv6_only    -> [IPv6];
656        dual_stack   -> [IPv6, IPv4];
657        ipv4_only    -> [IPv4]
658    end.
659
660ipv6_status(TestPort) ->
661    IPv4 = [inet,  {ip, {0,0,0,0}}],
662    IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}],
663    case gen_tcp:listen(TestPort, IPv6) of
664        {ok, LSock6} ->
665            case gen_tcp:listen(TestPort, IPv4) of
666                {ok, LSock4} ->
667                    %% Dual stack
668                    gen_tcp:close(LSock6),
669                    gen_tcp:close(LSock4),
670                    dual_stack;
671                %% Checking the error here would only let us
672                %% distinguish single stack IPv6 / IPv4 vs IPv6 only,
673                %% which we figure out below anyway.
674                {error, _} ->
675                    gen_tcp:close(LSock6),
676                    case gen_tcp:listen(TestPort, IPv4) of
677                        %% Single stack
678                        {ok, LSock4}            -> gen_tcp:close(LSock4),
679                                                   single_stack;
680                        %% IPv6-only machine. Welcome to the future.
681                        {error, eafnosupport}   -> ipv6_only; %% Linux
682                        {error, eprotonosupport}-> ipv6_only; %% FreeBSD
683                        %% Dual stack machine with something already
684                        %% on IPv4.
685                        {error, _}              -> ipv6_status(TestPort + 1)
686                    end
687            end;
688        %% IPv4-only machine. Welcome to the 90s.
689        {error, eafnosupport} -> %% Linux
690            ipv4_only;
691        {error, eprotonosupport} -> %% FreeBSD
692            ipv4_only;
693        %% Port in use
694        {error, _} ->
695            ipv6_status(TestPort + 1)
696    end.
697