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.
- checksums.yaml +4 -4
- data/lib/packetgen/config.rb +1 -0
- data/lib/packetgen/header.rb +3 -0
- data/lib/packetgen/header/bootp.rb +151 -0
- data/lib/packetgen/header/dhcp.rb +65 -0
- data/lib/packetgen/header/dhcp/option.rb +154 -0
- data/lib/packetgen/header/dhcp/options.rb +42 -0
- data/lib/packetgen/header/dns.rb +4 -4
- data/lib/packetgen/header/dns/name.rb +2 -2
- data/lib/packetgen/header/dns/rr.rb +1 -1
- data/lib/packetgen/header/eap/md5.rb +1 -1
- data/lib/packetgen/header/esp.rb +1 -1
- data/lib/packetgen/header/eth.rb +1 -1
- data/lib/packetgen/header/http.rb +8 -0
- data/lib/packetgen/header/http/headers.rb +76 -0
- data/lib/packetgen/header/http/request.rb +88 -0
- data/lib/packetgen/header/http/response.rb +116 -0
- data/lib/packetgen/header/ike/notify.rb +1 -1
- data/lib/packetgen/header/ike/sa.rb +2 -2
- data/lib/packetgen/header/ike/ts.rb +1 -1
- data/lib/packetgen/header/ip.rb +32 -2
- data/lib/packetgen/header/ipv6.rb +1 -1
- data/lib/packetgen/packet.rb +4 -2
- data/lib/packetgen/types.rb +1 -0
- data/lib/packetgen/types/cstring.rb +67 -0
- data/lib/packetgen/types/fields.rb +7 -5
- data/lib/packetgen/types/int.rb +1 -0
- data/lib/packetgen/types/int_string.rb +10 -3
- data/lib/packetgen/types/string.rb +12 -4
- data/lib/packetgen/types/tlv.rb +44 -12
- data/lib/packetgen/utils.rb +51 -1
- data/lib/packetgen/utils/arp_spoofer.rb +2 -2
- data/lib/packetgen/version.rb +1 -1
- metadata +11 -2
@@ -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) {
|
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) {
|
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
|
data/lib/packetgen/header/esp.rb
CHANGED
@@ -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
|
|
data/lib/packetgen/header/eth.rb
CHANGED
@@ -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) {
|
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) {
|
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) {
|
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: ->(
|
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
|
data/lib/packetgen/header/ip.rb
CHANGED
@@ -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
|
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 <
|
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
|