1717//-----------------------------------------------------------------------------
1818
1919using System ;
20+ using System . ComponentModel ;
2021using System . Net . Sockets ;
2122using SIPSorcery . Sys ;
2223
@@ -79,29 +80,29 @@ public enum STUNProtocolsEnum
7980 dtls = 4 ,
8081 }
8182
82- public class STUNUri
83+ public sealed class STUNUri : IEquatable < STUNUri >
8384 {
8485 public const string SCHEME_TRANSPORT_TCP = "transport=tcp" ;
8586 public const string SCHEME_TRANSPORT_TLS = "transport=tls" ;
8687
87- public static readonly string [ ] SCHEME_TRANSPORT_SEPARATOR = { "?transport=" } ;
88+ public static readonly string SCHEME_TRANSPORT_SEPARATOR = "?transport=" ;
8889 public const char SCHEME_ADDR_SEPARATOR = ':' ;
8990 public const int SCHEME_MAX_LENGTH = 5 ;
9091
9192 public const STUNSchemesEnum DefaultSTUNScheme = STUNSchemesEnum . stun ;
9293
93- public STUNProtocolsEnum Transport = STUNProtocolsEnum . udp ;
94- public STUNSchemesEnum Scheme = DefaultSTUNScheme ;
94+ public STUNProtocolsEnum Transport { get ; } = STUNProtocolsEnum . udp ;
95+ public STUNSchemesEnum Scheme { get ; } = DefaultSTUNScheme ;
9596
96- public string Host ;
97- public int Port ;
97+ public string Host { get ; }
98+ public int Port { get ; }
9899
99100 /// <summary>
100101 /// If the port is specified in a URI it affects the way a DNS lookup occurs.
101102 /// An explicit port means to lookup the A or AAAA record directly without
102103 /// checking for SRV records.
103104 /// </summary>
104- public bool ExplicitPort ;
105+ public bool ExplicitPort { get ; }
105106
106107 /// <summary>
107108 /// The network protocol for this URI type.
@@ -124,107 +125,126 @@ public ProtocolType Protocol
124125 private STUNUri ( )
125126 { }
126127
127- public STUNUri ( STUNSchemesEnum scheme , string host , int port = STUNConstants . DEFAULT_STUN_PORT )
128+ [ EditorBrowsable ( EditorBrowsableState . Advanced ) ]
129+ public STUNUri ( STUNSchemesEnum scheme , string host , int port )
128130 {
129131 Scheme = scheme ;
130132 Host = host ;
131133 Port = port ;
132134 }
133135
134- public static STUNUri ParseSTUNUri ( string uri )
136+ public STUNUri ( STUNSchemesEnum scheme , string host , int port = STUNConstants . DEFAULT_STUN_PORT , STUNProtocolsEnum transport = STUNProtocolsEnum . udp , bool explicitPort = false )
135137 {
136- STUNUri stunUri = new STUNUri ( ) ;
138+ Scheme = scheme ;
139+ Host = host ;
140+ Port = port ;
141+ Transport = transport ;
142+ ExplicitPort = explicitPort ;
143+ }
137144
138- if ( String . IsNullOrEmpty ( uri ) )
145+ public static STUNUri ParseSTUNUri ( string uriStr )
146+ {
147+ if ( ! TryParse ( uriStr , out var uri ) )
139148 {
140149 throw new ApplicationException ( "A STUN URI cannot be parsed from an empty string." ) ;
141150 }
142- else
151+
152+ return uri ;
153+ }
154+
155+ public static bool TryParse ( string uriStr , out STUNUri uri )
156+ {
157+ uri = null ;
158+
159+ if ( string . IsNullOrEmpty ( uriStr ) )
143160 {
144- //Split uri to include support to transport detection
145- var splitUri = uri . Split ( SCHEME_TRANSPORT_SEPARATOR , StringSplitOptions . RemoveEmptyEntries ) ;
146- if ( splitUri . Length > 1 )
161+ return false ;
162+ }
163+
164+ ReadOnlySpan < char > uriSpan = uriStr . AsSpan ( ) ;
165+ STUNProtocolsEnum transport = STUNProtocolsEnum . udp ;
166+
167+ // Handle transport protocol
168+ int transportIndex = uriSpan . IndexOf ( '?' ) ;
169+ if ( transportIndex >= 0 && uriSpan . Slice ( transportIndex , SCHEME_TRANSPORT_SEPARATOR . Length ) . SequenceEqual ( SCHEME_TRANSPORT_SEPARATOR . AsSpan ( ) ) )
170+ {
171+ var protocolSpan = uriSpan . Slice ( transportIndex + SCHEME_TRANSPORT_SEPARATOR . Length ) . Trim ( ) ;
172+ #if NET6_0_OR_GREATER
173+ if ( ! protocolSpan . IsEmpty && ! Enum . TryParse ( protocolSpan , true , out transport ) )
174+ #else
175+ if ( ! protocolSpan . IsEmpty && ! Enum . TryParse ( protocolSpan . ToString ( ) , true , out transport ) )
176+ #endif
147177 {
148- uri = splitUri [ 0 ] ;
149- var protocolStr = splitUri [ 1 ] . Trim ( ) ;
150- if ( string . IsNullOrEmpty ( protocolStr ) || ! Enum . TryParse < STUNProtocolsEnum > ( protocolStr , true , out stunUri . Transport ) )
151- {
152- stunUri . Transport = STUNProtocolsEnum . udp ;
153- }
178+ transport = STUNProtocolsEnum . udp ;
154179 }
180+ uriSpan = uriSpan . Slice ( 0 , transportIndex ) ;
181+ }
155182
156- uri = uri . Trim ( ) ;
183+ uriSpan = uriSpan . Trim ( ) ;
184+ var scheme = DefaultSTUNScheme ;
157185
158- // If the scheme is included it needs to be within the first 5 characters.
159- if ( uri . Length > SCHEME_MAX_LENGTH + 2 )
160- {
161- string schemeStr = uri . Substring ( 0 , SCHEME_MAX_LENGTH + 1 ) ;
162- int colonPosn = schemeStr . IndexOf ( SCHEME_ADDR_SEPARATOR ) ;
186+ // Handle scheme parsing
187+ if ( uriSpan . Length > SCHEME_MAX_LENGTH + 2 )
188+ {
189+ ReadOnlySpan < char > schemeSpan = uriSpan . Slice ( 0 , SCHEME_MAX_LENGTH + 1 ) ;
190+ int colonPosn = schemeSpan . IndexOf ( SCHEME_ADDR_SEPARATOR ) ;
163191
164- if ( colonPosn == - 1 )
165- {
166- // No scheme has been specified, use default.
167- stunUri . Scheme = DefaultSTUNScheme ;
168- }
169- else
192+ if ( colonPosn >= 0 )
193+ {
194+ #if NET6_0_OR_GREATER
195+ if ( ! Enum . TryParse ( schemeSpan . Slice ( 0 , colonPosn ) , true , out scheme ) )
196+ #else
197+ if ( ! Enum . TryParse ( schemeSpan . Slice ( 0 , colonPosn ) . ToString ( ) , true , out scheme ) )
198+ #endif
170199 {
171- if ( ! Enum . TryParse < STUNSchemesEnum > ( schemeStr . Substring ( 0 , colonPosn ) , true , out stunUri . Scheme ) )
172- {
173- stunUri . Scheme = DefaultSTUNScheme ;
174- }
175-
176- uri = uri . Substring ( colonPosn + 1 ) ;
200+ scheme = DefaultSTUNScheme ;
177201 }
202+ uriSpan = uriSpan . Slice ( colonPosn + 1 ) ;
178203 }
204+ }
179205
180- if ( uri . IndexOf ( ':' ) != - 1 )
181- {
182- stunUri . ExplicitPort = true ;
206+ var explicitPort = false ;
207+ int port ;
208+ string host ;
209+
210+ int lastColonPos = uriSpan . LastIndexOf ( ':' ) ;
211+ if ( lastColonPos != - 1 )
212+ {
213+ explicitPort = true ;
183214
184- if ( IPSocket . TryParseIPEndPoint ( uri , out var ipEndPoint ) )
215+ if ( IPSocket . TryParseIPEndPoint ( uriSpan , out var ipEndPoint ) )
216+ {
217+ if ( ipEndPoint . AddressFamily == AddressFamily . InterNetworkV6 )
185218 {
186- if ( ipEndPoint . AddressFamily == AddressFamily . InterNetworkV6 )
187- {
188- stunUri . Host = $ "[{ ipEndPoint . Address } ]";
189- }
190- else
191- {
192- stunUri . Host = ipEndPoint . Address . ToString ( ) ;
193- }
194-
195- stunUri . Port = ipEndPoint . Port ;
219+ host = $ "[{ ipEndPoint . Address } ]";
196220 }
197221 else
198222 {
199- stunUri . Host = uri . Substring ( 0 , uri . LastIndexOf ( ':' ) ) ;
200- if ( ! Int32 . TryParse ( uri . Substring ( uri . LastIndexOf ( ':' ) + 1 ) , out stunUri . Port ) )
201- {
202- stunUri . Port = STUNConstants . GetPortForScheme ( stunUri . Scheme ) ;
203- }
223+ host = ipEndPoint . Address . ToString ( ) ;
204224 }
225+ port = ipEndPoint . Port ;
205226 }
206227 else
207228 {
208- stunUri . Host = uri ? . Trim ( ) ;
209- stunUri . Port = STUNConstants . GetPortForScheme ( stunUri . Scheme ) ;
229+ host = uriSpan . Slice ( 0 , lastColonPos ) . ToString ( ) ;
230+ #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER
231+ if ( ! int . TryParse ( uriSpan . Slice ( lastColonPos + 1 ) , out port ) )
232+ #else
233+ if ( ! int . TryParse ( uriSpan . Slice ( lastColonPos + 1 ) . ToString ( ) , out port ) )
234+ #endif
235+ {
236+ port = STUNConstants . GetPortForScheme ( scheme ) ;
237+ }
210238 }
211239 }
212-
213- return stunUri ;
214- }
215-
216- public static bool TryParse ( string uriStr , out STUNUri uri )
217- {
218- try
219- {
220- uri = ParseSTUNUri ( uriStr ) ;
221- return ( uri != null ) ;
222- }
223- catch
240+ else
224241 {
225- uri = null ;
226- return false ;
242+ host = uriSpan . ToString ( ) ;
243+ port = STUNConstants . GetPortForScheme ( scheme ) ;
227244 }
245+
246+ uri = new STUNUri ( scheme , host , port : port , transport : transport , explicitPort : explicitPort ) ;
247+ return true ;
228248 }
229249
230250 public override string ToString ( )
@@ -261,9 +281,14 @@ public static bool AreEqual(STUNUri uri1, STUNUri uri2)
261281 return uri1 == uri2 ;
262282 }
263283
284+ public bool Equals ( STUNUri other )
285+ {
286+ return ( this == other ) ;
287+ }
288+
264289 public override bool Equals ( object obj )
265290 {
266- return AreEqual ( this , ( STUNUri ) obj ) ;
291+ return Equals ( this , ( STUNUri ) obj ) ;
267292 }
268293
269294 public static bool operator == ( STUNUri uri1 , STUNUri uri2 )
@@ -307,11 +332,7 @@ public override bool Equals(object obj)
307332
308333 public override int GetHashCode ( )
309334 {
310- return Scheme . GetHashCode ( )
311- + Transport . GetHashCode ( )
312- + ( ( Host != null ) ? Host . GetHashCode ( ) : 0 )
313- + Port
314- + ( ( ExplicitPort ) ? 1 : 0 ) ;
335+ return HashCode . Combine ( Scheme , Transport , Host , Port , ExplicitPort ) ;
315336 }
316337 }
317338}
0 commit comments