Skip to content

Commit 79085a9

Browse files
committed
Accept non-numeric ICE candidate foundations per RFC 8445
RFC 8839 Section 5.1 defines foundation as 1*32ice-char where ice-char = ALPHA / DIGIT / "+" / "/". Implementations like aiortc generate hex string foundations which caused a crash in unmarshal/1. - Add parse_foundation/1 that accepts both integer and string foundations - Preserve parsed foundation from remote candidates instead of recomputing - Fix else clause in unmarshal/1 to always return {:error, _} tuples Closes #96
1 parent 714bc5b commit 79085a9

2 files changed

Lines changed: 41 additions & 4 deletions

File tree

lib/ex_ice/candidate.ex

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ defmodule ExICE.Candidate do
1212
address: :inet.ip_address() | String.t(),
1313
base_address: :inet.ip_address() | nil,
1414
base_port: :inet.port_number() | nil,
15-
foundation: integer(),
15+
foundation: non_neg_integer() | String.t(),
1616
port: :inet.port_number(),
1717
priority: integer(),
1818
transport: :udp | :tcp,
@@ -80,7 +80,7 @@ defmodule ExICE.Candidate do
8080
def unmarshal(string) do
8181
with [f_str, c_str, tr_str, pr_str, a_str, po_str, "typ", ty_str | rest] <-
8282
String.split(string, " "),
83-
{foundation, ""} <- Integer.parse(f_str),
83+
{:ok, foundation} <- parse_foundation(f_str),
8484
{_component_id, ""} <- Integer.parse(c_str),
8585
{:ok, transport} <- parse_transport(String.downcase(tr_str)),
8686
{priority, ""} <- Integer.parse(pr_str),
@@ -96,10 +96,12 @@ defmodule ExICE.Candidate do
9696
transport: transport
9797
]
9898

99-
{:ok, new(type, config ++ extra_config)}
99+
candidate = new(type, config ++ extra_config)
100+
{:ok, %{candidate | foundation: foundation}}
100101
else
101102
err when is_list(err) -> {:error, :invalid_candidate}
102-
err -> err
103+
{:error, _} = err -> err
104+
_other -> {:error, :invalid_candidate}
103105
end
104106
end
105107

@@ -141,6 +143,14 @@ defmodule ExICE.Candidate do
141143
defp tcp_type_to_string(nil), do: ""
142144
defp tcp_type_to_string(type), do: "tcptype #{type}"
143145

146+
defp parse_foundation(str) do
147+
case Integer.parse(str) do
148+
{n, ""} -> {:ok, n}
149+
_ when byte_size(str) in 1..32 -> {:ok, str}
150+
_ -> {:error, :invalid_foundation}
151+
end
152+
end
153+
144154
defp parse_transport("udp"), do: {:ok, :udp}
145155
defp parse_transport("tcp"), do: {:ok, :tcp}
146156
defp parse_transport(_other), do: {:error, :invalid_transport}

test/candidate_test.exs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,31 @@ defmodule ExICE.CandidateTest do
8383
c = %Candidate{c | id: expected_c.id}
8484
assert c == expected_c
8585
end
86+
87+
test "unmarshal/1 with string foundation" do
88+
m_c = "834be7808a5c955b681935b0c6ad99df 1 UDP 2130706431 192.168.1.74 55474 typ host"
89+
90+
assert {:ok, c} = Candidate.unmarshal(m_c)
91+
assert c.foundation == "834be7808a5c955b681935b0c6ad99df"
92+
assert c.address == {192, 168, 1, 74}
93+
assert c.port == 55_474
94+
assert c.priority == 2_130_706_431
95+
assert c.type == :host
96+
assert c.transport == :udp
97+
end
98+
99+
test "unmarshal/1 preserves numeric foundation as integer" do
100+
m_c = "936255739 1 UDP 1234 192.168.1.1 12345 typ host"
101+
102+
assert {:ok, c} = Candidate.unmarshal(m_c)
103+
assert c.foundation == 936_255_739
104+
assert is_integer(c.foundation)
105+
end
106+
107+
test "marshal/1 roundtrips with string foundation" do
108+
m_c = "834be7808a5c955b681935b0c6ad99df 1 UDP 2130706431 192.168.1.74 55474 typ host"
109+
110+
assert {:ok, c} = Candidate.unmarshal(m_c)
111+
assert Candidate.marshal(c) == m_c
112+
end
86113
end

0 commit comments

Comments
 (0)