From 66ab427e7ada3e8d9710eb79b1f768a8431b42eb Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 3 Jul 2018 16:13:54 +0200 Subject: Add connection manager (to be fully fledged peer database) --- lib/application.ex | 3 ++- lib/cli/cli.ex | 2 +- lib/net/manager.ex | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/net/tcpconn.ex | 9 ++++---- lib/net/tcpserver.ex | 17 ++++----------- lib/web/httprouter.ex | 2 +- test/conn_test.exs | 7 +++--- 7 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 lib/net/manager.ex diff --git a/lib/application.ex b/lib/application.ex index a199e6c..3ad9325 100644 --- a/lib/application.ex +++ b/lib/application.ex @@ -15,8 +15,9 @@ defmodule Shard.Application do Shard.Identity, # Networking - { DynamicSupervisor, strategy: :one_for_one, name: SNet.ConnSupervisor }, + { SNet.Manager, listen_port }, { SNet.TCPServer, listen_port }, + { DynamicSupervisor, strategy: :one_for_one, name: SNet.ConnSupervisor }, # Applications & data store { SData.MerkleList, [&SApp.Chat.msg_cmp/2, name: SApp.Chat.Log] }, diff --git a/lib/cli/cli.ex b/lib/cli/cli.ex index 4643351..28ef5d0 100644 --- a/lib/cli/cli.ex +++ b/lib/cli/cli.ex @@ -17,7 +17,7 @@ defmodule SCLI do def handle_command(["connect", ipstr, portstr]) do {:ok, ip} = :inet.parse_address (to_charlist ipstr) {port, _} = Integer.parse portstr - SNet.TCPServer.add_peer(ip, port) + SNet.Manager.add_peer(ip, port) end def handle_command(["nick", nick]) do diff --git a/lib/net/manager.ex b/lib/net/manager.ex new file mode 100644 index 0000000..e5eb12d --- /dev/null +++ b/lib/net/manager.ex @@ -0,0 +1,60 @@ +defmodule SNet.Manager do + use GenServer + + def start_link(my_port) do + GenServer.start_link(__MODULE__, my_port, name: __MODULE__) + end + + def init(my_port) do + state = %{ + peers: %{}, + my_port: my_port + } + {:ok, state} + end + + def handle_cast({:peer_up, pk, pid, addr, ip}, state) do + new_peers = Map.put(state.peers, pk, {pid, addr, ip}) + new_state = %{ state | peers: new_peers } + {:noreply, new_state} + end + + def handle_cast({:peer_down, pk, addr, ip}, state) do + new_peers = Map.put(state.peers, pk, {nil, addr, ip}) + new_state = %{ state | peers: new_peers } + {:noreply, new_state} + end + + def handle_cast({:add_peer, ip, port}, state) do + add_peer(ip, port, state.my_port) + {:noreply, state} + end + + def handle_cast({:try_connect, pk_list}, state) do + for pk <- pk_list do + case state.peers[pk] do + {nil, ip, port} -> add_peer(ip, port) + _ -> nil + end + end + {:noreply, state} + end + + def handle_call({:get_connections, pk_list}, state) do + pid_list = (for pk <- pk_list, Map.has_key?(state.peers, pk), do: state.peers[pk]) + |> Enum.map(fn {pid, _, _} -> pid end) + |> Enum.filter(&(&1 != nil)) + {:ok, pid_list, state} + end + + def add_peer(ip, port) do + GenServer.cast(__MODULE__, {:add_peer, ip, port}) + end + + def add_peer(ip, port, my_port) do + {:ok, client} = :gen_tcp.connect(ip, port, [:binary, packet: 2, active: false]) + {:ok, pid} = DynamicSupervisor.start_child(SNet.ConnSupervisor, {SNet.TCPConn, %{socket: client, my_port: my_port}}) + :ok = :gen_tcp.controlling_process(client, pid) + pid + end +end diff --git a/lib/net/tcpconn.ex b/lib/net/tcpconn.ex index 5d6c912..301e931 100644 --- a/lib/net/tcpconn.ex +++ b/lib/net/tcpconn.ex @@ -25,11 +25,10 @@ defmodule SNet.TCPConn do {:ok, challenge} = Salty.Random.buf 32 # Exchange public keys and challenge - :gen_tcp.send(socket, srv_pkey <> sess_pkey <> challenge) + hello = {srv_pkey, sess_pkey, challenge, state.my_port} + :gen_tcp.send(socket, :erlang.term_to_binary hello) {:ok, pkt} = :gen_tcp.recv(socket, 0) - cli_pkey = binary_part(pkt, 0, Sign.publickeybytes) - cli_sess_pkey = binary_part(pkt, Sign.publickeybytes, Box.publickeybytes) - cli_challenge = binary_part(pkt, Sign.publickeybytes + Box.publickeybytes, 32) + {cli_pkey, cli_sess_pkey, cli_challenge, his_port} = :erlang.binary_to_term(pkt, [:safe]) # Do challenge and check their challenge {:ok, cli_challenge_sign} = Sign.sign_detached(cli_challenge, srv_skey) @@ -54,6 +53,7 @@ defmodule SNet.TCPConn do addr: addr, port: port } + GenServer.cast(SNet.Manager, {:peer_up, self(), cli_pkey, addr, his_port}) Logger.info "New peer: #{print_id state} at #{inspect addr}:#{port}" GenServer.cast(self(), :init_push) @@ -98,6 +98,7 @@ defmodule SNet.TCPConn do def handle_info({:tcp_closed, _socket}, state) do Logger.info "Disconnected: #{print_id state} at #{inspect state.addr}:#{state.port}" + GenServer.cast(SNet.Manager, {:peer_down, state.his_pkey, state.addr, state.port}) exit(:normal) end diff --git a/lib/net/tcpserver.ex b/lib/net/tcpserver.ex index e5ee996..7c758c1 100644 --- a/lib/net/tcpserver.ex +++ b/lib/net/tcpserver.ex @@ -6,7 +6,6 @@ defmodule SNet.TCPServer do Task.start_link(__MODULE__, :accept, [port]) end - @doc """ Starts accepting connections on the given `port`. """ @@ -14,22 +13,14 @@ defmodule SNet.TCPServer do {:ok, socket} = :gen_tcp.listen(port, [:binary, packet: 2, active: false, reuseaddr: true]) Logger.info "Accepting connections on port #{port}" - loop_acceptor(socket) + loop_acceptor(socket, port) end - defp loop_acceptor(socket) do + defp loop_acceptor(socket, my_port) do {:ok, client} = :gen_tcp.accept(socket) - {:ok, pid} = DynamicSupervisor.start_child(SNet.ConnSupervisor, {SNet.TCPConn, %{socket: client}}) + {:ok, pid} = DynamicSupervisor.start_child(SNet.ConnSupervisor, {SNet.TCPConn, %{socket: client, my_port: my_port}}) :ok = :gen_tcp.controlling_process(client, pid) - loop_acceptor(socket) + loop_acceptor(socket, my_port) end - - def add_peer(ip, port) do - {:ok, client} = :gen_tcp.connect(ip, port, [:binary, packet: 2, active: false]) - {:ok, pid} = DynamicSupervisor.start_child(SNet.ConnSupervisor, {SNet.TCPConn, %{socket: client}}) - :ok = :gen_tcp.controlling_process(client, pid) - pid - end - end diff --git a/lib/web/httprouter.ex b/lib/web/httprouter.ex index 5027df4..d37413d 100644 --- a/lib/web/httprouter.ex +++ b/lib/web/httprouter.ex @@ -22,7 +22,7 @@ defmodule SWeb.HTTPRouter do [ipstr, portstr] = String.split(conn.params["peer"], ":") {:ok, ip} = :inet.parse_address (to_charlist ipstr) {port, _} = Integer.parse portstr - SNet.TCPServer.add_peer(ip, port) + SNet.Manager.add_peer(ip, port) end main_page(conn) diff --git a/test/conn_test.exs b/test/conn_test.exs index 48c92f8..a8cda43 100644 --- a/test/conn_test.exs +++ b/test/conn_test.exs @@ -15,11 +15,10 @@ defmodule ShardTest.Conn do {:ok, challenge} = Salty.Random.buf 32 {:ok, socket} = :gen_tcp.connect {127,0,0,1}, 4044, [:binary, packet: 2, active: false] - :gen_tcp.send(socket, srv_pkey <> sess_pkey <> challenge) + hello = {srv_pkey, sess_pkey, challenge, 0} + :gen_tcp.send(socket, :erlang.term_to_binary hello) {:ok, pkt} = :gen_tcp.recv(socket, 0) - cli_pkey = binary_part(pkt, 0, Sign.publickeybytes) - cli_sess_pkey = binary_part(pkt, Sign.publickeybytes, Box.publickeybytes) - cli_challenge = binary_part(pkt, Sign.publickeybytes + Box.publickeybytes, 32) + {cli_pkey, cli_sess_pkey, cli_challenge, _his_port} = :erlang.binary_to_term(pkt, [:safe]) {:ok, cli_challenge_sign} = Sign.sign_detached(cli_challenge, srv_skey) sendmsg(socket, cli_challenge_sign, cli_sess_pkey, sess_skey) -- cgit v1.2.3