diff options
Diffstat (limited to 'src/ibrowse/ibrowse_socks5.erl')
-rw-r--r-- | src/ibrowse/ibrowse_socks5.erl | 109 |
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. |