@@ -136,47 +136,91 @@ package enum PeerAddress: Equatable {
136136 // - ipv4:<host>:<port> for ipv4 addresses
137137 // - ipv6:[<host>]:<port> for ipv6 addresses
138138 // - unix:<uds-pathname> for UNIX domain sockets
139+ let addressUTF8View = address. utf8
139140
140141 // First get the first component so that we know what type of address we're dealing with
141- let addressComponents = address . split ( separator : " : " , maxSplits : 1 )
142+ let firstColonIndex = addressUTF8View . firstIndex ( of : UInt8 ( ascii : " : " ) )
142143
143- guard addressComponents . count > 1 else {
144+ guard let firstColonIndex else {
144145 // This is some unexpected/unknown format
145146 return nil
146147 }
147148
149+ let addressType = addressUTF8View [ ..< firstColonIndex]
150+
151+ var addressWithoutType = addressUTF8View [ firstColonIndex... ]
152+ addressWithoutType. removeFirst ( )
153+
148154 // Check what type the transport is...
149- switch addressComponents [ 0 ] {
150- case " ipv4 " :
151- let ipv4AddressComponents = addressComponents [ 1 ] . split ( separator: " : " )
152- if ipv4AddressComponents. count == 2 , let port = Int ( ipv4AddressComponents [ 1 ] ) {
153- self = . ipv4( address: String ( ipv4AddressComponents [ 0 ] ) , port: port)
154- } else {
155+ if addressType. elementsEqual ( " ipv4 " . utf8) {
156+ guard let addressColon = addressWithoutType. firstIndex ( of: UInt8 ( ascii: " : " ) ) else {
157+ // This is some unexpected/unknown format
155158 return nil
156159 }
157160
158- case " ipv6 " :
159- if addressComponents [ 1 ] . first == " [ " {
160- // At this point, we are looking at an address with format: [<address>]:<port>
161- // We drop the first character ('[') and split by ']:' to keep two components: the address
162- // and the port.
163- let ipv6AddressComponents = addressComponents [ 1 ] . dropFirst ( ) . split ( separator: " ]: " )
164- if ipv6AddressComponents. count == 2 , let port = Int ( ipv6AddressComponents [ 1 ] ) {
165- self = . ipv6( address: String ( ipv6AddressComponents [ 0 ] ) , port: port)
166- } else {
167- return nil
168- }
161+ let hostComponent = addressWithoutType [ ..< addressColon]
162+ var portComponent = addressWithoutType [ addressColon... ]
163+ portComponent. removeFirst ( )
164+
165+ if let host = String ( hostComponent) , let port = Int ( ipAddressPortStringBytes: portComponent) {
166+ self = . ipv4( address: host, port: port)
169167 } else {
170168 return nil
171169 }
170+ } else if addressType. elementsEqual ( " ipv6 " . utf8) {
171+ guard let lastColonIndex = addressWithoutType. lastIndex ( of: UInt8 ( ascii: " : " ) ) else {
172+ // This is some unexpected/unknown format
173+ return nil
174+ }
172175
173- case " unix " :
174- // Whatever comes after "unix:" is the <pathname>
175- self = . unixDomainSocket ( path : String ( addressComponents [ 1 ] ) )
176+ var hostComponent = addressWithoutType [ ..< lastColonIndex ]
177+ var portComponent = addressWithoutType [ lastColonIndex ... ]
178+ portComponent . removeFirst ( )
176179
177- default :
180+ if let firstBracket = hostComponent. popFirst ( ) , let lastBracket = hostComponent. popLast ( ) ,
181+ firstBracket == UInt8 ( ascii: " [ " ) , lastBracket == UInt8 ( ascii: " ] " ) ,
182+ let host = String ( hostComponent) , let port = Int ( ipAddressPortStringBytes: portComponent)
183+ {
184+ self = . ipv6( address: host, port: port)
185+ } else {
186+ // This is some unexpected/unknown format
187+ return nil
188+ }
189+ } else if addressType. elementsEqual ( " unix " . utf8) {
190+ // Whatever comes after "unix:" is the <pathname>
191+ self = . unixDomainSocket( path: String ( addressWithoutType) ?? " " )
192+ } else {
178193 // This is some unexpected/unknown format
179194 return nil
180195 }
181196 }
182197}
198+
199+ extension Int {
200+ package init ? ( ipAddressPortStringBytes: some Collection < UInt8 > ) {
201+ guard ( 1 ... 5 ) . contains ( ipAddressPortStringBytes. count) else {
202+ // Valid IP port values go up to 2^16-1 (65535), which is 5 digits long.
203+ // If the string we get is over 5 characters, we know for sure that this is an invalid port.
204+ // If it's empty, we also know it's invalid as we need at least one digit.
205+ return nil
206+ }
207+
208+ var value = 0
209+ for utf8Char in ipAddressPortStringBytes {
210+ value &*= 10
211+ guard ( UInt8 ( ascii: " 0 " ) ... UInt8 ( ascii: " 9 " ) ) . contains ( utf8Char) else {
212+ // non-digit character
213+ return nil
214+ }
215+ value &+= Int ( utf8Char - UInt8( ascii: " 0 " ) )
216+ }
217+
218+ guard value <= Int ( UInt16 . max) else {
219+ // Valid IP port values go up to 2^16-1.
220+ // If a number greater than this was given, it can't be a valid port.
221+ return nil
222+ }
223+
224+ self = value
225+ }
226+ }
0 commit comments