diff --git a/lib/ex_ice/priv/candidate/relay.ex b/lib/ex_ice/priv/candidate/relay.ex index e0fe6d7..99042bc 100644 --- a/lib/ex_ice/priv/candidate/relay.ex +++ b/lib/ex_ice/priv/candidate/relay.ex @@ -25,20 +25,44 @@ defmodule ExICE.Priv.Candidate.Relay do @impl true def send_data(cand, dst_ip, dst_port, data) do - if MapSet.member?(cand.client.permissions, dst_ip) do - {:send, turn_addr, data, client} = ExTURN.Client.send(cand.client, {dst_ip, dst_port}, data) - cand = %{cand | client: client} - do_send(cand, turn_addr, data) - else - {:send, turn_addr, turn_data, client} = ExTURN.Client.create_permission(cand.client, dst_ip) - - cand = %{ - cand - | client: client, - buffered_packets: [{dst_ip, dst_port, data} | cand.buffered_packets] - } - - do_send(cand, turn_addr, turn_data) + permission = ExTURN.Client.has_permission?(cand.client, dst_ip) + channel = ExTURN.Client.has_channel?(cand.client, dst_ip, dst_port) + + case {permission, channel} do + {true, true} -> + {:send, turn_addr, data, client} = + ExTURN.Client.send(cand.client, {dst_ip, dst_port}, data) + + cand = %{cand | client: client} + do_send(cand, turn_addr, data) + + {true, false} -> + {:send, turn_addr, data, client} = + ExTURN.Client.send(cand.client, {dst_ip, dst_port}, data) + + cand = %{cand | client: client} + + case ExTURN.Client.create_channel(cand.client, dst_ip, dst_port) do + {:ok, client} -> + cand = %{cand | client: client} + do_send(cand, turn_addr, data) + + {:send, ^turn_addr, channel_data, client} -> + cand = %{cand | client: client} + + with {:ok, cand} <- do_send(cand, turn_addr, data) do + do_send(cand, turn_addr, channel_data) + end + end + + {false, false} -> + {:send, turn_addr, turn_data, client} = + ExTURN.Client.create_permission(cand.client, dst_ip) + + buffered_data = [{dst_ip, dst_port, data} | cand.buffered_packets] + cand = %{cand | client: client, buffered_packets: buffered_data} + + do_send(cand, turn_addr, turn_data) end end @@ -79,13 +103,12 @@ defmodule ExICE.Priv.Candidate.Relay do defp do_send_buffered_packets(cand, []), do: {:ok, cand} defp do_send_buffered_packets(cand, [{dst_ip, dst_port, packet} | packets]) do - {:send, turn_addr, data, client} = ExTURN.Client.send(cand.client, {dst_ip, dst_port}, packet) - - cand = %{cand | client: client} + case send_data(cand, dst_ip, dst_port, packet) do + {:ok, cand} -> + do_send_buffered_packets(cand, packets) - case do_send(cand, turn_addr, data) do - {:ok, cand} -> do_send_buffered_packets(cand, packets) - {:error, _reason, _cand} = error -> error + {:error, _reaons, _cand} = error -> + error end end diff --git a/lib/ex_ice/priv/gatherer.ex b/lib/ex_ice/priv/gatherer.ex index 9339211..214e316 100644 --- a/lib/ex_ice/priv/gatherer.ex +++ b/lib/ex_ice/priv/gatherer.ex @@ -60,6 +60,8 @@ defmodule ExICE.Priv.Gatherer do inet ]) do {:ok, socket} -> + {:ok, {^ip, sock_port}} = gatherer.transport_module.sockname(socket) + Logger.debug("Successfully open socket for: #{inspect(ip)}:#{sock_port}") socket {:error, reason} -> diff --git a/lib/ex_ice/priv/ice_agent.ex b/lib/ex_ice/priv/ice_agent.ex index b03808a..27c2cbb 100644 --- a/lib/ex_ice/priv/ice_agent.ex +++ b/lib/ex_ice/priv/ice_agent.ex @@ -519,7 +519,8 @@ defmodule ExICE.Priv.ICEAgent do |> update_gathering_state() end - {nil, cand} -> + # tr_id_tr might be nil or might be present with state == :complete + {_, cand} -> case ExTURN.Client.handle_message(cand.client, msg) do {:ok, client} -> cand = %{cand | client: client} @@ -866,7 +867,8 @@ defmodule ExICE.Priv.ICEAgent do case msg.type do %Type{class: :request, method: :binding} -> Logger.debug(""" - Received binding request from: #{inspect({src_ip, src_port})}, on: #{inspect(local_cand.base.base_address)} \ + Received binding request from: #{inspect({src_ip, src_port})}, \ + on: #{inspect({local_cand.base.base_address, local_cand.base.base_port})} \ """) handle_binding_request(ice_agent, local_cand, src_ip, src_port, msg) @@ -874,7 +876,8 @@ defmodule ExICE.Priv.ICEAgent do %Type{class: class, method: :binding} when is_response(class) and is_map_key(ice_agent.conn_checks, msg.transaction_id) -> Logger.debug(""" - Received conn check response from: #{inspect({src_ip, src_port})}, on: #{inspect(local_cand.base.base_address)} \ + Received conn check response from: #{inspect({src_ip, src_port})}, \ + on: #{inspect({local_cand.base.base_address, local_cand.base.base_port})} \ """) handle_conn_check_response(ice_agent, local_cand, src_ip, src_port, msg) @@ -882,7 +885,8 @@ defmodule ExICE.Priv.ICEAgent do %Type{class: class, method: :binding} when is_response(class) and is_map_key(ice_agent.gathering_transactions, msg.transaction_id) -> Logger.debug(""" - Received gathering transaction response from: #{inspect({src_ip, src_port})}, on: #{inspect(local_cand.base.base_address)} \ + Received gathering transaction response from: #{inspect({src_ip, src_port})}, \ + on: #{inspect({local_cand.base.base_address, local_cand.base.base_port})} \ """) handle_stun_gathering_transaction_response(ice_agent, msg) diff --git a/mix.exs b/mix.exs index 22d31da..1165014 100644 --- a/mix.exs +++ b/mix.exs @@ -52,7 +52,8 @@ defmodule ExICE.MixProject do [ # {:ex_stun, "~> 0.1.0"}, {:ex_stun, github: "elixir-webrtc/ex_stun"}, - {:ex_turn, github: "elixir-webrtc/ex_turn"}, + # {:ex_turn, github: "elixir-webrtc/ex_turn"}, + {:ex_turn, github: "elixir-webrtc/ex_turn", branch: "utils"}, {:excoveralls, "~> 0.15", only: :test, runtime: false}, {:ex_doc, "~> 0.27", only: :dev, runtime: false}, {:credo, "~> 1.6", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index a979fdf..f34ebf9 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, "ex_stun": {:git, "https://github.com/elixir-webrtc/ex_stun.git", "5d1243a6c3268d0cb402c6272ae6e0df1615779a", []}, - "ex_turn": {:git, "https://github.com/elixir-webrtc/ex_turn.git", "14df4a546f2e19a85731eef70258c490a71e856d", []}, + "ex_turn": {:git, "https://github.com/elixir-webrtc/ex_turn.git", "2cfa9cb0abfa9185e378f9d3ba30476fd0398d2c", [branch: "utils"]}, "excoveralls": {:hex, :excoveralls, "0.18.1", "a6f547570c6b24ec13f122a5634833a063aec49218f6fff27de9df693a15588c", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d65f79db146bb20399f23046015974de0079668b9abb2f5aac074d078da60b8d"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, diff --git a/test/priv/gatherer_test.exs b/test/priv/gatherer_test.exs index 3d9e26a..7199fed 100644 --- a/test/priv/gatherer_test.exs +++ b/test/priv/gatherer_test.exs @@ -58,7 +58,7 @@ defmodule ExICE.Priv.GathererTest do [%Candidate.Host{} = c] = Gatherer.gather_host_candidates(gatherer, sockets) assert :ok = Gatherer.gather_srflx_candidate(gatherer, 1234, c.base.socket, stun_server) - assert [{_socket, packet}] = :ets.lookup(:transport_mock, c.base.socket) + assert packet = Transport.Mock.recv(c.base.socket) assert {:ok, req} = ExSTUN.Message.decode(packet) assert req.attributes == [] assert req.type == %Type{class: :request, method: :binding} diff --git a/test/priv/ice_agent_test.exs b/test/priv/ice_agent_test.exs index 185ad46..c938ce3 100644 --- a/test/priv/ice_agent_test.exs +++ b/test/priv/ice_agent_test.exs @@ -977,7 +977,7 @@ defmodule ExICE.Priv.ICEAgentTest do ice_agent = ICEAgent.handle_udp(ice_agent, socket, @turn_ip, @turn_port, resp) - # assert client sends binding request + # assert client sends ice binding request and channel bind request assert packet = Transport.Mock.recv(socket) assert {:ok, req} = ExSTUN.Message.decode(packet) assert req.type.class == :indication @@ -988,6 +988,11 @@ defmodule ExICE.Priv.ICEAgentTest do assert req.type.class == :request assert req.type.method == :binding + assert packet = Transport.Mock.recv(socket) + assert {:ok, channel_req} = ExSTUN.Message.decode(packet) + assert channel_req.type.class == :request + assert channel_req.type.method == :channel_bind + # send binding success response resp = Message.new(req.transaction_id, %Type{class: :success_response, method: :binding}, [ @@ -1028,8 +1033,27 @@ defmodule ExICE.Priv.ICEAgentTest do ]) |> Message.encode() - _ice_agent = ICEAgent.handle_udp(ice_agent, socket, @turn_ip, @turn_port, indication) + ice_agent = ICEAgent.handle_udp(ice_agent, socket, @turn_ip, @turn_port, indication) assert_receive {:ex_ice, _pid, {:data, "someremotedata"}} + + # send channel bind success response + channel_resp = + Message.new( + channel_req.transaction_id, + %Type{class: :success_response, method: :channel_bind}, + [] + ) + |> Message.with_integrity(Message.lt_key(@turn_username, @turn_password, @turn_realm)) + |> Message.encode() + + ice_agent = ICEAgent.handle_udp(ice_agent, socket, @turn_ip, @turn_port, channel_resp) + + # try to once again send some data, this time it should be sent over channel + _ice_agent = ICEAgent.send_data(ice_agent, "somedata") + assert packet = Transport.Mock.recv(socket) + assert nil == Transport.Mock.recv(socket) + assert ExTURN.channel_data?(packet) + assert <<_channel_number::16, _len::16, "somedata">> = packet end defp binding_response(t_id, transport_module, socket, remote_pwd) do diff --git a/test/support/transport/mock.ex b/test/support/transport/mock.ex index 16a58d7..1bf2a2d 100644 --- a/test/support/transport/mock.ex +++ b/test/support/transport/mock.ex @@ -28,8 +28,14 @@ defmodule ExICE.Support.Transport.Mock do @spec recv(ExICE.Transport.socket()) :: binary() | nil def recv(socket) do - [{_socket, packet}] = :ets.lookup(:transport_mock, socket) - packet + case :ets.lookup(:transport_mock, socket) do + [{_socket, []}] -> + nil + + [{_socket, [head | tail]}] -> + :ets.insert(:transport_mock, {socket, tail}) + head + end end @impl true @@ -57,7 +63,7 @@ defmodule ExICE.Support.Transport.Mock do port -> socket = %{port: port, ip: ip} - unless :ets.insert_new(:transport_mock, {socket, nil}) do + unless :ets.insert_new(:transport_mock, {socket, []}) do raise "Couldn't open socket: #{inspect(socket)}, reason: eaddrinuse." end @@ -72,7 +78,8 @@ defmodule ExICE.Support.Transport.Mock do @impl true def send(socket, _dst, packet) do - :ets.insert(:transport_mock, {socket, packet}) + [{_socket, buffer}] = :ets.lookup(:transport_mock, socket) + :ets.insert(:transport_mock, {socket, buffer ++ [packet]}) :ok end @@ -86,7 +93,7 @@ defmodule ExICE.Support.Transport.Mock do Enum.find_value(49_152..65_535, fn port -> socket = %{ip: ip, port: port} - if :ets.insert_new(:transport_mock, {socket, nil}) do + if :ets.insert_new(:transport_mock, {socket, []}) do socket else false