1: -module(ejabberd_listener_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: 5: -include_lib("eunit/include/eunit.hrl"). 6: 7: -import(ejabberd_helper, [copy/2, 8: data/2]). 9: 10: -define(DEFAULT_PORTS, [5222, 5280, 5269]). 11: 12: all() -> 13: [tcp_socket_is_started_with_default_backlog, 14: tcp_socket_is_started_with_options, 15: tcp_socket_supports_proxy_protocol, 16: tcp_socket_has_connection_details, 17: tcp_socket_supports_proxy_protocol, 18: udp_socket_is_started_with_defaults, 19: tcp_start_stop_reload 20: ]. 21: 22: init_per_testcase(_Case, Config) -> 23: meck:new([gen_udp, gen_tcp], [unstick, passthrough]), 24: meck:expect(gen_udp, open, 25: fun(Port, Opts) -> meck:passthrough([Port, Opts]) end), 26: meck:expect(gen_tcp, listen, 27: fun(Port, Opts) -> meck:passthrough([Port, Opts]) end), 28: Config. 29: 30: end_per_testcase(_Case, Config) -> 31: meck:unload(), 32: Config. 33: 34: init_per_suite(C) -> 35: C. 36: 37: end_per_suite(_C) -> 38: mnesia:stop(), 39: mnesia:delete_schema([node()]). 40: 41: tcp_socket_is_started_with_default_backlog(_C) -> 42: {ok, _Pid} = listener_started(#{}), 43: [{_Pid, {gen_tcp, listen, [_, Opts]}, _Result}] = meck:history(gen_tcp), 44: 100 = proplists:get_value(backlog, Opts). 45: 46: tcp_socket_is_started_with_options(_C) -> 47: {ok, _Pid} = listener_started(#{backlog => 50}), 48: [{_Pid, {gen_tcp, listen, [_, Opts]}, _Result}] = meck:history(gen_tcp), 49: 50 = proplists:get_value(backlog, Opts). 50: 51: tcp_socket_has_connection_details(_C) -> 52: {ok, _Pid} = listener_started(#{}), 53: {Port, _, _} = tcp_port_ip(), 54: 55: meck:new(ejabberd_socket), 56: TestPid = self(), 57: meck:expect(ejabberd_socket, start, 58: fun(_Module, _SockMode, Socket, Opts) -> 59: TestPid ! {socket_started, Socket, Opts}, 60: ok 61: end), 62: 63: {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), 64: {ok, SrcPort} = inet:port(Socket), 65: 66: receive 67: {socket_started, _Socket, Opts} -> 68: ConnectionDetails = proplists:get_value(connection_details, Opts), 69: ?assertEqual(#{proxy => false, 70: src_address => {127, 0, 0, 1}, 71: src_port => SrcPort, 72: dest_address => {127, 0, 0, 1}, 73: dest_port => Port}, ConnectionDetails) 74: after 75: 5000 -> 76: ct:fail(timeout_waiting_for_tcp) 77: end. 78: 79: tcp_socket_supports_proxy_protocol(_C) -> 80: {ok, _Pid} = listener_started(#{proxy_protocol => true}), 81: 82: CommonProxyInfo = #{src_address => {1, 2, 3, 4}, 83: src_port => 444, 84: dest_address => {192, 168, 0, 1}, 85: dest_port => 443, 86: version => 2}, 87: RanchProxyInfo = CommonProxyInfo#{command => proxy, 88: transport_family => ipv4, 89: transport_protocol => stream}, 90: {Port, _, _} = tcp_port_ip(), 91: 92: meck:new(ejabberd_socket), 93: TestPid = self(), 94: meck:expect(ejabberd_socket, start, 95: fun(_Module, _SockMode, Socket, Opts) -> 96: TestPid ! {socket_started, Socket, Opts}, 97: ok 98: end), 99: 100: {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), 101: ok = gen_tcp:send(Socket, [ranch_proxy_header:header(RanchProxyInfo)]), 102: 103: receive 104: {socket_started, _Socket, Opts} -> 105: ConnectionDetails = proplists:get_value(connection_details, Opts), 106: ?assertEqual(CommonProxyInfo#{proxy => true}, ConnectionDetails) 107: after 108: 5000 -> 109: ct:fail(timeout_waiting_for_tcp_with_proxy_protocol) 110: end. 111: 112: udp_socket_is_started_with_defaults(_C) -> 113: {ok, _Pid} = receiver_started(#{}), 114: 115: [{_Pid, {gen_udp, open, [_, Opts]}, _Result}] = meck:history(gen_udp), 116: 117: {0,0,0,0} = proplists:get_value(ip, Opts). 118: 119: listener_started(Opts) -> 120: mim_ct_sup:start_link(ejabberd_sup), 121: ejabberd_listener:start_link(), 122: ejabberd_listener:start_listener(maps:merge(listener_opts(tcp), Opts)). 123: 124: receiver_started(Opts) -> 125: mim_ct_sup:start_link(ejabberd_sup), 126: ejabberd_listener:start_link(), 127: ets:new(listen_sockets, [named_table, public]), 128: ejabberd_listener:start_listener(maps:merge(listener_opts(udp), Opts)). 129: 130: udp_port_ip() -> 131: {1805, {0,0,0,0}, udp}. 132: 133: tcp_port_ip() -> 134: {1805, {0,0,0,0}, tcp}. 135: 136: listener_opts(Proto) -> 137: #{module => ?MODULE, 138: ip_address => "0", 139: ip_tuple => {0, 0, 0, 0}, 140: ip_version => 4, 141: port => 1805, 142: proto => Proto}. 143: 144: tcp_start_stop_reload(C) -> 145: %% start server 146: copy(data(C, "mongooseim.basic.toml"), data(C, "mongooseim.toml")), 147: ejabberd_helper:start_ejabberd_with_config(C, "mongooseim.toml"), 148: ?assert(lists:keymember(mongooseim, 1, application:which_applications())), 149: %% make sure all ports are open 150: lists:map(fun assert_open/1, ?DEFAULT_PORTS), 151: %% stop listeners, now they should be closed 152: ejabberd_listener:stop_listeners(), 153: lists:map(fun assert_closed/1, ?DEFAULT_PORTS), 154: %% and start them all again 155: ejabberd_listener:start_listeners(), 156: lists:map(fun assert_open/1, ?DEFAULT_PORTS), 157: 158: %% we want to make sure that connection to an unchanged port survives reload 159: UnchPort = 5222, 160: {ok, Sock} = gen_tcp:connect("localhost", UnchPort,[{active, false}, {packet, 2}]), 161: assert_connected(Sock, UnchPort), 162: 163: %% and that to the changed port does too (this is current implementation) 164: ChgPort = 5269, 165: {ok, Sock2} = gen_tcp:connect("localhost", ChgPort,[{active, false}, {packet, 2}]), 166: assert_connected(Sock2, ChgPort), 167: 168: ok = ejabberd_helper:stop_ejabberd(), 169: ok. 170: 171: assert_open(PortNo) -> 172: case gen_tcp:connect("localhost", PortNo, [{active, false}, {packet, 2}]) of 173: {ok, Socket} -> 174: gen_tcp:close(Socket), 175: ok; 176: E -> 177: ct:fail("Failed: port ~p is closed, should be open; error was: ~p", [PortNo, E]) 178: end . 179: 180: assert_closed(PortNo) -> 181: F = fun() -> 182: gen_tcp:connect("localhost", PortNo, [{active, false}, {packet, 2}]) 183: end, 184: async_helper:wait_until(F, {error, econnrefused}). 185: 186: 187: assert_connected(Sock, Port) -> 188: case gen_tcp:recv(Sock, 0, 500) of 189: {error, timeout} -> 190: ok; 191: Else -> 192: ct:fail("Failed: connection to ~p is broken, error was: ~p", [Port, Else]) 193: end. 194: 195: socket_type() -> 196: xml_stream.