Class: Rex::Proto::Http::Packet

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/proto/http/packet.rb

Overview

This class represents an HTTP packet.

Direct Known Subclasses

Request, Response

Defined Under Namespace

Modules: ParseCode, ParseState Classes: Header, UnitTest

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePacket

Initializes an instance of an HTTP packet.



39
40
41
42
43
44
# File 'lib/rex/proto/http/packet.rb', line 39

def initialize()
	self.headers = Header.new
	self.auto_cl = true

	reset
end

Instance Attribute Details

#auto_clObject

Returns the value of attribute auto_cl.



232
233
234
# File 'lib/rex/proto/http/packet.rb', line 232

def auto_cl
  @auto_cl
end

#bodyObject

Returns the value of attribute body.



231
232
233
# File 'lib/rex/proto/http/packet.rb', line 231

def body
  @body
end

#bufqObject

Returns the value of attribute bufq.



230
231
232
# File 'lib/rex/proto/http/packet.rb', line 230

def bufq
  @bufq
end

#chunk_max_sizeObject

Returns the value of attribute chunk_max_size.



239
240
241
# File 'lib/rex/proto/http/packet.rb', line 239

def chunk_max_size
  @chunk_max_size
end

#chunk_min_sizeObject

Returns the value of attribute chunk_min_size.



238
239
240
# File 'lib/rex/proto/http/packet.rb', line 238

def chunk_min_size
  @chunk_min_size
end

#compressObject

Returns the value of attribute compress.



235
236
237
# File 'lib/rex/proto/http/packet.rb', line 235

def compress
  @compress
end

#errorObject

Returns the value of attribute error.



228
229
230
# File 'lib/rex/proto/http/packet.rb', line 228

def error
  @error
end

#headersObject

Returns the value of attribute headers.



227
228
229
# File 'lib/rex/proto/http/packet.rb', line 227

def headers
  @headers
end

#incompleteObject

Returns the value of attribute incomplete.



236
237
238
# File 'lib/rex/proto/http/packet.rb', line 236

def incomplete
  @incomplete
end

#max_dataObject

Returns the value of attribute max_data.



233
234
235
# File 'lib/rex/proto/http/packet.rb', line 233

def max_data
  @max_data
end

#stateObject

Returns the value of attribute state.



229
230
231
# File 'lib/rex/proto/http/packet.rb', line 229

def state
  @state
end

#transfer_chunkedObject

Returns the value of attribute transfer_chunked.



234
235
236
# File 'lib/rex/proto/http/packet.rb', line 234

def transfer_chunked
  @transfer_chunked
end

Instance Method Details

#[](key) ⇒ Object

Return the associated header value, if any.



49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rex/proto/http/packet.rb', line 49

def [](key)
	if (self.headers.include?(key))
		return self.headers[key]
	end

	self.headers.each_pair do |k,v|
		if (k.downcase == key.downcase)
			return v
		end
	end

	return nil
end

#[]=(key, value) ⇒ Object

Set the associated header value.



66
67
68
# File 'lib/rex/proto/http/packet.rb', line 66

def []=(key, value)
	self.headers[key] = value
end

#chunk(str, min_size = 1, max_size = 1000) ⇒ Object

Build a ‘Transfer-Encoding: chunked’ payload with random chunk sizes



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/rex/proto/http/packet.rb', line 149

def chunk(str, min_size = 1, max_size = 1000)
	chunked = ''

	# min chunk size is 1 byte
	if (min_size < 1); min_size = 1; end

	# don't be dumb
	if (max_size < min_size); max_size = min_size; end

	while (str.size > 0)
		chunk = str.slice!(0, rand(max_size - min_size) + min_size)
		chunked += sprintf("%x", chunk.size) + "\r\n" + chunk + "\r\n"
	end
	chunked += "0\r\n\r\n"
end

#cmd_stringObject

Returns the command string, such as:

HTTP/1.0 200 OK for a response

or

GET /foo HTTP/1.0 for a request



223
224
225
# File 'lib/rex/proto/http/packet.rb', line 223

def cmd_string
	self.headers.cmd_string
end

#completed?Boolean

Returns whether or not parsing has completed.

Returns:

  • (Boolean)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/rex/proto/http/packet.rb', line 131

def completed?

	return true if self.state == ParseState::Completed

	# If the parser state is processing the body and there are an
	# undetermined number of bytes left to read, we just need to say that
	# things are completed as it's hard to tell whether or not they really
	# are.
	if (self.state == ParseState::ProcessingBody and self.body_bytes_left < 0)
		return true
	end

	false
end

#from_s(str) ⇒ Object

Converts the packet from a string.



209
210
211
212
# File 'lib/rex/proto/http/packet.rb', line 209

def from_s(str)
	reset
	parse(str)
end

#parse(buf) ⇒ Object

Parses the supplied buffer. Returns one of the two parser processing codes (Completed, Partial, or Error).



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/rex/proto/http/packet.rb', line 74

def parse(buf)

	# Append the incoming buffer to the buffer queue.
	self.bufq += buf.to_s

	begin

		# Process the header
		if(self.state == ParseState::ProcessingHeader)
			parse_header
		end

		# Continue on to the body if the header was processed
		if(self.state == ParseState::ProcessingBody)
			# Chunked encoding sets the parsing state on its own
			if (self.body_bytes_left == 0 and not self.transfer_chunked)
				self.state = ParseState::Completed
			else
				parse_body
			end
		end
	rescue
		# XXX: BUG: This rescue might be a problem because it will swallow TimeoutError
		self.error = $!
		return ParseCode::Error
	end

	# Return completed or partial to the parsing status to the caller
	(self.state == ParseState::Completed) ? ParseCode::Completed : ParseCode::Partial
end

#resetObject

Reset the parsing state and buffers.



108
109
110
111
112
113
114
115
# File 'lib/rex/proto/http/packet.rb', line 108

def reset
	self.state = ParseState::ProcessingHeader
	self.transfer_chunked = false
	self.inside_chunk     = false
	self.headers.reset
	self.bufq  = ''
	self.body  = ''
end

#reset_except_queueObject

Reset the parsing state but leave the buffers.



120
121
122
123
124
125
126
# File 'lib/rex/proto/http/packet.rb', line 120

def reset_except_queue
	self.state = ParseState::ProcessingHeader
	self.transfer_chunked = false
	self.inside_chunk     = false
	self.headers.reset
	self.body  = ''
end

#to_sObject

Converts the packet to a string.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rex/proto/http/packet.rb', line 168

def to_s
	content = self.body.dup

	# Update the content length field in the header with the body length.
	if (content)
		if !self.compress.nil?
			case self.compress
				when 'gzip'
					self.headers['Content-Encoding'] = 'gzip'
					content = Rex::Text.gzip(content)
				when 'deflate'
					self.headers['Content-Encoding'] = 'deflate'
					content = Rex::Text.zlib_deflate(content)
				when 'none'
				# this one is fine...
				# when 'compress'
				else
					raise RuntimeError, 'Invalid Content-Encoding'
			end
		end

		if (self.auto_cl == true && self.transfer_chunked == true)
			raise RuntimeError, "'Content-Length' and 'Transfer-Encoding: chunked' are incompatible"
		elsif self.auto_cl == true
			self.headers['Content-Length'] = content.length
		elsif self.transfer_chunked == true
			if self.proto != '1.1'
				raise RuntimeError, 'Chunked encoding is only available via 1.1'
			end
			self.headers['Transfer-Encoding'] = 'chunked'
			content = self.chunk(content, self.chunk_min_size, self.chunk_max_size)
		end
	end

	str  = self.headers.to_s(cmd_string)
	str += content || ''
end