@@ -125,7 +125,7 @@ public final class Pinger: PingerProtocol {
125
125
}
126
126
127
127
let sequenceNumber = nextSequenceNumber ( )
128
- let packetData = Self . createICMPPacket (
128
+ let packetData = ICMP . createICMPPacket (
129
129
identifier: identifier,
130
130
sequenceNumber: sequenceNumber
131
131
)
@@ -177,7 +177,13 @@ public final class Pinger: PingerProtocol {
177
177
do {
178
178
guard bytesRead > 0 else { throw Error . receivePacket ( errno) }
179
179
180
- let icmpHeader = try parseICMPResponse ( buffer: & readBuffer, length: bytesRead)
180
+ let icmpHeader = try ICMP . parseICMPResponse ( buffer: & readBuffer, length: bytesRead)
181
+ guard icmpHeader. identifier == identifier else {
182
+ throw Error . clientIdentifierMismatch
183
+ }
184
+ guard icmpHeader. type == ICMP_ECHOREPLY else {
185
+ throw Error . invalidICMPType ( icmpHeader. type)
186
+ }
181
187
guard let sender = Self . makeIPAddress ( from: address) else { throw Error . parseIPAddress }
182
188
183
189
replyQueue. async {
@@ -192,65 +198,6 @@ public final class Pinger: PingerProtocol {
192
198
}
193
199
}
194
200
195
- private func parseICMPResponse( buffer: inout [ UInt8 ] , length: Int ) throws -> ICMPHeader {
196
- try buffer. withUnsafeMutableBytes { bufferPointer in
197
- // Check IP packet size.
198
- guard length >= MemoryLayout< IPv4Header> . size else {
199
- throw Error . malformedResponse ( . ipv4PacketTooSmall)
200
- }
201
-
202
- // Verify IPv4 header.
203
- let ipv4Header = bufferPointer. load ( as: IPv4Header . self)
204
- let payloadLength = length - ipv4Header. headerLength
205
-
206
- guard payloadLength >= MemoryLayout< ICMPHeader> . size else {
207
- throw Error . malformedResponse ( . icmpHeaderTooSmall)
208
- }
209
-
210
- guard ipv4Header. isIPv4Version else {
211
- throw Error . malformedResponse ( . invalidIPVersion)
212
- }
213
-
214
- // Parse ICMP header.
215
- let icmpHeaderPointer = bufferPointer. baseAddress!
216
- . advanced ( by: ipv4Header. headerLength)
217
- . assumingMemoryBound ( to: ICMPHeader . self)
218
-
219
- // Check if ICMP response identifier matches the one from sender.
220
- guard icmpHeaderPointer. pointee. identifier. bigEndian == identifier else {
221
- throw Error . clientIdentifierMismatch
222
- }
223
-
224
- // Verify ICMP type.
225
- guard icmpHeaderPointer. pointee. type == ICMP_ECHOREPLY else {
226
- throw Error . malformedResponse ( . invalidEchoReplyType)
227
- }
228
-
229
- // Copy server checksum.
230
- let serverChecksum = icmpHeaderPointer. pointee. checksum. bigEndian
231
-
232
- // Reset checksum field before calculating checksum.
233
- icmpHeaderPointer. pointee. checksum = 0
234
-
235
- // Verify ICMP checksum.
236
- let payloadPointer = UnsafeRawBufferPointer (
237
- start: icmpHeaderPointer,
238
- count: payloadLength
239
- )
240
- let clientChecksum = in_chksum ( payloadPointer)
241
- if clientChecksum != serverChecksum {
242
- throw Error . malformedResponse ( . checksumMismatch( clientChecksum, serverChecksum) )
243
- }
244
-
245
- // Ensure endianness before returning ICMP packet to delegate.
246
- var icmpHeader = icmpHeaderPointer. pointee
247
- icmpHeader. identifier = icmpHeader. identifier. bigEndian
248
- icmpHeader. sequenceNumber = icmpHeader. sequenceNumber. bigEndian
249
- icmpHeader. checksum = serverChecksum
250
- return icmpHeader
251
- }
252
- }
253
-
254
201
private func bindSocket( _ socket: CFSocket , to interfaceName: String ) throws {
255
202
var index = if_nametoindex ( interfaceName)
256
203
guard index > 0 else {
@@ -270,19 +217,6 @@ public final class Pinger: PingerProtocol {
270
217
}
271
218
}
272
219
273
- private class func createICMPPacket( identifier: UInt16 , sequenceNumber: UInt16 ) -> Data {
274
- var header = ICMPHeader (
275
- type: UInt8 ( ICMP_ECHO) ,
276
- code: 0 ,
277
- checksum: 0 ,
278
- identifier: identifier. bigEndian,
279
- sequenceNumber: sequenceNumber. bigEndian
280
- )
281
- header. checksum = withUnsafeBytes ( of: & header) { in_chksum ( $0) . bigEndian }
282
-
283
- return withUnsafeBytes ( of: & header) { Data ( $0) }
284
- }
285
-
286
220
private class func makeIPAddress( from sa: sockaddr ) -> IPAddress ? {
287
221
if sa. sa_family == AF_INET {
288
222
return withUnsafeBytes ( of: sa) { buffer -> IPAddress ? in
@@ -337,12 +271,12 @@ extension Pinger {
337
271
/// Failure to receive packet. Contains the `errno`.
338
272
case receivePacket( Int32 )
339
273
274
+ /// Unexpected ICMP reply type
275
+ case invalidICMPType( UInt8 )
276
+
340
277
/// Response identifier does not match the sender identifier.
341
278
case clientIdentifierMismatch
342
279
343
- /// Malformed response.
344
- case malformedResponse( MalformedResponseReason )
345
-
346
280
/// Failure to parse IP address.
347
281
case parseIPAddress
348
282
@@ -362,51 +296,13 @@ extension Pinger {
362
296
return " Failure to send packet (errno: \( code) ). "
363
297
case let . receivePacket( code) :
364
298
return " Failure to receive packet (errno: \( code) ). "
299
+ case let . invalidICMPType( type) :
300
+ return " Unexpected ICMP reply type: \( type) "
365
301
case . clientIdentifierMismatch:
366
302
return " Response identifier does not match the sender identifier. "
367
- case let . malformedResponse( reason) :
368
- return " Malformed response: \( reason) . "
369
303
case . parseIPAddress:
370
304
return " Failed to parse IP address. "
371
305
}
372
306
}
373
307
}
374
-
375
- public enum MalformedResponseReason {
376
- case ipv4PacketTooSmall
377
- case icmpHeaderTooSmall
378
- case invalidIPVersion
379
- case invalidEchoReplyType
380
- case checksumMismatch( UInt16 , UInt16 )
381
- }
382
- }
383
-
384
- private func in_chksum( _ data: some Sequence < UInt8 > ) -> UInt16 {
385
- var iterator = data. makeIterator ( )
386
- var words = [ UInt16] ( )
387
-
388
- while let byte = iterator. next ( ) {
389
- let nextByte = iterator. next ( ) ?? 0
390
- let word = UInt16 ( byte) << 8 | UInt16 ( nextByte)
391
-
392
- words. append ( word)
393
- }
394
-
395
- let sum = words. reduce ( 0 , &+ )
396
-
397
- return ~ sum
398
- }
399
-
400
- private extension IPv4Header {
401
- /// Returns IPv4 header length.
402
- var headerLength : Int {
403
- Int ( versionAndHeaderLength & 0x0F ) * MemoryLayout< UInt32> . size
404
- }
405
-
406
- /// Returns `true` if version header indicates IPv4.
407
- var isIPv4Version : Bool {
408
- ( versionAndHeaderLength & 0xF0 ) == 0x40
409
- }
410
-
411
- // swiftlint:disable:next file_length
412
308
}
0 commit comments