Created
November 22, 2014 23:57
-
-
Save 2garryn/828754779457a526ba2e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -module(nasoc_conn_handler2). | |
| -export([]). | |
| -record(state, {cli_socket :: inet:socket(), | |
| ext_socket :: inet:socket(), | |
| cli_ip_port :: {ip_address(), ip_port()}, | |
| ext_ip_port :: {ip_address(), ip_port()}, | |
| target_ip_port :: {ip_address(), ip_port()}, | |
| parent :: reference()). | |
| start(CliSocket) -> | |
| Self = self(), | |
| Pid = spawn(fun() -> init_loop(Self, CliSocket) end), | |
| Pid ! set_ctrl, | |
| Pid. | |
| init_loop(ParentPid, CliSocket) -> | |
| Ref = erlang:monitor(process, ParentPid), | |
| State = #state{ parent = Ref, cli_socket = CliSocket }, | |
| Fsm = connected, | |
| loop(Fsm, State). | |
| loop(Fsm, State) -> | |
| receive | |
| set_ctrl -> | |
| set_active(State#state.cli_socket, once), | |
| loop(State, Fsm); | |
| Message -> | |
| {NewFsm, NewState, Socket, Reply} = | |
| process(Message, Fsm, State), | |
| calc_do(Fsm, NewFsm, Socket, Reply, NewState) | |
| end. | |
| calc_do(connected, await_cmd, Socket, Reply, State = #state{cli_socket = Socket}) -> | |
| case gen_tcp:send(Socket, Reply) of | |
| ok -> | |
| set_active(Socket, once), | |
| loop(await_cmd, State); | |
| {error, Reason} -> | |
| {error, Reason} | |
| end; | |
| calc_do(await_cmd, await_msg, Socket, Reply, State = #state{cli_socket = Socket}) -> | |
| case gen_tcp:send(Socket, Reply) of | |
| ok -> | |
| set_active(Socket, true), | |
| loop(await_msg, State); | |
| {error, Reason} -> | |
| close(State#state.ext_socket), | |
| close(Socket), | |
| {error, Reason} | |
| end; | |
| calc_do(await_msg, await_msg, Socket, Reply, State) -> | |
| case gen_tcp:send(Socket, Reply) of | |
| ok -> | |
| loop(await_msg, State); | |
| {error, Reason} -> | |
| close(State#state.ext_socket), | |
| close(State#state.cli_socket), | |
| {error, Reason} | |
| end; | |
| calc_do(OldState, close, Socket, undefined, State) -> | |
| close(State#state.ext_socket), | |
| close(State#state.cli_socket). | |
| close(undefined) -> ok; | |
| close(Socket) -> catch gen_tcp:close(Socket). | |
| set_active(Socket, Active) -> | |
| inet:setopts(Socket,[{active, Active}]), | |
| process({tcp, Socket, Data}, Fsm, State = #state{cli_socket = Socket}) -> | |
| client_msg(Fsm, Data, State); | |
| process({tcp, Socket, Data}, Fsm, State = #state{ext_socket = Socket}) -> | |
| target_msg(Fsm, Data, State); | |
| process({tcp_closed, Socket}, Fsm, State = #state{cli_socket = Socket}) -> | |
| client_close(Fsm, State); | |
| process({tcp_closed, Socket}, Fsm, State = #state{ext_socket = Socket}) -> | |
| target_close(Fsm, State); | |
| process({tcp_error, Socket, Reason}, Fsm, State = #state{cli_socket = Socket}) -> | |
| client_error(Fsm, Reason, State); | |
| process({tcp_error, Socket, Reason}, Fsm, State = #state{ext_socket = Socket}) -> | |
| target_error(Fsm, Reason, State). | |
| client_msg(connected, <<?PROTO_VER5:8, MethNumber:8, ReqMethods/binary>>, State) -> | |
| case is_method_supported(?NO_AUTH, MethNumber, ReqMethods) of | |
| true -> {await_cmd, State, State#state.cli_socket, <<?V5, ?NO_AUTH>>}; | |
| false -> {close, State, State#state.cli_socket, <<?V5, ?NO_ACPT_METHODS>>} | |
| end; | |
| client_msg(await_cmd, <<?PROTO_VER5:8, ?CMD_CONNECT:8, ?RSV:8, AType:8, AddrPort/binary>>, State) -> | |
| case parse_atype(AType, AddrPort) of | |
| {ok, Addr, Port} -> | |
| connect_to_target(Addr, Port, AType, State); | |
| {error, BinReply, Reason} -> | |
| Binary = <<?PROTO_VER5, BinReply, ?RSV, AType, AddrPort/binary>>, | |
| {close, State, State#state.cli_socket, Binary} | |
| end; | |
| client_msg(await_cmd, <<?PROTO_VER5:8, UnsupCmd:8, Tail/binary>>, State) -> | |
| Binary = <<?PROTO_VER5, ?CMD_NOT_SUPPORTED, Tail/binary>>, | |
| {close, State, State#state.cli_socket, Binary}; | |
| client_msg(await_msg, Binary, State) -> | |
| {await_msg, State, State#state.ext_socket, Binary}. | |
| target_msg(await_msg, Binary, State) -> | |
| {await_msg, State, State#state.cli_socket, Binary}. | |
| client_close(Fsm, State) -> | |
| {close, State, State#state.ext_socket, undefined}. | |
| target_close(Fsm, State) -> | |
| {close, State, State#state.cli_socket, undefined}. | |
| client_error(Fsm, Reason, State) -> | |
| {close, State, State#state.cli_socket, undefined}. | |
| target_error(Fsm, Reason, State) -> | |
| {close, State, State#state.ext_socket, undefined}. | |
| is_method_supported(Method, MethNumber, ReqMethods) | |
| when MethNumber == byte_size(ReqMethods) -> | |
| MethodsTrunc = erlang:binary_part(ReqMethods, 0, MethNumber), | |
| lists:member(Method, binary_to_list(MethodsTrunc)); | |
| is_method_supported(_Method, _MethNumber, _ReqMethods) -> | |
| false. | |
| parse_atype(?ATYPE_IPV4, <<Ip1, Ip2, Ip3, Ip4, Port:16, _/binary>>) -> | |
| {ok, {Ip1, Ip2, Ip3, Ip4}, Port}; | |
| parse_atype(?ATYTE_DOMAIN, <<DLength:8, DomainPort/binary>>) -> | |
| <<Domain:DLength/binary, Port:16, _/binary>> = DomainPort, | |
| {ok, binary_to_list(Domain), Port}; | |
| parse_atype(AType, Bin) -> | |
| {error, ?ATYPE_NOT_SUPPORTED, {not_supported, AType, Bin}}. | |
| connect_to_target(Addr, Port, AType, State) -> | |
| #state{ext_ip_port = {ExtIp, _ExtPort}} = State, | |
| Result = gen_tcp:connect(Addr, Port, [{ip, ExtIp}, {active, true}, binary]), | |
| process_connect(Result, Addr, Port, AType, State). | |
| process_connect({ok, ExtSocket}, Addr, Port, AType, State) -> | |
| {ok, {ExtIp, ExtPort}} = inet:sockname(ExtSocket) | |
| NewState = State#state{ext_socket = ExtSocket, | |
| ext_ip_port = {ExtIp, ExtPort}}, | |
| BinAddress = address_to_binary(?ATYPE_IPV4, ExtIp), | |
| BinaryReply = <<?PROTO_VER5, ?SUCCESS, ?RSV, AType, BinAddress/binary, ExtPort:16>>, | |
| {await_msg, NewState, State#state.cli_socket, BinaryReply}; | |
| process_connect({error, Error}, Addr, Port, AType, State) -> | |
| BinError = error_to_bin(Error), | |
| BinAddress = address_to_binary(AType, Addr), | |
| BinaryReply = <<?PROTO_VER5, BinErrir, ?RSV, AType, BinAddress/binary, Port:16>>, | |
| {close, State, State#state.cli_socket, BinaryReply}. | |
| error_to_bin(enetunreach) -> ?NETWORK_UNREACH; | |
| error_to_bin(ehostunreach) -> ?HOST_UNREACH; | |
| error_to_bin(econnrefused) -> ?CONN_REFUSED; | |
| error_to_bin(Error) -> ?ANY_OTHER_ERROR. | |
| address_to_binary(?ATYPE_IPV4, {Ip1, Ip2, Ip3, Ip4}) -> | |
| <<Ip1, Ip2, Ip3, Ip4>>; | |
| address_to_binary(?ATYPE_DOMAIN, Domain) -> | |
| Length = length(Domain), | |
| Binary = list_to_binary(Domain), | |
| <<Length, Binary/binary>>. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment