redhound 0.1.0 → 1.0.0

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 +15 -2
  3. data/Dockerfile +1 -1
  4. data/README.md +10 -1
  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 +8 -2
  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 +8 -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 +26 -6
  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 +53 -0
  33. data/lib/redhound.rb +7 -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 +49 -11
  58. data/lib/redhound/header/ether.rb +0 -68
  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
@@ -1,24 +1,26 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Redhound
4
- class Header
5
- class Ipv4
5
+ class L3
6
+ class Ipv4 < Base
6
7
  class << self
8
+ # @rbs (bytes: Array[Integer]) -> Redhound::L3::Ipv4
7
9
  def generate(bytes:)
8
10
  new(bytes:).generate
9
11
  end
10
12
  end
11
13
 
12
- # ref: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml#protocol-numbers-1
13
- ICMP = 1
14
- UDP = 17
14
+ attr_reader :protocol #: Protocol
15
15
 
16
+ # @rbs (bytes: Array[Integer]) -> void
16
17
  def initialize(bytes:)
17
- raise ArgumentError, 'bytes must be 20 bytes' unless bytes.size == 20
18
+ raise ArgumentError, "bytes must be #{size} bytes" unless bytes.size >= size
18
19
 
19
20
  @bytes = bytes
20
21
  end
21
22
 
23
+ # @rbs () -> Redhound::L3::Ipv4
22
24
  def generate
23
25
  @version = @bytes[0]
24
26
  @ihl = @bytes[0]
@@ -27,85 +29,66 @@ module Redhound
27
29
  @id = @bytes[4..5]
28
30
  @frag_off = @bytes[6..7]
29
31
  @ttl = @bytes[8]
30
- @protocol = @bytes[9]
32
+ @protocol = Protocol.new(protocol: @bytes[9])
31
33
  @check = @bytes[10..11]
32
34
  @saddr = @bytes[12..15]
33
35
  @daddr = @bytes[16..19]
34
36
  self
35
37
  end
36
38
 
37
- def icmp?
38
- @protocol == ICMP
39
- end
40
-
41
- def udp?
42
- @protocol == UDP
43
- end
39
+ # @rbs () -> Integer
40
+ def size = 20
44
41
 
45
- def dump
46
- puts 'IPv4 HEADER----------------'
47
- puts self
42
+ # @rbs () -> String
43
+ def to_s
44
+ " └─ IPv4 Ver: #{version} IHL: #{ihl} TOS: #{@tos} Total Length: #{tot_len} ID: #{id} Offset: #{frag_off} TTL: #{@ttl} Protocol: #{@protocol} Checksum: #{check} Src: #{saddr} Dst: #{daddr}"
48
45
  end
49
46
 
50
- def to_s
51
- <<~IPV4
52
- Version: #{@version}
53
- IHL: #{@ihl}
54
- TOS: #{@tos}
55
- Total Length: #{tot_len}
56
- ID: #{id}
57
- Fragment Offset: #{frag_off}
58
- TTL: #{@ttl}
59
- Protocol: #{protocol}
60
- Checksum: #{check}
61
- Source IP: #{saddr}
62
- Destination IP: #{daddr}
63
- IPV4
47
+ # @rbs () -> bool
48
+ def supported_protocol?
49
+ @protocol.udp? || @protocol.icmp? # steep:ignore
64
50
  end
65
51
 
66
52
  private
67
53
 
54
+ # @rbs () -> Integer
68
55
  def version
69
56
  @version & 0xF0
70
57
  end
71
58
 
59
+ # @rbs () -> Integer
72
60
  def ihl
73
61
  @ihl & 0x0F
74
62
  end
75
63
 
64
+ # @rbs () -> Integer
76
65
  def tot_len
77
66
  @tot_len.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
78
67
  end
79
68
 
69
+ # @rbs () -> Integer
80
70
  def id
81
71
  @id.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
82
72
  end
83
73
 
74
+ # @rbs () -> Integer
84
75
  def frag_off
85
76
  @frag_off.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16) & 0x1FFF
86
77
  end
87
78
 
88
- def protocol
89
- case @protocol
90
- when ICMP
91
- 'ICMP'
92
- when UDP
93
- 'UDP'
94
- else
95
- 'Unknown'
96
- end
97
- end
98
-
79
+ # @rbs () -> Integer
99
80
  def check
100
81
  @check.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
101
82
  end
102
83
 
84
+ # @rbs () -> String
103
85
  def saddr
104
- @saddr.map { |b| b.to_s(16).rjust(2, '0') }.join('.')
86
+ @saddr.join('.')
105
87
  end
106
88
 
89
+ # @rbs () -> String
107
90
  def daddr
108
- @daddr.map { |b| b.to_s(16).rjust(2, '0') }.join('.')
91
+ @daddr.join('.')
109
92
  end
110
93
  end
111
94
  end
@@ -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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'l3/arp'
4
+ require_relative 'l3/base'
5
+ require_relative 'l3/ipv4'
6
+ require_relative 'l3/ipv6'
7
+ require_relative 'l3/protocol'
8
+ 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'