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