Implement demo of DER comms

This commit is contained in:
Camden Dixie O'Brien 2025-02-25 22:01:38 +00:00
parent f5cb3b7166
commit 727c0aedd6
8 changed files with 119 additions and 41 deletions

View File

@ -14,7 +14,9 @@ namespace StudySystemClient {
this.connection = connection; this.connection = connection;
connection.received.connect((msg) => { connection.received.connect((msg) => {
response_label.label = "Response: " + (string)msg; var der = Der.decode(msg) as Der.Choice;
var str = der.value as Der.Utf8String;
response_label.label = "Response: " + str.value;
}); });
var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10);
@ -24,7 +26,10 @@ namespace StudySystemClient {
box.margin_bottom = 10; box.margin_bottom = 10;
send_button = new Gtk.Button.with_label("Send"); send_button = new Gtk.Button.with_label("Send");
send_button.clicked.connect(() => connection.send("Foo".data)); send_button.clicked.connect(() => {
var msg = new Der.Choice(0, new Der.Null());
connection.send(msg.encode());
});
box.append(send_button); box.append(send_button);
response_label = new Gtk.Label(""); response_label = new Gtk.Label("");

1
server/.gitignore vendored
View File

@ -1,2 +1,3 @@
_build/* _build/*
*.beam *.beam
src/StudySystemProtocol.erl

View File

@ -0,0 +1,11 @@
StudySystemProtocol DEFINITIONS EXPLICIT TAGS ::= BEGIN
Request ::= CHOICE {
foo [0] NULL
}
Response ::= CHOICE {
msg [0] UTF8String
}
END

View File

@ -1,2 +1,6 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, []}. {deps, []}.
{plugins, [{provider_asn1, "0.4.1"}]}.
{provider_hooks, [{pre, [{compile, {asn, compile}}]},
{post, [{clean, {asn, clean}}]}]}.
{asn1_args, [{compile_opts, [der]}]}.

View File

@ -11,10 +11,16 @@ start_link(Port, CertDir) ->
supervisor:start_link({local, ?MODULE}, ?MODULE, [Port, CertDir]). supervisor:start_link({local, ?MODULE}, ?MODULE, [Port, CertDir]).
init([Port, CertDir]) -> init([Port, CertDir]) ->
SupFlags = #{stragegy => one_for_all, SupFlags = #{stragegy => one_for_one,
intensity => 1, intensity => 1,
period => 5}, period => 5},
ChildSpecs = [#{id => tcp_server, ChildSpecs = [#{id => session_sup,
start => {session_sup, start_link, []},
restart => permanent,
shutdown => 5000,
type => supervisor,
modules => [session_sup]},
#{id => tcp_server,
start => {tcp_server, start_link, [Port, CertDir]}, start => {tcp_server, start_link, [Port, CertDir]},
restart => permanent, restart => permanent,
shutdown => 5000, shutdown => 5000,

View File

@ -0,0 +1,42 @@
% Copyright (c) Camden Dixie O'Brien
% SPDX-License-Identifier: AGPL-3.0-only
-module(session_server).
-behaviour(gen_server).
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link(Socket) ->
gen_server:start_link(?MODULE, Socket, []).
init(Socket) ->
ssl:setopts(Socket, [{active, true}]),
{ok, #{socket => Socket}}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({ssl, Socket, Data}, State) ->
case 'StudySystemProtocol':decode('Request', Data) of
{ok, {foo, _}} ->
{ok, Encoded}
= 'StudySystemProtocol':encode('Response', {msg, "Foo"}),
ssl:send(Socket, Encoded);
Result ->
io:format("Invalid message: ~p~n", [Result]),
ok
end,
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, #{socket := Socket}) ->
ssl:close(Socket).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.

View File

@ -0,0 +1,25 @@
% Copyright (c) Camden Dixie O'Brien
% SPDX-License-Identifier: AGPL-3.0-only
-module(session_sup).
-behaviour(supervisor).
-export([start_link/0, init/1, start_session/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
SupFlags = #{strategy => simple_one_for_one,
intensity => 10,
period => 1},
ChildSpec = #{id => session_server,
start => {session_server, start_link, []},
restart => temporary,
shutdown => 5000,
type => worker,
modules => [session_server]},
{ok, {SupFlags, [ChildSpec]}}.
start_session(Socket) ->
supervisor:start_child(?MODULE, [Socket]).

View File

@ -20,8 +20,8 @@ init([Port, CertDir]) ->
{verify, verify_peer}, {verify, verify_peer},
{fail_if_no_peer_cert, true}], {fail_if_no_peer_cert, true}],
{ok, Socket} = ssl:listen(Port, TcpOpts ++ SslOpts), {ok, Socket} = ssl:listen(Port, TcpOpts ++ SslOpts),
Pid = spawn_link(fun() -> acceptor_loop(Socket) end), self() ! accept,
{ok, #state{socket = Socket, acceptor = Pid}}. {ok, #state{socket = Socket}}.
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
{reply, ok, State}. {reply, ok, State}.
@ -29,46 +29,30 @@ handle_call(_Request, _From, State) ->
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State}.
handle_info({'EXIT', Pid, Reason}, State = #state{acceptor = Pid}) -> handle_info(accept, State = #state{socket = Socket}) ->
{stop, {acceptor_died, Reason}, State}; case ssl:transport_accept(Socket) of
{ok, TlsSocket} ->
self() ! {handshake, TlsSocket},
self() ! accept;
{error, closed} ->
ok
end,
{noreply, State};
handle_info({handshake, TlsSocket}, State) ->
case ssl:handshake(TlsSocket) of
{ok, ClientSocket} ->
{ok, Pid} = session_sup:start_session(ClientSocket),
ok = ssl:controlling_process(ClientSocket, Pid);
{error, _Reason} ->
ok
end,
{noreply, State};
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.
terminate(_Reason, #state{socket = Socket}) -> terminate(_Reason, #state{socket = Socket}) ->
gen_tcp:close(Socket), ssl:close(Socket),
ok. ok.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
acceptor_loop(Socket) ->
case ssl:transport_accept(Socket) of
{ok, TlsSocket} ->
case ssl:handshake(TlsSocket) of
{ok, ClientSocket} ->
Pid = spawn(
fun() -> handle_connection(ClientSocket) end),
ok = ssl:controlling_process(ClientSocket, Pid),
acceptor_loop(Socket);
{error, _Reason} ->
acceptor_loop(Socket)
end;
{error, closed} ->
ok
end.
handle_connection(Socket) ->
ssl:setopts(Socket, [{active, true}]),
handle_connection_loop(Socket).
handle_connection_loop(Socket) ->
receive
{ssl, Socket, Data} ->
handle_client_msg(Socket, Data),
handle_connection_loop(Socket);
{ssl_closed, Socket} ->
ok
end.
handle_client_msg(Socket, Msg) ->
ssl:send(Socket, Msg).