Created
November 22, 2014 23:57
-
-
Save 2garryn/828754779457a526ba2e to your computer and use it in GitHub Desktop.
This file contains 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