From 4664da8703d4a1d5225fd71e1fc3164f9441ff36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Fri, 20 Sep 2019 17:14:35 +0200 Subject: Add more tests with two Socks5 proxies We now properly support TCP across two TLS proxies, and TLS across two TCP/TLS proxies. --- src/gun.erl | 27 ++++++++++++++------------- src/gun_http.erl | 2 +- test/socks_SUITE.erl | 46 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/gun.erl b/src/gun.erl index 93a8d40..c1a8be5 100644 --- a/src/gun.erl +++ b/src/gun.erl @@ -892,13 +892,14 @@ connecting(_, {retries, Retries, LookupInfo}, State=#state{opts=Opts, EvHandlerState1 = EvHandler:connect_start(ConnectEvent, EvHandlerState0), case gun_tcp:connect(LookupInfo, ConnectTimeout) of {ok, Socket} when Transport =:= gun_tcp -> - Protocol = case maps:get(protocols, Opts, [http]) of - [{P, _}] -> P; - [P] -> P + [Protocol] = maps:get(protocols, Opts, [http]), + ProtocolName = case Protocol of + {P, _} -> P; + P -> P end, EvHandlerState = EvHandler:connect_end(ConnectEvent#{ socket => Socket, - protocol => Protocol + protocol => ProtocolName }, EvHandlerState1), {next_state, connected, State#state{event_handler_state=EvHandlerState}, {next_event, internal, {connected, Socket, Protocol}}}; @@ -943,7 +944,7 @@ ensure_alpn(Protocols0, TransOpts) -> %% Normal TLS handshake. tls_handshake(internal, {tls_handshake, HandshakeEvent, Protocols}, - State0=#state{socket=Socket, transport=gun_tcp, protocol=CurrentProtocol}) -> + State0=#state{socket=Socket, transport=gun_tcp}) -> case normal_tls_handshake(Socket, State0, HandshakeEvent, Protocols) of {ok, TLSSocket, NewProtocol, State} -> commands([ @@ -971,9 +972,7 @@ tls_handshake(internal, {tls_handshake, %% When using gun_tls_proxy we need a separate message to know whether %% the handshake succeeded and whether we need to switch to a different protocol. tls_handshake(info, {gun_tls_proxy, Socket, {ok, Negotiated}, {HandshakeEvent, Protocols}}, - State0=#state{socket=Socket, transport=Transport, - protocol=CurrentProtocol, protocol_state=ProtoState0, - event_handler=EvHandler, event_handler_state=EvHandlerState0}) -> + State0=#state{socket=Socket, event_handler=EvHandler, event_handler_state=EvHandlerState0}) -> NewProtocol = protocol_negotiated(Negotiated, Protocols), EvHandlerState = EvHandler:tls_handshake_end(HandshakeEvent#{ socket => Socket, @@ -1014,7 +1013,7 @@ normal_tls_handshake(Socket, State=#state{event_handler=EvHandler, event_handler protocol_negotiated({ok, <<"h2">>}, _) -> http2; protocol_negotiated({ok, <<"http/1.1">>}, _) -> http; -protocol_negotiated({error, protocol_not_negotiated}, [{socks, _}]) -> socks; +protocol_negotiated({error, protocol_not_negotiated}, [Protocol]) -> Protocol; protocol_negotiated({error, protocol_not_negotiated}, _) -> http. connected_no_input(Type, Event, State) -> @@ -1022,11 +1021,13 @@ connected_no_input(Type, Event, State) -> connected(internal, {connected, Socket, Protocol0}, State0=#state{owner=Owner, opts=Opts, transport=Transport}) -> - Protocol = protocol_handler(Protocol0), %% Protocol options may have been given along the protocol name. - ProtoOpts = case lists:keyfind(Protocol0, 1, maps:get(protocols, Opts, [http])) of - {_, PO} -> PO; - false -> maps:get(Protocol:opts_name(), Opts, #{}) + {Protocol, ProtoOpts} = case Protocol0 of + {P, PO} -> + {protocol_handler(P), PO}; + _ -> + P = protocol_handler(Protocol0), + {P, maps:get(P:opts_name(), Opts, #{})} end, {StateName, ProtoState} = Protocol:init(Owner, Socket, Transport, ProtoOpts), Owner ! {gun_up, self(), Protocol:name()}, diff --git a/src/gun_http.erl b/src/gun_http.erl index 44575bf..ce174ce 100644 --- a/src/gun_http.erl +++ b/src/gun_http.erl @@ -298,7 +298,7 @@ handle_head(Data, State=#http_state{version=ClientVersion, content_handlers=Hand }, EvHandlerState0), %% We expect there to be no additional data after the CONNECT response. <<>> = Rest2, - State2 = end_stream(State#http_state{streams=[Stream|Tail]}), + _ = end_stream(State#http_state{streams=[Stream|Tail]}), NewHost = maps:get(host, Destination), NewPort = maps:get(port, Destination), Protocols = maps:get(protocols, Destination, [http]), diff --git a/test/socks_SUITE.erl b/test/socks_SUITE.erl index e565017..80a2796 100644 --- a/test/socks_SUITE.erl +++ b/test/socks_SUITE.erl @@ -258,21 +258,45 @@ do_socks5(OriginScheme, OriginTransport, OriginProtocol, ProxyTransport, SocksAu }]} = gun:info(ConnPid), gun:close(ConnPid). -socks5_through_multiple_proxies(_) -> +socks5_tcp_through_multiple_tcp_proxies(_) -> doc("Gun can be used to establish a TCP connection " "to an HTTP/1.1 server via a tunnel going through " - "two separate Socks5 proxies."), - {ok, OriginPid, OriginPort} = init_origin(tcp, http), - {ok, Proxy1Pid, Proxy1Port} = do_proxy_start(tcp, none), - {ok, Proxy2Pid, Proxy2Port} = do_proxy_start(tcp, none), + "two separate TCP Socks5 proxies."), + do_socks5_through_multiple_proxies(tcp, tcp). + +socks5_tcp_through_multiple_tls_proxies(_) -> + doc("Gun can be used to establish a TCP connection " + "to an HTTP/1.1 server via a tunnel going through " + "two separate TLS Socks5 proxies."), + do_socks5_through_multiple_proxies(tcp, tls). + +socks5_tls_through_multiple_tcp_proxies(_) -> + doc("Gun can be used to establish a TLS connection " + "to an HTTP/1.1 server via a tunnel going through " + "two separate TCP Socks5 proxies."), + do_socks5_through_multiple_proxies(tcp, tcp). + +socks5_tls_through_multiple_tls_proxies(_) -> + doc("Gun can be used to establish a TLS connection " + "to an HTTP/1.1 server via a tunnel going through " + "two separate TLS Socks5 proxies."), + do_socks5_through_multiple_proxies(tcp, tls). + +do_socks5_through_multiple_proxies(OriginTransport, ProxyTransport) -> + {ok, OriginPid, OriginPort} = init_origin(OriginTransport, http), + {ok, Proxy1Pid, Proxy1Port} = do_proxy_start(ProxyTransport, none), + {ok, Proxy2Pid, Proxy2Port} = do_proxy_start(ProxyTransport, none), Authority = iolist_to_binary(["localhost:", integer_to_binary(OriginPort)]), {ok, ConnPid} = gun:open("localhost", Proxy1Port, #{ + transport => ProxyTransport, protocols => [{socks, #{ host => "localhost", port => Proxy2Port, + transport => ProxyTransport, protocols => [{socks, #{ host => "localhost", - port => OriginPort + port => OriginPort, + transport => OriginTransport }}] }}] }), @@ -291,8 +315,12 @@ socks5_through_multiple_proxies(_) -> Data = receive_from(OriginPid), Lines = binary:split(Data, <<"\r\n">>, [global]), [<<"host: ", Authority/bits>>] = [L || <<"host: ", _/bits>> = L <- Lines], + Proxy2Transport = case ProxyTransport of + tcp -> tcp; + tls -> tls_proxy + end, #{ - transport := tcp, + transport := OriginTransport, protocol := http, origin_scheme := <<"http">>, origin_host := "localhost", @@ -301,13 +329,13 @@ socks5_through_multiple_proxies(_) -> type := socks5, host := "localhost", port := Proxy1Port, - transport := tcp, + transport := ProxyTransport, protocol := socks }, #{ type := socks5, host := "localhost", port := Proxy2Port, - transport := tcp, + transport := Proxy2Transport, protocol := socks }]} = gun:info(ConnPid), gun:close(ConnPid). -- cgit v1.2.3