enoise more or less just works
This commit is contained in:
Executable
+202
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env escript
|
||||
|
||||
-mode(compile).
|
||||
-include("./ansi.hrl").
|
||||
|
||||
% ho from pimp pov
|
||||
-record(pho,
|
||||
{pid :: pid(),
|
||||
ref :: reference(),
|
||||
name :: string()}).
|
||||
|
||||
%% pimp state
|
||||
-record(ps,
|
||||
{lsock = none :: gen_tcp:socket(),
|
||||
hos = [] :: [#pho{}],
|
||||
ho_names = init_ho_names() :: [string()]}).
|
||||
|
||||
|
||||
%% ho state
|
||||
-record(hs,
|
||||
{name = none :: string(),
|
||||
daddy = none :: pid(),
|
||||
asock = none :: gen_tcp:socket(),
|
||||
nsock = none :: enoise:noise_socket()}).
|
||||
|
||||
|
||||
main([]) ->
|
||||
io:format("RBX example 4 telnet echo server daemon~n"
|
||||
"`one' part of the many-to-one relationship~n",
|
||||
[]),
|
||||
p_init().
|
||||
|
||||
|
||||
noise_options() ->
|
||||
% totally safe crypto
|
||||
Secret = crypto:hash(sha3_256, <<"alice">>),
|
||||
Public = <<116, 159, 91, 248, 138, 250, 73, 40, 231, 50, 81, 110,
|
||||
137, 163, 44, 76, 48, 130, 225, 95, 168, 121, 93, 44,
|
||||
148, 42, 180, 103, 11, 40, 168, 96>>,
|
||||
KP = enoise_keypair:new(dh25519, Secret, Public),
|
||||
[{noise, "Noise_NN_25519_ChaChaPoly_BLAKE2b"},
|
||||
{e, KP}].
|
||||
|
||||
|
||||
% pimp process startup
|
||||
p_init() ->
|
||||
p_logln("pimp startup (p_init)"),
|
||||
Port = 6969,
|
||||
Opts = [binary, {packet, 0}, {active, once}],
|
||||
case gen_tcp:listen(Port, Opts) of
|
||||
{ok, LSock} ->
|
||||
ok = p_logfln("pimp starting: ~tp", [self()]),
|
||||
PS_I = #ps{lsock = LSock},
|
||||
PS_II = p_spawn_ho(PS_I),
|
||||
p_loop(PS_II);
|
||||
{error, closed} ->
|
||||
%% dont be pimp
|
||||
p_logln("listen socket closed")
|
||||
end.
|
||||
|
||||
p_loop(PS = #ps{hos = Hos}) ->
|
||||
receive
|
||||
{ho, _HoPid, taken} ->
|
||||
NewPS = p_spawn_ho(PS),
|
||||
p_loop(NewPS);
|
||||
{ho, HoPid, sent, Data} ->
|
||||
HoName = p_ho_name(HoPid, PS),
|
||||
SendMessage = io_lib:format("~ts :: ~ts\r\n", [HoName, Data]),
|
||||
ok = p_echo_except(HoPid, HoName, SendMessage, Hos),
|
||||
p_loop(PS);
|
||||
Down = {'DOWN', _, _, _, _} ->
|
||||
NewPS = p_ho_down(Down, PS),
|
||||
p_loop(NewPS);
|
||||
Unknown ->
|
||||
ok = p_logfln("unknown message: ~tw", [Unknown]),
|
||||
p_loop(PS)
|
||||
end.
|
||||
|
||||
% given pid of ho, get her name
|
||||
p_ho_name(HoPid, #ps{hos = Hos}) ->
|
||||
case lists:keyfind(HoPid, #pho.pid, Hos) of
|
||||
#pho{name = Name} -> Name;
|
||||
false -> "JaneDoe"
|
||||
end.
|
||||
|
||||
% pimp spawns new ho
|
||||
p_spawn_ho(PS = #ps{lsock = LSock, hos = Hos, ho_names = Names}) ->
|
||||
% new name for ho, initial ho state
|
||||
{N, NewNames} = recycle(Names),
|
||||
InitHS = #hs{name = N, daddy = self()},
|
||||
% spawn the new ho
|
||||
ok = p_logfln("spawning new ho: ~ts", [N]),
|
||||
SpawnHo = fun() -> h_init(LSock, InitHS) end,
|
||||
{HPid, HRef} = erlang:spawn_monitor(SpawnHo),
|
||||
% new ho record from Pimp POV
|
||||
NewHo = #pho{pid = HPid, ref = HRef, name = N},
|
||||
ok = p_logfln("spawned new ho: ~tw", [NewHo]),
|
||||
NewPS = PS#ps{hos = [NewHo | Hos], ho_names = NewNames},
|
||||
NewPS.
|
||||
|
||||
% ho down, remove her from roster
|
||||
p_ho_down({'DOWN', _Ref, process, Pid, Info}, PS = #ps{hos = Hos}) ->
|
||||
ok = p_logfln("ho down: ~tp; reason: ~tw", [Pid, Info]),
|
||||
NewHos = lists:keydelete(Pid, #pho.pid, Hos),
|
||||
NewPS = PS#ps{hos = NewHos},
|
||||
NewPS.
|
||||
|
||||
|
||||
% echo message from one ho to all the rest
|
||||
% skip if equal
|
||||
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = SrcPid} | Rest]) ->
|
||||
p_echo_except(SrcPid, SrcName, Data, Rest);
|
||||
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = DstPid} | Rest]) ->
|
||||
DstPid ! {pimp, send, Data},
|
||||
p_echo_except(SrcPid, SrcName, Data, Rest);
|
||||
p_echo_except(_, _, _, []) ->
|
||||
ok.
|
||||
|
||||
%% runs in ho context
|
||||
h_init(LSock, HS = #hs{name = Name, daddy = Daddy, asock = none, nsock = none}) ->
|
||||
h_logln(Name, "h_init"),
|
||||
% this is key: accept needs to run in ho context, otherwise she
|
||||
% doesn't own the acceptor socket
|
||||
%
|
||||
% blocks until client connects
|
||||
case gen_tcp:accept(LSock) of
|
||||
{ok, ASock} ->
|
||||
h_logln(Name, "taken"),
|
||||
% tell daddy to spawn the next ho
|
||||
Daddy ! {ho, self(), taken},
|
||||
{ok, NSock, _} = enoise:accept(ASock, noise_options()),
|
||||
Welcome = io_lib:format("HI! I'm ~ts. What's your name?\r\n",
|
||||
[Name]),
|
||||
ok = h_send(Name, NSock, Welcome),
|
||||
HS_II = HS#hs{asock = ASock, nsock = NSock},
|
||||
h_loop(HS_II);
|
||||
{error, closed} ->
|
||||
h_logln(Name, "didn't get taken")
|
||||
end.
|
||||
|
||||
|
||||
h_loop(HS = #hs{name = Name, daddy = Daddy, asock = ASock, nsock = NSock}) ->
|
||||
ok = inet:setopts(ASock, [{active, once}]),
|
||||
receive
|
||||
{noise, NSock, Data} ->
|
||||
Data_II = string:chomp(Data),
|
||||
ok = h_rcvd(Name, Data_II),
|
||||
Daddy ! {ho, self(), sent, Data_II},
|
||||
h_loop(HS);
|
||||
{pimp, send, Data} ->
|
||||
ok = h_send(Name, NSock, Data),
|
||||
h_loop(HS);
|
||||
{tcp_closed, ASock} ->
|
||||
exit(its_fine);
|
||||
Unknown ->
|
||||
ok = h_logfln(Name, "unknown message: ~tw", [Unknown]),
|
||||
h_loop(HS)
|
||||
end.
|
||||
|
||||
|
||||
% pimp log in bold
|
||||
p_logln(LogStr) ->
|
||||
Msg = io_lib:format("!! Daddy :: ~ts~n", [LogStr]),
|
||||
PimpChars = ?ANSI_BOLD([Msg]),
|
||||
io:put_chars(PimpChars).
|
||||
|
||||
p_logfln(LogStr, Args) ->
|
||||
p_logln(io_lib:format(LogStr, Args)).
|
||||
|
||||
|
||||
% ho log dimmed
|
||||
h_logln(HoName, LogStr) ->
|
||||
FMsg = io_lib:format("!! ~ts :: ~ts~n", [HoName, LogStr]),
|
||||
HoChars = ?ANSI_DIM([FMsg]),
|
||||
io:put_chars(HoChars).
|
||||
|
||||
h_logfln(HoName, MsgStr, Args) ->
|
||||
h_logln(HoName, io_lib:format(MsgStr, Args)).
|
||||
|
||||
% normal: john messages
|
||||
h_rcvd(Name, MsgStr) ->
|
||||
Msg = io_lib:format("<< ~ts :: ~ts~n", [Name, MsgStr]),
|
||||
io:put_chars(Msg).
|
||||
|
||||
h_send(Name, NSock, Msg) ->
|
||||
ok = io:format(">> ~ts :: ~ts~n", [Name, Msg]),
|
||||
enoise:send(NSock, Msg).
|
||||
|
||||
|
||||
init_ho_names() ->
|
||||
["Crystal", "Tiffany", "Amber", "Brandy", "Lola", "Angel",
|
||||
"Ginger", "Candy", "Charity", "Anastasia", "Cherry", "Kitty",
|
||||
"Jade", "Destiny", "Devon", "Chastity", "Raven", "Scarlett",
|
||||
"Bambi", "Star", "Paris", "Dallas", "Diamond", "Skye",
|
||||
"Trinity", "Tawny", "Layla", "Lexie", "Roxy", "Porsche",
|
||||
"Nevaeh", "Ashlynn", "Aspen", "Chyna", "Lexus", "Unique",
|
||||
"Chardonnay", "Houston", "London", "Coco", "Luscious",
|
||||
"Delight", "Capri", "Trixie", "Cinnamon"].
|
||||
|
||||
|
||||
recycle([N | Ns]) ->
|
||||
{N, Ns ++ [N]}.
|
||||
Reference in New Issue
Block a user