Skip to content

Commit 8b63363

Browse files
committed
more PCAP
1 parent 54def83 commit 8b63363

File tree

7 files changed

+538
-351
lines changed

7 files changed

+538
-351
lines changed

Package.swift

Lines changed: 9 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,13 @@ 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+
.product(name: "NIOPosix", package: "swift-nio"),
118+
]),
112119
]
113120

114121
let package = Package(

[email protected]

Lines changed: 9 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,13 @@ 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+
.product(name: "NIOPosix", package: "swift-nio"),
118+
]),
112119
]
113120

114121
let package = Package(

Sources/NIOExtras/WritePCAPHandler.swift

Lines changed: 7 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the SwiftNIO open source project
44
//
5-
// Copyright (c) 2019-2021 Apple Inc. and the SwiftNIO project authors
5+
// Copyright (c) 2019-2022 Apple Inc. and the SwiftNIO project authors
66
// Licensed under Apache License v2.0
77
//
88
// See LICENSE.txt for license information
@@ -12,105 +12,18 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
#if os(macOS) || os(tvOS) || os(iOS) || os(watchOS)
15+
import Dispatch
16+
17+
import NIOCore
18+
import NIOPCAP
19+
#if canImport(Darwin)
1620
import Darwin
1721
#else
1822
import Glibc
1923
#endif
20-
import Dispatch
21-
22-
import NIOCore
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
///
@@ -187,7 +100,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler {
187100
/// Reusable header for `.pcap` file.
188101
public static var pcapFileHeader: ByteBuffer {
189102
var buffer = ByteBufferAllocator().buffer(capacity: 24)
190-
buffer.writePCAPHeader()
103+
buffer.writePCAPHeader(.default)
191104
return buffer
192105
}
193106

@@ -497,104 +410,6 @@ extension NIOWritePCAPHandler: ChannelDuplexHandler {
497410
}
498411
}
499412

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-
598413
extension NIOWritePCAPHandler {
599414
/// A synchronised file sink that uses a `DispatchQueue` to do all the necessary write synchronously.
600415
///

0 commit comments

Comments
 (0)