@@ -181,47 +181,91 @@ package enum PeerAddress: Equatable {
181181 // - ipv4:<host>:<port> for ipv4 addresses
182182 // - ipv6:[<host>]:<port> for ipv6 addresses
183183 // - unix:<uds-pathname> for UNIX domain sockets
184+ let addressUTF8View = address. utf8
184185
185186 // First get the first component so that we know what type of address we're dealing with
186- let addressComponents = address . split ( separator : " : " , maxSplits : 1 )
187+ let firstColonIndex = addressUTF8View . firstIndex ( of : UInt8 ( ascii : " : " ) )
187188
188- guard addressComponents . count > 1 else {
189+ guard let firstColonIndex else {
189190 // This is some unexpected/unknown format
190191 return nil
191192 }
192193
194+ let addressType = addressUTF8View [ ..< firstColonIndex]
195+
196+ var addressWithoutType = addressUTF8View [ firstColonIndex... ]
197+ addressWithoutType. removeFirst ( )
198+
193199 // Check what type the transport is...
194- switch addressComponents [ 0 ] {
195- case " ipv4 " :
196- let ipv4AddressComponents = addressComponents [ 1 ] . split ( separator: " : " )
197- if ipv4AddressComponents. count == 2 , let port = Int ( ipv4AddressComponents [ 1 ] ) {
198- self = . ipv4( address: String ( ipv4AddressComponents [ 0 ] ) , port: port)
199- } else {
200+ if addressType. elementsEqual ( " ipv4 " . utf8) {
201+ guard let addressColon = addressWithoutType. firstIndex ( of: UInt8 ( ascii: " : " ) ) else {
202+ // This is some unexpected/unknown format
200203 return nil
201204 }
202205
203- case " ipv6 " :
204- if addressComponents [ 1 ] . first == " [ " {
205- // At this point, we are looking at an address with format: [<address>]:<port>
206- // We drop the first character ('[') and split by ']:' to keep two components: the address
207- // and the port.
208- let ipv6AddressComponents = addressComponents [ 1 ] . dropFirst ( ) . split ( separator: " ]: " )
209- if ipv6AddressComponents. count == 2 , let port = Int ( ipv6AddressComponents [ 1 ] ) {
210- self = . ipv6( address: String ( ipv6AddressComponents [ 0 ] ) , port: port)
211- } else {
212- return nil
213- }
206+ let hostComponent = addressWithoutType [ ..< addressColon]
207+ var portComponent = addressWithoutType [ addressColon... ]
208+ portComponent. removeFirst ( )
209+
210+ if let host = String ( hostComponent) , let port = Int ( ipAddressPortStringBytes: portComponent) {
211+ self = . ipv4( address: host, port: port)
214212 } else {
215213 return nil
216214 }
215+ } else if addressType. elementsEqual ( " ipv6 " . utf8) {
216+ guard let lastColonIndex = addressWithoutType. lastIndex ( of: UInt8 ( ascii: " : " ) ) else {
217+ // This is some unexpected/unknown format
218+ return nil
219+ }
217220
218- case " unix " :
219- // Whatever comes after "unix:" is the <pathname>
220- self = . unixDomainSocket ( path : String ( addressComponents [ 1 ] ) )
221+ var hostComponent = addressWithoutType [ ..< lastColonIndex ]
222+ var portComponent = addressWithoutType [ lastColonIndex ... ]
223+ portComponent . removeFirst ( )
221224
222- default :
225+ if let firstBracket = hostComponent. popFirst ( ) , let lastBracket = hostComponent. popLast ( ) ,
226+ firstBracket == UInt8 ( ascii: " [ " ) , lastBracket == UInt8 ( ascii: " ] " ) ,
227+ let host = String ( hostComponent) , let port = Int ( ipAddressPortStringBytes: portComponent)
228+ {
229+ self = . ipv6( address: host, port: port)
230+ } else {
231+ // This is some unexpected/unknown format
232+ return nil
233+ }
234+ } else if addressType. elementsEqual ( " unix " . utf8) {
235+ // Whatever comes after "unix:" is the <pathname>
236+ self = . unixDomainSocket( path: String ( addressWithoutType) ?? " " )
237+ } else {
223238 // This is some unexpected/unknown format
224239 return nil
225240 }
226241 }
227242}
243+
244+ extension Int {
245+ package init ? ( ipAddressPortStringBytes: some Collection < UInt8 > ) {
246+ guard ( 1 ... 5 ) . contains ( ipAddressPortStringBytes. count) else {
247+ // Valid IP port values go up to 2^16-1 (65535), which is 5 digits long.
248+ // If the string we get is over 5 characters, we know for sure that this is an invalid port.
249+ // If it's empty, we also know it's invalid as we need at least one digit.
250+ return nil
251+ }
252+
253+ var value = 0
254+ for utf8Char in ipAddressPortStringBytes {
255+ value &*= 10
256+ guard ( UInt8 ( ascii: " 0 " ) ... UInt8 ( ascii: " 9 " ) ) . contains ( utf8Char) else {
257+ // non-digit character
258+ return nil
259+ }
260+ value &+= Int ( utf8Char &- UInt8 ( ascii: " 0 " ) )
261+ }
262+
263+ guard value <= Int ( UInt16 . max) else {
264+ // Valid IP port values go up to 2^16-1.
265+ // If a number greater than this was given, it can't be a valid port.
266+ return nil
267+ }
268+
269+ self = value
270+ }
271+ }
0 commit comments