Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Move seds to a standalone repository

  • Loading branch information...
commit 16e3252dc7bf47e96de8bf62c8dd37ca222142ab 1 parent 93660b1
Michael Santos authored
6 seds/Emakefile
View
@@ -1,6 +0,0 @@
-{["src/*"],
- [{i, "include"},
- {outdir, "ebin"},
- debug_info]
-}.
-
15 seds/Makefile
View
@@ -1,15 +0,0 @@
-
-ERL=erl
-
-all: dir erl
-
-dir:
- -@mkdir -p ebin deps
-
-erl:
- @$(ERL) -noinput +B \
- -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.'
-
-clean:
- @rm -fv ebin/*.beam
-
56 seds/include/seds.hrl
View
@@ -1,56 +0,0 @@
-%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
-%% All rights reserved.
-%%
-%% Redistribution and use in source and binary forms, with or without
-%% modification, are permitted provided that the following conditions
-%% are met:
-%%
-%% Redistributions of source code must retain the above copyright
-%% notice, this list of conditions and the following disclaimer.
-%%
-%% Redistributions in binary form must reproduce the above copyright
-%% notice, this list of conditions and the following disclaimer in the
-%% documentation and/or other materials provided with the distribution.
-%%
-%% Neither the name of the author nor the names of its contributors
-%% may be used to endorse or promote products derived from this software
-%% without specific prior written permission.
-%%
-%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-%% POSSIBILITY OF SUCH DAMAGE.
-
--define(DNS_PORT, 53).
--define(PROXY_TIMEOUT, 5 * 60 * 1000).
-
--define(CFG, "seds.cfg").
-
--define(MAXLABEL, 63). % 1 byte for length of label
-
--record(seds, {
- q, % decoded DNS query
- type, % 'up' or 'down'
- id = 0, % 2 or 4 byte session ID
- forward, % tuple describing destination ip/port
- sum = 0, % byte count
- domain = [], % domain names
- data = [] % base64 encoded data
- }).
-
--record(config, {
- acf = false, % allow client forwarding
- acl = [], % forward IP blacklist
- acl_port = [22], % forward IP blacklist
- f, % forwarders map
- d = [] % domains
- }).
-
2  seds/priv/seds.cfg.dist
View
@@ -1,2 +0,0 @@
-{forward, [ {{127,0,0,1}, 22} ]}.
-{domains, []}.
101 seds/src/base32.erl
View
@@ -1,101 +0,0 @@
-%% Taken from eadc-hub (http://github.com/JLarky/eadc-hub)
-%% http://github.com/JLarky/eadc-hub/blob/master/src/eadc_utils.erl
--module(base32).
--author('jlarky@gmail.com').
-
--export([b32/1, encode/1,unb32/1, decode/1 ]).
-
-
-%% @spec b32(integer()) -> [base32char()]
-%% @doc returns base32 character corresponding to V like 1 -> 'B', 31 -> '7'.
-%% V is integer from 0 to 31
-%% @see unb32/1
-b32(V) when V < 0 -> % wrong argument
- throw({b32,wrong_argument, V});
-b32(V) when V < 26 ->
- [V+65];
-b32(V) when V < 32 ->
- [V+24]; % V-26+48+2
-b32(V) when is_integer(V) ->
- b32_(V, "").
-
-b32_(0, Buf) ->
- Buf;
-b32_(V, Buf) ->
- [A]=b32(V rem 32),
- b32_(V bsr 5, [A|Buf]).
-
-%% @spec encode(string()) -> base32string()
-%% @doc returns base32 encoded string of String
-%% @see decode/1
-encode(Bin) when is_binary(Bin) ->
- lists:reverse(encode_(Bin, _Out=[]));
-encode(String) ->
- encode(list_to_binary(String)).
-
-encode_(Bin, Out) ->
- case Bin of
- <<>> ->
- Out;
- <<A:1>> ->
- [B]=b32(A bsl 4),[B|Out];
- <<A:2>> ->
- [B]=b32(A bsl 3),[B|Out];
- <<A:3>> ->
- [B]=b32(A bsl 2),[B|Out];
- <<A:4>> ->
- [B]=b32(A bsl 1),[B|Out];
- Bin ->
- <<A:5, T/bitstring>>=Bin,
- [B]=b32(A),encode_(T, [B|Out])
- end.
-
-%% @spec unb32(base32char()) -> integer()
-%% @doc A=unb32(b32(A))
-%% @see b32/1
-unb32([V]) when ((V >= $A) and (V =< $Z)) ->
- V-$A;
-unb32([V]) when ((V >= $2) and (V =< $7)) ->
- V-$2+26;
-unb32([V]) ->
- throw({badarg, [V]});
-unb32(String=[_|_]) ->
- lists:foldl(fun(Char, Acc) ->
- Acc*32+unb32([Char])
- end, 0, String).
-
-%% @spec decode(base32string()) -> string()
-%% @doc returns base32 decoded string of String
-%% @see encode/1
-decode(Bin) when is_binary(Bin) ->
- decode(binary_to_list(Bin));
-decode(String) ->
- Bits=lists:foldl(fun(Elem, Acc) ->
- A= unb32([Elem]),
- New= <<Acc/bitstring, A:5>>,
- New
- end, <<>>, String),
- decode_(Bits, _Out=[]).
-
-decode_(<<>>, Out) ->
- Out;
-decode_(Bits, Out) ->
- case Bits of
- <<Head:8, Rest/bitstring>> ->
- decode_(Rest, Out++[Head]);
- <<0:1>> -> Out;
- <<0:2>> -> Out;
- <<0:3>> -> Out;
- <<0:4>> -> Out;
- <<0:5>> -> Out;
- <<0:6>> -> Out;
- <<0:7>> -> Out;
- <<H:1>> -> Out++[H bsl 7];
- <<H:2>> -> Out++[H bsl 6];
- <<H:3>> -> Out++[H bsl 5];
- <<H:4>> -> Out++[H bsl 4];
- <<H:5>> -> Out++[H bsl 3];
- <<H:6>> -> Out++[H bsl 2];
- <<H:7>> -> Out++[H bsl 1]
- end.
-
185 seds/src/seds.erl
View
@@ -1,185 +0,0 @@
-%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
-%% All rights reserved.
-%%
-%% Redistribution and use in source and binary forms, with or without
-%% modification, are permitted provided that the following conditions
-%% are met:
-%%
-%% Redistributions of source code must retain the above copyright
-%% notice, this list of conditions and the following disclaimer.
-%%
-%% Redistributions in binary form must reproduce the above copyright
-%% notice, this list of conditions and the following disclaimer in the
-%% documentation and/or other materials provided with the distribution.
-%%
-%% Neither the name of the author nor the names of its contributors
-%% may be used to endorse or promote products derived from this software
-%% without specific prior written permission.
-%%
-%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-%% POSSIBILITY OF SUCH DAMAGE.
--module(seds).
--behaviour(gen_server).
-
--include_lib("kernel/src/inet_dns.hrl").
--include("seds.hrl").
-
--define(SERVER, ?MODULE).
-
--export([start_link/0, start_link/1, send/2]).
--export([config/2, privpath/1]).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--record(state, {
- acf = false, % allow client forwarding
- acl = [], % forward IP blacklist
- acl_port = [], % allowed ports (whitelist)
-
- f, % forwarders map
- s, % socket
- d = [], % domains
- p = [] % list of proxies
- }).
-
-
-send({IP, Port, #dns_rec{} = Rec}, #seds{} = Query) ->
- gen_server:call(?SERVER, {send, {IP, Port, Rec, Query}}).
-
-
-start_link() ->
- start_link(?DNS_PORT).
-start_link(Port) ->
- gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
-
-init([Port]) when Port > 1024 ->
- init(Port, []);
-init([Port]) ->
- {ok, FD} = procket:listen(Port, [
- {protocol, udp},
- {family, inet},
- {type, dgram}
- ]),
- init(Port, [{fd, FD}]).
-
-init(Port, Opt) ->
- {ok, Socket} = gen_udp:open(Port, [
- binary,
- {active, once}
- ] ++ Opt),
- {ok, #state{
- acf = config(dynamic, ?CFG, false),
- acl = config(acl, ?CFG, []),
- acl_port = config(allowed_ports, ?CFG, [22]),
- f = config(forward, ?CFG, []),
- d = [ string:tokens(N, ".") || N <- config(domains, ?CFG) ],
- s = Socket,
- p = dict:new()
- }}.
-
-
-handle_call({send, {IP, Port, #dns_rec{} = Rec,
- #seds{type = Type, sum = Sum, data = Data} = Query}},
- _From, State) ->
- Session = seds_protocol:session(Query, map(State)),
- {Proxy, Proxies} = proxy(Session, State),
- ok = seds_proxy:send(Proxy, IP, Port, Rec, {Type, Sum, Data}),
- {reply, ok, State#state{p = Proxies}};
-
-handle_call(Request, _From, State) ->
- error_logger:error_report([{wtf, Request}]),
- {reply, ok, State}.
-
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-% DNS request from client
-handle_info({udp, Socket, IP, Port, Data}, #state{
- s = Socket
- } = State) ->
- ok = inet:setopts(Socket, [{active, once}]),
- spawn(seds_protocol, decode, [{IP, Port, Data}, map(State)]),
- {noreply, State};
-
-% Session terminated
-handle_info({'DOWN', _Ref, process, Pid, _Reason}, #state{
- p = Proxies
- } = State) ->
- {noreply, State#state{
- p = dict:filter(
- fun (_,V) when V == Pid -> false;
- (_,_) -> true
- end,
- Proxies)
- }};
-
-% WTF?
-handle_info(Info, State) ->
- error_logger:error_report([{wtf, Info}]),
- {noreply, State}.
-
-terminate(_Reason, _State) ->
- ok.
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-
-%%%
-%%% Internal Functions
-%%%
-proxy({{IP, Port}, Id} = Session, #state{
- s = Socket,
- p = Proxies
- }) ->
- case dict:find(Session, Proxies) of
- error ->
- error_logger:info_report([
- {session_start, {IP, Port}},
- {id, Id}
- ]),
- {ok, Pid} = seds_proxy:start_link(Socket, {IP, Port}),
- {Pid, dict:store(Session, Pid, Proxies)};
- {ok, Pid} ->
- {Pid, Proxies}
- end.
-
-config(Key, Cfg) ->
- config(Key, Cfg, undefined).
-config(Key, Cfg, Default) ->
- {ok, Map} = file:consult(privpath(Cfg)),
- proplists:get_value(Key, Map, Default).
-
-privpath(Cfg) ->
- filename:join([
- filename:dirname(code:which(?MODULE)),
- "..",
- "priv",
- Cfg
- ]).
-
-map(#state{
- acf = ACF,
- acl = ACL,
- acl_port = ACP,
- f = Fwd,
- d = Domains
- }) ->
- #config{
- acf = ACF,
- acl = ACL,
- acl_port = ACP,
- f = Fwd,
- d = Domains
- }.
-
-
245 seds/src/seds_protocol.erl
View
@@ -1,245 +0,0 @@
-%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
-%% All rights reserved.
-%%
-%% Redistribution and use in source and binary forms, with or without
-%% modification, are permitted provided that the following conditions
-%% are met:
-%%
-%% Redistributions of source code must retain the above copyright
-%% notice, this list of conditions and the following disclaimer.
-%%
-%% Redistributions in binary form must reproduce the above copyright
-%% notice, this list of conditions and the following disclaimer in the
-%% documentation and/or other materials provided with the distribution.
-%%
-%% Neither the name of the author nor the names of its contributors
-%% may be used to endorse or promote products derived from this software
-%% without specific prior written permission.
-%%
-%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-%% POSSIBILITY OF SUCH DAMAGE.
--module(seds_protocol).
-
--include_lib("kernel/src/inet_dns.hrl").
--include("seds.hrl").
-
--export([decode/2, session/2]).
-
-
-%%%
-%%% Handle decoding of the data embedded in the different
-%%% record types.
-%%%
-
-%%--------------------------------------------------------------------
-%%% Sessions: which IP:Port to send the data
-%%--------------------------------------------------------------------
-
-% Static list of forwarded hosts:port, identified from offset 0
-session(#seds{
- forward = {session, Forward},
- id = Id
- },
- #config{f = Map}) ->
- F = case Forward + 1 of
- N when N > length(Map) -> 1;
- N when N < 1 -> 1;
- N -> N
- end,
- {lists:nth(F, Map), Id};
-
-% Dynamic forwaring requested by client
-session(#seds{
- forward = {forward, Forward},
- id = Id
- }, #config{}) ->
- {Forward, Id}.
-
-
-%%--------------------------------------------------------------------
-%%% Decode the data embedded in the DNS record.
-%%%
-%%% The decode function is spawned as an unlinked process. If the
-%%% parsing succeeds, the data is returned to the gen_server. If
-%%% the parsing fails, the data is bad and is thrown away.
-%%%
-%%--------------------------------------------------------------------
-
-% mfz.wiztb.onsgmcq.40966-0.id-372571.u.192.168.100.101-2222.x.example.com
-% B64._Nonce-Sum.id-SessionId.u.IP1.IP2.IP3.IP4-Port.x.Domain
-decode({domain, {a, [Base64Nonce, Sum, "id", SessionId, "u",
- IP1, IP2, IP3, IP4, Port, "x"|Domain]}},
- #config{d = Domains, acf = true, acl = ACL, acl_port = ACP}) ->
-
- IP = makeaddr({IP1,IP2,IP3,IP4}),
- Port1 = list_to_integer(Port),
-
- true = check_dn(Domain, Domains),
- true = check_acl(IP, ACL),
- true = check_port(Port1, ACP),
-
- B64 = string:tokens(Base64Nonce, "."),
- Forward = forward({IP, Port1}),
-
- #seds{
- type = up,
- forward = Forward,
- id = SessionId,
- data = lists:flatten(lists:sublist(B64, length(B64)-1)),
- sum = list_to_integer(Sum),
- domain = Domain
- };
-decode({domain, {a, [Base64Nonce, Sum, "id", SessionId, "u",
- IP1, IP2, IP3, IP4, "x"|Domain]}},
- #config{d = Domains, acf = true, acl = ACL}) ->
-
- IP = makeaddr({IP1,IP2,IP3,IP4}),
-
- true = check_dn(Domain, Domains),
- true = check_acl(IP, ACL),
-
- B64 = string:tokens(Base64Nonce, "."),
- Forward = forward({IP, 22}),
-
- #seds{
- type = up,
- forward = Forward,
- id = SessionId,
- data = lists:flatten(lists:sublist(B64, length(B64)-1)),
- sum = list_to_integer(Sum),
- domain = Domain
- };
-
-% mfz.wiztb.onsgmcq.40966-0.id-372571.up.p.example.com
-% B64._Nonce-Sum.id-SessionId.up.Domain
-decode({domain, {a, [Base64Nonce, Sum, "id", SessionId, "up"|Domain]}},
- #config{d = Domains}) ->
- true = check_dn(Domain, Domains),
-
- B64 = string:tokens(Base64Nonce, "."),
- {Forward, Id} = forward(list_to_integer(SessionId)),
-
- #seds{
- type = up,
- forward = Forward,
- id = Id,
- data = lists:flatten(lists:sublist(B64, length(B64)-1)),
- sum = list_to_integer(Sum),
- domain = Domain
- };
-
-% 0-29941.id-10498.d.192.168.100.101.s.p.example.com
-% Sum-Nonce.id-SessionId.d.IP1.IP2.IP3.IP4.Domain
-%
-% 0-29941.id-10498.d.192.168.100.101-2222.x.p.example.com
-% Sum-Nonce.id-SessionId.d.IP1.IP2.IP3.IP4-Port.x.Domain
-decode({domain, {_Type, [Sum, _Nonce, "id", SessionId, "d",
- IP1, IP2, IP3, IP4, Port, "x"|Domain]}},
- #config{d = Domains, acf = true, acl = ACL, acl_port = ACP}) ->
-
- IP = makeaddr({IP1,IP2,IP3,IP4}),
- Port1 = list_to_integer(Port),
-
- true = check_dn(Domain, Domains),
- true = check_acl(IP, ACL),
- true = check_port(Port1, ACP),
-
- Forward = forward({IP, Port1}),
-
- #seds{
- type = down,
- forward = Forward,
- id = SessionId,
- sum = list_to_sum(Sum),
- domain = Domain
- };
-decode({domain, {_Type, [Sum, _Nonce, "id", SessionId, "d",
- IP1, IP2, IP3, IP4, "x"|Domain]}},
- #config{d = Domains, acf = true, acl = ACL}) ->
-
- IP = makeaddr({IP1,IP2,IP3,IP4}),
-
- true = check_dn(Domain, Domains),
- true = check_acl(IP, ACL),
-
- Forward = forward({IP, 22}),
-
- #seds{
- type = down,
- forward = Forward,
- id = SessionId,
- sum = list_to_sum(Sum),
- domain = Domain
- };
-
-% 0-29941.id-10498.down.s.p.example.com
-% Sum-Nonce.id-SessionId.down.Domain
-decode({domain, {_Type, [Sum, _Nonce, "id", SessionId, "down"|Domain]}},
- #config{d = Domains}) ->
- true = check_dn(Domain, Domains),
-
- {Forward, Id} = forward(list_to_integer(SessionId)),
-
- #seds{
- type = down,
- forward = Forward,
- id = Id,
- sum = list_to_sum(Sum),
- domain = Domain
- };
-
-decode({dns_rec, #dns_rec{
- header = #dns_header{
- qr = false,
- opcode = 'query'
- },
- qdlist = [#dns_query{
- domain = Query,
- type = Type,
- class = in
- }|_]
- }
- }, State) ->
- {Prefix, Session} = lists:split(string:chr(Query, $-), Query),
- decode({domain, {Type, [Prefix|string:tokens(Session, ".-")]}}, State);
-
-decode({IP, Port, Data}, State) ->
- {ok, Query} = inet_dns:decode(Data),
- seds:send({IP, Port, Query}, decode({dns_rec, Query}, State)).
-
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-makeaddr({IP1,IP2,IP3,IP4}) when is_list(IP1), is_list(IP2), is_list(IP3), is_list(IP4) ->
- {list_to_integer(IP1), list_to_integer(IP2), list_to_integer(IP3), list_to_integer(IP4)}.
-
-% Respond only to the configured list of domains
-check_dn(Domain, Domains) ->
- [ N || N <- Domains, lists:suffix(N, Domain) ] /= [].
-
-check_acl({IP1,IP2,IP3,IP4}, ACL) ->
- [ N || N <- ACL, lists:prefix(N, [IP1,IP2,IP3,IP4]) ] == [].
-
-check_port(Port, Allowed) ->
- lists:member(Port, Allowed).
-
-% Remove the trailing dash and convert to an integer
-list_to_sum(N) when is_list(N) ->
- list_to_integer(string:strip(N, right, $-)).
-
-forward({_IP, _Port} = Forward) ->
- {forward, Forward};
-forward(Id) when is_integer(Id) ->
- <<_Opt:8, Forward:8, SessionId:16>> = <<Id:32>>,
- {{session, Forward}, SessionId}.
299 seds/src/seds_proxy.erl
View
@@ -1,299 +0,0 @@
-%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
-%% All rights reserved.
-%%
-%% Redistribution and use in source and binary forms, with or without
-%% modification, are permitted provided that the following conditions
-%% are met:
-%%
-%% Redistributions of source code must retain the above copyright
-%% notice, this list of conditions and the following disclaimer.
-%%
-%% Redistributions in binary form must reproduce the above copyright
-%% notice, this list of conditions and the following disclaimer in the
-%% documentation and/or other materials provided with the distribution.
-%%
-%% Neither the name of the author nor the names of its contributors
-%% may be used to endorse or promote products derived from this software
-%% without specific prior written permission.
-%%
-%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-%% POSSIBILITY OF SUCH DAMAGE.
--module(seds_proxy).
--behaviour(gen_fsm).
-
--include_lib("kernel/src/inet_dns.hrl").
--include("seds.hrl").
-
--record(state, {
- ip,
- port,
- dnsfd, % dns server socket
- s, % proxied socket
-
- sum_up = 0, % number of bytes sent to server
- sum_down = 0, % number of bytes received from server
- buf = [], % last packet sent (for resend)
- data = [<<>>] % list of binaries: data returned by proxied server
- }).
-
-
--define(MAXDATA, 110).
-
-% Interface
--export([send/5]).
--export([start_link/2]).
--export([label/1]).
-% States
--export([connect/2,proxy/2]).
-% Behaviours
--export([init/1, handle_event/3, handle_sync_event/4,
- handle_info/3, terminate/3, code_change/4]).
-
-
-%%--------------------------------------------------------------------
-%%% Interface
-%%--------------------------------------------------------------------
-send(Pid, IP, Port, #dns_rec{} = Query, {up, Sum, Data}) when is_pid(Pid) ->
- gen_fsm:send_event(Pid, {up, IP, Port, Query, Sum, Data});
-send(Pid, IP, Port, #dns_rec{} = Query, {down, Sum, _}) when is_pid(Pid) ->
- gen_fsm:send_event(Pid, {down, IP, Port, Query, Sum}).
-
-%%--------------------------------------------------------------------
-%%% Behaviours
-%%--------------------------------------------------------------------
-start_link(Socket, {ServerIP, ServerPort}) ->
- {ok, Pid} = gen_fsm:start(?MODULE, [
- Socket,
- {ServerIP, ServerPort}
- ], []),
- erlang:monitor(process, Pid),
- {ok, Pid}.
-
-init([DNSSocket, {ServerIP, ServerPort}]) ->
- process_flag(trap_exit, true),
- {ok, connect, #state{
- dnsfd = DNSSocket,
- ip = ServerIP,
- port = ServerPort
- }, 0}.
-
-
-handle_event(_Event, StateName, State) ->
- {next_state, StateName, State}.
-
-handle_sync_event(_Event, _From, StateName, State) ->
- {next_state, StateName, State}.
-
-
-%%
-%% State: proxy
-%%
-
-% From server
-handle_info({tcp, Socket, Data}, proxy, #state{s = Socket} = State) ->
- {next_state, proxy, State#state{
- data = [Data|State#state.data]
- }, ?PROXY_TIMEOUT};
-
-% Connection closed
-handle_info({tcp_closed, Socket}, proxy, #state{s = Socket} = State) ->
- {stop, shutdown, State}.
-
-terminate(Reason, StateName, #state{
- ip = IP,
- port = Port,
- sum_up = Up,
- sum_down = Down
- }) ->
- error_logger:info_report([
- {session_end, {IP, Port}},
- {bytes_sent, Up},
- {bytes_rcvd, Down},
- {state, StateName},
- {reason, Reason}
- ]),
- ok.
-
-code_change(_OldVsn, StateName, State, _Extra) ->
- {ok, StateName, State}.
-
-
-%%--------------------------------------------------------------------
-%%% States
-%%--------------------------------------------------------------------
-
-%%
-%% connect
-%%
-connect(timeout, #state{ip = IP, port = Port} = State) ->
- {ok, Socket} = gen_tcp:connect(IP, Port, [
- binary,
- {packet, 0},
- {active, once}
- ], 5000),
- {next_state, proxy, State#state{s = Socket}}.
-
-
-%%
-%% proxy
-%%
-
-% client sent data to be forwarded to server
-proxy({up, IP, Port, Rec, ClientSum, Data},
- #state{
- sum_up = Sum,
- dnsfd = DNSSocket,
- s = Socket
- } = State) ->
-
- Payload = base32:decode(string:to_upper(Data)),
- Sum1 = Sum + length(Payload),
-
- case response(up, ClientSum, Sum, Rec) of
- error ->
- {stop, {up, out_of_sync}, State};
- duplicate ->
- error_logger:info_report([{dropping, {IP, Port}}]),
- {next_state, proxy, State, ?PROXY_TIMEOUT};
- Packet ->
-% error_logger:info_report([
-% {direction, up},
-% {dns_query, Packet}
-% ]),
- ok = gen_tcp:send(Socket, Payload),
- ok = gen_udp:send(DNSSocket, IP, Port, Packet),
- {next_state, proxy, State#state{sum_up = Sum1},
- ?PROXY_TIMEOUT}
- end;
-
-% client requested pending data from server
-proxy({down, IP, Port,
- #dns_rec{
- qdlist = [#dns_query{
- type = Type
- }|_]} = Rec, ClientSum},
- #state{
- sum_down = Sum,
- dnsfd = DNSSocket,
- s = Socket,
- data = Data,
- buf = Buf
- } = State) ->
-
- {Payload, Size, Rest} = data(Type, Data),
-
- case response({down, Payload}, ClientSum, Sum, Rec) of
- error ->
- {stop, {down, out_of_sync}, State};
- duplicate ->
- error_logger:info_report([{resending, {IP, Port, Sum}}]),
- ok = resend(DNSSocket, IP, Port, Type, Buf, Rec),
- {next_state, proxy, State#state{sum_down = Sum},
- ?PROXY_TIMEOUT};
- Packet ->
-% error_logger:info_report([
-% {direction, down},
-% {dns_query, Rec}
-% ]),
- ok = inet:setopts(Socket, [{active, once}]),
- ok = gen_udp:send(DNSSocket, IP, Port, Packet),
- {next_state, proxy, State#state{
- sum_down = Sum + Size,
- data = [Rest],
- buf = Data}, ?PROXY_TIMEOUT}
- end;
-
-proxy(timeout, State) ->
- {stop, timeout, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal Functions
-%%--------------------------------------------------------------------
-seq(N) when is_integer(N) ->
- <<I1,I2,I3,I4>> = <<N:32>>,
- {I1,I2,I3,I4}.
-
-
-%% Encode the data returned by the server as a DNS record
-data(_, [<<>>]) ->
- {[],0,<<>>};
-data(Type, Data) when is_list(Data) ->
- data(Type, list_to_binary(lists:reverse(Data)));
-
-% TXT records
-data(txt, <<D1:?MAXDATA/bytes, D2:?MAXDATA/bytes, Rest/binary>>) ->
- {[base64:encode_to_string(D1), base64:encode_to_string(D2)], 2*?MAXDATA, Rest};
-data(txt, <<D1:?MAXDATA/bytes, Rest/binary>>) ->
- {[base64:encode_to_string(D1)], ?MAXDATA, Rest};
-data(txt, Data) ->
- {[base64:encode_to_string(Data)], byte_size(Data), <<>>};
-
-% NULL records
-data(null, <<D1:(?MAXDATA*2)/bytes, Rest/binary>>) ->
- {base64:encode(D1), ?MAXDATA*2, Rest};
-data(null, Data) ->
- {base64:encode(Data), byte_size(Data), <<>>};
-
-% CNAME records
-data(cname, <<D1:?MAXDATA/bytes, Rest/binary>>) ->
- {label(base32:encode(D1)), ?MAXDATA, Rest};
-data(cname, Data) ->
- {label(base32:encode(Data)), byte_size(Data), <<>>}.
-
-
-%% Each component (or label) of a CNAME can have a
-%% max length of 63 bytes. A "." divides the labels.
-label(String) when byte_size(String) < ?MAXLABEL ->
- String;
-label(String) ->
- re:replace(String, ".{63}", "&.", [global, {return, list}]).
-
-
-%% Packet sum checks
-response(up, Sum, Sum, Rec) ->
- encode(seq(Sum), Rec);
-response({down, Payload}, Sum, Sum, Rec) ->
- encode(Payload, Rec);
-response(_, Sum1, Sum2, _) when Sum1 < Sum2 ->
- duplicate;
-response(_, Sum1, Sum2, _) when Sum1 > Sum2 ->
- error.
-
-
-%% Encode the DNS response to the client
-encode(Data, #dns_rec{
- header = Header,
- qdlist = [#dns_query{
- domain = Domain,
- type = Type
- }|_]} = Rec) ->
- inet_dns:encode(Rec#dns_rec{
- header = Header#dns_header{
- qr = true,
- ra = true
- },
- anlist = [#dns_rr{
- domain = Domain,
- type = Type,
- data = Data
- }]}).
-
-
-% Resend a lost packet
-resend(_Socket, _IP, _Port, _Type, [], _Rec) ->
- ok;
-resend(Socket, IP, Port, Type, Data, Rec) ->
- {Payload, _, _} = data(Type, Data),
- gen_udp:send(Socket, IP, Port, encode(Payload, Rec)).
-
-
3  seds/start.sh
View
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-exec erl -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -s seds start_link
Please sign in to comment.
Something went wrong with that request. Please try again.