aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2018-07-03 16:13:54 +0200
committerAlex Auvolat <alex@adnab.me>2018-07-03 16:13:54 +0200
commit66ab427e7ada3e8d9710eb79b1f768a8431b42eb (patch)
treed5a70563113b3cb3a9c164c1bc2cf66229a80c2d
parent8f3009715ee9ccdd7ecb54fea1244a32a29b62c0 (diff)
downloadshard-66ab427e7ada3e8d9710eb79b1f768a8431b42eb.tar.gz
shard-66ab427e7ada3e8d9710eb79b1f768a8431b42eb.zip
Add connection manager (to be fully fledged peer database)
-rw-r--r--lib/application.ex3
-rw-r--r--lib/cli/cli.ex2
-rw-r--r--lib/net/manager.ex60
-rw-r--r--lib/net/tcpconn.ex9
-rw-r--r--lib/net/tcpserver.ex17
-rw-r--r--lib/web/httprouter.ex2
-rw-r--r--test/conn_test.exs7
7 files changed, 76 insertions, 24 deletions
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)