Skip to content

Commit 1c34e2b

Browse files
committed
Add option to write packets to file as PCAP Capture File Format
1 parent 97f2b6d commit 1c34e2b

File tree

6 files changed

+71
-4
lines changed

6 files changed

+71
-4
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## [Unreleased]
22

3+
- Add option to write packets to file as PCAP Capture File Format.
4+
35
## [0.1.0] - 2024-11-05
46

57
- Initial release

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,20 @@ gem install redhound
2020
## Usage
2121

2222
```command
23+
___ ____ __
24+
/ _ \___ ___/ / / ___ __ _____ ___/ /
25+
/ , _/ -_) _ / _ \/ _ \/ // / _ \/ _ /
26+
/_/|_|\__/\_,_/_//_/\___/\_,_/_//_/\_,_/
27+
28+
Version: 0.1.0
29+
Dump and analyze network packets.
30+
2331
Usage: redhound [options] ...
2432

2533
Options:
2634
-i, --interface INTERFACE name or idx of interface
2735
-D, --list-interfaces print list of interfaces and exit
36+
-w FILE write packets to a pcap capture file format to file
2837
-h, --help display this help and exit
2938
-v, --version display version information and exit
3039
```

lib/redhound.rb

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
require_relative 'redhound/receiver'
88
require_relative 'redhound/socket_builder'
99
require_relative 'redhound/version'
10+
require_relative 'redhound/writer'

lib/redhound/command.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def run(argv)
1515
warn 'Error: interface is required'
1616
exit 1
1717
end
18-
Receiver.run(ifname: @options[:ifname])
18+
Receiver.run(ifname: @options[:ifname], filename: @options[:filename])
1919
end
2020

2121
def parse(argv)
@@ -39,6 +39,7 @@ def parse(argv)
3939
list_interfaces
4040
exit
4141
end
42+
o.on('-w FILE', 'write packets to a pcap capture file format to file') { |v| @options[:filename] = v }
4243
o.on('-h', '--help', 'display this help and exit') do
4344
puts o
4445
exit

lib/redhound/receiver.rb

+11-3
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,28 @@
55
module Redhound
66
class Receiver
77
class << self
8-
def run(ifname:)
9-
new(ifname:).run
8+
def run(ifname:, filename:)
9+
new(ifname:, filename:).run
1010
end
1111
end
1212

13-
def initialize(ifname:)
13+
def initialize(ifname:, filename:)
1414
@ifname = ifname
1515
@socket = SocketBuilder.build(ifname:)
16+
if filename
17+
@writer = Writer.new(filename:)
18+
@writer.start
19+
end
1620
end
1721

1822
def run
1923
loop do
2024
msg, = @socket.recvfrom(2048)
2125
Analyzer.analyze(msg:)
26+
@writer.write(msg) if @writer
27+
rescue Interrupt
28+
@writer.stop if @writer
29+
break
2230
end
2331
end
2432
end

lib/redhound/writer.rb

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# frozen_string_literal: true
2+
3+
module Redhound
4+
class Writer
5+
def initialize(filename:)
6+
@filename = filename
7+
end
8+
9+
def start
10+
@file = File.open(@filename, 'wb')
11+
@file.write(file_header)
12+
end
13+
14+
def write(msg)
15+
@file.write(packet_record(Time.now, msg.bytesize, msg.bytesize))
16+
@file.write(msg)
17+
end
18+
19+
def stop
20+
@file.close
21+
end
22+
23+
private
24+
25+
def file_header
26+
[
27+
0xa1b2c3d4, # Magic Number (little-endian)
28+
2, # Version Major
29+
4, # Version Minor
30+
0, # Timezone offset (GMT)
31+
0, # Timestamp accuracy
32+
65535, # Snapshot length
33+
1 # Link-layer header type (Ethernet)
34+
].pack('VvvVVVV')
35+
end
36+
37+
def packet_record(timestamp, captured_length, original_length)
38+
[
39+
timestamp.to_i, # Timestamp seconds
40+
(timestamp.usec || 0), # Timestamp microseconds
41+
captured_length, # Captured packet length
42+
original_length # Original packet length
43+
].pack('VVVV')
44+
end
45+
end
46+
end

0 commit comments

Comments
 (0)