Files
2026-09-03 15:47:09 -07:00

116 lines
3.1 KiB
Erlang
Executable File

#!/usr/bin/env escript
%% authentication
%% needs:
%% - line parsing
%% - keyboard input
%% - drawing to screen
-mode(compile).
% john state
-record(js,
{rcv = <<>> :: binary(),
kbdq = [] :: iolist(),
ibuf = [] :: string(),
tsock = none :: none | gen_tcp:socket(),
nsock = none :: none | enoise:noise_socket()}).
main([]) -> main_ii(6969);
main([PortNum]) -> main_ii(list_to_integer(PortNum)).
main_ii(PortNum) ->
TcpOpts = [binary, {packet, 0}, {active, true}],
{ok, TSock} = gen_tcp:connect("localhost", PortNum, TcpOpts),
%ok = inet:setopts(TSock, [{active, once}]),
{ok, NSock, _HandshakeState} = enoise:connect(TSock, noise_options()),
% need keyboard process
Self = self(),
spawn_link(fun() -> kbd_loop(Self) end),
j_loop(#js{tsock = TSock, nsock = NSock}).
server_pubkey() ->
<<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>>.
noise_options() ->
% totally safe crypto
KP = enoise_keypair:new(dh25519),
[{noise, "Noise_NK_25519_ChaChaPoly_BLAKE2b"},
{rs, server_pubkey()},
{e, KP}].
j_loop(JS = #js{rcv = Rcv, tsock = TSock, nsock = NSock}) ->
receive
{noise, NSock, Bytes} ->
NewRcv = <<Rcv/binary, Bytes/binary>>,
io:format("NewRcv = ~p~n", [NewRcv]),
NewJS = j_received(NewRcv, JS),
j_loop(NewJS);
{kbd, line, Line} ->
NewJS = j_kbdln(Line, JS),
j_loop(NewJS);
{tcp_closed, TSock} ->
io:format("tcp socket closed unexpectedly, exiting~n"),
exit(tcp_closed);
Unknown ->
ok = io:format("unknown message ~tp~n", [Unknown]),
j_loop(JS)
end.
j_kbdln(Line, JS = #js{nsock = NS}) ->
FinalMsg = unicode:characters_to_binary([string:chomp(Line), "\r\n"]),
ok = enoise:send(NS, FinalMsg),
JS.
% not the time for readline
% % user hit return, flush queue
% j_kbd("\r", JS = #js{kbdq = KQ, nsock = NS}) ->
% FinalMsg = unicode:characters_to_binary([KQ, "\r\n"]),
% ok = enoise:send(NS, FinalMsg),
% NewJS = JS#js{kbdq = []}
% NewJS;
% % user hit something else, add to queue
% % fixme: parse ANSI escape sequences here, maybe xterm
% j_kbd(Str1, JS = #js{kbdq = KQ}) ->
% JS#js{kbdq = KQ ++ Str1}.
j_received(Rcv, JS) ->
case takeln(Rcv) of
none ->
JS#js{rcv = Rcv};
{line, Line, Rcv2} ->
ok = j_rln(Line),
j_received(Rcv2, JS)
end.
j_rln(Line) ->
io:format("< ~ts~n", [string:chomp(Line)]).
takeln(X) ->
takeln(<<>>, X).
takeln(Acc, <<"\r\n", Rest/binary>>) ->
{line, <<Acc/binary, "\r\n">>, Rest};
takeln(Acc, <<B:8, Rest/binary>>) ->
takeln(<<Acc/binary, B:8>>, Rest);
takeln(_Acc, <<>>) ->
none.
kbd_loop(Parent) ->
Line = io:get_line("> "),
Parent ! {kbd, line, Line},
kbd_loop(Parent).
%kbd_loop(Parent) ->
% Char = io:get_chars("", 1),
% Parent ! {kbd, Char},
% kbd_loop(Parent).