summaryrefslogtreecommitdiff
path: root/src/ibrowse/ibrowse_socks5.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/ibrowse/ibrowse_socks5.erl')
-rw-r--r--src/ibrowse/ibrowse_socks5.erl109
1 files changed, 109 insertions, 0 deletions
diff --git a/src/ibrowse/ibrowse_socks5.erl b/src/ibrowse/ibrowse_socks5.erl
new file mode 100644
index 000000000..d00df4400
--- /dev/null
+++ b/src/ibrowse/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.