study-system/server/src/tcp_server.erl

59 lines
1.5 KiB
Erlang

% Copyright (c) Camden Dixie O'Brien
% SPDX-License-Identifier: AGPL-3.0-only
-module(tcp_server).
-behaviour(gen_server).
-export([start_link/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {socket, acceptor}).
start_link(Port, CertDir) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [Port, CertDir], []).
init([Port, CertDir]) ->
TcpOpts = [binary, inet6, {active, false}, {reuseaddr, true}],
SslOpts = [{certfile, filename:join([CertDir, "server.pem"])},
{cacertfile, filename:join([CertDir, "ca.pem"])},
{verify, verify_peer},
{fail_if_no_peer_cert, true}],
{ok, Socket} = ssl:listen(Port, TcpOpts ++ SslOpts),
self() ! accept,
{ok, #state{socket = Socket}}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(accept, State = #state{socket = Socket}) ->
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, 5000) 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) ->
{noreply, State}.
terminate(_Reason, #state{socket = Socket}) ->
ssl:close(Socket),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.