aboutsummaryrefslogtreecommitdiff
path: root/shard/lib
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2018-10-10 15:04:11 +0200
committerAlex Auvolat <alex@adnab.me>2018-10-10 15:04:11 +0200
commit1ee9c3fa6d4259d63685aea95d23b515f59a74cf (patch)
tree418decf98286040d67aa11533092f1bcf0a996e5 /shard/lib
parent3ba44d72a09d3da72d0dd3e495e5cdea07a0bdac (diff)
downloadshard-1ee9c3fa6d4259d63685aea95d23b515f59a74cf.tar.gz
shard-1ee9c3fa6d4259d63685aea95d23b515f59a74cf.zip
BROKE EVERYTHING SORRY
Diffstat (limited to 'shard/lib')
-rw-r--r--shard/lib/keys.ex17
-rw-r--r--shard/lib/manager.ex169
-rw-r--r--shard/lib/net/chan.ex_ (renamed from shard/lib/net/chan.ex)0
-rw-r--r--shard/lib/net/tcpconn.ex211
-rw-r--r--shard/lib/net/tcpserver.ex3
5 files changed, 263 insertions, 137 deletions
diff --git a/shard/lib/keys.ex b/shard/lib/keys.ex
index b6ff461..fe63148 100644
--- a/shard/lib/keys.ex
+++ b/shard/lib/keys.ex
@@ -16,16 +16,7 @@ defmodule Shard.Keys do
def init() do
:dets.start
{:ok, @key_db} = :dets.open_file(@key_db, [type: :set])
-
- case :dets.lookup(@key_db, :peer) do
- [] ->
- Logger.info "Generating peer keypair..."
- {pk, sk} = gen_keypair(Application.get_env(:shard, :peer_id_suffix))
- :dets.insert @key_db, {:peer, pk, sk}
- {pk, sk}
- [{:peer, pk, sk}] ->
- {pk, sk}
- end
+ nil
end
defp gen_keypair(suffix, n \\ 0) do
@@ -44,10 +35,6 @@ defmodule Shard.Keys do
:binary.longest_common_suffix([pk, suffix]) == byte_size(suffix)
end
- def get_peer_keypair() do
- Agent.get(__MODULE__, &(&1))
- end
-
def get_any_identity() do
Agent.get(__MODULE__, fn _ ->
case list_identities() do
@@ -64,7 +51,7 @@ defmodule Shard.Keys do
{pk, sk} = gen_keypair(Application.get_env(:shard, :identity_suffix))
Logger.info "New identity: #{pk|>Base.encode16}"
:dets.insert @key_db, {pk, sk}
- SApp.Identity.start_link(pk)
+ Shard.Manager.Manifest.start %SApp.Identity.Manifest{pk: pk}
pk
end
diff --git a/shard/lib/manager.ex b/shard/lib/manager.ex
index 617378c..ccb750f 100644
--- a/shard/lib/manager.ex
+++ b/shard/lib/manager.ex
@@ -19,38 +19,42 @@ defmodule Shard.Manager do
@moduledoc"""
Maintains several important tables :
- - :peer_db
-
- List of
- { id, pid | nil, ip, port }
-
- - :shard_db
+ - :shard_db (persistent with DETS)
List of
{ id, manifest, pid | nil }
- - :shard_state
+ - :shard_state (persistent with DETS)
List of
{ id, state }
- - :shard_procs
+ - :peer_db (persistent with DETS)
+
+ Mult-list of
+ { shard_id, peer_info } # TODO: add health info (last seen, ping, etc)
+
+ peer_info := {:inet4, ip, port} | {:inet6, ip, port} | {:onion, name}
+
+ - :shard_procs (not persistent)
List of
{ {id, path}, pid }
- - :shard_peer_db
-
- Mult-list of
- { shard_id, peer_id }
+ - :connections (not persistent)
+ List of
+ { nil | his_pk, nil | my_pk, pid, peer_info }
And an internal table :
- - :outbox
+ - :outbox (not persistent)
Multi-list of
- { peer_id, message, time_inserted }
+ { dest, auth_info, message, time_inserted }
+
+ dest := peer_info
+ auth_info := nil | { his_pk, my_pk_list }
"""
@@ -58,23 +62,15 @@ defmodule Shard.Manager do
require Logger
- @peer_db [Application.get_env(:shard, :data_path), "peer_db"] |> Path.join |> String.to_atom
@shard_db [Application.get_env(:shard, :data_path), "shard_db"] |> Path.join |> String.to_atom
@shard_state [Application.get_env(:shard, :data_path), "shard_state"] |> Path.join |> String.to_atom
- @shard_peer_db [Application.get_env(:shard, :data_path), "shard_peer_db"] |> Path.join |> String.to_atom
+ @peer_db [Application.get_env(:shard, :data_path), "peer_db"] |> Path.join |> String.to_atom
def start_link(my_port) do
GenServer.start_link(__MODULE__, my_port, name: __MODULE__)
end
def init(my_port) do
- :dets.open_file(@peer_db, [type: :set])
- for [{id, _pid, ip, port}] <- :dets.match @peer_db, :"$1" do
- :dets.insert @peer_db, {id, nil, ip, port}
- # connect blindly to everyone
- add_peer(ip, port)
- end
-
:dets.open_file(@shard_db, [type: :set])
for [{id, manifest, _pid}] <- :dets.match @shard_db, :"$1" do
:dets.insert @shard_db, {id, manifest, nil}
@@ -82,9 +78,10 @@ defmodule Shard.Manager do
end
:dets.open_file(@shard_state, [type: :set])
- :dets.open_file(@shard_peer_db, [type: :bag])
+ :dets.open_file(@peer_db, [type: :bag])
:ets.new(:shard_procs, [:set, :protected, :named_table])
+ :ets.new(:connections, [:bag, :protected, :named_table])
outbox = :ets.new(:outbox, [:bag, :private])
{:ok, %{my_port: my_port, outbox: outbox} }
@@ -111,12 +108,12 @@ defmodule Shard.Manager do
{:noreply, state}
end
- def handle_cast({:interested, peer_id, shards}, state) do
+ def handle_cast({:interested, peer_info, shards}, state) do
for shard_id <- shards do
case :dets.lookup(@shard_db, shard_id) do
[{ ^shard_id, _, pid }] ->
- :dets.insert(@shard_peer_db, {shard_id, peer_id})
- GenServer.cast(pid, {:interested, peer_id})
+ :dets.insert(@peer_db, {shard_id, peer_info})
+ GenServer.cast(pid, {:interested, peer_info})
[] -> nil
end
end
@@ -209,34 +206,23 @@ defmodule Shard.Manager do
end
- # ================
- # PUBLIC INTERFACE
- # ================
-
+ # ====================
+ # INTERFACE WITH PEERS
+ # ====================
- @doc"""
- Connect to a peer specified by ip address and port
- """
- def add_peer(ip, port) do
- GenServer.cast(__MODULE__, {:add_peer, ip, port})
+ def incoming(conn_pid, {:interested, shards}) do
+ GenServer.cast(__MODULE__, {:interested, peer_id, shards})
end
- @doc"""
- Send message to a peer specified by peer id
- """
- def send(peer_id, msg) do
- case :dets.lookup(@peer_db, peer_id) do
- [{ ^peer_id, pid, _, _}] when pid != nil->
- GenServer.cast(pid, {:send_msg, msg})
- _ ->
- GenServer.cast(__MODULE__, {:connect_and_send, peer_id, msg})
- end
+ def incoming(conn_pid, {:not_interested, shard}) do
+ GenServer.cast(__MODULE__, {:not_interested, peer_id, shard})
end
@doc"""
Dispatch incoming message to correct shard process
"""
- def dispatch(peer_id, {shard_id, path, msg}) do
+ defp dispatch(conn_pid, {shard_id, path, msg}) do
+ # TODO: auth
case :dets.lookup(@shard_db, shard_id) do
[] ->
__MODULE__.send(peer_id, {:not_interested, shard_id})
@@ -255,12 +241,33 @@ defmodule Shard.Manager do
end
end
- def dispatch(peer_id, {:interested, shards}) do
- GenServer.cast(__MODULE__, {:interested, peer_id, shards})
+
+ # =====================
+ # INTERFACE WITH SHARDS
+ # =====================
+
+ @doc"""
+ Send message to a peer specified by peer id
+ """
+ def send(peer_id, msg) do
+ case :dets.lookup(@peer_db, peer_id) do
+ [{ ^peer_id, pid, _, _}] when pid != nil->
+ GenServer.cast(pid, {:send_msg, msg})
+ _ ->
+ GenServer.cast(__MODULE__, {:connect_and_send, peer_id, msg})
+ end
end
- def dispatch(peer_id, {:not_interested, shard}) do
- GenServer.cast(__MODULE__, {:not_interested, peer_id, shard})
+ @doc"""
+ Send message to a peer through an authenticated channel
+
+ his_auth: accepted users to talk to, either single pk or list of pk
+
+ Returns true if a corresponding channel was open and msg was sent,
+ false otherwise.
+ """
+ def send(peer_id, my_auth, his_auth, msg) do
+ # TODO
end
@doc"""
@@ -273,16 +280,6 @@ defmodule Shard.Manager do
end
@doc"""
- Returns the pid for a shard if it exists
- """
- def find_proc(shard_id) do
- case :dets.lookup(@shard_db, shard_id) do
- [{^shard_id, _, pid}] -> pid
- _ -> nil
- end
- end
-
- @doc"""
Register a process as the handler for shard packets for a given path.
"""
def dispatch_to(shard_id, path, pid) do
@@ -290,20 +287,6 @@ defmodule Shard.Manager do
end
@doc"""
- Return the list of all shards.
- """
- def list_shards() do
- for [x] <- :dets.match(@shard_db, :"$1"), do: x
- end
-
- @doc"""
- Return the list of all peers
- """
- def list_peers() do
- for [x] <- :dets.match(@peer_db, :"$1"), do: x
- end
-
- @doc"""
Return the list of all peer IDs that are interested in a certain shard
"""
def get_shard_peers(shard_id) do
@@ -326,4 +309,40 @@ defmodule Shard.Manager do
def save_state(shard_id, state) do
:dets.insert(@shard_state, {shard_id, state})
end
+
+
+ # ==========================
+ # INTERFACE FOR OTHER THINGS
+ # ==========================
+
+ @doc"""
+ Connect to a peer specified by ip address and port
+ """
+ def add_peer(ip, port) do
+ GenServer.cast(__MODULE__, {:add_peer, ip, port})
+ end
+
+ @doc"""
+ Returns the pid for a shard if it exists
+ """
+ def find_proc(shard_id) do
+ case :dets.lookup(@shard_db, shard_id) do
+ [{^shard_id, _, pid}] -> pid
+ _ -> nil
+ end
+ end
+
+ @doc"""
+ Return the list of all shards.
+ """
+ def list_shards() do
+ for [x] <- :dets.match(@shard_db, :"$1"), do: x
+ end
+
+ @doc"""
+ Return the list of all peers
+ """
+ def list_peers() do
+ for [x] <- :dets.match(@peer_db, :"$1"), do: x
+ end
end
diff --git a/shard/lib/net/chan.ex b/shard/lib/net/chan.ex_
index 5aba960..5aba960 100644
--- a/shard/lib/net/chan.ex
+++ b/shard/lib/net/chan.ex_
diff --git a/shard/lib/net/tcpconn.ex b/shard/lib/net/tcpconn.ex
index 543341a..35bf9d1 100644
--- a/shard/lib/net/tcpconn.ex
+++ b/shard/lib/net/tcpconn.ex
@@ -1,7 +1,15 @@
defmodule SNet.TCPConn do
+ @moduledoc"""
+ Secret handshake as described in this document:
+ https://ssbc.github.io/scuttlebutt-protocol-guide/#peer-connections
+
+ Does not implement the stream protocol, we don't hide the length of packets.
+ (TODO ^)
+ """
+
+
use GenServer, restart: :temporary
- require Salty.Box.Curve25519xchacha20poly1305, as: Box
- require Salty.Sign.Ed25519, as: Sign
+
require Logger
def start_link(state) do
@@ -9,60 +17,166 @@ defmodule SNet.TCPConn do
end
def init(state) do
- GenServer.cast(self(), :handshake)
- {:ok, state}
- end
+ if state.is_client do
+ GenServer.cast(self(), :client_handshake)
+ else
+ GenServer.cast(self(), :server_handshake)
+ end
- def handle_call(:get_host_str, _from, state) do
- {:reply, "#{state.his_pkey|>Base.encode16|>String.downcase}@#{to_string(:inet_parse.ntoa(state.addr))}:#{state.port}", state}
+ {:ok, {addr, port}} = :inet.peername state.socket
+ {:ok, %{state | addr: addr, port: port}}
end
- def handle_cast(:handshake, state) do
- socket = state.socket
- {srv_pkey, srv_skey} = Shard.Keys.get_peer_keypair
- {:ok, sess_pkey, sess_skey} = Box.keypair
- {:ok, challenge} = Salty.Random.buf 32
+ def handle_call(:get_peer_info, _from, state) do
+ {:reply, {:tcp4, state.addr, state.port}, state}
+ end
- # Exchange public keys and 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, 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)
- pkt = encode_pkt(cli_challenge_sign, cli_sess_pkey, sess_skey)
- :gen_tcp.send(socket, pkt)
+ def handle_cast(:client_handshake, state) do
+ socket = state.socket
- {:ok, pkt} = :gen_tcp.recv(socket, 0)
- challenge_sign = decode_pkt(pkt, cli_sess_pkey, sess_skey)
- :ok = Sign.verify_detached(challenge_sign, challenge, cli_pkey)
+ net_key = Application.get_env(:shard, :network_key)
+ {:ok, cli_eph_pk, cli_eph_sk} = :enacl.box_keypair
+
+ [srv_longterm_pk] = state.his_auth
+ cli_longterm_pk = state.my_auth
+ cli_longterm_sk = Keys.get_sk cli_longterm_pk
+
+ # 1. Client hello
+ {:ok, cli_hello_hmac} = :enacl.auth(cli_eph_pk, net_key)
+ cli_hello = cli_hello_hmac <> cli_eph_pk
+ :gen_tcp.send(socket, cli_hello)
+
+ # 2. Server hello
+ {:ok, srv_hello} = :gen_tcp.recv(socket, 0)
+ 64 = byte_size srv_hello
+ srv_hmac = :binary.part srv_hello, 0, 32
+ srv_eph_pk = :binary.part srv_hello, 32, 32
+ true = :enacl.auth_verify(srv_hmac, srv_eph_pk, net_key)
+
+ # Shared secret derivation
+ sh_sec_ab = :enacl.curve25519_scalarmult(cli_eph_sk, srv_eph_pk)
+ sh_sec_aB = :enacl.curve25519_scalarmult(cli_eph_sk, :enacl.crypto_sign_ed25519_public_to_curve25519(srv_longterm_pk))
+
+ # 3. Client authenticate
+ msg1 = net_key <> srv_longterm_pk <> :crypto.hash(:sha256, sh_sec_ab)
+ det_sign_A = :enacl.sign_detached(msg1, cli_longterm_sk)
+ key3 = :crypto.hash(:sha256, net_key <> sh_sec_ab <> sh_sec_aB)
+ cli_auth = :crypto.secretbox(det_sign_A <> cli_longterm_pk, <<0 :: 24*8>>, key3)
+ :gen_tcp.send(socket, cli_auth)
+
+ # Shared secret derivation, again
+ sh_sec_Ab = :enacl.curve25519_scalarmult(:enacl.crypto_sign_ed25519_secret_to_curve25519(cli_longterm_sk), srv_eph_pk)
+
+ # 4. Server accept
+ {:ok, srv_accept} = :gen_tcp.recv(socket, 0)
+ key4 = :crypto.hash(:sha256, net_key <> sh_sec_ab <> sh_sec_aB <> sh_sec_Ab)
+ {:ok, det_sign_B} = :enacl.secretbox_open(srv_accept, <<0 :: 24*8>>, key4)
+ true = :enacl.sign_verify_detached(det_sign_B, net_key <> det_sign_A <> cli_longterm_pk <> :crypto.sha256(sh_sec_ab), srv_longterm_pk)
+
+ # Derive secrets and initial nonces for stream communication
+ secret_common = :crypto.hash(:sha256, :crypto.hash(:sha256, net_key <> sh_sec_ab <> sh_sec_aB <> sh_sec_Ab))
+ secret_cli2srv = :crypto.hash(:sha256, secret_common <> srv_longterm_pk)
+ secret_srv2cli = :crypto.hash(:sha256, secret_common <> cli_longterm_pk)
+ {:ok, hmac1} = :enacl.auth(srv_eph_pk, net_key)
+ nonce_cli2srv = :binary.part(hmac1, 0, 24)
+ {:ok, hmac2} = :enacl.auth(cli_eph_pk, net_key)
+ nonce_srv2cli = :binary.part(hmac2, 0, 24)
+
+ # Set up the rest
+ :inet.setopts(socket, [active: true])
+ {:ok, {addr, port}} = :inet.peername socket
+ state = %{
+ socket: socket,
+ my_pk: cli_longterm_pk,
+ his_pk: srv_longterm_pk,
+ secret_send: secret_cli2srv,
+ secret_recv: secret_srv2cli,
+ nonce_send: nonce_cli2srv,
+ nonce_recv: nonce_srv2cli,
+ addr: addr,
+ port: port,
+ # his_port: his_port
+ }
+
+ GenServer.cast(Shard.Manager, {:peer_up, state.his_pk, self(), addr, port})
+ Logger.info "New peer: #{print_id state} at #{inspect addr}:#{port}"
- expected_suffix = Application.get_env(:shard, :peer_id_suffix)
- len = byte_size(expected_suffix)
- ^len = :binary.longest_common_suffix([cli_pkey, expected_suffix])
+ {:noreply, state}
+ end
- if srv_pkey == cli_pkey do
- exit :normal
- end
+ def handle_cast(:server_handshake, state) do
+ socket = state.socket
- # Connected
+ net_key = Application.get_env(:shard, :network_key)
+ {:ok, srv_eph_pk, srv_eph_sk} = :enacl.box_keypair
+
+ srv_longterm_pk = state.my_auth
+ srv_longterm_sk = Keys.get_sk srv_longterm_pk
+
+ # 1. Client hello
+ {:ok, cli_hello} = :gen_tcp.recv(socket, 0)
+ 64 = byte_size cli_hello
+ cli_hmac = :binary.part cli_hello, 0, 32
+ cli_eph_pk = :binary.part cli_hello, 32, 32
+ true = :enacl.auth_verify(cli_hmac, cli_eph_pk, net_key)
+
+ # 2. Server hello
+ {:ok, srv_hello_hmac} = :enacl.auth(srv_eph_pk, net_key)
+ srv_hello = srv_hello_hmac <> srv_eph_pk
+ :gen_tcp.send(socket, srv_hello)
+
+ # Shared secret derivation
+ sh_sec_ab = :enacl.curve25519_scalarmult(srv_eph_sk, cli_eph_pk)
+ sh_sec_aB = :enacl.curve25519_scalarmult(:enacl.crypto_sign_ed25519_secret_to_curve25519(srv_longterm_sk), cli_eph_pk)
+
+ # 3. Client authenticate
+ {:ok, cli_auth} = :gen_tcp.recv(socket, 0)
+ key3 = :crypto.hash(:sha256, net_key <> sh_sec_ab <> sh_sec_aB)
+ {:ok, cli_auth_plain} = :enacl.secretbox_open(cli_auth, <<0 :: 24*8>>, key3)
+ 96 = byte_size cli_auth_plain
+ det_sign_A = :binary.part(cli_auth_plain, 0, 64)
+ cli_longterm_pk = :binary.part(cli_auth_plain, 64, 32)
+ true = :enacl.sign_verify_deteached(det_sign_A, net_key <> srv_longterm_pk <> :crypto.hash(:sha256, sh_sec_ab), cli_longterm_pk)
+
+ # Shared secret derivation
+ sh_sec_Ab = :enacl.curve25519_scalarmult(srv_eph_sk, :enacl.crypto_sign_ed25519_public_to_curve25519(cli_longterm_pk))
+
+ # TODO: here we can stop if we don't like the client's longterm pk
+
+ # 4. Server accept
+ det_sign_B = :enacl.sign_detached(net_key <> det_sign_A <> cli_longterm_pk <> :crypto.hash(:sha256, sh_sec_ab), srv_longterm_sk)
+ key4 = :crypto.hash(:sha256, net_key <> sh_sec_ab <> sh_sec_aB <> sh_sec_Ab)
+ msg4 = :enacl.secretbox(det_sign_B, <<0 :: 24*8>>, key4)
+ :gen_tcp.send(socket, msg4)
+
+ # Derive secrets and initial nonces for stream communication
+ secret_common = :crypto.hash(:sha256, :crypto.hash(:sha256, net_key <> sh_sec_ab <> sh_sec_aB <> sh_sec_Ab))
+ secret_cli2srv = :crypto.hash(:sha256, secret_common <> srv_longterm_pk)
+ secret_srv2cli = :crypto.hash(:sha256, secret_common <> cli_longterm_pk)
+ {:ok, hmac1} = :enacl.auth(srv_eph_pk, net_key)
+ nonce_cli2srv = :binary.part(hmac1, 0, 24)
+ {:ok, hmac2} = :enacl.auth(cli_eph_pk, net_key)
+ nonce_srv2cli = :binary.part(hmac2, 0, 24)
+
+ # Set up the rest
:inet.setopts(socket, [active: true])
-
{:ok, {addr, port}} = :inet.peername socket
- state =%{ socket: socket,
- my_pkey: srv_pkey,
- my_skey: srv_skey,
- his_pkey: cli_pkey,
- conn_my_pkey: sess_pkey,
- conn_my_skey: sess_skey,
- conn_his_pkey: cli_sess_pkey,
- addr: addr,
- port: port,
- his_port: his_port
- }
- GenServer.cast(Shard.Manager, {:peer_up, cli_pkey, self(), addr, his_port})
+ state = %{
+ socket: socket,
+ my_pk: cli_longterm_pk,
+ his_pk: srv_longterm_pk,
+ secret_send: secret_srv2cli,
+ secret_recv: secret_cli2srv,
+ nonce_send: nonce_srv2cli,
+ nonce_recv: nonce_cli2srv,
+ addr: addr,
+ port: port,
+ # his_port: his_port
+ }
+
+ GenServer.cast(Shard.Manager, {:peer_up, state.his_pk, self(), addr, port})
Logger.info "New peer: #{print_id state} at #{inspect addr}:#{port}"
{:noreply, state}
@@ -75,6 +189,11 @@ defmodule SNet.TCPConn do
{:noreply, state}
end
+ defp next_nonce(nonce) do
+ i = :crypto.bytes_to_integer(nonce)
+ <<i+1 :: 24*8>>
+ end
+
defp encode_pkt(pkt, pk, sk) do
{:ok, n} = Salty.Random.buf Box.noncebytes
{:ok, msg} = Box.easy(pkt, n, pk, sk)
@@ -91,7 +210,7 @@ defmodule SNet.TCPConn do
def handle_info({:tcp, _socket, raw_data}, state) do
msg = decode_pkt(raw_data, state.conn_his_pkey, state.conn_my_skey)
msg_data = :erlang.binary_to_term(msg, [:safe])
- Shard.Manager.dispatch(state.his_pkey, msg_data)
+ Shard.Manager.incoming(state.his_pkey, msg_data)
{:noreply, state}
end
@@ -102,7 +221,7 @@ defmodule SNet.TCPConn do
end
defp print_id(state) do
- state.his_pkey
+ state.his_pk
|> binary_part(0, 8)
|> Base.encode16
|> String.downcase
diff --git a/shard/lib/net/tcpserver.ex b/shard/lib/net/tcpserver.ex
index 46552a4..1aa5738 100644
--- a/shard/lib/net/tcpserver.ex
+++ b/shard/lib/net/tcpserver.ex
@@ -18,7 +18,8 @@ defmodule SNet.TCPServer do
defp loop_acceptor(socket, my_port) do
{:ok, client} = :gen_tcp.accept(socket)
- {:ok, pid} = DynamicSupervisor.start_child(Shard.DynamicSupervisor, {SNet.TCPConn, %{socket: client, my_port: my_port}})
+ {:ok, pid} = DynamicSupervisor.start_child(Shard.DynamicSupervisor,
+ {SNet.TCPConn, %{socket: client, my_port: my_port, is_client: false}})
:ok = :gen_tcp.controlling_process(client, pid)
loop_acceptor(socket, my_port)
end