blob: d00df440089d600db4365b04ec31c87c5a61f645 [file] [log] [blame]
% 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.