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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePacket

Initializes an instance of an HTTP packet.


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

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

  reset
end

Instance Attribute Details

#auto_clObject

Returns the value of attribute auto_cl


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

def auto_cl
  @auto_cl
end

#bodyObject

Returns the value of attribute body


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

def body
  @body
end

#bufqObject

Returns the value of attribute bufq


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

def bufq
  @bufq
end

#chunk_max_sizeObject

Returns the value of attribute chunk_max_size


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

def chunk_max_size
  @chunk_max_size
end

#chunk_min_sizeObject

Returns the value of attribute chunk_min_size


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

def chunk_min_size
  @chunk_min_size
end

#compressObject

Returns the value of attribute compress


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

def compress
  @compress
end

#errorObject

Returns the value of attribute error


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

def error
  @error
end

#headersObject

Returns the value of attribute headers


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

def headers
  @headers
end

#incompleteObject

Returns the value of attribute incomplete


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

def incomplete
  @incomplete
end

#max_dataObject

Returns the value of attribute max_data


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

def max_data
  @max_data
end

#stateObject

Returns the value of attribute state


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

def state
  @state
end

#transfer_chunkedObject

Returns the value of attribute transfer_chunked


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

def transfer_chunked
  @transfer_chunked
end

Instance Method Details

#[](key) ⇒ Object

Return the associated header value, if any.


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

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.


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

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


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

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


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

def cmd_string
  self.headers.cmd_string
end

#completed?Boolean

Returns whether or not parsing has completed.


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

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.


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

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).


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
104
# File 'lib/rex/proto/http/packet.rb', line 75

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.


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

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.


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

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.


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
205
# File 'lib/rex/proto/http/packet.rb', line 169

def to_s
  content = self.body.to_s.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