Skip to content

Commit

Permalink
tpic2 over http
Browse files Browse the repository at this point in the history
  • Loading branch information
cleverfox committed Apr 6, 2023
1 parent 5d49ee3 commit df848be
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 59 deletions.
27 changes: 21 additions & 6 deletions apps/tpic2/src/tpic2.erl
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ certificate() ->
{cert, DerCert},
{cacerts, [DerCert]},
{verify_fun, {fun tpic2:verfun/3, []}},
{fail_if_no_peer_cert, true},
% {key, {'ECPrivateKey', DERKey}}
% {fail_if_no_peer_cert, true},
% {key, {'ECPrivateKey', DERKey}}
{key, {'PrivateKeyInfo', DERKey}}
].

Expand All @@ -216,8 +216,8 @@ childspec() ->
HTTPOpts = fun(E) -> #{
connection_type => supervisor,
socket_opts => [{port,Port},
{next_protocols_advertised, [<<"tpic2">>]},
{alpn_preferred_protocols, [<<"tpic2">>]}
{next_protocols_advertised, [<<"tpic2">>, <<"h2">>, <<"http/1.1">>]},
{alpn_preferred_protocols, [<<"tpic2">>, <<"h2">>, <<"http/1.1">>]}
] ++ E ++ certificate()
}
end,
Expand All @@ -232,17 +232,32 @@ childspec() ->
ranch_ssl,
HTTPOpts([]),
tpic2_tls,
#{}
http_routing()
),
ranch:child_spec(
tpic_tls6,
ranch_ssl,
HTTPOpts([inet6, {ipv6_v6only, true}]),
tpic2_tls,
#{}
http_routing()
)
].

http_routing() ->
HTTPDispatch = cowboy_router:compile(
[
{'_', [
{"/tpic2/[...]", tpnode_tpicapi, #{}}
]}
]),
#{
connection_type => supervisor,
env => #{
dispatch => HTTPDispatch
}
}.


node_addresses() ->
{ok,IA}=inet:getifaddrs(),
AllowLocal=maps:get(allow_localip,application:get_env(tpnode,tpic,#{}),false)==true,
Expand Down
110 changes: 84 additions & 26 deletions apps/tpic2/src/tpic2_tls.erl
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,111 @@ start_link(Ref, Socket, Transport, Opts) ->
-spec connection_process(pid(), ranch:ref(), ssl:sslsocket(), module(), cowboy:opts()) -> ok.
connection_process(Parent, Ref, Socket, Transport, Opts) ->
ok = ranch:accept_ack(Ref),
PeerPK=case ssl:peercert(Socket) of
{ok, PC} ->
DCert=tpic2:extract_cert_info(public_key:pkix_decode_cert(PC,otp)),
case DCert of
#{pubkey:=Der} ->
Der;
_ ->
?LOG_NOTICE("Unknown cert ~p",[DCert]),
undefined
end;
{error, no_peercert} ->
undefined
end,

?LOG_DEBUG("tpic2_tls accept ~p",[PeerPK]),

Proto=case ssl:negotiated_protocol(Socket) of
{ok, <<"tpic2">>} ->
tpic2;
{ok, Name} ->
Name;
_ ->
undefined
end,
case ssl:negotiated_protocol(Socket) of
{ok, Proto} ->
?LOG_DEBUG("tpic2_tls protocol ~s",[Proto]),
conn_proto(Parent, Ref, Socket, Transport, Opts, Proto, PeerPK);
_ ->
ssl:send(Socket,msgpack:pack(#{null=><<"no_protocol">>})),
Transport:close(Socket)
end.

?LOG_INFO("tpic2_tls protocol ~p",[Proto]),
conn_proto(Parent, Ref, Socket, Transport, Opts, <<"h2">>,undefined) ->
cowboy_http2:init(Parent, Ref, Socket, Transport, undefined, Opts);
conn_proto(Parent, Ref, Socket, Transport, Opts, <<"http/1.1">>, undefined) ->
cowboy_http:init(Parent, Ref, Socket, Transport, undefined, Opts);

conn_proto(Parent, Ref, Socket, Transport, Opts, <<"h2">>, PubKey) ->
IsOurNode=chainsettings:is_our_node(PubKey),
if(IsOurNode==false) ->
ssl:send(Socket,<<"unknown_node">>),
?LOG_NOTICE("tpic2_tls h2 connection unknwon key ~p",[PubKey]),
Transport:close(Socket);
true ->
cowboy_http2:init(Parent, Ref, Socket, Transport, undefined, Opts)
end;

conn_proto(Parent, Ref, Socket, Transport, Opts, <<"http/1.1">>, PubKey) ->
IsOurNode=chainsettings:is_our_node(PubKey),
if(IsOurNode==false) ->
ssl:send(Socket,<<"unknown_node">>),
?LOG_NOTICE("tpic2_tls http/1.1 connection unknown key ~p",[PubKey]),
Transport:close(Socket);
true ->
cowboy_http:init(Parent, Ref, Socket, Transport, undefined, Opts)
end;

conn_proto(_Parent, _Ref, Socket, Transport, _Opts, <<"tpic2">>, undefined) ->
ssl:send(Socket,msgpack:pack(#{null=><<"unknown_node">>})),
timer:sleep(1000),
Transport:close(Socket),
done;

conn_proto(Parent, Ref, Socket, Transport, Opts, <<"tpic2">>, PeerPK) ->
{ok,PeerInfo}=ssl:connection_information(Socket),
Transport:setopts(Socket, [{active, once},{packet,4}]),
State=#{parent=>Parent,
State=#{
parent=>Parent,
ref=>Ref,
socket=>Socket,
peerinfo=>PeerInfo,
timer=>undefined,
transport=>Transport,
protocol => Proto,
protocol => tpic2,
peerpk => PeerPK,
nodeid=> try
nodekey:get_pub()
catch _:_ -> atom_to_binary(node(),utf8)
end,
role=>server,
opts=>Opts
},
tpic2_tls:send_msg(hello, State),
?MODULE:loop1(State).

loop1(State=#{socket:=Socket,role:=Role,opts:=Opts,transport:=Transport}) ->
{ok,PC}=ssl:peercert(Socket),
DCert=tpic2:extract_cert_info(public_key:pkix_decode_cert(PC,otp)),
Pubkey=case DCert of
#{pubkey:=Der} ->
Der;
_ ->
?LOG_NOTICE("Unknown cert ~p",[DCert]),
undefined
end,
?MODULE:loop1(State);

conn_proto(_Parent, _Ref, Socket, Transport, _Opts, _, _) ->
ssl:send(Socket,msgpack:pack(#{null=><<"error">>,error=><<"bad protocol">>})),
Transport:close(Socket).

loop1(State=#{socket:=Socket,role:=Role,opts:=Opts,
transport:=Transport,peerpk:=Pubkey}) ->
%{ok,PC}=ssl:peercert(Socket),
%DCert=tpic2:extract_cert_info(public_key:pkix_decode_cert(PC,otp)),
%Pubkey=case DCert of
% #{pubkey:=Der} ->
% Der;
% _ ->
% ?LOG_NOTICE("Unknown cert ~p",[DCert]),
% undefined
% end,
IsItMe=tpecdsa:cmp_pubkey(Pubkey)==tpecdsa:cmp_pubkey(nodekey:get_pub()),
IsOurNode=chainsettings:is_our_node(Pubkey),
?LOG_INFO("Peer PubKey ~s ~p",[hex:encode(Pubkey),
try IsOurNode catch _:_ -> unkn0wn end]),
?LOG_INFO("Peer PubKey ~s ~p",[hex:encode(Pubkey), IsOurNode]),

if IsItMe andalso Role==server ->
tpic2_tls:send_msg(hello, State),
?LOG_NOTICE("I received connection from myself, dropping session"),
timer:sleep(1000),
Transport:close(Socket),
done;

IsItMe ->
tpic2_tls:send_msg(hello, State),
?LOG_NOTICE("I connected to myself, dropping session"),
timer:sleep(1000),
Transport:close(Socket),
Expand All @@ -77,17 +131,20 @@ loop1(State=#{socket:=Socket,role:=Role,opts:=Opts,transport:=Transport}) ->
done;

Role == server -> %server, known peer
tpic2_tls:send_msg(hello, State),
{ok,PPID}=gen_server:call(tpic2_cmgr, {peer,Pubkey, {register, undefined, in, self()}}),
?MODULE:loop(State#{pubkey=>Pubkey,peerpid=>PPID});

IsOurNode == false -> %client unknown node
tpic2_tls:send_msg(unknown_node, State),
{IP, Port} = maps:get(address, State),
gen_server:call(tpic2_cmgr,{peer, Pubkey, {del, IP, Port}, unknown_node}),
timer:sleep(5000),
Transport:close(Socket),
done;

true -> %client, known node
tpic2_tls:send_msg(hello, State),
Stream=maps:get(stream, Opts, 0),
{IP, Port} = maps:get(address, State),
gen_server:call(tpic2_cmgr,{peer, Pubkey, {add, IP, Port}}),
Expand Down Expand Up @@ -220,6 +277,7 @@ send_msg(hello, #{socket:=Socket, opts:=Opts}) ->
addrs=>tpic2:node_addresses(),
port=>Port,
sid=>Stream,
v=>2,
services=>Announce
},
?LOG_DEBUG("Hello ~p",[Hello]),
Expand Down
5 changes: 5 additions & 0 deletions apps/tpnode/src/blockchain_reader.erl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ handle_call({get_block, BlockHash}, _From, #{ldb:=LDB, lastblock:=#{hash:=LBH}=L
end,
{reply, Block, State};

handle_call({get_block, last, Rel}, _From, #{ldb:=LDB, lastblock:=#{hash:=LBH}}=State)
when is_atom(Rel) ->
Res=block_rel(LDB, LBH, Rel),
{reply, Res, State};

handle_call({get_block, BlockHash, Rel}, _From, #{ldb:=LDB, lastblock:=#{hash:=LBH}}=State)
when is_binary(BlockHash) andalso is_atom(Rel) ->
%?LOG_DEBUG("Get block ~p", [BlockHash]),
Expand Down
26 changes: 18 additions & 8 deletions apps/tpnode/src/tpnode_http.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ get_http_conn_type() ->
{'_', [
{"/api/ws", tpnode_ws, []},
{"/api/[...]", apixiom, {tpnode_httpapi, #{}}},
{"/tpic2/[...]", tpnode_tpicapi, #{}},
{"/xchain/ws", xchain_server, []},
{"/xchain/api/[...]", apixiom, {xchain_api, #{}}},
{"/", cowboy_static, {priv_file, tpnode, "index.html"}},
Expand Down Expand Up @@ -80,16 +81,25 @@ childspec_ssl(CertFile, KeyFile) ->
case ensure_cert(CertFile, KeyFile) of
true ->
CaFile = utils:make_list(CertFile)++".ca.crt",
{pub,ed25519,Pub} = tpecdsa:rawkey(nodekey:get_pub()),
PH=binary_to_list(hex:encode(Pub)),
SslOpts = [
{sni_fun,fun(Hostname) when Hostname==PH ->
tpic2:certificate();
("tpnode") ->
tpic2:certificate();
(_) ->
[]
end},
{certfile, utils:make_list(CertFile)},
{keyfile, utils:make_list(KeyFile)}] ++
case file:read_file_info(CaFile) of
{ok,_} ->
[{cacertfile, CaFile}];
_ ->
[]
end,

{keyfile, utils:make_list(KeyFile)} |
case file:read_file_info(CaFile) of
{ok,_} ->
[{cacertfile, CaFile}];
_ ->
[]
end
],
HTTPConnType = get_http_conn_type(),
[
ranch:child_spec(
Expand Down
Loading

0 comments on commit df848be

Please sign in to comment.