redhound 0.2.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -3
  3. data/Dockerfile +1 -1
  4. data/README.md +2 -2
  5. data/Rakefile +12 -1
  6. data/Steepfile +4 -0
  7. data/lib/redhound/analyzer.rb +19 -15
  8. data/lib/redhound/builder/packet_mreq.rb +56 -0
  9. data/lib/redhound/builder/socket.rb +35 -0
  10. data/lib/redhound/builder.rb +4 -0
  11. data/lib/redhound/command.rb +6 -1
  12. data/lib/redhound/l2/ether.rb +68 -0
  13. data/lib/redhound/l2/protocol.rb +33 -0
  14. data/lib/redhound/l2.rb +4 -0
  15. data/lib/redhound/l3/arp.rb +114 -0
  16. data/lib/redhound/l3/base.rb +49 -0
  17. data/lib/redhound/{header → l3}/ipv4.rb +27 -44
  18. data/lib/redhound/l3/ipv6.rb +69 -0
  19. data/lib/redhound/l3/protocol.rb +173 -0
  20. data/lib/redhound/l3/resolver.rb +30 -0
  21. data/lib/redhound/l3.rb +9 -0
  22. data/lib/redhound/l4/base.rb +31 -0
  23. data/lib/redhound/{header → l4}/icmp.rb +20 -26
  24. data/lib/redhound/l4/resolver.rb +28 -0
  25. data/lib/redhound/{header → l4}/udp.rb +19 -19
  26. data/lib/redhound/l4.rb +6 -0
  27. data/lib/redhound/receiver.rb +17 -5
  28. data/lib/redhound/resolver.rb +22 -0
  29. data/lib/redhound/source/socket.rb +18 -0
  30. data/lib/redhound/source.rb +3 -0
  31. data/lib/redhound/version.rb +2 -1
  32. data/lib/redhound/writer.rb +9 -2
  33. data/lib/redhound.rb +6 -3
  34. data/rbs_collection.lock.yaml +20 -0
  35. data/rbs_collection.yaml +17 -0
  36. data/sig/generated/redhound/analyzer.rbs +14 -0
  37. data/sig/generated/redhound/builder/packet_mreq.rbs +39 -0
  38. data/sig/generated/redhound/builder/socket.rbs +24 -0
  39. data/sig/generated/redhound/command.rbs +19 -0
  40. data/sig/generated/redhound/l2/ether.rbs +41 -0
  41. data/sig/generated/redhound/l2/protocol.rbs +15 -0
  42. data/sig/generated/redhound/l3/arp.rbs +57 -0
  43. data/sig/generated/redhound/l3/base.rbs +28 -0
  44. data/sig/generated/redhound/l3/ipv4.rbs +53 -0
  45. data/sig/generated/redhound/l3/ipv6.rbs +38 -0
  46. data/sig/generated/redhound/l3/protocol.rbs +16 -0
  47. data/sig/generated/redhound/l3/resolver.rbs +16 -0
  48. data/sig/generated/redhound/l4/base.rbs +19 -0
  49. data/sig/generated/redhound/l4/icmp.rbs +33 -0
  50. data/sig/generated/redhound/l4/resolver.rbs +16 -0
  51. data/sig/generated/redhound/l4/udp.rbs +36 -0
  52. data/sig/generated/redhound/receiver.rbs +19 -0
  53. data/sig/generated/redhound/resolver.rbs +11 -0
  54. data/sig/generated/redhound/source/socket.rbs +13 -0
  55. data/sig/generated/redhound/version.rbs +5 -0
  56. data/sig/generated/redhound/writer.rbs +25 -0
  57. metadata +48 -11
  58. data/lib/redhound/header/ether.rb +0 -67
  59. data/lib/redhound/header.rb +0 -6
  60. data/lib/redhound/packet_mreq.rb +0 -46
  61. data/lib/redhound/socket_builder.rb +0 -30
  62. 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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'l3/base'
4
+
5
+ require_relative 'l3/arp'
6
+ require_relative 'l3/ipv4'
7
+ require_relative 'l3/ipv6'
8
+ require_relative 'l3/protocol'
9
+ require_relative 'l3/resolver'
@@ -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 Header
5
- class Icmp
6
- class << self
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, 'bytes must be bigger than 8 bytes' unless bytes.size >= 8
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
- def dump
34
- puts 'ICMP HEADER----------------'
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
- <<~ICMP
41
- Type: #{@type}
42
- Code: #{@code}
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
- <<~ICMP
50
- Type: #{@type}
51
- Code: #{@code}
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 Header
5
- class Udp
6
- class << self
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, 'bytes must be bigger than 8 bytes' unless bytes.size >= 8
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
- def dump
28
- puts 'UDP HEADER----------------'
29
- puts self
30
- end
24
+ # @rbs () -> Integer
25
+ def size = 8
31
26
 
27
+ # @rbs () -> String
32
28
  def to_s
33
- <<~UDP
34
- Source Port: #{sport}
35
- Destination Port: #{dport}
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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'l4/base'
4
+ require_relative 'l4/icmp'
5
+ require_relative 'l4/resolver'
6
+ require_relative 'l4/udp'
@@ -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
- @socket = SocketBuilder.build(ifname:)
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, = @socket.recvfrom(2048)
25
- Analyzer.analyze(msg:)
26
- @writer.write(msg) if @writer
29
+ msg, = @source.next_packet
30
+ Analyzer.analyze(msg:, count: increment)
31
+ @writer&.write(msg:)
27
32
  rescue Interrupt
28
- @writer.stop if @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
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'source/socket'
@@ -1,5 +1,6 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Redhound
4
- VERSION = '0.2.0'
5
+ VERSION = '1.0.1'
5
6
  end
@@ -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
- def write(msg)
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
- (timestamp.usec || 0), # Timestamp microseconds
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/header'
6
- require_relative 'redhound/packet_mreq'
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/socket_builder'
10
+ require_relative 'redhound/resolver'
11
+ require_relative 'redhound/source'
9
12
  require_relative 'redhound/version'
10
13
  require_relative 'redhound/writer'