You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/02/12 07:21:22 UTC
[33/33] ibrowse commit: updated refs/heads/import-master to 1167b0e
Support SOCKS5 protocol for replication
Using "socks5" as the protocol in the "proxy" parameter of replication
requests will cause DNS resolution and data transfer to happen via a
SOCKS5 proxy server.
COUCHDB-2025
Project: http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/commit/1167b0e3
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/tree/1167b0e3
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/diff/1167b0e3
Branch: refs/heads/import-master
Commit: 1167b0e3cdea6dc71c415cb40d96a383c1e8f098
Parents: 50ee48d
Author: Robert Newson <rn...@apache.org>
Authored: Sat Jan 4 17:32:00 2014 +0000
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Jan 6 23:34:53 2014 +0000
----------------------------------------------------------------------
Makefile.am | 2 +
ibrowse_http_client.erl | 62 ++++++++++++++++--------
ibrowse_lib.erl | 7 +--
ibrowse_socks5.erl | 109 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 158 insertions(+), 22 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/blob/1167b0e3/Makefile.am
----------------------------------------------------------------------
diff --git a/Makefile.am b/Makefile.am
index 869bd10..7c48169 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,7 @@ ibrowse_file_collection = \
ibrowse_http_client.erl \
ibrowse_lb.erl \
ibrowse_lib.erl \
+ ibrowse_socks5.erl \
ibrowse_sup.erl \
ibrowse_test.erl
@@ -29,6 +30,7 @@ ibrowseebin_make_generated_file_list = \
ibrowse_http_client.beam \
ibrowse_lb.beam \
ibrowse_lib.beam \
+ ibrowse_socks5.beam \
ibrowse_sup.beam \
ibrowse_test.beam
http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/blob/1167b0e3/ibrowse_http_client.erl
----------------------------------------------------------------------
diff --git a/ibrowse_http_client.erl b/ibrowse_http_client.erl
index c01385a..a1cf6eb 100644
--- a/ibrowse_http_client.erl
+++ b/ibrowse_http_client.erl
@@ -39,7 +39,8 @@
-record(state, {host, port, connect_timeout,
inactivity_timer_ref,
- use_proxy = false, proxy_auth_digest,
+ use_http_proxy = false, http_proxy_auth_digest,
+ socks5_host, socks5_port, socks5_user, socks5_password,
ssl_options = [], is_ssl = false, socket,
proxy_tunnel_setup = false,
tunnel_setup_queue = [],
@@ -488,9 +489,21 @@ handle_sock_closed(#state{reply_buffer = Buf, reqs = Reqs, http_status_code = SC
State
end.
-do_connect(Host, Port, Options, #state{is_ssl = true,
- use_proxy = false,
- ssl_options = SSLOptions},
+do_connect(Host, Port, Options, #state{socks5_host = SocksHost}=State, Timeout)
+ when SocksHost /= undefined ->
+ ProxyOptions = [
+ {user, State#state.socks5_user},
+ {password, State#state.socks5_password},
+ {host, SocksHost},
+ {port, State#state.socks5_port},
+ {is_ssl, State#state.is_ssl},
+ {ssl_opts, State#state.ssl_options}],
+ ibrowse_socks5:connect(Host, Port, ProxyOptions,
+ get_sock_options(SocksHost, Options, []),
+ Timeout);
+do_connect(Host, Port, Options, #state{is_ssl = true,
+ use_http_proxy = false,
+ ssl_options = SSLOptions},
Timeout) ->
ssl:connect(Host, Port, get_sock_options(Host, Options, SSLOptions), Timeout);
do_connect(Host, Port, Options, _State, Timeout) ->
@@ -541,7 +554,7 @@ filter_sock_options(Opts) ->
do_send(Req, #state{socket = Sock,
is_ssl = true,
- use_proxy = true,
+ use_http_proxy = true,
proxy_tunnel_setup = Pts}) when Pts /= done -> gen_tcp:send(Sock, Req);
do_send(Req, #state{socket = Sock, is_ssl = true}) -> ssl:send(Sock, Req);
do_send(Req, #state{socket = Sock, is_ssl = false}) -> gen_tcp:send(Sock, Req).
@@ -589,7 +602,7 @@ maybe_chunked_encode(Data, true) ->
do_close(#state{socket = undefined}) -> ok;
do_close(#state{socket = Sock,
is_ssl = true,
- use_proxy = true,
+ use_http_proxy = true,
proxy_tunnel_setup = Pts
}) when Pts /= done -> catch gen_tcp:close(Sock);
do_close(#state{socket = Sock, is_ssl = true}) -> catch ssl:close(Sock);
@@ -602,7 +615,7 @@ active_once(#state{socket = Socket} = State) ->
do_setopts(_Sock, [], _) -> ok;
do_setopts(Sock, Opts, #state{is_ssl = true,
- use_proxy = true,
+ use_http_proxy = true,
proxy_tunnel_setup = Pts}
) when Pts /= done -> inet:setopts(Sock, Opts);
do_setopts(Sock, Opts, #state{is_ssl = true}) -> ssl:setopts(Sock, Opts);
@@ -621,17 +634,28 @@ send_req_1(From,
port = Port} = Url,
Headers, Method, Body, Options, Timeout,
#state{socket = undefined} = State) ->
+ ProxyHost = get_value(proxy_host, Options, false),
+ ProxyProtocol = get_value(proxy_protocol, Options, http),
{Host_1, Port_1, State_1} =
- case get_value(proxy_host, Options, false) of
- false ->
+ case {ProxyHost, ProxyProtocol} of
+ {false, _} ->
{Host, Port, State};
- PHost ->
+ {_, http} ->
ProxyUser = get_value(proxy_user, Options, []),
ProxyPassword = get_value(proxy_password, Options, []),
Digest = http_auth_digest(ProxyUser, ProxyPassword),
- {PHost, get_value(proxy_port, Options, 80),
- State#state{use_proxy = true,
- proxy_auth_digest = Digest}}
+ {ProxyHost, get_value(proxy_port, Options, 80),
+ State#state{use_http_proxy = true,
+ http_proxy_auth_digest = Digest}};
+ {_, socks5} ->
+ ProxyUser = list_to_binary(get_value(proxy_user, Options, [])),
+ ProxyPassword = list_to_binary(get_value(proxy_password, Options, [])),
+ ProxyPort = get_value(proxy_port, Options, 1080),
+ {Host, Port,
+ State#state{socks5_host = ProxyHost,
+ socks5_port = ProxyPort,
+ socks5_user = ProxyUser,
+ socks5_password = ProxyPassword}}
end,
State_2 = check_ssl_options(Options, State_1),
do_trace("Connecting...~n", []),
@@ -662,7 +686,7 @@ send_req_1(From,
Headers, Method, Body, Options, Timeout,
#state{
proxy_tunnel_setup = false,
- use_proxy = true,
+ use_http_proxy = true,
is_ssl = true} = State) ->
Ref = case Timeout of
infinity ->
@@ -850,11 +874,11 @@ add_auth_headers(#url{username = User,
end,
add_proxy_auth_headers(State, Headers_1).
-add_proxy_auth_headers(#state{use_proxy = false}, Headers) ->
+add_proxy_auth_headers(#state{use_http_proxy = false}, Headers) ->
Headers;
-add_proxy_auth_headers(#state{proxy_auth_digest = []}, Headers) ->
+add_proxy_auth_headers(#state{http_proxy_auth_digest = []}, Headers) ->
Headers;
-add_proxy_auth_headers(#state{proxy_auth_digest = Auth_digest}, Headers) ->
+add_proxy_auth_headers(#state{http_proxy_auth_digest = Auth_digest}, Headers) ->
[{"Proxy-Authorization", ["Basic ", Auth_digest]} | Headers].
http_auth_digest([], []) ->
@@ -863,7 +887,7 @@ http_auth_digest(Username, Password) ->
ibrowse_lib:encode_base64(Username ++ [$: | Password]).
make_request(Method, Headers, AbsPath, RelPath, Body, Options,
- #state{use_proxy = UseProxy, is_ssl = Is_ssl}, ReqId) ->
+ #state{use_http_proxy = UseHttpProxy, is_ssl = Is_ssl}, ReqId) ->
HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})),
Fun1 = fun({X, Y}) when is_atom(X) ->
{to_lower(atom_to_list(X)), X, Y};
@@ -906,7 +930,7 @@ make_request(Method, Headers, AbsPath, RelPath, Body, Options,
Headers_2
end,
Headers_4 = cons_headers(Headers_3),
- Uri = case get_value(use_absolute_uri, Options, false) or UseProxy of
+ Uri = case get_value(use_absolute_uri, Options, false) or UseHttpProxy of
true ->
case Is_ssl of
true ->
http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/blob/1167b0e3/ibrowse_lib.erl
----------------------------------------------------------------------
diff --git a/ibrowse_lib.erl b/ibrowse_lib.erl
index 1ce6bd4..7b12cb3 100644
--- a/ibrowse_lib.erl
+++ b/ibrowse_lib.erl
@@ -362,9 +362,10 @@ parse_url([], get_password, Url, TmpAcc) ->
parse_url([], State, Url, TmpAcc) ->
{invalid_uri_2, State, Url, TmpAcc}.
-default_port(http) -> 80;
-default_port(https) -> 443;
-default_port(ftp) -> 21.
+default_port(socks5) -> 1080;
+default_port(http) -> 80;
+default_port(https) -> 443;
+default_port(ftp) -> 21.
printable_date() ->
{{Y,Mo,D},{H, M, S}} = calendar:local_time(),
http://git-wip-us.apache.org/repos/asf/couchdb-ibrowse/blob/1167b0e3/ibrowse_socks5.erl
----------------------------------------------------------------------
diff --git a/ibrowse_socks5.erl b/ibrowse_socks5.erl
new file mode 100644
index 0000000..d00df44
--- /dev/null
+++ b/ibrowse_socks5.erl
@@ -0,0 +1,109 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(ibrowse_socks5).
+
+-define(VERSION, 5).
+-define(CONNECT, 1).
+
+-define(NO_AUTH, 0).
+-define(USERPASS, 2).
+-define(UNACCEPTABLE, 16#FF).
+-define(RESERVED, 0).
+
+-define(ATYP_IPV4, 1).
+-define(ATYP_DOMAINNAME, 3).
+-define(ATYP_IPV6, 4).
+
+-define(SUCCEEDED, 0).
+
+-export([connect/5]).
+
+-import(ibrowse_lib, [get_value/2, get_value/3]).
+
+connect(TargetHost, TargetPort, ProxyOptions, Options, Timeout) ->
+ case gen_tcp:connect(get_value(host, ProxyOptions),
+ get_value(port, ProxyOptions),
+ Options, Timeout) of
+ {ok, Socket} ->
+ case handshake(Socket, Options) of
+ ok ->
+ case connect(TargetHost, TargetPort, Socket) of
+ ok ->
+ maybe_ssl(Socket, ProxyOptions, Timeout);
+ Else ->
+ gen_tcp:close(Socket),
+ Else
+ end;
+ Else ->
+ gen_tcp:close(Socket),
+ Else
+ end;
+ Else ->
+ Else
+ end.
+
+handshake(Socket, ProxyOptions) when is_port(Socket) ->
+ {Handshake, Success} = case get_value(user, ProxyOptions, <<>>) of
+ <<>> ->
+ {<<?VERSION, 1, ?NO_AUTH>>, ?NO_AUTH};
+ User ->
+ Password = get_value(password, ProxyOptions, <<>>),
+ {<<?VERSION, 1, ?USERPASS, (byte_size(User)), User,
+ (byte_size(Password)), Password>>, ?USERPASS}
+ end,
+ ok = gen_tcp:send(Socket, Handshake),
+ case gen_tcp:recv(Socket, 0) of
+ {ok, <<?VERSION, Success>>} ->
+ ok;
+ {ok, <<?VERSION, ?UNACCEPTABLE>>} ->
+ {error, unacceptable};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+connect(Host, Port, Via) when is_list(Host) ->
+ connect(list_to_binary(Host), Port, Via);
+connect(Host, Port, Via) when is_binary(Host), is_integer(Port),
+ is_port(Via) ->
+ ok = gen_tcp:send(Via,
+ <<?VERSION, ?CONNECT, ?RESERVED, ?ATYP_DOMAINNAME,
+ (byte_size(Host)), Host/binary,
+ (Port):16>>),
+ case gen_tcp:recv(Via, 0) of
+ {ok, <<?VERSION, ?SUCCEEDED, ?RESERVED, _/binary>>} ->
+ ok;
+ {ok, <<?VERSION, Rep, ?RESERVED, _/binary>>} ->
+ {error, rep(Rep)};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+maybe_ssl(Socket, ProxyOptions, Timeout) ->
+ IsSsl = get_value(is_ssl, ProxyOptions, false),
+ SslOpts = get_value(ssl_opts, ProxyOptions, []),
+ case IsSsl of
+ false ->
+ {ok, Socket};
+ true ->
+ ssl:connect(Socket, SslOpts, Timeout)
+ end.
+
+rep(0) -> succeeded;
+rep(1) -> server_fail;
+rep(2) -> disallowed_by_ruleset;
+rep(3) -> network_unreachable;
+rep(4) -> host_unreachable;
+rep(5) -> connection_refused;
+rep(6) -> ttl_expired;
+rep(7) -> command_not_supported;
+rep(8) -> address_type_not_supported.