1%%%------------------------------------------------------------------- 2%%% @author Chandru Mullaparthi <> 3%%% @copyright (C) 2016, Chandru Mullaparthi 4%%% @doc 5%%% 6%%% @end 7%%% Created : 19 Apr 2016 by Chandru Mullaparthi <> 8%%%------------------------------------------------------------------- 9-module(ibrowse_socks_server). 10 11-behaviour(gen_server). 12 13%% API 14-export([start/2, stop/1]). 15 16%% gen_server callbacks 17-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 18 terminate/2, code_change/3]). 19 20-define(SERVER, ?MODULE). 21 22-record(state, {listen_port, listen_socket, auth_method}). 23 24-define(NO_AUTH, 0). 25-define(AUTH_USER_PW, 2). 26 27%%%=================================================================== 28%%% API 29%%%=================================================================== 30 31start(Port, Auth_method) -> 32 Name = make_proc_name(Port), 33 gen_server:start({local, Name}, ?MODULE, [Port, Auth_method], []). 34 35stop(Port) -> 36 make_proc_name(Port) ! stop. 37 38make_proc_name(Port) -> 39 list_to_atom("ibrowse_socks_server_" ++ integer_to_list(Port)). 40 41%%%=================================================================== 42%%% gen_server callbacks 43%%%=================================================================== 44 45init([Port, Auth_method]) -> 46 State = #state{listen_port = Port, auth_method = Auth_method}, 47 {ok, Sock} = gen_tcp:listen(State#state.listen_port, [{active, false}, binary, {reuseaddr, true}]), 48 self() ! accept_connection, 49 process_flag(trap_exit, true), 50 {ok, State#state{listen_socket = Sock}}. 51 52handle_call(_Request, _From, State) -> 53 Reply = ok, 54 {reply, Reply, State}. 55 56handle_cast(_Msg, State) -> 57 {noreply, State}. 58 59handle_info(accept_connection, State) -> 60 case gen_tcp:accept(State#state.listen_socket, 1000) of 61 {error, timeout} -> 62 self() ! accept_connection, 63 {noreply, State}; 64 {ok, Socket} -> 65 Pid = proc_lib:spawn_link(fun() -> 66 socks_server_loop(Socket, State#state.auth_method) 67 end), 68 gen_tcp:controlling_process(Socket, Pid), 69 Pid ! ready, 70 self() ! accept_connection, 71 {noreply, State}; 72 _Err -> 73 {stop, normal, State} 74 end; 75 76handle_info(stop, State) -> 77 {stop, normal, State}; 78 79handle_info(_Info, State) -> 80 {noreply, State}. 81 82terminate(_Reason, _State) -> 83 ok. 84 85code_change(_OldVsn, State, _Extra) -> 86 {ok, State}. 87 88%%%=================================================================== 89%%% Internal functions 90%%%=================================================================== 91socks_server_loop(In_socket, Auth_method) -> 92 receive 93 ready -> 94 socks_server_loop(In_socket, Auth_method, <<>>, unauth) 95 end. 96 97socks_server_loop(In_socket, Auth_method, Acc, unauth) -> 98 inet:setopts(In_socket, [{active, once}]), 99 receive 100 {tcp, In_socket, Data} -> 101 Acc_1 = list_to_binary([Acc, Data]), 102 case Acc_1 of 103 <<5, ?NO_AUTH>> when Auth_method == ?NO_AUTH -> 104 ok = gen_tcp:send(In_socket, <<5, ?NO_AUTH>>), 105 socks_server_loop(In_socket, Auth_method, <<>>, auth_done); 106 <<5, Num_auth_methods, Auth_methods:Num_auth_methods/binary>> -> 107 case lists:member(Auth_method, binary_to_list(Auth_methods)) of 108 true -> 109 ok = gen_tcp:send(In_socket, <<5, Auth_method>>), 110 Conn_state = case Auth_method of 111 ?NO_AUTH -> auth_done; 112 _ -> auth_pending 113 end, 114 socks_server_loop(In_socket, Auth_method, <<>>, Conn_state); 115 false -> 116 ok = gen_tcp:send(In_socket, <<5, 16#ff>>), 117 gen_tcp:close(In_socket) 118 end; 119 _ -> 120 ok = gen_tcp:send(In_socket, <<5, 0>>), 121 gen_tcp:close(In_socket) 122 end; 123 {tcp_closed, In_socket} -> 124 ok; 125 {tcp_error, In_socket, _Rsn} -> 126 ok 127 end; 128socks_server_loop(In_socket, Auth_method, Acc, auth_pending) -> 129 inet:setopts(In_socket, [{active, once}]), 130 receive 131 {tcp, In_socket, Data} -> 132 Acc_1 = list_to_binary([Acc, Data]), 133 case Acc_1 of 134 <<1, U_len, Username:U_len/binary, P_len, Password:P_len/binary>> -> 135 case check_user_pw(Username, Password) of 136 ok -> 137 ok = gen_tcp:send(In_socket, <<1, 0>>), 138 socks_server_loop(In_socket, Auth_method, <<>>, auth_done); 139 notok -> 140 ok = gen_tcp:send(In_socket, <<1, 1>>), 141 gen_tcp:close(In_socket) 142 end; 143 _ -> 144 socks_server_loop(In_socket, Auth_method, Acc_1, auth_pending) 145 end; 146 {tcp_closed, In_socket} -> 147 ok; 148 {tcp_error, In_socket, _Rsn} -> 149 ok 150 end; 151socks_server_loop(In_socket, Auth_method, Acc, auth_done) -> 152 inet:setopts(In_socket, [{active, once}]), 153 receive 154 {tcp, In_socket, Data} -> 155 Acc_1 = list_to_binary([Acc, Data]), 156 case Acc_1 of 157 <<5, 1, 0, Addr_type, Dest_ip:4/binary, Dest_port:16>> when Addr_type == 1-> 158 handle_connect(In_socket, Addr_type, Dest_ip, Dest_port); 159 <<5, 1, 0, Addr_type, Dest_len, Dest_hostname:Dest_len/binary, Dest_port:16>> when Addr_type == 3 -> 160 handle_connect(In_socket, Addr_type, Dest_hostname, Dest_port); 161 <<5, 1, 0, Addr_type, Dest_ip:16/binary, Dest_port:16>> when Addr_type == 4-> 162 handle_connect(In_socket, Addr_type, Dest_ip, Dest_port); 163 _ -> 164 socks_server_loop(In_socket, Auth_method, Acc_1, auth_done) 165 end; 166 {tcp_closed, In_socket} -> 167 ok; 168 {tcp_error, In_socket, _Rsn} -> 169 ok 170 end. 171 172handle_connect(In_socket, Addr_type, Dest_host, Dest_port) -> 173 Dest_host_1 = case Addr_type of 174 1 -> 175 list_to_tuple(binary_to_list(Dest_host)); 176 3 -> 177 binary_to_list(Dest_host); 178 4 -> 179 list_to_tuple(binary_to_list(Dest_host)) 180 end, 181 case gen_tcp:connect(Dest_host_1, Dest_port, [binary, {active, once}]) of 182 {ok, Out_socket} -> 183 Addr = case Addr_type of 184 1 -> 185 <<Dest_host/binary, Dest_port:16>>; 186 3 -> 187 Len = size(Dest_host), 188 <<Len, Dest_host/binary, Dest_port:16>>; 189 4 -> 190 <<Dest_host/binary, Dest_port:16>> 191 end, 192 ok = gen_tcp:send(In_socket, <<5, 0, 0, Addr_type, Addr/binary>>), 193 inet:setopts(In_socket, [{active, once}]), 194 inet:setopts(Out_socket, [{active, once}]), 195 connected_loop(In_socket, Out_socket); 196 _Err -> 197 ok = gen_tcp:send(<<5, 1>>), 198 gen_tcp:close(In_socket) 199 end. 200 201check_user_pw(<<"user">>, <<"password">>) -> 202 ok; 203check_user_pw(_, _) -> 204 notok. 205 206connected_loop(In_socket, Out_socket) -> 207 receive 208 {tcp, In_socket, Data} -> 209 inet:setopts(In_socket, [{active, once}]), 210 ok = gen_tcp:send(Out_socket, Data), 211 connected_loop(In_socket, Out_socket); 212 {tcp, Out_socket, Data} -> 213 inet:setopts(Out_socket, [{active, once}]), 214 ok = gen_tcp:send(In_socket, Data), 215 connected_loop(In_socket, Out_socket); 216 _ -> 217 ok 218 end. 219