Skip to content

Commit

Permalink
write PCAP handler (#46)
Browse files Browse the repository at this point in the history
Motivation:

Especially with TLS but also without, in real production environments it
can be handy to be able to write pcap files from NIO directly.

Modifications:

add a ChannelHandler that can write a PCAP trace from what's going on in
the ChannelPipeline.

Result:

easier debugging in production
  • Loading branch information
weissi authored Apr 12, 2019
1 parent aad5c1c commit 96e8335
Show file tree
Hide file tree
Showing 8 changed files with 1,461 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var targets: [PackageDescription.Target] = [
.target(name: "NIOExtras", dependencies: ["NIO"]),
.target(name: "NIOHTTPCompression", dependencies: ["NIO", "NIOHTTP1", "CNIOExtrasZlib"]),
.target(name: "HTTPServerWithQuiescingDemo", dependencies: ["NIOExtras", "NIOHTTP1"]),
.target(name: "NIOWritePCAPDemo", dependencies: ["NIO", "NIOExtras", "NIOHTTP1"]),
.target(name: "CNIOExtrasZlib",
dependencies: [],
linkerSettings: [
Expand All @@ -32,6 +33,7 @@ let package = Package(
name: "swift-nio-extras",
products: [
.executable(name: "HTTPServerWithQuiescingDemo", targets: ["HTTPServerWithQuiescingDemo"]),
.executable(name: "NIOWritePCAPDemo", targets: ["NIOWritePCAPDemo"]),
.library(name: "NIOExtras", targets: ["NIOExtras"]),
.library(name: "NIOHTTPCompression", targets: ["NIOHTTPCompression"]),
],
Expand Down
5 changes: 5 additions & 0 deletions Sources/HTTPServerWithQuiescingDemo/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ private final class HTTPHandler: ChannelInboundHandler {
}
}
}

func errorCaught(context: ChannelHandlerContext, error: Error) {
print(error)
}
}

let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
Expand All @@ -70,6 +74,7 @@ signal(SIGINT, SIG_IGN)
signalSource.resume()

do {

let serverChannel = try ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
Expand Down
645 changes: 645 additions & 0 deletions Sources/NIOExtras/WritePCAPHandler.swift

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions Sources/NIOWritePCAPDemo/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import NIO
import NIOExtras
import NIOHTTP1

class SendSimpleRequestHandler: ChannelInboundHandler {
typealias InboundIn = HTTPClientResponsePart
typealias OutboundOut = HTTPClientRequestPart

private let allDonePromise: EventLoopPromise<ByteBuffer>

init(allDonePromise: EventLoopPromise<ByteBuffer>) {
self.allDonePromise = allDonePromise
}

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
if case .body(let body) = self.unwrapInboundIn(data) {
self.allDonePromise.succeed(body)
}
}

func errorCaught(context: ChannelHandlerContext, error: Error) {
self.allDonePromise.fail(error)
context.close(promise: nil)
}

func channelActive(context: ChannelHandlerContext) {
let headers = HTTPHeaders([("host", "httpbin.org"),
("accept", "application/json")])
context.write(self.wrapOutboundOut(.head(.init(version: .init(major: 1, minor: 1),
method: .GET,
uri: "/delay/0.2",
headers: headers))), promise: nil)
context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil)
}
}

guard let outputFile = CommandLine.arguments.dropFirst().first else {
print("Usage: \(CommandLine.arguments[0]) OUTPUT.pcap")
exit(0)
}

let fileSink = try NIOWritePCAPHandler.SynchronizedFileSink.fileSinkWritingToFile(path: outputFile) { error in
print("ERROR: \(error)")
exit(1)
}

let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
defer {
try! group.syncShutdownGracefully()
}
let allDonePromise = group.next().makePromise(of: ByteBuffer.self)
let connection = try ClientBootstrap(group: group.next())
.channelInitializer { channel in
return channel.pipeline.addHandler(NIOWritePCAPHandler(mode: .client, fileSink: fileSink.write)).flatMap {
channel.pipeline.addHTTPClientHandlers()
}.flatMap {
channel.pipeline.addHandler(SendSimpleRequestHandler(allDonePromise: allDonePromise))
}
}
.connect(host: "httpbin.org", port: 80)
.wait()
let bytesReceived = try allDonePromise.futureResult.wait()
print("# Success!", String(decoding: bytesReceived.readableBytesView, as: Unicode.UTF8.self), separator: "\n")
try connection.close().wait()
try fileSink.syncClose()
print("# Your pcap file should have been written to '\(outputFile)'")
print("#")
print("# You can view \(outputFile) with")
print("# - Wireshark")
print("# - tcpdump -r '\(outputFile)'")
1 change: 1 addition & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ import XCTest
testCase(LineBasedFrameDecoderTest.allTests),
testCase(QuiescingHelperTest.allTests),
testCase(RequestResponseHandlerTest.allTests),
testCase(WritePCAPHandlerTest.allTests),
])
#endif
43 changes: 43 additions & 0 deletions Tests/NIOExtrasTests/WritePCAPHandlerTest+XCTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
//
// WritePCAPHandlerTest+XCTest.swift
//
import XCTest

///
/// NOTE: This file was generated by generate_linux_tests.rb
///
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
///

extension WritePCAPHandlerTest {

static var allTests : [(String, (WritePCAPHandlerTest) -> () throws -> Void)] {
return [
("testConnectIssuesThreePacketsForIPv4", testConnectIssuesThreePacketsForIPv4),
("testConnectIssuesThreePacketsForIPv6", testConnectIssuesThreePacketsForIPv6),
("testAcceptConnectionFromRemote", testAcceptConnectionFromRemote),
("testCloseOriginatingFromLocal", testCloseOriginatingFromLocal),
("testCloseOriginatingFromRemote", testCloseOriginatingFromRemote),
("testInboundData", testInboundData),
("testOutboundData", testOutboundData),
("testOversizedInboundDataComesAsTwoPacketsIPv4", testOversizedInboundDataComesAsTwoPacketsIPv4),
("testOversizedInboundDataComesAsTwoPacketsIPv6", testOversizedInboundDataComesAsTwoPacketsIPv6),
("testOversizedOutboundDataComesAsTwoPacketsIPv4", testOversizedOutboundDataComesAsTwoPacketsIPv4),
("testOversizedOutboundDataComesAsTwoPacketsIPv6", testOversizedOutboundDataComesAsTwoPacketsIPv6),
]
}
}

Loading

0 comments on commit 96e8335

Please sign in to comment.