Skip to content

Commit ffa1262

Browse files
committed
more PCAP
1 parent 59971a8 commit ffa1262

File tree

6 files changed

+527
-349
lines changed

6 files changed

+527
-349
lines changed

Package.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var targets: [PackageDescription.Target] = [
1919
.target(
2020
name: "NIOExtras",
2121
dependencies: [
22+
"NIOPCAP",
2223
.product(name: "NIO", package: "swift-nio"),
2324
.product(name: "NIOCore", package: "swift-nio"),
2425
]),
@@ -85,7 +86,7 @@ var targets: [PackageDescription.Target] = [
8586
.testTarget(
8687
name: "NIOExtrasTests",
8788
dependencies: [
88-
"NIOExtras",
89+
"NIOExtras", "NIOPCAP",
8990
.product(name: "NIOCore", package: "swift-nio"),
9091
.product(name: "NIOEmbedded", package: "swift-nio"),
9192
.product(name: "NIOPosix", package: "swift-nio"),
@@ -108,7 +109,12 @@ var targets: [PackageDescription.Target] = [
108109
"NIOSOCKS",
109110
.product(name: "NIOCore", package: "swift-nio"),
110111
.product(name: "NIOEmbedded", package: "swift-nio"),
111-
])
112+
]),
113+
.target(
114+
name: "NIOPCAP", // For now, this is not a product, ie. it's not exported and just an internal "helper module".
115+
dependencies: [
116+
.product(name: "NIOCore", package: "swift-nio"),
117+
]),
112118
]
113119

114120
let package = Package(

Sources/NIOExtras/WritePCAPHandler.swift

Lines changed: 7 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -12,105 +12,18 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
#if os(macOS) || os(tvOS) || os(iOS) || os(watchOS)
16-
import Darwin
17-
#else
18-
import Glibc
19-
#endif
15+
//#if os(macOS) || os(tvOS) || os(iOS) || os(watchOS)
16+
//import Darwin
17+
//#else
18+
//import Glibc
19+
//#endif
2020
import Dispatch
2121

2222
import NIOCore
23+
import NIOPCAP
2324

2425
let sysWrite = write
2526

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-
11427
/// A `ChannelHandler` that can write a [`.pcap` file](https://en.wikipedia.org/wiki/Pcap) containing the send/received
11528
/// data as synthesized TCP packet captures.
11629
///
@@ -186,7 +99,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler {
18699

187100
public static var pcapFileHeader: ByteBuffer {
188101
var buffer = ByteBufferAllocator().buffer(capacity: 24)
189-
buffer.writePCAPHeader()
102+
buffer.writePCAPHeader(.default)
190103
return buffer
191104
}
192105

@@ -491,104 +404,6 @@ extension NIOWritePCAPHandler: ChannelDuplexHandler {
491404
}
492405
}
493406

494-
extension ByteBuffer {
495-
mutating func writePCAPHeader() {
496-
// guint32 magic_number; /* magic number */
497-
self.writeInteger(0xa1b2c3d4, endianness: .host, as: UInt32.self)
498-
// guint16 version_major; /* major version number */
499-
self.writeInteger(2, endianness: .host, as: UInt16.self)
500-
// guint16 version_minor; /* minor version number *
501-
self.writeInteger(4, endianness: .host, as: UInt16.self)
502-
// gint32 thiszone; /* GMT to local correction */
503-
self.writeInteger(0, endianness: .host, as: UInt32.self)
504-
// guint32 sigfigs; /* accuracy of timestamps */
505-
self.writeInteger(0, endianness: .host, as: UInt32.self)
506-
// guint32 snaplen; /* max length of captured packets, in octets */
507-
self.writeInteger(.max, endianness: .host, as: UInt32.self)
508-
// guint32 network; /* data link type */
509-
self.writeInteger(0, endianness: .host, as: UInt32.self)
510-
}
511-
512-
mutating func writePCAPRecord(_ record: PCAPRecordHeader) throws {
513-
let rawDataLength = record.payloadLength
514-
let tcpLength = rawDataLength + 20 /* TCP header length */
515-
516-
// record
517-
// guint32 ts_sec; /* timestamp seconds */
518-
self.writeInteger(.init(record.time.tv_sec), endianness: .host, as: UInt32.self)
519-
// guint32 ts_usec; /* timestamp microseconds */
520-
self.writeInteger(.init(record.time.tv_usec), endianness: .host, as: UInt32.self)
521-
// continued below ...
522-
523-
switch record.addresses {
524-
case .v4(let la, let ra):
525-
let ipv4WholeLength = tcpLength + 20 /* IPv4 header length, included in IPv4 */
526-
let recordLength = ipv4WholeLength + 4 /* 32 bits for protocol id */
527-
528-
// record, continued
529-
// guint32 incl_len; /* number of octets of packet saved in file */
530-
self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self)
531-
// guint32 orig_len; /* actual length of packet */
532-
self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self)
533-
534-
self.writeInteger(2, endianness: .host, as: UInt32.self) // IPv4
535-
536-
// IPv4 packet
537-
self.writeInteger(0x45, as: UInt8.self) // IP version (4) & IHL (5)
538-
self.writeInteger(0, as: UInt8.self) // DSCP
539-
self.writeInteger(.init(ipv4WholeLength), as: UInt16.self)
540-
541-
self.writeInteger(0, as: UInt16.self) // identification
542-
self.writeInteger(0x4000 /* this set's "don't fragment" */, as: UInt16.self) // flags & fragment offset
543-
self.writeInteger(.max /* we don't care about TTL */, as: UInt8.self) // TTL
544-
self.writeInteger(6, as: UInt8.self) // TCP
545-
self.writeInteger(0, as: UInt16.self) // checksum
546-
self.writeInteger(la.address.sin_addr.s_addr, endianness: .host, as: UInt32.self)
547-
self.writeInteger(ra.address.sin_addr.s_addr, endianness: .host, as: UInt32.self)
548-
case .v6(let la, let ra):
549-
let ipv6PayloadLength = tcpLength
550-
let recordLength = ipv6PayloadLength + 4 /* 32 bits for protocol id */ + 40 /* IPv6 header length */
551-
552-
// record, continued
553-
// guint32 incl_len; /* number of octets of packet saved in file */
554-
self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self)
555-
// guint32 orig_len; /* actual length of packet */
556-
self.writeInteger(.init(recordLength), endianness: .host, as: UInt32.self)
557-
558-
self.writeInteger(24, endianness: .host, as: UInt32.self) // IPv6
559-
560-
// IPv6 packet
561-
self.writeInteger(/* version */ (6 << 28), as: UInt32.self) // IP version (6) & fancy stuff
562-
self.writeInteger(.init(ipv6PayloadLength), as: UInt16.self)
563-
self.writeInteger(6, as: UInt8.self) // TCP
564-
self.writeInteger(.max /* we don't care about TTL */, as: UInt8.self) // hop limit (like TTL)
565-
566-
var laAddress = la.address
567-
withUnsafeBytes(of: &laAddress.sin6_addr) { ptr in
568-
assert(ptr.count == 16)
569-
self.writeBytes(ptr)
570-
}
571-
var raAddress = ra.address
572-
withUnsafeBytes(of: &raAddress.sin6_addr) { ptr in
573-
assert(ptr.count == 16)
574-
self.writeBytes(ptr)
575-
}
576-
}
577-
578-
// TCP
579-
self.writeInteger(record.tcp.srcPort, as: UInt16.self)
580-
self.writeInteger(record.tcp.dstPort, as: UInt16.self)
581-
582-
self.writeInteger(record.tcp.sequenceNumber, as: UInt32.self) // seq no
583-
self.writeInteger(record.tcp.ackNumber ?? 0, as: UInt32.self) // ack no
584-
585-
self.writeInteger(5 << 12 | UInt16(record.tcp.flags.rawValue), as: UInt16.self) // data offset + reserved bits + fancy stuff
586-
self.writeInteger(.max /* we don't do actual window sizes */, as: UInt16.self) // window size
587-
self.writeInteger(0xbad /* fake */, as: UInt16.self) // checksum
588-
self.writeInteger(0, as: UInt16.self) // urgent pointer
589-
}
590-
}
591-
592407
extension NIOWritePCAPHandler {
593408
/// A synchronised file sink that uses a `DispatchQueue` to do all the necessary write synchronously.
594409
///

0 commit comments

Comments
 (0)