redhound 0.2.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -3
- data/Dockerfile +1 -1
- data/README.md +2 -2
- data/Rakefile +12 -1
- data/Steepfile +4 -0
- data/lib/redhound/analyzer.rb +19 -15
- data/lib/redhound/builder/packet_mreq.rb +56 -0
- data/lib/redhound/builder/socket.rb +35 -0
- data/lib/redhound/builder.rb +4 -0
- data/lib/redhound/command.rb +6 -1
- data/lib/redhound/l2/ether.rb +68 -0
- data/lib/redhound/l2/protocol.rb +33 -0
- data/lib/redhound/l2.rb +4 -0
- data/lib/redhound/l3/arp.rb +114 -0
- data/lib/redhound/l3/base.rb +49 -0
- data/lib/redhound/{header → l3}/ipv4.rb +27 -44
- data/lib/redhound/l3/ipv6.rb +69 -0
- data/lib/redhound/l3/protocol.rb +173 -0
- data/lib/redhound/l3/resolver.rb +30 -0
- data/lib/redhound/l3.rb +9 -0
- data/lib/redhound/l4/base.rb +31 -0
- data/lib/redhound/{header → l4}/icmp.rb +20 -26
- data/lib/redhound/l4/resolver.rb +28 -0
- data/lib/redhound/{header → l4}/udp.rb +19 -19
- data/lib/redhound/l4.rb +6 -0
- data/lib/redhound/receiver.rb +17 -5
- data/lib/redhound/resolver.rb +22 -0
- data/lib/redhound/source/socket.rb +18 -0
- data/lib/redhound/source.rb +3 -0
- data/lib/redhound/version.rb +2 -1
- data/lib/redhound/writer.rb +9 -2
- data/lib/redhound.rb +6 -3
- data/rbs_collection.lock.yaml +20 -0
- data/rbs_collection.yaml +17 -0
- data/sig/generated/redhound/analyzer.rbs +14 -0
- data/sig/generated/redhound/builder/packet_mreq.rbs +39 -0
- data/sig/generated/redhound/builder/socket.rbs +24 -0
- data/sig/generated/redhound/command.rbs +19 -0
- data/sig/generated/redhound/l2/ether.rbs +41 -0
- data/sig/generated/redhound/l2/protocol.rbs +15 -0
- data/sig/generated/redhound/l3/arp.rbs +57 -0
- data/sig/generated/redhound/l3/base.rbs +28 -0
- data/sig/generated/redhound/l3/ipv4.rbs +53 -0
- data/sig/generated/redhound/l3/ipv6.rbs +38 -0
- data/sig/generated/redhound/l3/protocol.rbs +16 -0
- data/sig/generated/redhound/l3/resolver.rbs +16 -0
- data/sig/generated/redhound/l4/base.rbs +19 -0
- data/sig/generated/redhound/l4/icmp.rbs +33 -0
- data/sig/generated/redhound/l4/resolver.rbs +16 -0
- data/sig/generated/redhound/l4/udp.rbs +36 -0
- data/sig/generated/redhound/receiver.rbs +19 -0
- data/sig/generated/redhound/resolver.rbs +11 -0
- data/sig/generated/redhound/source/socket.rbs +13 -0
- data/sig/generated/redhound/version.rbs +5 -0
- data/sig/generated/redhound/writer.rbs +25 -0
- metadata +48 -11
- data/lib/redhound/header/ether.rb +0 -67
- data/lib/redhound/header.rb +0 -6
- data/lib/redhound/packet_mreq.rb +0 -46
- data/lib/redhound/socket_builder.rb +0 -30
- data/sig/redhound.rbs +0 -4
@@ -0,0 +1,69 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L3
|
6
|
+
class Ipv6 < Base
|
7
|
+
class << self
|
8
|
+
# @rbs (bytes: Array[Integer]) -> Redhound::L3::Ipv6
|
9
|
+
def generate(bytes:)
|
10
|
+
new(bytes:).generate
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :protocol #: Protocol
|
15
|
+
|
16
|
+
# @rbs (bytes: Array[Integer]) -> void
|
17
|
+
def initialize(bytes:)
|
18
|
+
raise ArgumentError, "bytes must be bigger than #{size} bytes" unless bytes.size >= size
|
19
|
+
|
20
|
+
@bytes = bytes
|
21
|
+
end
|
22
|
+
|
23
|
+
# @rbs () -> Integer
|
24
|
+
def size = 40
|
25
|
+
|
26
|
+
# @rbs () -> Redhound::L3::Ipv6
|
27
|
+
def generate
|
28
|
+
version_traffic_flow = @bytes[0..3].join.to_i(16)
|
29
|
+
@version = (version_traffic_flow >> 28) & 0xF
|
30
|
+
@traffic_class = (version_traffic_flow >> 20) & 0xFF
|
31
|
+
@flow_label = version_traffic_flow & 0xFFFFF
|
32
|
+
@payload_length = @bytes[4..5]
|
33
|
+
@next_header = @bytes[6]
|
34
|
+
@hop_limit = @bytes[7]
|
35
|
+
@saddr = @bytes[8..23]
|
36
|
+
@daddr = @bytes[24..39]
|
37
|
+
@protocol = Protocol.new(protocol: @next_header)
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# @rbs () -> String
|
42
|
+
def to_s
|
43
|
+
" └─ IPv6 Ver: #{@version} Traffic Class: #{@traffic_class} Flow Label: #{@flow_label} Payload Length: #{payload_length} Next Header: #{@protocol} Hop Limit: #{@hop_limit} Src: #{saddr} Dst: #{daddr}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# @rbs () -> bool
|
47
|
+
def supported_protocol?
|
48
|
+
@protocol.udp? # steep:ignore
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# @rbs () -> Integer
|
54
|
+
def payload_length
|
55
|
+
@payload_length.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
56
|
+
end
|
57
|
+
|
58
|
+
# @rbs () -> Integer
|
59
|
+
def saddr
|
60
|
+
@saddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
|
61
|
+
end
|
62
|
+
|
63
|
+
# @rbs () -> Integer
|
64
|
+
def daddr
|
65
|
+
@daddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L3
|
6
|
+
class Protocol
|
7
|
+
# refs: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml#protocol-numbers-1
|
8
|
+
PROTO_TABLE = {
|
9
|
+
0 => 'HOPOPT',
|
10
|
+
1 => 'ICMP',
|
11
|
+
2 => 'IGMP',
|
12
|
+
3 => 'GGP',
|
13
|
+
4 => 'IP-in-IP',
|
14
|
+
5 => 'ST',
|
15
|
+
6 => 'TCP',
|
16
|
+
7 => 'CBT',
|
17
|
+
8 => 'EGP',
|
18
|
+
9 => 'IGP',
|
19
|
+
10 => 'BBN-RCC-MON',
|
20
|
+
11 => 'NVP-II',
|
21
|
+
12 => 'PUP',
|
22
|
+
13 => 'ARGUS',
|
23
|
+
14 => 'EMCON',
|
24
|
+
15 => 'XNET',
|
25
|
+
16 => 'CHAOS',
|
26
|
+
17 => 'UDP',
|
27
|
+
18 => 'MUX',
|
28
|
+
19 => 'DCN-MEAS',
|
29
|
+
20 => 'HMP',
|
30
|
+
21 => 'PRM',
|
31
|
+
22 => 'XNS-IDP',
|
32
|
+
23 => 'TRUNK-1',
|
33
|
+
24 => 'TRUNK-2',
|
34
|
+
25 => 'LEAF-1',
|
35
|
+
26 => 'LEAF-2',
|
36
|
+
27 => 'RDP',
|
37
|
+
28 => 'IRTP',
|
38
|
+
29 => 'ISO-TP4',
|
39
|
+
30 => 'NETBLT',
|
40
|
+
31 => 'MFE-NSP',
|
41
|
+
32 => 'MERIT-INP',
|
42
|
+
33 => 'DCCP',
|
43
|
+
34 => '3PC',
|
44
|
+
35 => 'IDPR',
|
45
|
+
36 => 'XTP',
|
46
|
+
37 => 'DDP',
|
47
|
+
38 => 'IDPR-CMTP',
|
48
|
+
39 => 'TP++',
|
49
|
+
40 => 'IL',
|
50
|
+
41 => 'IPv6',
|
51
|
+
42 => 'SDRP',
|
52
|
+
43 => 'IPv6-Route',
|
53
|
+
44 => 'IPv6-Frag',
|
54
|
+
45 => 'IDRP',
|
55
|
+
46 => 'RSVP',
|
56
|
+
47 => 'GRE',
|
57
|
+
48 => 'DSR',
|
58
|
+
49 => 'BNA',
|
59
|
+
50 => 'ESP',
|
60
|
+
51 => 'AH',
|
61
|
+
52 => 'I-NLSP',
|
62
|
+
53 => 'SWIPE',
|
63
|
+
54 => 'NARP',
|
64
|
+
55 => 'MOBILE',
|
65
|
+
56 => 'TLSP',
|
66
|
+
57 => 'SKIP',
|
67
|
+
58 => 'IPv6-ICMP',
|
68
|
+
59 => 'IPv6-NoNxt',
|
69
|
+
60 => 'IPv6-Opts',
|
70
|
+
61 => 'Any host internal protocol',
|
71
|
+
62 => 'CFTP',
|
72
|
+
63 => 'Any local network',
|
73
|
+
64 => 'SAT-EXPAK',
|
74
|
+
65 => 'KRYPTOLAN',
|
75
|
+
66 => 'RVD',
|
76
|
+
67 => 'IPPC',
|
77
|
+
68 => 'Any distributed file system',
|
78
|
+
69 => 'SAT-MON',
|
79
|
+
70 => 'VISA',
|
80
|
+
71 => 'IPCV',
|
81
|
+
72 => 'CPNX',
|
82
|
+
73 => 'CPHB',
|
83
|
+
74 => 'WSN',
|
84
|
+
75 => 'PVP',
|
85
|
+
76 => 'BR-SAT-MON',
|
86
|
+
77 => 'SUN-ND',
|
87
|
+
78 => 'WB-MON',
|
88
|
+
79 => 'WB-EXPAK',
|
89
|
+
80 => 'ISO-IP',
|
90
|
+
81 => 'VMTP',
|
91
|
+
82 => 'SECURE-VMTP',
|
92
|
+
83 => 'VINES',
|
93
|
+
84 => 'TTP',
|
94
|
+
85 => 'NSFNET-IGP',
|
95
|
+
86 => 'DGP',
|
96
|
+
87 => 'TCF',
|
97
|
+
88 => 'EIGRP',
|
98
|
+
89 => 'OSPFIGP',
|
99
|
+
90 => 'Sprite-RPC',
|
100
|
+
91 => 'LARP',
|
101
|
+
92 => 'MTP',
|
102
|
+
93 => 'AX.25',
|
103
|
+
94 => 'IPIP',
|
104
|
+
95 => 'MICP',
|
105
|
+
96 => 'SCC-SP',
|
106
|
+
97 => 'ETHERIP',
|
107
|
+
98 => 'ENCAP',
|
108
|
+
99 => 'Any private encryption scheme',
|
109
|
+
100 => 'GMTP',
|
110
|
+
101 => 'IFMP',
|
111
|
+
102 => 'PNNI',
|
112
|
+
103 => 'PIM',
|
113
|
+
104 => 'ARIS',
|
114
|
+
105 => 'SCPS',
|
115
|
+
106 => 'QNX',
|
116
|
+
107 => 'A/N',
|
117
|
+
108 => 'IPComp',
|
118
|
+
109 => 'SNP',
|
119
|
+
110 => 'Compaq-Peer',
|
120
|
+
111 => 'IPX-in-IP',
|
121
|
+
112 => 'VRRP',
|
122
|
+
113 => 'PGM',
|
123
|
+
114 => 'Any 0-hop protocol',
|
124
|
+
115 => 'L2TP',
|
125
|
+
116 => 'DDX',
|
126
|
+
117 => 'IATP',
|
127
|
+
118 => 'STP',
|
128
|
+
119 => 'SRP',
|
129
|
+
120 => 'UTI',
|
130
|
+
121 => 'SMP',
|
131
|
+
122 => 'SM',
|
132
|
+
123 => 'PTP',
|
133
|
+
124 => 'IS-IS over IPv4',
|
134
|
+
125 => 'FIRE',
|
135
|
+
126 => 'CRTP',
|
136
|
+
127 => 'CRUDP',
|
137
|
+
128 => 'SSCOPMCE',
|
138
|
+
129 => 'IPLT',
|
139
|
+
130 => 'SPS',
|
140
|
+
131 => 'PIPE',
|
141
|
+
132 => 'SCTP',
|
142
|
+
133 => 'FC',
|
143
|
+
134 => 'RSVP-E2E-IGNORE',
|
144
|
+
135 => 'Mobility Header',
|
145
|
+
136 => 'UDPLite',
|
146
|
+
137 => 'MPLS-in-IP',
|
147
|
+
138 => 'manet',
|
148
|
+
139 => 'HIP',
|
149
|
+
140 => 'Shim6',
|
150
|
+
141 => 'WESP',
|
151
|
+
142 => 'ROHC',
|
152
|
+
255 => 'Reserved'
|
153
|
+
}.freeze
|
154
|
+
|
155
|
+
# @rbs (protocol: Integer) -> void
|
156
|
+
def initialize(protocol:)
|
157
|
+
@protocol = protocol
|
158
|
+
end
|
159
|
+
|
160
|
+
# @rbs () -> String
|
161
|
+
def to_s
|
162
|
+
PROTO_TABLE[@protocol] || 'Unknown'
|
163
|
+
end
|
164
|
+
|
165
|
+
PROTO_TABLE.each do |code, name|
|
166
|
+
method_name = name.downcase.gsub(/[ \-]/, '_') + '?'
|
167
|
+
define_method(method_name) do
|
168
|
+
@protocol == code
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L3
|
6
|
+
class Resolver
|
7
|
+
# @rbs (bytes: Array[Integer], l2: Redhound::L2::Ether) -> Redhound::L3::Base?
|
8
|
+
def self.resolve(bytes:, l2:)
|
9
|
+
new(bytes:, l2:).resolve
|
10
|
+
end
|
11
|
+
|
12
|
+
# @rbs (bytes: Array[Integer], l2: Redhound::L2::Ether) -> void
|
13
|
+
def initialize(bytes:, l2:)
|
14
|
+
@bytes = bytes
|
15
|
+
@l2 = l2
|
16
|
+
end
|
17
|
+
|
18
|
+
# @rbs () -> Redhound::L3::Base?
|
19
|
+
def resolve
|
20
|
+
if @l2.type.ipv4?
|
21
|
+
Ipv4.generate(bytes: @bytes)
|
22
|
+
elsif @l2.type.ipv6?
|
23
|
+
Ipv6.generate(bytes: @bytes)
|
24
|
+
elsif @l2.type.arp?
|
25
|
+
Arp.generate(bytes: @bytes)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/redhound/l3.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L4
|
6
|
+
class Base
|
7
|
+
class << self
|
8
|
+
# @rbs (bytes: Array[Integer]) -> Redhound::L4::Base
|
9
|
+
def generate(bytes:)
|
10
|
+
new(bytes:).generate
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @rbs (bytes: Array[Integer]) -> void
|
15
|
+
def initialize(bytes:)
|
16
|
+
warn 'initialize method must be implemented'
|
17
|
+
end
|
18
|
+
|
19
|
+
# @rbs () -> Redhound::L4::Base
|
20
|
+
def generate
|
21
|
+
warn 'generate method must be implemented'
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# @rbs () -> void
|
26
|
+
def dump
|
27
|
+
puts self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,20 +1,17 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Redhound
|
4
|
-
class
|
5
|
-
class Icmp
|
6
|
-
|
7
|
-
def generate(bytes:)
|
8
|
-
new(bytes:).generate
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
5
|
+
class L4
|
6
|
+
class Icmp < Base
|
7
|
+
# @rbs (bytes: Array[Integer]) -> void
|
12
8
|
def initialize(bytes:)
|
13
|
-
raise ArgumentError,
|
9
|
+
raise ArgumentError, "bytes must be bigger than #{size} bytes" unless bytes.size >= size
|
14
10
|
|
15
11
|
@bytes = bytes
|
16
12
|
end
|
17
13
|
|
14
|
+
# @rbs () -> Redhound::L4::Icmp
|
18
15
|
def generate
|
19
16
|
@type = @bytes[0]
|
20
17
|
@code = @bytes[1]
|
@@ -30,47 +27,44 @@ module Redhound
|
|
30
27
|
self
|
31
28
|
end
|
32
29
|
|
33
|
-
|
34
|
-
|
35
|
-
puts self
|
36
|
-
end
|
30
|
+
# @rbs () -> Integer
|
31
|
+
def size = 8
|
37
32
|
|
33
|
+
# @rbs () -> String
|
38
34
|
def to_s
|
39
35
|
if @type.zero? || @type == 8
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
Checksum: #{check}
|
44
|
-
ID: #{id}
|
45
|
-
Sequence: #{seq}
|
46
|
-
Data: #{data}
|
36
|
+
<<-ICMP.chomp
|
37
|
+
└─ ICMP Type: #{@type} Code: #{@code} Checksum: #{check} ID: #{id} Sequence: #{seq}
|
38
|
+
└─ Payload: #{data}
|
47
39
|
ICMP
|
48
40
|
else
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
Checksum: #{check}
|
53
|
-
Data: #{data}
|
41
|
+
<<-ICMP.chomp
|
42
|
+
└─ ICMP Type: #{@type} Code: #{@code} Checksum: #{check}
|
43
|
+
└─ Payload: #{data}
|
54
44
|
ICMP
|
55
45
|
end
|
56
46
|
end
|
57
47
|
|
58
48
|
private
|
59
49
|
|
50
|
+
# @rbs () -> Integer
|
60
51
|
def check
|
61
52
|
@check.map { |b| b.to_s(16).rjust(2, '0') }.join
|
62
53
|
end
|
63
54
|
|
55
|
+
# @rbs () -> Integer
|
64
56
|
def id
|
65
57
|
@id.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
66
58
|
end
|
67
59
|
|
60
|
+
# @rbs () -> Integer
|
68
61
|
def seq
|
69
62
|
@seq.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
70
63
|
end
|
71
64
|
|
65
|
+
# @rbs () -> String
|
72
66
|
def data
|
73
|
-
@data.map(&:chr).join
|
67
|
+
@data.map(&:chr).join.force_encoding("UTF-8")
|
74
68
|
end
|
75
69
|
end
|
76
70
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L4
|
6
|
+
class Resolver
|
7
|
+
# @rbs (bytes: Array[Integer], l3: Redhound::L3::Base) -> Redhound::L4::Base?
|
8
|
+
def self.resolve(bytes:, l3:)
|
9
|
+
new(bytes:, l3:).resolve
|
10
|
+
end
|
11
|
+
|
12
|
+
# @rbs (bytes: Array[Integer], l3: Redhound::L3::Base) -> void
|
13
|
+
def initialize(bytes:, l3:)
|
14
|
+
@bytes = bytes
|
15
|
+
@l3 = l3
|
16
|
+
end
|
17
|
+
|
18
|
+
# @rbs () -> Redhound::L4::Base?
|
19
|
+
def resolve
|
20
|
+
if @l3.protocol.udp?
|
21
|
+
Udp.generate(bytes: @bytes)
|
22
|
+
elsif @l3.protocol.icmp?
|
23
|
+
Icmp.generate(bytes: @bytes)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,20 +1,17 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Redhound
|
4
|
-
class
|
5
|
-
class Udp
|
6
|
-
|
7
|
-
def generate(bytes:)
|
8
|
-
new(bytes:).generate
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
5
|
+
class L4
|
6
|
+
class Udp < Base
|
7
|
+
# @rbs (bytes: Array[Integer]) -> void
|
12
8
|
def initialize(bytes:)
|
13
|
-
raise ArgumentError,
|
9
|
+
raise ArgumentError, "bytes must be bigger than #{size} bytes" unless bytes.size >= size
|
14
10
|
|
15
11
|
@bytes = bytes
|
16
12
|
end
|
17
13
|
|
14
|
+
# @rbs () -> Redhound::L4::Udp
|
18
15
|
def generate
|
19
16
|
@sport = @bytes[0..1]
|
20
17
|
@dport = @bytes[2..3]
|
@@ -24,37 +21,40 @@ module Redhound
|
|
24
21
|
self
|
25
22
|
end
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
puts self
|
30
|
-
end
|
24
|
+
# @rbs () -> Integer
|
25
|
+
def size = 8
|
31
26
|
|
27
|
+
# @rbs () -> String
|
32
28
|
def to_s
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
Length: #{len}
|
37
|
-
Checksum: #{check}
|
38
|
-
Data: #{data}
|
29
|
+
<<-UDP
|
30
|
+
└─ UDP Src: #{sport} Dst: #{dport} Len: #{len} Checksum: #{check}
|
31
|
+
└─ Payload: #{data}
|
39
32
|
UDP
|
40
33
|
end
|
41
34
|
|
35
|
+
private
|
36
|
+
|
37
|
+
# @rbs () -> Integer
|
42
38
|
def sport
|
43
39
|
@sport.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
44
40
|
end
|
45
41
|
|
42
|
+
# @rbs () -> Integer
|
46
43
|
def dport
|
47
44
|
@dport.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
48
45
|
end
|
49
46
|
|
47
|
+
# @rbs () -> Integer
|
50
48
|
def len
|
51
49
|
@len.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
52
50
|
end
|
53
51
|
|
52
|
+
# @rbs () -> Integer
|
54
53
|
def check
|
55
54
|
@check.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
56
55
|
end
|
57
56
|
|
57
|
+
# @rbs () -> String
|
58
58
|
def data
|
59
59
|
@data.map(&:chr).join
|
60
60
|
end
|
data/lib/redhound/l4.rb
ADDED
data/lib/redhound/receiver.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'socket'
|
@@ -5,29 +6,40 @@ require 'socket'
|
|
5
6
|
module Redhound
|
6
7
|
class Receiver
|
7
8
|
class << self
|
9
|
+
# @rbs (ifname: String, filename: String) -> void
|
8
10
|
def run(ifname:, filename:)
|
9
11
|
new(ifname:, filename:).run
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
15
|
+
# @rbs (ifname: String, filename: String) -> void
|
13
16
|
def initialize(ifname:, filename:)
|
14
17
|
@ifname = ifname
|
15
|
-
@
|
18
|
+
@source = Resolver.resolve(ifname:)
|
16
19
|
if filename
|
17
20
|
@writer = Writer.new(filename:)
|
18
21
|
@writer.start
|
19
22
|
end
|
23
|
+
@count = 0
|
20
24
|
end
|
21
25
|
|
26
|
+
# @rbs () -> void
|
22
27
|
def run
|
23
28
|
loop do
|
24
|
-
msg, = @
|
25
|
-
Analyzer.analyze(msg:)
|
26
|
-
@writer
|
29
|
+
msg, = @source.next_packet
|
30
|
+
Analyzer.analyze(msg:, count: increment)
|
31
|
+
@writer&.write(msg:)
|
27
32
|
rescue Interrupt
|
28
|
-
@writer
|
33
|
+
@writer&.stop
|
29
34
|
break
|
30
35
|
end
|
31
36
|
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# @rbs () -> Integer
|
41
|
+
def increment
|
42
|
+
@count.tap { @count += 1 }
|
43
|
+
end
|
32
44
|
end
|
33
45
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class Resolver
|
6
|
+
class << self
|
7
|
+
# @rbs (ifname: String) -> void
|
8
|
+
def resolve(ifname:)
|
9
|
+
new.resolve(ifname:)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# @rbs (ifname: String) -> Redhound::Source::Socket
|
14
|
+
def resolve(ifname:)
|
15
|
+
if RUBY_PLATFORM =~ /darwin/
|
16
|
+
raise 'Not implemented yet'
|
17
|
+
else
|
18
|
+
Redhound::Builder::Socket.build(ifname:)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
module Source
|
6
|
+
class Socket
|
7
|
+
# @rbs (socket: ::Socket) -> void
|
8
|
+
def initialize(socket:)
|
9
|
+
@socket = socket
|
10
|
+
end
|
11
|
+
|
12
|
+
# @rbs (Integer size) -> [String, Addrinfo]
|
13
|
+
def next_packet(size = 2048)
|
14
|
+
@socket.recvfrom(size)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/redhound/version.rb
CHANGED
data/lib/redhound/writer.rb
CHANGED
@@ -1,27 +1,33 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Redhound
|
4
5
|
class Writer
|
6
|
+
# @rbs (filename: String) -> void
|
5
7
|
def initialize(filename:)
|
6
8
|
@filename = filename
|
7
9
|
end
|
8
10
|
|
11
|
+
# @rbs () -> void
|
9
12
|
def start
|
10
13
|
@file = File.open(@filename, 'wb')
|
11
14
|
@file.write(file_header)
|
12
15
|
end
|
13
16
|
|
14
|
-
|
17
|
+
# @rbs (msg: String) -> void
|
18
|
+
def write(msg:)
|
15
19
|
@file.write(packet_record(Time.now, msg.bytesize, msg.bytesize))
|
16
20
|
@file.write(msg)
|
17
21
|
end
|
18
22
|
|
23
|
+
# @rbs () -> void
|
19
24
|
def stop
|
20
25
|
@file.close
|
21
26
|
end
|
22
27
|
|
23
28
|
private
|
24
29
|
|
30
|
+
# @rbs () -> String
|
25
31
|
def file_header
|
26
32
|
[
|
27
33
|
0xa1b2c3d4, # Magic Number (little-endian)
|
@@ -34,10 +40,11 @@ module Redhound
|
|
34
40
|
].pack('VvvVVVV')
|
35
41
|
end
|
36
42
|
|
43
|
+
# @rbs (Time timestamp, Integer captured_length, Integer original_length) -> String
|
37
44
|
def packet_record(timestamp, captured_length, original_length)
|
38
45
|
[
|
39
46
|
timestamp.to_i, # Timestamp seconds
|
40
|
-
|
47
|
+
timestamp.usec || 0, # Timestamp microseconds
|
41
48
|
captured_length, # Captured packet length
|
42
49
|
original_length # Original packet length
|
43
50
|
].pack('VVVV')
|
data/lib/redhound.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'redhound/analyzer'
|
4
|
+
require_relative 'redhound/builder'
|
4
5
|
require_relative 'redhound/command'
|
5
|
-
require_relative 'redhound/
|
6
|
-
require_relative 'redhound/
|
6
|
+
require_relative 'redhound/l2'
|
7
|
+
require_relative 'redhound/l3'
|
8
|
+
require_relative 'redhound/l4'
|
7
9
|
require_relative 'redhound/receiver'
|
8
|
-
require_relative 'redhound/
|
10
|
+
require_relative 'redhound/resolver'
|
11
|
+
require_relative 'redhound/source'
|
9
12
|
require_relative 'redhound/version'
|
10
13
|
require_relative 'redhound/writer'
|