|
2 | 2 | // |
3 | 3 | // This source file is part of the SwiftNIO open source project |
4 | 4 | // |
5 | | -// Copyright (c) 2019-2021 Apple Inc. and the SwiftNIO project authors |
| 5 | +// Copyright (c) 2019-2022 Apple Inc. and the SwiftNIO project authors |
6 | 6 | // Licensed under Apache License v2.0 |
7 | 7 | // |
8 | 8 | // See LICENSE.txt for license information |
|
12 | 12 | // |
13 | 13 | //===----------------------------------------------------------------------===// |
14 | 14 |
|
15 | | -#if os(macOS) || os(tvOS) || os(iOS) || os(watchOS) |
16 | | -import Darwin |
17 | | -#else |
18 | | -import Glibc |
19 | | -#endif |
20 | 15 | import Dispatch |
21 | 16 |
|
22 | 17 | import NIOCore |
| 18 | +import NIOPCAP |
23 | 19 |
|
24 | 20 | let sysWrite = write |
25 | 21 |
|
26 | | -struct TCPHeader { |
27 | | - struct Flags: OptionSet { |
28 | | - var rawValue: UInt8 |
29 | | - |
30 | | - init(rawValue: UInt8) { |
31 | | - self.rawValue = rawValue |
32 | | - } |
33 | | - |
34 | | - static let fin = Flags(rawValue: 1 << 0) |
35 | | - static let syn = Flags(rawValue: 1 << 1) |
36 | | - static let rst = Flags(rawValue: 1 << 2) |
37 | | - static let psh = Flags(rawValue: 1 << 3) |
38 | | - static let ack = Flags(rawValue: 1 << 4) |
39 | | - static let urg = Flags(rawValue: 1 << 5) |
40 | | - static let ece = Flags(rawValue: 1 << 6) |
41 | | - static let cwr = Flags(rawValue: 1 << 7) |
42 | | - } |
43 | | - |
44 | | - var flags: Flags |
45 | | - var ackNumber: UInt32? |
46 | | - var sequenceNumber: UInt32 |
47 | | - var srcPort: UInt16 |
48 | | - var dstPort: UInt16 |
49 | | -} |
50 | | - |
51 | | -struct PCAPRecordHeader { |
52 | | - enum Error: Swift.Error { |
53 | | - case incompatibleAddressPair(SocketAddress, SocketAddress) |
54 | | - } |
55 | | - enum AddressTuple { |
56 | | - case v4(src: SocketAddress.IPv4Address, dst: SocketAddress.IPv4Address) |
57 | | - case v6(src: SocketAddress.IPv6Address, dst: SocketAddress.IPv6Address) |
58 | | - |
59 | | - var srcPort: UInt16 { |
60 | | - switch self { |
61 | | - case .v4(src: let src, dst: _): |
62 | | - return UInt16(bigEndian: src.address.sin_port) |
63 | | - case .v6(src: let src, dst: _): |
64 | | - return UInt16(bigEndian: src.address.sin6_port) |
65 | | - } |
66 | | - } |
67 | | - |
68 | | - var dstPort: UInt16 { |
69 | | - switch self { |
70 | | - case .v4(src: _, dst: let dst): |
71 | | - return UInt16(bigEndian: dst.address.sin_port) |
72 | | - case .v6(src: _, dst: let dst): |
73 | | - return UInt16(bigEndian: dst.address.sin6_port) |
74 | | - } |
75 | | - } |
76 | | - } |
77 | | - |
78 | | - var payloadLength: Int |
79 | | - var addresses: AddressTuple |
80 | | - var time: timeval |
81 | | - var tcp: TCPHeader |
82 | | - |
83 | | - init(payloadLength: Int, addresses: AddressTuple, time: timeval, tcp: TCPHeader) { |
84 | | - self.payloadLength = payloadLength |
85 | | - self.addresses = addresses |
86 | | - self.time = time |
87 | | - self.tcp = tcp |
88 | | - |
89 | | - assert(addresses.srcPort == Int(tcp.srcPort)) |
90 | | - assert(addresses.dstPort == Int(tcp.dstPort)) |
91 | | - assert(tcp.ackNumber == nil ? !tcp.flags.contains([.ack]) : tcp.flags.contains([.ack])) |
92 | | - } |
93 | | - |
94 | | - init(payloadLength: Int, src: SocketAddress, dst: SocketAddress, tcp: TCPHeader) throws { |
95 | | - let addressTuple: AddressTuple |
96 | | - switch (src, dst) { |
97 | | - case (.v4(let src), .v4(let dst)): |
98 | | - addressTuple = .v4(src: src, dst: dst) |
99 | | - case (.v6(let src), .v6(let dst)): |
100 | | - addressTuple = .v6(src: src, dst: dst) |
101 | | - default: |
102 | | - throw Error.incompatibleAddressPair(src, dst) |
103 | | - } |
104 | | - self = .init(payloadLength: payloadLength, addresses: addressTuple, tcp: tcp) |
105 | | - } |
106 | | - |
107 | | - init(payloadLength: Int, addresses: AddressTuple, tcp: TCPHeader) { |
108 | | - var tv = timeval() |
109 | | - gettimeofday(&tv, nil) |
110 | | - self = .init(payloadLength: payloadLength, addresses: addresses, time: tv, tcp: tcp) |
111 | | - } |
112 | | -} |
113 | | - |
114 | 22 | /// A `ChannelHandler` that can write a [`.pcap` file](https://en.wikipedia.org/wiki/Pcap) containing the send/received |
115 | 23 | /// data as synthesized TCP packet captures. |
116 | 24 | /// |
@@ -187,7 +95,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { |
187 | 95 | /// Reusable header for `.pcap` file. |
188 | 96 | public static var pcapFileHeader: ByteBuffer { |
189 | 97 | var buffer = ByteBufferAllocator().buffer(capacity: 24) |
190 | | - buffer.writePCAPHeader() |
| 98 | + buffer.writePCAPHeader(.default) |
191 | 99 | return buffer |
192 | 100 | } |
193 | 101 |
|
@@ -497,104 +405,6 @@ extension NIOWritePCAPHandler: ChannelDuplexHandler { |
497 | 405 | } |
498 | 406 | } |
499 | 407 |
|
500 | | -extension ByteBuffer { |
501 | | - mutating func writePCAPHeader() { |
502 | | - // guint32 magic_number; /* magic number */ |
503 | | - self.writeInteger(0xa1b2c3d4, endianness: .host, as: UInt32.self) |
504 | | - // guint16 version_major; /* major version number */ |
505 | | - self.writeInteger(2, endianness: .host, as: UInt16.self) |
506 | | - // guint16 version_minor; /* minor version number * |
507 | | - self.writeInteger(4, endianness: .host, as: UInt16.self) |
508 | | - // gint32 thiszone; /* GMT to local correction */ |
509 | | - self.writeInteger(0, endianness: .host, as: UInt32.self) |
510 | | - // guint32 sigfigs; /* accuracy of timestamps */ |
511 | | - self.writeInteger(0, endianness: .host, as: UInt32.self) |
512 | | - // guint32 snaplen; /* max length of captured packets, in octets */ |
513 | | - self.writeInteger(.max, endianness: .host, as: UInt32.self) |
514 | | - // guint32 network; /* data link type */ |
515 | | - self.writeInteger(0, endianness: .host, as: UInt32.self) |
516 | | - } |
517 | | - |
518 | | - mutating func writePCAPRecord(_ record: PCAPRecordHeader) throws { |
519 | | - let rawDataLength = record.payloadLength |
520 | | - let tcpLength = rawDataLength + 20 /* TCP header length */ |
521 | | - |
522 | | - // record |
523 | | - // guint32 ts_sec; /* timestamp seconds */ |
524 | | - self.writeInteger(.init(record.time.tv_sec), endianness: .host, as: UInt32.self) |
525 | | - // guint32 ts_usec; /* timestamp microseconds */ |
526 | | - self.writeInteger(.init(record.time.tv_usec), endianness: .host, as: UInt32.self) |
527 | | - // continued below ... |
528 | | - |
529 | | - switch record.addresses { |
530 | | - case .v4(let la, let ra): |
531 | | - let ipv4WholeLength = tcpLength + 20 /* IPv4 header length, included in IPv4 */ |
532 | | - let recordLength = ipv4WholeLength + 4 /* 32 bits for protocol id */ |
533 | | - |
534 | | - // record, continued |
535 | | - // guint32 incl_len; /* number of octets of packet saved in file */ |
536 | | - self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self) |
537 | | - // guint32 orig_len; /* actual length of packet */ |
538 | | - self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self) |
539 | | - |
540 | | - self.writeInteger(2, endianness: .host, as: UInt32.self) // IPv4 |
541 | | - |
542 | | - // IPv4 packet |
543 | | - self.writeInteger(0x45, as: UInt8.self) // IP version (4) & IHL (5) |
544 | | - self.writeInteger(0, as: UInt8.self) // DSCP |
545 | | - self.writeInteger(.init(ipv4WholeLength), as: UInt16.self) |
546 | | - |
547 | | - self.writeInteger(0, as: UInt16.self) // identification |
548 | | - self.writeInteger(0x4000 /* this set's "don't fragment" */, as: UInt16.self) // flags & fragment offset |
549 | | - self.writeInteger(.max /* we don't care about TTL */, as: UInt8.self) // TTL |
550 | | - self.writeInteger(6, as: UInt8.self) // TCP |
551 | | - self.writeInteger(0, as: UInt16.self) // checksum |
552 | | - self.writeInteger(la.address.sin_addr.s_addr, endianness: .host, as: UInt32.self) |
553 | | - self.writeInteger(ra.address.sin_addr.s_addr, endianness: .host, as: UInt32.self) |
554 | | - case .v6(let la, let ra): |
555 | | - let ipv6PayloadLength = tcpLength |
556 | | - let recordLength = ipv6PayloadLength + 4 /* 32 bits for protocol id */ + 40 /* IPv6 header length */ |
557 | | - |
558 | | - // record, continued |
559 | | - // guint32 incl_len; /* number of octets of packet saved in file */ |
560 | | - self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self) |
561 | | - // guint32 orig_len; /* actual length of packet */ |
562 | | - self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self) |
563 | | - |
564 | | - self.writeInteger(24, endianness: .host, as: UInt32.self) // IPv6 |
565 | | - |
566 | | - // IPv6 packet |
567 | | - self.writeInteger(/* version */ (6 << 28), as: UInt32.self) // IP version (6) & fancy stuff |
568 | | - self.writeInteger(.init(ipv6PayloadLength), as: UInt16.self) |
569 | | - self.writeInteger(6, as: UInt8.self) // TCP |
570 | | - self.writeInteger(.max /* we don't care about TTL */, as: UInt8.self) // hop limit (like TTL) |
571 | | - |
572 | | - var laAddress = la.address |
573 | | - withUnsafeBytes(of: &laAddress.sin6_addr) { ptr in |
574 | | - assert(ptr.count == 16) |
575 | | - self.writeBytes(ptr) |
576 | | - } |
577 | | - var raAddress = ra.address |
578 | | - withUnsafeBytes(of: &raAddress.sin6_addr) { ptr in |
579 | | - assert(ptr.count == 16) |
580 | | - self.writeBytes(ptr) |
581 | | - } |
582 | | - } |
583 | | - |
584 | | - // TCP |
585 | | - self.writeInteger(record.tcp.srcPort, as: UInt16.self) |
586 | | - self.writeInteger(record.tcp.dstPort, as: UInt16.self) |
587 | | - |
588 | | - self.writeInteger(record.tcp.sequenceNumber, as: UInt32.self) // seq no |
589 | | - self.writeInteger(record.tcp.ackNumber ?? 0, as: UInt32.self) // ack no |
590 | | - |
591 | | - self.writeInteger(5 << 12 | UInt16(record.tcp.flags.rawValue), as: UInt16.self) // data offset + reserved bits + fancy stuff |
592 | | - self.writeInteger(.max /* we don't do actual window sizes */, as: UInt16.self) // window size |
593 | | - self.writeInteger(0xbad /* fake */, as: UInt16.self) // checksum |
594 | | - self.writeInteger(0, as: UInt16.self) // urgent pointer |
595 | | - } |
596 | | -} |
597 | | - |
598 | 408 | extension NIOWritePCAPHandler { |
599 | 409 | /// A synchronised file sink that uses a `DispatchQueue` to do all the necessary write synchronously. |
600 | 410 | /// |
|
0 commit comments