|
|
- %%%-------------------------------------------------------------------
- %%% @author Chandru Mullaparthi <>
- %%% @copyright (C) 2016, Chandru Mullaparthi
- %%% @doc
- %%%
- %%% @end
- %%% Created : 19 Apr 2016 by Chandru Mullaparthi <>
- %%%-------------------------------------------------------------------
- -module(ibrowse_socks_server).
-
- -behaviour(gen_server).
-
- %% API
- -export([start/2, stop/1]).
-
- %% gen_server callbacks
- -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
- -define(SERVER, ?MODULE).
-
- -record(state, {listen_port, listen_socket, auth_method}).
-
- -define(NO_AUTH, 0).
- -define(AUTH_USER_PW, 2).
-
- %%%===================================================================
- %%% API
- %%%===================================================================
-
- start(Port, Auth_method) ->
- Name = make_proc_name(Port),
- gen_server:start({local, Name}, ?MODULE, [Port, Auth_method], []).
-
- stop(Port) ->
- make_proc_name(Port) ! stop.
-
- make_proc_name(Port) ->
- list_to_atom("ibrowse_socks_server_" ++ integer_to_list(Port)).
-
- %%%===================================================================
- %%% gen_server callbacks
- %%%===================================================================
-
- init([Port, Auth_method]) ->
- State = #state{listen_port = Port, auth_method = Auth_method},
- {ok, Sock} = gen_tcp:listen(State#state.listen_port, [{active, false}, binary, {reuseaddr, true}]),
- self() ! accept_connection,
- process_flag(trap_exit, true),
- {ok, State#state{listen_socket = Sock}}.
-
- handle_call(_Request, _From, State) ->
- Reply = ok,
- {reply, Reply, State}.
-
- handle_cast(_Msg, State) ->
- {noreply, State}.
-
- handle_info(accept_connection, State) ->
- case gen_tcp:accept(State#state.listen_socket, 1000) of
- {error, timeout} ->
- self() ! accept_connection,
- {noreply, State};
- {ok, Socket} ->
- Pid = proc_lib:spawn_link(fun() ->
- socks_server_loop(Socket, State#state.auth_method)
- end),
- gen_tcp:controlling_process(Socket, Pid),
- Pid ! ready,
- self() ! accept_connection,
- {noreply, State};
- _Err ->
- {stop, normal, State}
- end;
-
- handle_info(stop, State) ->
- {stop, normal, State};
-
- handle_info(_Info, State) ->
- {noreply, State}.
-
- terminate(_Reason, _State) ->
- ok.
-
- code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
- %%%===================================================================
- %%% Internal functions
- %%%===================================================================
- socks_server_loop(In_socket, Auth_method) ->
- receive
- ready ->
- socks_server_loop(In_socket, Auth_method, <<>>, unauth)
- end.
-
- socks_server_loop(In_socket, Auth_method, Acc, unauth) ->
- inet:setopts(In_socket, [{active, once}]),
- receive
- {tcp, In_socket, Data} ->
- Acc_1 = list_to_binary([Acc, Data]),
- case Acc_1 of
- <<5, ?NO_AUTH>> when Auth_method == ?NO_AUTH ->
- ok = gen_tcp:send(In_socket, <<5, ?NO_AUTH>>),
- socks_server_loop(In_socket, Auth_method, <<>>, auth_done);
- <<5, Num_auth_methods, Auth_methods:Num_auth_methods/binary>> ->
- case lists:member(Auth_method, binary_to_list(Auth_methods)) of
- true ->
- ok = gen_tcp:send(In_socket, <<5, Auth_method>>),
- Conn_state = case Auth_method of
- ?NO_AUTH -> auth_done;
- _ -> auth_pending
- end,
- socks_server_loop(In_socket, Auth_method, <<>>, Conn_state);
- false ->
- ok = gen_tcp:send(In_socket, <<5, 16#ff>>),
- gen_tcp:close(In_socket)
- end;
- _ ->
- ok = gen_tcp:send(In_socket, <<5, 0>>),
- gen_tcp:close(In_socket)
- end;
- {tcp_closed, In_socket} ->
- ok;
- {tcp_error, In_socket, _Rsn} ->
- ok
- end;
- socks_server_loop(In_socket, Auth_method, Acc, auth_pending) ->
- inet:setopts(In_socket, [{active, once}]),
- receive
- {tcp, In_socket, Data} ->
- Acc_1 = list_to_binary([Acc, Data]),
- case Acc_1 of
- <<1, U_len, Username:U_len/binary, P_len, Password:P_len/binary>> ->
- case check_user_pw(Username, Password) of
- ok ->
- ok = gen_tcp:send(In_socket, <<1, 0>>),
- socks_server_loop(In_socket, Auth_method, <<>>, auth_done);
- notok ->
- ok = gen_tcp:send(In_socket, <<1, 1>>),
- gen_tcp:close(In_socket)
- end;
- _ ->
- socks_server_loop(In_socket, Auth_method, Acc_1, auth_pending)
- end;
- {tcp_closed, In_socket} ->
- ok;
- {tcp_error, In_socket, _Rsn} ->
- ok
- end;
- socks_server_loop(In_socket, Auth_method, Acc, auth_done) ->
- inet:setopts(In_socket, [{active, once}]),
- receive
- {tcp, In_socket, Data} ->
- Acc_1 = list_to_binary([Acc, Data]),
- case Acc_1 of
- <<5, 1, 0, Addr_type, Dest_ip:4/binary, Dest_port:16>> when Addr_type == 1->
- handle_connect(In_socket, Addr_type, Dest_ip, Dest_port);
- <<5, 1, 0, Addr_type, Dest_len, Dest_hostname:Dest_len/binary, Dest_port:16>> when Addr_type == 3 ->
- handle_connect(In_socket, Addr_type, Dest_hostname, Dest_port);
- <<5, 1, 0, Addr_type, Dest_ip:16/binary, Dest_port:16>> when Addr_type == 4->
- handle_connect(In_socket, Addr_type, Dest_ip, Dest_port);
- _ ->
- socks_server_loop(In_socket, Auth_method, Acc_1, auth_done)
- end;
- {tcp_closed, In_socket} ->
- ok;
- {tcp_error, In_socket, _Rsn} ->
- ok
- end.
-
- handle_connect(In_socket, Addr_type, Dest_host, Dest_port) ->
- Dest_host_1 = case Addr_type of
- 1 ->
- list_to_tuple(binary_to_list(Dest_host));
- 3 ->
- binary_to_list(Dest_host);
- 4 ->
- list_to_tuple(binary_to_list(Dest_host))
- end,
- case gen_tcp:connect(Dest_host_1, Dest_port, [binary, {active, once}]) of
- {ok, Out_socket} ->
- Addr = case Addr_type of
- 1 ->
- <<Dest_host/binary, Dest_port:16>>;
- 3 ->
- Len = size(Dest_host),
- <<Len, Dest_host/binary, Dest_port:16>>;
- 4 ->
- <<Dest_host/binary, Dest_port:16>>
- end,
- ok = gen_tcp:send(In_socket, <<5, 0, 0, Addr_type, Addr/binary>>),
- inet:setopts(In_socket, [{active, once}]),
- inet:setopts(Out_socket, [{active, once}]),
- connected_loop(In_socket, Out_socket);
- _Err ->
- ok = gen_tcp:send(<<5, 1>>),
- gen_tcp:close(In_socket)
- end.
-
- check_user_pw(<<"user">>, <<"password">>) ->
- ok;
- check_user_pw(_, _) ->
- notok.
-
- connected_loop(In_socket, Out_socket) ->
- receive
- {tcp, In_socket, Data} ->
- inet:setopts(In_socket, [{active, once}]),
- ok = gen_tcp:send(Out_socket, Data),
- connected_loop(In_socket, Out_socket);
- {tcp, Out_socket, Data} ->
- inet:setopts(Out_socket, [{active, once}]),
- ok = gen_tcp:send(In_socket, Data),
- connected_loop(In_socket, Out_socket);
- _ ->
- ok
- end.
|