packetgen 2.1.4 → 2.2.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.
@@ -33,9 +33,9 @@ module PacketGen
33
33
  return self if str.nil?
34
34
 
35
35
  str.split('.').each do |label|
36
- self << Types::IntString.new(label)
36
+ self << Types::IntString.new(string: label)
37
37
  end
38
- self << Types::IntString.new('')
38
+ self << Types::IntString.new
39
39
  end
40
40
 
41
41
  # Read a sequence of label from a string
@@ -17,7 +17,7 @@ module PacketGen
17
17
  # @!attribute rdata
18
18
  # @return [Types::String]
19
19
  define_field :rdata, Types::String,
20
- builder: ->(rr) { Types::String.new('', length_from: rr[:rdlength]) }
20
+ builder: ->(rr, t) { t.new(length_from: rr[:rdlength]) }
21
21
 
22
22
  # @param [DNS] dns
23
23
  # @param [Hash] options
@@ -19,7 +19,7 @@ module PacketGen
19
19
  # @!attribute value
20
20
  # @return [::String]
21
21
  define_field :value, Types::String,
22
- builder: ->(h) { Types::String.new('', length_from: h[:value_size]) }
22
+ builder: ->(h, t) { t.new(length_from: h[:value_size]) }
23
23
  # @!attribute optional_name
24
24
  # @return [::String]
25
25
  define_field :optional_name, Types::String
@@ -218,7 +218,7 @@ module PacketGen
218
218
  # as padding is used to pad for CBC mode, this is unused
219
219
  cipher.final
220
220
 
221
- self[:body] = Types::String.new(iv) << enc_msg[0..-3]
221
+ self[:body] = Types::String.new.read(iv) << enc_msg[0..-3]
222
222
  self[:pad_length].read enc_msg[-2]
223
223
  self[:next].read enc_msg[-1]
224
224
 
@@ -32,7 +32,7 @@ module PacketGen
32
32
 
33
33
  # Ethernet MAC address, as a group of 6 bytes
34
34
  # @author Sylvain Daubert
35
- class MacAddr < Base
35
+ class MacAddr < Types::Fields
36
36
  # @!attribute a0
37
37
  # @return [Integer] first byte from MacAddr
38
38
  define_field :a0, Types::Int8
@@ -0,0 +1,8 @@
1
+ # This file is part of PacketGen
2
+ # See https://github.com/sdaubert/packetgen for more informations
3
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
+ # This program is published under MIT license.
5
+
6
+ require_relative 'http/headers'
7
+ require_relative 'http/response'
8
+ require_relative 'http/request'
@@ -0,0 +1,76 @@
1
+ # This file is part of PacketGen
2
+ # See https://github.com/sdaubert/packetgen for more informations
3
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
+ # This program is published under MIT license.
5
+
6
+ module PacketGen
7
+ module Header
8
+
9
+ # @since 2.2.0
10
+ module HTTP
11
+ # @abstract Base class for HTTP headers.
12
+ # @author Kent 'picat' Gruber
13
+ class Headers
14
+ def initialize
15
+ @data = nil
16
+ end
17
+
18
+ # Populate object from a string or directly from a hash.
19
+ # @param [String, Hash]
20
+ # @return [self]
21
+ def read(s_or_h)
22
+ case s_or_h
23
+ when String
24
+ @data = s_or_h.split("\n").map do |h|
25
+ k, v = h.split(":", 2)
26
+ [k, v.strip]
27
+ end.to_h
28
+ when Hash
29
+ @data = s_or_h
30
+ end
31
+ self
32
+ end
33
+
34
+ # Get binary string.
35
+ # @return [String]
36
+ def to_s
37
+ return "\r\n" if @data.nil? || @data.empty?
38
+ d = []
39
+ @data.map do |k, v|
40
+ str = ""
41
+ str << k << ": " << v
42
+ d << str
43
+ end
44
+ d.join("\r\n") << "\r\n\r\n"
45
+ end
46
+
47
+ # Get a human readable string.
48
+ # @return [Hash]
49
+ def to_human
50
+ @data
51
+ end
52
+
53
+ # Read human-readable data to populate header data.
54
+ # @param [String, Hash]
55
+ # @return [self]
56
+ def from_human(data)
57
+ read(data)
58
+ end
59
+
60
+ # Check if any headers were given.
61
+ # @return [Boolean]
62
+ def given?
63
+ return true unless @data.nil? || @data.empty?
64
+ false
65
+ end
66
+
67
+ # Shorcut to the underlying Headers data or nil.
68
+ # @return [Hash, nil]
69
+ def data
70
+ @data
71
+ end
72
+ alias to_h data
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,88 @@
1
+ # This file is part of PacketGen
2
+ # See https://github.com/sdaubert/packetgen for more informations
3
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
+ # This program is published under MIT license.
5
+
6
+ module PacketGen
7
+ module Header
8
+ module HTTP
9
+ # An HTTP/1.1 Request packet consits of:
10
+ # * the http method ({Types::String}).
11
+ # * the path ({Types::String}).
12
+ # * the version ({Types::String}).
13
+ # * associated http headers ({HTTP::Headers}).
14
+ #
15
+ # == Create a HTTP Request header
16
+ # # standalone
17
+ # http_rqst = PacketGen::Header::HTTP::Request.new
18
+ # # in a packet
19
+ # pkt = PacketGen.gen("IP").add("TCP").add("HTTP::Request")
20
+ # # access to HTTP Request header
21
+ # pkt.http_request # => PacketGen::Header::HTTP::Request
22
+ #
23
+ # Note: When creating a HTTP Request packet, +sport+ and +dport+
24
+ # attributes of TCP header are not set.
25
+ #
26
+ # == HTTP Request attributes
27
+ # http_rqst.version = "HTTP/1.1"
28
+ # http_rqst.method = "GET"
29
+ # http_rqst.path = "/meow.html"
30
+ # http_rqst.headers = "Host: tcpdump.org" # string or
31
+ # http_rqst.headers = { "Host": "tcpdump.org" } # even a hash
32
+ #
33
+ # @author Kent 'picat' Gruber
34
+ class Request < Base
35
+ # @!attribute method
36
+ # @return [Types::String]
37
+ define_field :method, Types::String
38
+ # @!attribute path
39
+ # @return [Types::String]
40
+ define_field :path, Types::String
41
+ # @!attribute version
42
+ # @return [Types::String]
43
+ define_field :version, Types::String, default: "HTTP/1.1"
44
+ # @!attribute headers
45
+ # associated http/1.1 headers
46
+ # @return [HTTP::Headers]
47
+ define_field :headers, HTTP::Headers
48
+
49
+ # @param [Hash] options
50
+ # @option options [String] :method
51
+ # @option options [String] :path
52
+ # @option options [String] :version
53
+ # @option options [Hash] :headers
54
+ def initialize(options={})
55
+ super(options)
56
+ self.headers ||= options[:headers]
57
+ end
58
+
59
+ # Read in the HTTP portion of the packet, and parse it.
60
+ # @return [PacketGen::HTTP::Request]
61
+ def read(str)
62
+ # prepare data to parse
63
+ str = str.split("\n").map(&:strip).reject(&:empty?)
64
+ first_line = str.shift.split
65
+ self[:method] = first_line[0]
66
+ self[:path] = first_line[1]
67
+ self[:version] = first_line[2]
68
+ headers = str.join("\n")
69
+ self[:headers].read(headers)
70
+ self
71
+ end
72
+
73
+ # String representation of data.
74
+ # @return [String]
75
+ def to_s
76
+ raise FormatError, "Missing #method." if self.method.empty?
77
+ raise FormatError, "Missing #path." if self.path.empty?
78
+ raise FormatError, "Missing #version." if self.version.empty?
79
+ str = "" # build 'dat string
80
+ str << self[:method] << " " << self[:path] << " " << self[:version] << "\r\n" << self[:headers].to_s
81
+ end
82
+ end
83
+ end
84
+
85
+ self.add_class HTTP::Request
86
+ TCP.bind_header HTTP::Request, body: ->(b) { /^(CONNECT|DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT)/ =~ b }
87
+ end
88
+ end
@@ -0,0 +1,116 @@
1
+ # This file is part of PacketGen
2
+ # See https://github.com/sdaubert/packetgen for more informations
3
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
+ # This program is published under MIT license.
5
+
6
+ module PacketGen
7
+ module Header
8
+ module HTTP
9
+ # An HTTP/1.1 Response packet consits of:
10
+ # * the version ({Types::String}).
11
+ # * the status code ({Types::String}).
12
+ # * the status message ({Types::String}).
13
+ # * associated http headers ({HTTP::Headers}).
14
+ # * the actual http payload body ({Types::String}).
15
+ #
16
+ # == Create a HTTP Response header
17
+ # # standalone
18
+ # http_resp = PacketGen::Header::HTTP::Response.new
19
+ # # in a packet
20
+ # pkt = PacketGen.gen("IP").add("TCP").add("HTTP::Response")
21
+ # # access to HTTP Response header
22
+ # pkt.http_response # => PacketGen::Header::HTTP::Response
23
+ #
24
+ # Note: When creating a HTTP Response packet, +sport+ and +dport+
25
+ # attributes of TCP header are not set.
26
+ #
27
+ # == HTTP Response attributes
28
+ # http_resp.version = "HTTP/1.1"
29
+ # http_resp.status_code = "200"
30
+ # http_resp.status_mesg = "OK"
31
+ # http_resp.body = "this is a body"
32
+ # http_resp.headers = "Host: tcpdump.org" # string or
33
+ # http_resp.headers = { "Host": "tcpdump.org" } # even a hash
34
+ #
35
+ # @author Kent 'picat' Gruber
36
+ class Response < Base
37
+ # @!attribute version
38
+ # @return [Types::String]
39
+ define_field :version, Types::String, default: "HTTP/1.1"
40
+ # @!attribute status_code
41
+ # @return [Types::String]
42
+ define_field :status_code, Types::String
43
+ # @!attribute status_mesg
44
+ # @return [Types::String]
45
+ define_field :status_mesg, Types::String
46
+ # @!attribute headers
47
+ # associated http/1.1 headers
48
+ # @return [Types::String]
49
+ define_field :headers, HTTP::Headers
50
+ # @!attribute body
51
+ # @return [HTTP::PHeaders]
52
+ define_field :body, Types::String
53
+
54
+ # @param [Hash] options
55
+ # @option options [String] :version
56
+ # @option options [String] :status_code
57
+ # @option options [String] :status_mesg
58
+ # @option options [String] :body
59
+ # @option options [Hash] :headers
60
+ def initialize(options={})
61
+ super(options)
62
+ self.headers ||= options[:headers]
63
+ end
64
+
65
+ # Read in the HTTP portion of the packet, and parse it.
66
+ # @return [PacketGen::HTTP::Response]
67
+ def read(str)
68
+ # prepare data to parse
69
+ arr = str.split("\r\n")
70
+ headers = [] # header stream
71
+ data = [] # data stream
72
+ switch = false
73
+ arr.each do |line|
74
+ if line.empty?
75
+ data << line if switch # already done
76
+ switch = true
77
+ next
78
+ end
79
+ case switch
80
+ when true
81
+ data << line
82
+ else
83
+ headers << line
84
+ end
85
+ end
86
+ unless headers.empty?
87
+ first_line = headers.shift.split
88
+ self[:version] = first_line[0]
89
+ self[:status_code] = first_line[1]
90
+ self[:status_mesg] = first_line[2..-1].join(" ")
91
+ self[:headers].read(headers.join("\n"))
92
+ end
93
+ self[:body] = data.join("\n")
94
+ self
95
+ end
96
+
97
+ # String representation of data.
98
+ # @return [String]
99
+ def to_s
100
+ raise FormatError, "Missing #status_code." if self.status_code.empty?
101
+ raise FormatError, "Missing #status_mesg." if self.status_mesg.empty?
102
+ raise FormatError, "Missing #version." if self.version.empty?
103
+ str = "" # build 'dat string
104
+ str << self[:version] << " " << self[:status_code] << " " << self[:status_mesg] << "\r\n"
105
+ if self[:headers].given?
106
+ str << self[:headers].to_s
107
+ end
108
+ str << self.body
109
+ end
110
+ end
111
+ end
112
+
113
+ self.add_class HTTP::Response
114
+ TCP.bind_header HTTP::Response, body: ->(b) { /^HTTP\/1\.1\s\d{3,}\s.+/ =~ b }
115
+ end
116
+ end
@@ -107,7 +107,7 @@ module PacketGen
107
107
  # this field is not present in the proposal.
108
108
  # @return [String]
109
109
  define_field_before :content, :spi, Types::String,
110
- builder: ->(t) { Types::String.new('', length_from: t[:spi_size]) }
110
+ builder: ->(h, t) { t.new(length_from: h[:spi_size]) }
111
111
 
112
112
  alias type message_type
113
113
 
@@ -398,11 +398,11 @@ module PacketGen
398
398
  # the sending entity's SPI. When the {#spi_size} field is zero,
399
399
  # this field is not present in the proposal.
400
400
  # @return [String]
401
- define_field :spi, Types::String, builder: ->(t) { Types::String.new('', length_from: t[:spi_size]) }
401
+ define_field :spi, Types::String, builder: ->(h, t) { t.new(length_from: h[:spi_size]) }
402
402
  # @!attribute transforms
403
403
  # 8-bit set of tranforms for this proposal
404
404
  # @return [Transforms]
405
- define_field :transforms, Transforms, builder: ->(t) { Transforms.new(counter: t[:num_trans]) }
405
+ define_field :transforms, Transforms, builder: ->(h, t) { t.new(counter: h[:num_trans]) }
406
406
 
407
407
  def initialize(options={})
408
408
  if options[:spi] and options[:spi_size].nil?
@@ -234,7 +234,7 @@ module PacketGen
234
234
  # Set of {TrafficSelector}
235
235
  # @return {TrafficSelectors}
236
236
  define_field_before :body, :traffic_selectors, TrafficSelectors,
237
- builder: ->(ts) { TrafficSelectors.new(counter: ts[:num_ts]) }
237
+ builder: ->(h, t) { t.new(counter: h[:num_ts]) }
238
238
  alias :selectors :traffic_selectors
239
239
 
240
240
  # Populate object from a string
@@ -11,6 +11,7 @@ module PacketGen
11
11
  # * a first byte ({#u8} of {Types::Int8} type) composed of:
12
12
  # * a 4-bit {#version} field,
13
13
  # * a 4-bit IP header length ({#ihl}) field,
14
+ # * a Type of Service field ({#tos}, {Types::Int8} type),
14
15
  # * a total length ({#length}, {Types::Int16} type),
15
16
  # * a ID ({#id}, +Int16+ type),
16
17
  # * a {#frag} worg (+Int16+) composed of:
@@ -20,7 +21,8 @@ module PacketGen
20
21
  # * a {#protocol} field (+Int8+),
21
22
  # * a {#checksum} field (+Int16+),
22
23
  # * a source IP address ({#src}, {Addr} type),
23
- # * a destination IP ddress ({#dst}, +Addr+ type),
24
+ # * a destination IP address ({#dst}, +Addr+ type),
25
+ # * an optional {#options} field ({Types::String} type),
24
26
  # * and a {#body} ({Types::String} type).
25
27
  #
26
28
  # == Create a IP header
@@ -62,7 +64,7 @@ module PacketGen
62
64
 
63
65
  # IP address, as a group of 4 bytes
64
66
  # @author Sylvain Daubert
65
- class Addr < Base
67
+ class Addr < Types::Fields
66
68
  # @!attribute a1
67
69
  # @return [Integer] IP address first byte
68
70
  define_field :a1, Types::Int8
@@ -141,6 +143,11 @@ module PacketGen
141
143
  # @!attribute dst
142
144
  # @return [Addr] destination IP address
143
145
  define_field :dst, Addr, default: '127.0.0.1'
146
+ # @!attribute options
147
+ # @since 2.2.0
148
+ # @return [Types::String]
149
+ define_field :options, Types::String, optional: ->(h) { h.ihl > 5 },
150
+ builder: ->(h,t) { t.new(length_from: ->() { (h.ihl - 5) * 4 }) }
144
151
  # @!attribute body
145
152
  # @return [Types::String,Header::Base]
146
153
  define_field :body, Types::String
@@ -161,6 +168,29 @@ module PacketGen
161
168
  # @return [Integer] 13-bit fragment offset
162
169
  define_bit_fields_on :frag, :flag_rsv, :flag_df, :flag_mf, :fragment_offset, 13
163
170
 
171
+ # Populate object from a binary string
172
+ # @param [String] str
173
+ # @return [Fields] self
174
+ #def read(str)
175
+ # return self if str.nil?
176
+ # force_binary str
177
+ # self[:u8].read str[0, 1]
178
+ # self[:tos].read str[1, 1]
179
+ # self[:length].read str[2, 2]
180
+ # self[:id].read str[4, 2]
181
+ # self[:frag].read str[6, 2]
182
+ # self[:ttl].read str[8, 1]
183
+ # self[:protocol].read str[9, 1]
184
+ # self[:checksum].read str[10, 2]
185
+ # self[:src].read str[12, 4]
186
+ # self[:dst].read str[16, 4]
187
+ # if self.ihl > 5
188
+ # opt_size = (self.ihl - 5) * 4
189
+ # self[:options].read str[20, opt_size]
190
+ # end
191
+ # self
192
+ #end
193
+
164
194
  # Compute checksum and set +checksum+ field
165
195
  # @return [Integer]
166
196
  def calc_checksum