@@ -3,9 +3,10 @@ defmodule ExICE.Candidate do
33 ICE candidate representation.
44 """
55
6- @ type type ( ) :: :host | :srflx | :prflx | :relay
6+ @ type type :: :host | :srflx | :prflx | :relay
7+ @ type tcp_type :: :active | :passive | :so
78
8- @ type t ( ) :: % __MODULE__ {
9+ @ type t :: % __MODULE__ {
910 id: integer ( ) ,
1011 type: type ( ) ,
1112 address: :inet . ip_address ( ) | String . t ( ) ,
@@ -14,7 +15,8 @@ defmodule ExICE.Candidate do
1415 foundation: integer ( ) ,
1516 port: :inet . port_number ( ) ,
1617 priority: integer ( ) ,
17- transport: :udp | :tcp
18+ transport: :udp | :tcp ,
19+ tcp_type: tcp_type ( ) | nil
1820 }
1921
2022 @ enforce_keys [
@@ -24,7 +26,8 @@ defmodule ExICE.Candidate do
2426 :port ,
2527 :foundation ,
2628 :priority ,
27- :transport
29+ :transport ,
30+ :tcp_type
2831 ]
2932 defstruct @ enforce_keys ++ [ :base_address , :base_port ]
3033
@@ -38,7 +41,8 @@ defmodule ExICE.Candidate do
3841 priority: priority ,
3942 address: address ,
4043 port: port ,
41- type: type
44+ type: type ,
45+ tcp_type: tcp_type
4246 } = cand
4347
4448 # This is based on RFC 8839 sec. 5.1.
@@ -54,31 +58,45 @@ defmodule ExICE.Candidate do
5458
5559 transport = transport_to_string ( transport )
5660 address = address_to_string ( address )
57-
58- "#{ foundation } #{ component_id } #{ transport } #{ priority } #{ address } #{ port } typ #{ type } #{ related_addr } "
59- |> String . trim ( )
61+ tcp_type = tcp_type_to_string ( tcp_type )
62+
63+ [
64+ foundation ,
65+ component_id ,
66+ transport ,
67+ priority ,
68+ address ,
69+ port ,
70+ "typ" ,
71+ type ,
72+ related_addr ,
73+ tcp_type
74+ ]
75+ |> Enum . reject ( & ( & 1 == "" ) )
76+ |> Enum . join ( " " )
6077 end
6178
6279 @ spec unmarshal ( String . t ( ) ) :: { :ok , t ( ) } | { :error , term ( ) }
6380 def unmarshal ( string ) do
64- with [ f_str , c_str , tr_str , pr_str , a_str , po_str , "typ" , ty_str ] <-
65- String . split ( string , " " , parts: 8 ) ,
81+ with [ f_str , c_str , tr_str , pr_str , a_str , po_str , "typ" , ty_str | rest ] <-
82+ String . split ( string , " " ) ,
6683 { foundation , "" } <- Integer . parse ( f_str ) ,
6784 { _component_id , "" } <- Integer . parse ( c_str ) ,
6885 { :ok , transport } <- parse_transport ( String . downcase ( tr_str ) ) ,
6986 { priority , "" } <- Integer . parse ( pr_str ) ,
7087 { :ok , address } <- parse_address ( a_str ) ,
7188 { port , "" } <- Integer . parse ( po_str ) ,
72- { :ok , type } <- parse_type ( ty_str ) do
73- { :ok ,
74- new (
75- type ,
76- address: address ,
77- port: port ,
78- priority: priority ,
79- foundation: foundation ,
80- transport: transport
81- ) }
89+ { :ok , type } <- parse_type ( ty_str ) ,
90+ { :ok , extra_config } <- parse_optional_attributes ( rest ) do
91+ config = [
92+ address: address ,
93+ port: port ,
94+ priority: priority ,
95+ foundation: foundation ,
96+ transport: transport
97+ ]
98+
99+ { :ok , new ( type , config ++ extra_config ) }
82100 else
83101 err when is_list ( err ) -> { :error , :invalid_candidate }
84102 err -> err
@@ -89,12 +107,16 @@ defmodule ExICE.Candidate do
89107 def family ( % __MODULE__ { address: { _ , _ , _ , _ } } ) , do: :ipv4
90108 def family ( % __MODULE__ { address: { _ , _ , _ , _ , _ , _ , _ , _ } } ) , do: :ipv6
91109
110+ def tcp_type ( % __MODULE__ { tcp_type: tt } ) , do: tt
111+
92112 @ doc false
93113 @ spec new ( type ( ) , Keyword . t ( ) ) :: t ( )
94114 def new ( type , config ) when type in [ :host , :srflx , :prflx , :relay ] do
95115 transport = Keyword . get ( config , :transport , :udp )
96116 address = Keyword . fetch! ( config , :address )
97117
118+ tcp_type = if transport == :tcp , do: Keyword . fetch! ( config , :tcp_type )
119+
98120 % __MODULE__ {
99121 id: ExICE.Priv.Utils . id ( ) ,
100122 address: address ,
@@ -104,16 +126,22 @@ defmodule ExICE.Candidate do
104126 port: Keyword . fetch! ( config , :port ) ,
105127 priority: Keyword . fetch! ( config , :priority ) ,
106128 transport: transport ,
107- type: type
129+ type: type ,
130+ tcp_type: tcp_type
108131 }
109132 end
110133
111134 defp address_to_string ( address ) when is_binary ( address ) , do: address
112135 defp address_to_string ( address ) , do: :inet . ntoa ( address )
113136
114137 defp transport_to_string ( :udp ) , do: "UDP"
138+ defp transport_to_string ( :tcp ) , do: "TCP"
139+
140+ defp tcp_type_to_string ( nil ) , do: ""
141+ defp tcp_type_to_string ( type ) , do: "tcptype #{ type } "
115142
116143 defp parse_transport ( "udp" ) , do: { :ok , :udp }
144+ defp parse_transport ( "tcp" ) , do: { :ok , :tcp }
117145 defp parse_transport ( _other ) , do: { :error , :invalid_transport }
118146
119147 defp parse_address ( address ) do
@@ -124,9 +152,29 @@ defmodule ExICE.Candidate do
124152 end
125153 end
126154
127- defp parse_type ( "host" <> _rest ) , do: { :ok , :host }
128- defp parse_type ( "srflx" <> _rest ) , do: { :ok , :srflx }
129- defp parse_type ( "prflx" <> _rest ) , do: { :ok , :prflx }
130- defp parse_type ( "relay" <> _rest ) , do: { :ok , :relay }
155+ defp parse_type ( "host" ) , do: { :ok , :host }
156+ defp parse_type ( "srflx" ) , do: { :ok , :srflx }
157+ defp parse_type ( "prflx" ) , do: { :ok , :prflx }
158+ defp parse_type ( "relay" ) , do: { :ok , :relay }
131159 defp parse_type ( _other ) , do: { :error , :invalid_type }
160+
161+ defp parse_optional_attributes ( list , config \\ [ ] )
162+ defp parse_optional_attributes ( [ ] , config ) , do: { :ok , config }
163+
164+ defp parse_optional_attributes ( [ "raddr" , _2 , _3 , _4 | rest ] , config ) ,
165+ do: parse_optional_attributes ( rest , config )
166+
167+ defp parse_optional_attributes ( [ "tcptype" , tcp_type | rest ] , config ) do
168+ case parse_tcp_type ( tcp_type ) do
169+ { :ok , tcp_type } -> parse_optional_attributes ( rest , config ++ [ tcp_type: tcp_type ] )
170+ err -> err
171+ end
172+ end
173+
174+ defp parse_optional_attributes ( _other , config ) , do: { :ok , config }
175+
176+ defp parse_tcp_type ( "active" ) , do: { :ok , :active }
177+ defp parse_tcp_type ( "passive" ) , do: { :ok , :passive }
178+ defp parse_tcp_type ( "so" ) , do: { :ok , :so }
179+ defp parse_tcp_type ( _other ) , do: { :error , :invalid_tcp_type }
132180end
0 commit comments