ssrf_proxy 0.0.2
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.
- data/bin/ssrf-proxy +227 -0
- data/bin/ssrf-scan +452 -0
- data/lib/ssrf_proxy.rb +18 -0
- data/lib/ssrf_proxy/http.rb +1306 -0
- data/lib/ssrf_proxy/server.rb +147 -0
- data/lib/ssrf_proxy/version.rb +10 -0
- metadata +201 -0
data/lib/ssrf_proxy.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2015 Brendan Coles <bcoles@gmail.com>
|
|
4
|
+
# SSRF Proxy - https://github.com/bcoles/ssrf_proxy
|
|
5
|
+
# See the file 'LICENSE' for copying permission
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
require "ssrf_proxy/version"
|
|
9
|
+
require "ssrf_proxy/http"
|
|
10
|
+
require "ssrf_proxy/server"
|
|
11
|
+
|
|
12
|
+
module SSRFProxy
|
|
13
|
+
|
|
14
|
+
require 'logger'
|
|
15
|
+
require 'colorize'
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
@@ -0,0 +1,1306 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2015 Brendan Coles <bcoles@gmail.com>
|
|
4
|
+
# SSRF Proxy - https://github.com/bcoles/ssrf_proxy
|
|
5
|
+
# See the file 'LICENSE' for copying permission
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
require "ssrf_proxy"
|
|
9
|
+
|
|
10
|
+
module SSRFProxy
|
|
11
|
+
#
|
|
12
|
+
# @note SSRFProxy::HTTP
|
|
13
|
+
#
|
|
14
|
+
class HTTP
|
|
15
|
+
attr_accessor = :logger
|
|
16
|
+
|
|
17
|
+
# @note output status messages
|
|
18
|
+
def print_status(msg='')
|
|
19
|
+
puts '[*] '.blue + msg
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @note output progress messages
|
|
23
|
+
def print_good(msg='')
|
|
24
|
+
puts '[+] '.green + msg
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# url parsing
|
|
28
|
+
require 'net/http'
|
|
29
|
+
require 'uri'
|
|
30
|
+
require 'cgi'
|
|
31
|
+
|
|
32
|
+
# client http request parsing
|
|
33
|
+
require 'webrick'
|
|
34
|
+
require 'stringio'
|
|
35
|
+
|
|
36
|
+
# rules
|
|
37
|
+
require 'digest'
|
|
38
|
+
require 'base64'
|
|
39
|
+
|
|
40
|
+
# ip encoding
|
|
41
|
+
require 'ipaddress'
|
|
42
|
+
|
|
43
|
+
# @note logger
|
|
44
|
+
def logger
|
|
45
|
+
@logger || ::Logger.new(STDOUT).tap do |log|
|
|
46
|
+
log.progname = 'ssrf-proxy'
|
|
47
|
+
log.level = ::Logger::WARN
|
|
48
|
+
log.datetime_format = '%Y-%m-%d %H:%M:%S '
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# @note SSRFProxy:HTTP errors
|
|
54
|
+
#
|
|
55
|
+
module Error
|
|
56
|
+
# custom errors
|
|
57
|
+
class Error < StandardError; end
|
|
58
|
+
exceptions = %w(
|
|
59
|
+
NoUrlPlaceholder
|
|
60
|
+
InvalidSsrfRequest
|
|
61
|
+
InvalidRequestMethod
|
|
62
|
+
InvalidUpstreamProxy
|
|
63
|
+
InvalidIpEncoding
|
|
64
|
+
InvalidHttpRequest
|
|
65
|
+
InvalidUriRequest )
|
|
66
|
+
exceptions.each { |e| const_set(e, Class.new(Error)) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# @note SSRFProxy::HTTP
|
|
71
|
+
#
|
|
72
|
+
# @options
|
|
73
|
+
# - url - String - SSRF URL with 'xxURLxx' placeholder
|
|
74
|
+
# - opts - Hash - SSRF and HTTP connection options:
|
|
75
|
+
# - 'proxy' => String
|
|
76
|
+
# - 'method' => String
|
|
77
|
+
# - 'post_data' => String
|
|
78
|
+
# - 'rules' => String
|
|
79
|
+
# - 'ip_encoding' => String
|
|
80
|
+
# - 'match' => Regex
|
|
81
|
+
# - 'strip' => String
|
|
82
|
+
# - 'guess_status' => Boolean
|
|
83
|
+
# - 'guess_mime' => Boolean
|
|
84
|
+
# - 'forward_cookies'=> Boolean
|
|
85
|
+
# - 'body_to_uri' => Boolean
|
|
86
|
+
# - 'auth_to_uri' => Boolean
|
|
87
|
+
# - 'cookie' => String
|
|
88
|
+
# - 'timeout' => Integer
|
|
89
|
+
# - 'user_agent' => String
|
|
90
|
+
#
|
|
91
|
+
def initialize(url='', opts={})
|
|
92
|
+
@logger = ::Logger.new(STDOUT).tap do |log|
|
|
93
|
+
log.progname = 'ssrf-proxy'
|
|
94
|
+
log.level = ::Logger::WARN
|
|
95
|
+
log.datetime_format = '%Y-%m-%d %H:%M:%S '
|
|
96
|
+
end
|
|
97
|
+
begin
|
|
98
|
+
@ssrf_url = URI::parse(url.to_s)
|
|
99
|
+
rescue URI::InvalidURIError
|
|
100
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequest.new,
|
|
101
|
+
"Invalid SSRF request specified."
|
|
102
|
+
end
|
|
103
|
+
if @ssrf_url.scheme.nil? || @ssrf_url.host.nil? || @ssrf_url.port.nil?
|
|
104
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequest.new,
|
|
105
|
+
"Invalid SSRF request specified."
|
|
106
|
+
end
|
|
107
|
+
if @ssrf_url.scheme !~ /\Ahttps?\z/
|
|
108
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequest.new,
|
|
109
|
+
"Invalid SSRF request specified. Scheme must be http(s)."
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# SSRF options
|
|
113
|
+
@upstream_proxy = nil
|
|
114
|
+
@method = 'GET'
|
|
115
|
+
@post_data = ''
|
|
116
|
+
@ip_encoding = nil
|
|
117
|
+
@rules = []
|
|
118
|
+
@forward_cookies = false
|
|
119
|
+
@body_to_uri = false
|
|
120
|
+
@auth_to_uri = false
|
|
121
|
+
opts.each do |option, value|
|
|
122
|
+
next if value.eql?('')
|
|
123
|
+
case option
|
|
124
|
+
when 'proxy'
|
|
125
|
+
begin
|
|
126
|
+
@upstream_proxy = URI::parse(value)
|
|
127
|
+
rescue URI::InvalidURIError => e
|
|
128
|
+
raise SSRFProxy::HTTP::Error::InvalidUpstreamProxy.new,
|
|
129
|
+
"Invalid upstream HTTP proxy specified."
|
|
130
|
+
end
|
|
131
|
+
if @upstream_proxy.scheme !~ /\Ahttps?\z/
|
|
132
|
+
raise SSRFProxy::HTTP::Error::InvalidUpstreamProxy.new,
|
|
133
|
+
"Invalid upstream HTTP proxy specified."
|
|
134
|
+
end
|
|
135
|
+
when 'method'
|
|
136
|
+
if @method !~ /\A(get|post|head)+?\z/i
|
|
137
|
+
raise SSRFProxy::HTTP::Error::InvalidRequestMethod.new,
|
|
138
|
+
"Invalid SSRF request method specified. Method must be GET/POST/HEAD."
|
|
139
|
+
end
|
|
140
|
+
@method = 'GET' if value =~ /\Aget\z/i
|
|
141
|
+
@method = 'POST' if value =~ /\Apost\z/i
|
|
142
|
+
@method = 'HEAD' if value =~ /\Ahead\z/i
|
|
143
|
+
when 'post_data'
|
|
144
|
+
@post_data = value.to_s
|
|
145
|
+
when 'ip_encoding'
|
|
146
|
+
if value.to_s !~ /\A[a-z0-9]+\z/i
|
|
147
|
+
raise SSRFProxy::HTTP::Error::InvalidIpEncoding.new,
|
|
148
|
+
"Invalid IP encoding method specified."
|
|
149
|
+
end
|
|
150
|
+
@ip_encoding = value.to_s
|
|
151
|
+
when 'rules'
|
|
152
|
+
@rules = value.to_s.split(/,/)
|
|
153
|
+
when 'forward_cookies'
|
|
154
|
+
@forward_cookies = true if value
|
|
155
|
+
when 'body_to_uri'
|
|
156
|
+
@body_to_uri = true if value
|
|
157
|
+
when 'auth_to_uri'
|
|
158
|
+
@auth_to_uri = true if value
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
if @ssrf_url.request_uri !~ /xxURLxx/ && @post_data.to_s !~ /xxURLxx/
|
|
162
|
+
raise SSRFProxy::HTTP::Error::NoUrlPlaceholder.new,
|
|
163
|
+
"You must specify a URL placeholder with 'xxURLxx' in the SSRF request"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# HTTP connection options
|
|
167
|
+
@cookie = nil
|
|
168
|
+
@timeout = 10
|
|
169
|
+
@user_agent = 'Mozilla/5.0'
|
|
170
|
+
opts.each do |option, value|
|
|
171
|
+
next if value.eql?('')
|
|
172
|
+
case option
|
|
173
|
+
when 'cookie'
|
|
174
|
+
@cookie = value.to_s
|
|
175
|
+
when 'timeout'
|
|
176
|
+
@timeout = value.to_i
|
|
177
|
+
when 'user_agent'
|
|
178
|
+
@user_agent = value.to_s
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# HTTP response modification options
|
|
183
|
+
@match_regex = "\\A(.+)\\z"
|
|
184
|
+
@strip = []
|
|
185
|
+
@guess_status = false
|
|
186
|
+
@guess_mime = false
|
|
187
|
+
opts.each do |option, value|
|
|
188
|
+
next if value.eql?('')
|
|
189
|
+
case option
|
|
190
|
+
when 'match'
|
|
191
|
+
@match_regex = value.to_s
|
|
192
|
+
when 'strip'
|
|
193
|
+
@strip = value.to_s.split(/,/)
|
|
194
|
+
when 'guess_status'
|
|
195
|
+
@guess_status = true if value
|
|
196
|
+
when 'guess_mime'
|
|
197
|
+
@guess_mime = true if value
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
#
|
|
204
|
+
# @note URL accessor
|
|
205
|
+
#
|
|
206
|
+
# @returns - String - SSRF URL
|
|
207
|
+
#
|
|
208
|
+
def url
|
|
209
|
+
@ssrf_url
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
#
|
|
213
|
+
# @note Host accessor
|
|
214
|
+
#
|
|
215
|
+
# @returns - String - SSRF host
|
|
216
|
+
#
|
|
217
|
+
def host
|
|
218
|
+
@ssrf_url.host
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
#
|
|
222
|
+
# @note Port accessor
|
|
223
|
+
#
|
|
224
|
+
# @returns - String - SSRF port
|
|
225
|
+
#
|
|
226
|
+
def port
|
|
227
|
+
@ssrf_url.port
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
#
|
|
231
|
+
# @note Cookie accessor
|
|
232
|
+
#
|
|
233
|
+
# @returns - String - SSRF request cookie
|
|
234
|
+
#
|
|
235
|
+
def cookie
|
|
236
|
+
@cookie
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
#
|
|
240
|
+
# @note Upstream proxy accessor
|
|
241
|
+
#
|
|
242
|
+
# @returns - String - Upstream proxy
|
|
243
|
+
#
|
|
244
|
+
def proxy
|
|
245
|
+
@upstream_proxy
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
#
|
|
249
|
+
# @note Encode IP address of a given URL
|
|
250
|
+
#
|
|
251
|
+
# @options
|
|
252
|
+
# - url - String - target url
|
|
253
|
+
# - mode - String - encoding (int, ipv6, oct, hex)
|
|
254
|
+
#
|
|
255
|
+
# @returns - String - encoded ip address
|
|
256
|
+
#
|
|
257
|
+
def encode_ip(url, mode)
|
|
258
|
+
return if url.nil?
|
|
259
|
+
new_host = nil
|
|
260
|
+
host = URI::parse(url.to_s.split('?').first).host.to_s
|
|
261
|
+
begin
|
|
262
|
+
ip = IPAddress.parse(host)
|
|
263
|
+
rescue
|
|
264
|
+
logger.warn("Could not parse requested host as IP address: #{host}")
|
|
265
|
+
return
|
|
266
|
+
end
|
|
267
|
+
case mode
|
|
268
|
+
when 'int'
|
|
269
|
+
new_host = url.to_s.gsub!(host, "#{ip.to_u32}")
|
|
270
|
+
when 'ipv6'
|
|
271
|
+
new_host = url.to_s.gsub!(host, "#{ip.to_ipv6}")
|
|
272
|
+
when 'oct'
|
|
273
|
+
new_host = url.to_s.gsub!(host, "0#{ip.to_u32.to_s(8)}")
|
|
274
|
+
when 'hex'
|
|
275
|
+
new_host = url.to_s.gsub!(host, "0x#{ip.to_u32.to_s(16)}")
|
|
276
|
+
else
|
|
277
|
+
logger.warn("Invalid IP encoding: #{mode}")
|
|
278
|
+
end
|
|
279
|
+
new_host
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
#
|
|
283
|
+
# @note Run a specified URL through SSRF rules
|
|
284
|
+
#
|
|
285
|
+
# @options
|
|
286
|
+
# - url - String - request URL
|
|
287
|
+
# - rules - String - comma separated list of rules
|
|
288
|
+
#
|
|
289
|
+
# @returns - String - modified request URL
|
|
290
|
+
#
|
|
291
|
+
def run_rules(url, rules)
|
|
292
|
+
str = url.to_s
|
|
293
|
+
return str if rules.nil?
|
|
294
|
+
rules.each do |rule|
|
|
295
|
+
case rule
|
|
296
|
+
when 'noproto'
|
|
297
|
+
str = str.gsub(/^https?:\/\//, '')
|
|
298
|
+
when 'nossl', 'http'
|
|
299
|
+
str = str.gsub(/^https:\/\//, 'http://')
|
|
300
|
+
when 'ssl', 'https'
|
|
301
|
+
str = str.gsub(/^http:\/\//, 'https://')
|
|
302
|
+
when 'base64'
|
|
303
|
+
str = Base64.encode64(str).chomp
|
|
304
|
+
when 'md4'
|
|
305
|
+
str = OpenSSL::Digest::MD4.hexdigest(str)
|
|
306
|
+
when 'md5'
|
|
307
|
+
md5 = Digest::MD5.new
|
|
308
|
+
md5.update str
|
|
309
|
+
str = md5.hexdigest
|
|
310
|
+
when 'sha1'
|
|
311
|
+
str = Digest::SHA1.hexdigest(str)
|
|
312
|
+
when 'reverse'
|
|
313
|
+
str = str.reverse
|
|
314
|
+
when 'urlencode'
|
|
315
|
+
str = CGI.escape(str)
|
|
316
|
+
when 'urldecode'
|
|
317
|
+
str = CGI.unescape(str)
|
|
318
|
+
else
|
|
319
|
+
logger.warn("Unknown rule: #{rule}")
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
str
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
#
|
|
326
|
+
# @note Format a HTTP request as a URL and request via SSRF
|
|
327
|
+
#
|
|
328
|
+
# @options
|
|
329
|
+
# - request - String - raw HTTP request
|
|
330
|
+
#
|
|
331
|
+
# @returns String - raw HTTP response headers and body
|
|
332
|
+
#
|
|
333
|
+
def send_request(request)
|
|
334
|
+
if request.to_s !~ /\A[A-Z]{1,20} /
|
|
335
|
+
logger.warn("Received malformed client HTTP request.")
|
|
336
|
+
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
337
|
+
elsif request.to_s =~ /\ACONNECT ([^\s]+) .*$/
|
|
338
|
+
logger.warn("CONNECT tunneling is not supported: #{$1}")
|
|
339
|
+
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
340
|
+
elsif request.to_s =~ /\A(DEBUG|TRACE|TRACK|OPTIONS) /
|
|
341
|
+
logger.warn("Client request method is not supported: #{$1}")
|
|
342
|
+
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
343
|
+
end
|
|
344
|
+
if request.to_s !~ /\A[A-Z]{1,20} https?:\/\//
|
|
345
|
+
if request.to_s =~ /^Host: ([^\s]+)\r?\n/
|
|
346
|
+
logger.info("Using host header: #{$1}")
|
|
347
|
+
else
|
|
348
|
+
logger.warn("No host specified")
|
|
349
|
+
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
opts = {}
|
|
353
|
+
begin
|
|
354
|
+
# change POST to GET if the request body is empty
|
|
355
|
+
if request.to_s =~ /\APOST /
|
|
356
|
+
request = request.gsub!(/\APOST /, 'GET ') if request.split(/\r?\n\r?\n/)[1].nil?
|
|
357
|
+
end
|
|
358
|
+
# parse request
|
|
359
|
+
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
|
|
360
|
+
req.parse(StringIO.new(request))
|
|
361
|
+
if req.to_s =~ /^Upgrade: WebSocket/
|
|
362
|
+
logger.warn("WebSocket tunneling is not supported: #{req.host}:#{req.port}")
|
|
363
|
+
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
364
|
+
end
|
|
365
|
+
uri = req.request_uri
|
|
366
|
+
raise SSRFProxy::HTTP::Error::InvalidHttpRequest if uri.nil?
|
|
367
|
+
rescue => e
|
|
368
|
+
logger.info("Received malformed client HTTP request.")
|
|
369
|
+
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# parse request body and move to uri
|
|
373
|
+
if @body_to_uri && !req.body.nil?
|
|
374
|
+
logger.debug "Parsing request body: #{req.body}"
|
|
375
|
+
begin
|
|
376
|
+
new_query = URI.decode_www_form(req.body)
|
|
377
|
+
if req.query_string.nil?
|
|
378
|
+
uri = "#{uri}?#{URI.encode_www_form(new_query)}"
|
|
379
|
+
else
|
|
380
|
+
URI.decode_www_form(req.query_string).each { |p| new_query << p }
|
|
381
|
+
uri = "#{uri}&#{URI.encode_www_form(new_query)}"
|
|
382
|
+
end
|
|
383
|
+
rescue
|
|
384
|
+
logger.warn "Could not parse request POST data"
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# move basic authentication credentials to uri
|
|
389
|
+
if @auth_to_uri && !req.header.nil?
|
|
390
|
+
req.header['authorization'].each do |header|
|
|
391
|
+
next unless header.split(' ').first =~ /^basic$/i
|
|
392
|
+
begin
|
|
393
|
+
creds = header.split(' ')[1]
|
|
394
|
+
user = Base64.decode64(creds).chomp
|
|
395
|
+
logger.info "Using basic authentication credentials: #{user}"
|
|
396
|
+
uri = uri.to_s.gsub!(/:(\/\/)/, "://#{user}@")
|
|
397
|
+
rescue
|
|
398
|
+
logger.warn "Could not parse request authorization header: #{header}"
|
|
399
|
+
end
|
|
400
|
+
break
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# forward client cookies
|
|
405
|
+
new_cookie = []
|
|
406
|
+
new_cookie << @cookie unless @cookie.nil?
|
|
407
|
+
if @forward_cookies
|
|
408
|
+
req.cookies.each do |c|
|
|
409
|
+
new_cookie << "#{c}"
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
unless new_cookie.empty?
|
|
413
|
+
opts['cookie'] = new_cookie.uniq.join('; ').to_s
|
|
414
|
+
logger.info "Using cookie: #{opts['cookie']}"
|
|
415
|
+
end
|
|
416
|
+
send_uri(uri, opts)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
#
|
|
420
|
+
# @note Fetch a URI via SSRF
|
|
421
|
+
#
|
|
422
|
+
# @options
|
|
423
|
+
# - uri - URI - URI to fetch
|
|
424
|
+
# - opts - Hash - request options (keys: cookie)
|
|
425
|
+
#
|
|
426
|
+
# @returns String - raw HTTP response headers and body
|
|
427
|
+
#
|
|
428
|
+
def send_uri(uri, opts={})
|
|
429
|
+
raise SSRFProxy::HTTP::Error::InvalidUriRequest if uri.nil?
|
|
430
|
+
|
|
431
|
+
# convert ip
|
|
432
|
+
if @ip_encoding
|
|
433
|
+
encoded_url = encode_ip(uri, @ip_encoding)
|
|
434
|
+
uri = encoded_url unless encoded_url.nil?
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# send request
|
|
438
|
+
status_msg = "Request -> #{@method}"
|
|
439
|
+
status_msg << " -> PROXY[#{@upstream_proxy.host}:#{@upstream_proxy.port}]" unless @upstream_proxy.nil?
|
|
440
|
+
status_msg << " -> SSRF[#{@ssrf_url.host}:#{@ssrf_url.port}] -> URI[#{uri}]"
|
|
441
|
+
print_status(status_msg)
|
|
442
|
+
response = send_http_request(uri, opts)
|
|
443
|
+
response = parse_http_response(response) unless response.class == Hash
|
|
444
|
+
body = response["body"]||''
|
|
445
|
+
headers = response["headers"]
|
|
446
|
+
|
|
447
|
+
# handle HTTP response
|
|
448
|
+
if response["status"] == 'fail'
|
|
449
|
+
status_msg = "Response <- #{response["code"]}"
|
|
450
|
+
status_msg << " <- PROXY[#{@upstream_proxy.host}:#{@upstream_proxy.port}]" unless @upstream_proxy.nil?
|
|
451
|
+
status_msg << " <- SSRF[#{@ssrf_url.host}:#{@ssrf_url.port}] <- URI[#{uri}]"
|
|
452
|
+
print_status(status_msg)
|
|
453
|
+
return "#{response['headers']}#{response['body']}"
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
# guess mime type and add content-type header
|
|
457
|
+
if @guess_mime
|
|
458
|
+
content_type = guess_mime(File.extname(uri.to_s.split('?').first))
|
|
459
|
+
unless content_type.nil?
|
|
460
|
+
logger.info "Using content-type: #{content_type}"
|
|
461
|
+
if headers =~ /^content\-type:.*$/i
|
|
462
|
+
headers.gsub!(/^content\-type:.*$/i, "Content-Type: #{content_type}")
|
|
463
|
+
else
|
|
464
|
+
headers.gsub!(/\n\n\z/, "\nContent-Type: #{content_type}\n\n")
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# match response content
|
|
470
|
+
unless @match_regex.nil?
|
|
471
|
+
matches = body.scan(/#{@match_regex}/m)
|
|
472
|
+
if matches.length
|
|
473
|
+
body = matches.flatten.first.to_s
|
|
474
|
+
logger.info "Response matches pattern '#{@match_regex}'"
|
|
475
|
+
else
|
|
476
|
+
logger.warn "Response does not match pattern"
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# set content length
|
|
481
|
+
content_length = body.to_s.length
|
|
482
|
+
if headers =~ /^transfer\-encoding:.*$/i
|
|
483
|
+
headers.gsub!(/^transfer\-encoding:.*$/i, "Content-Length: #{content_length}")
|
|
484
|
+
elsif headers =~ /^content\-length:.*$/i
|
|
485
|
+
headers.gsub!(/^content\-length:.*$/i, "Content-Length: #{content_length}")
|
|
486
|
+
else
|
|
487
|
+
headers.gsub!(/\n\n\z/, "\nContent-Length: #{content_length}\n\n")
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# return HTTP response
|
|
491
|
+
logger.debug("Response:\n#{headers}#{body}")
|
|
492
|
+
status_msg = "Response <- #{response["code"]}"
|
|
493
|
+
status_msg << " <- PROXY[#{@upstream_proxy.host}:#{@upstream_proxy.port}]" unless @upstream_proxy.nil?
|
|
494
|
+
status_msg << " <- SSRF[#{@ssrf_url.host}:#{@ssrf_url.port}] <- URI[#{uri}]"
|
|
495
|
+
status_msg << " -- TITLE[#{$1}]" if body[0..1024] =~ /<title>([^<]*)<\/title>/im
|
|
496
|
+
status_msg << " -- SIZE[#{body.size} bytes]"
|
|
497
|
+
print_good(status_msg)
|
|
498
|
+
return "#{headers}#{body}"
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
#
|
|
502
|
+
# @note Send HTTP request
|
|
503
|
+
#
|
|
504
|
+
# @options
|
|
505
|
+
# - url - String - URI to fetch
|
|
506
|
+
# - opts - Hash - request options (keys: cookie)
|
|
507
|
+
#
|
|
508
|
+
# @returns Hash of HTTP response (status, code, headers, body)
|
|
509
|
+
#
|
|
510
|
+
def send_http_request(url, opts={})
|
|
511
|
+
# use upstream proxy
|
|
512
|
+
if @upstream_proxy.nil?
|
|
513
|
+
http = Net::HTTP.new(@ssrf_url.host, @ssrf_url.port)
|
|
514
|
+
else
|
|
515
|
+
http = Net::HTTP::Proxy(@upstream_proxy.host, @upstream_proxy.port).new(@ssrf_url.host, @ssrf_url.port)
|
|
516
|
+
end
|
|
517
|
+
# run target url through rules
|
|
518
|
+
target = run_rules(url, @rules)
|
|
519
|
+
# replace xxURLxx placeholder in SSRF HTTP GET parameters
|
|
520
|
+
ssrf_url = "#{@ssrf_url.path}?#{@ssrf_url.query}".gsub(/xxURLxx/, "#{target}")
|
|
521
|
+
# replace xxURLxx placeholder in SSRF HTTP POST parameters
|
|
522
|
+
post_data = @post_data.gsub(/xxURLxx/, "#{target}") unless @post_data.nil?
|
|
523
|
+
if @ssrf_url.scheme == 'https'
|
|
524
|
+
http.use_ssl = true
|
|
525
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE #PEER
|
|
526
|
+
end
|
|
527
|
+
# set socket options
|
|
528
|
+
http.open_timeout = @timeout
|
|
529
|
+
http.read_timeout = @timeout
|
|
530
|
+
# set request headers
|
|
531
|
+
headers = {}
|
|
532
|
+
headers['User-Agent'] = @user_agent unless @user_agent.nil?
|
|
533
|
+
headers['Cookie'] = opts['cookie'].to_s unless opts['cookie'].nil?
|
|
534
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded' if @method == 'POST'
|
|
535
|
+
response = {}
|
|
536
|
+
# send http request
|
|
537
|
+
begin
|
|
538
|
+
if @method == 'GET'
|
|
539
|
+
response = http.request Net::HTTP::Get.new(ssrf_url, headers.to_hash)
|
|
540
|
+
elsif @method == 'HEAD'
|
|
541
|
+
response = http.request Net::HTTP::Head.new(ssrf_url, headers.to_hash)
|
|
542
|
+
elsif @method == 'POST'
|
|
543
|
+
request = Net::HTTP::Post.new(ssrf_url, headers.to_hash)
|
|
544
|
+
request.body = post_data
|
|
545
|
+
response = http.request(request)
|
|
546
|
+
else
|
|
547
|
+
logger.info("SSRF request method not implemented - METHOD[#{@method}]")
|
|
548
|
+
response["status"] = 'fail'
|
|
549
|
+
response["code"] = 501
|
|
550
|
+
response["message"] = 'Error'
|
|
551
|
+
response["headers"] = "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
552
|
+
end
|
|
553
|
+
rescue Timeout::Error,Errno::ETIMEDOUT
|
|
554
|
+
logger.warn("Connection timeout - TIMEOUT[#{@timeout}] - URI[#{url}]\n")
|
|
555
|
+
response["status"] = 'fail'
|
|
556
|
+
response["code"] = 504
|
|
557
|
+
response["message"] = 'Timeout'
|
|
558
|
+
response["headers"] = "HTTP\/1.1 504 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
559
|
+
rescue => e
|
|
560
|
+
response["status"] = 'fail'
|
|
561
|
+
response["code"] = 500
|
|
562
|
+
response["message"] = 'Error'
|
|
563
|
+
response["headers"] = "HTTP\/1.1 500 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
564
|
+
logger.error("Unhandled exception: #{e.message}: #{e}")
|
|
565
|
+
end
|
|
566
|
+
return response
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
#
|
|
570
|
+
# @note Parse HTTP response
|
|
571
|
+
#
|
|
572
|
+
# @options
|
|
573
|
+
# - response - Net::HTTPResponse - HTTP response object
|
|
574
|
+
#
|
|
575
|
+
# @returns - Hash - HTTP response object
|
|
576
|
+
#
|
|
577
|
+
def parse_http_response(response)
|
|
578
|
+
return response if response.class == Hash
|
|
579
|
+
result = {}
|
|
580
|
+
begin
|
|
581
|
+
result["status"] = 'complete'
|
|
582
|
+
result["http_version"] = response.http_version
|
|
583
|
+
result["code"] = response.code
|
|
584
|
+
result["message"] = response.message
|
|
585
|
+
if @guess_status
|
|
586
|
+
head = response.body[0..4096]
|
|
587
|
+
# generic page titles containing HTTP status
|
|
588
|
+
if head =~ />401 Unauthorized</
|
|
589
|
+
result["code"] = 401
|
|
590
|
+
result["message"] = 'Unauthorized'
|
|
591
|
+
elsif head =~ />403 Forbidden</
|
|
592
|
+
result["code"] = 403
|
|
593
|
+
result["message"] = 'Forbidden'
|
|
594
|
+
elsif head =~ />404 Not Found</
|
|
595
|
+
result["code"] = 404
|
|
596
|
+
result["message"] = 'Not Found'
|
|
597
|
+
elsif head =~ />500 Internal Server Error</
|
|
598
|
+
result["code"] = 500
|
|
599
|
+
result["message"] = 'Internal Server Error'
|
|
600
|
+
# getaddrinfo() errors
|
|
601
|
+
elsif head =~ /getaddrinfo: /
|
|
602
|
+
if head =~ /getaddrinfo: nodename nor servname provided/
|
|
603
|
+
result["code"] = 502
|
|
604
|
+
result["message"] = 'Bad Gateway'
|
|
605
|
+
elsif head =~ /getaddrinfo: Name or service not known/
|
|
606
|
+
result["code"] = 502
|
|
607
|
+
result["message"] = 'Bad Gateway'
|
|
608
|
+
end
|
|
609
|
+
# getnameinfo() errors
|
|
610
|
+
elsif head =~ /getnameinfo failed: /
|
|
611
|
+
result["code"] = 502
|
|
612
|
+
result["message"] = 'Bad Gateway'
|
|
613
|
+
# PHP 'failed to open stream' errors
|
|
614
|
+
elsif head =~ /failed to open stream: /
|
|
615
|
+
# HTTP request failed! HTTP/[version] [code] [message]
|
|
616
|
+
if head =~ /failed to open stream: HTTP request failed! HTTP\/(0\.9|1\.0|1\.1) ([\d]+) /
|
|
617
|
+
result["code"] = "#{$2}"
|
|
618
|
+
result["message"] = ''
|
|
619
|
+
if head =~ /failed to open stream: HTTP request failed! HTTP\/(0\.9|1\.0|1\.1) [\d]+ ([a-zA-Z ]+)/
|
|
620
|
+
result["message"] = "#{$2}"
|
|
621
|
+
end
|
|
622
|
+
# No route to host
|
|
623
|
+
elsif head =~ /failed to open stream: No route to host in/
|
|
624
|
+
result["code"] = 502
|
|
625
|
+
result["message"] = 'Bad Gateway'
|
|
626
|
+
# Connection refused
|
|
627
|
+
elsif head =~ /failed to open stream: Connection refused in/
|
|
628
|
+
result["code"] = 502
|
|
629
|
+
result["message"] = 'Bad Gateway'
|
|
630
|
+
# Connection timed out
|
|
631
|
+
elsif head =~ /failed to open stream: Connection timed out/
|
|
632
|
+
result["code"] = 504
|
|
633
|
+
result["message"] = 'Timeout'
|
|
634
|
+
end
|
|
635
|
+
# Java 'java.net.ConnectException' errors
|
|
636
|
+
elsif head =~ /java\.net\.ConnectException: /
|
|
637
|
+
# No route to host
|
|
638
|
+
if head =~ /java\.net\.ConnectException: No route to host/
|
|
639
|
+
result["code"] = 502
|
|
640
|
+
result["message"] = 'Bad Gateway'
|
|
641
|
+
# Connection refused
|
|
642
|
+
elsif head =~ /java\.net\.ConnectException: Connection refused/
|
|
643
|
+
result["code"] = 502
|
|
644
|
+
result["message"] = 'Bad Gateway'
|
|
645
|
+
# Connection timed out
|
|
646
|
+
elsif head =~ /java\.net\.ConnectException: Connection timed out/
|
|
647
|
+
result["code"] = 504
|
|
648
|
+
result["message"] = 'Timeout'
|
|
649
|
+
end
|
|
650
|
+
# Java 'java.net.UnknownHostException' errors
|
|
651
|
+
elsif head =~ /java\.net\.UnknownHostException: /
|
|
652
|
+
if head =~ /java\.net\.UnknownHostException: Invalid hostname/
|
|
653
|
+
result["code"] = 502
|
|
654
|
+
result["message"] = 'Bad Gateway'
|
|
655
|
+
end
|
|
656
|
+
# Python errors
|
|
657
|
+
elsif head =~ /\[Errno -?[\d]{1,3}\]/
|
|
658
|
+
if head =~ /\[Errno 113\] No route to host/
|
|
659
|
+
result["code"] = 502
|
|
660
|
+
result["message"] = 'Bad Gateway'
|
|
661
|
+
elsif head =~ /\[Errno -2\] Name or service not known/
|
|
662
|
+
result["code"] = 502
|
|
663
|
+
result["message"] = 'Bad Gateway'
|
|
664
|
+
elsif head =~ /\[Errno 111\] Connection refused/
|
|
665
|
+
result["code"] = 502
|
|
666
|
+
result["message"] = 'Bad Gateway'
|
|
667
|
+
elsif head =~ /\[Errno 110\] Connection timed out/
|
|
668
|
+
result["code"] = 504
|
|
669
|
+
result["message"] = 'Timeout'
|
|
670
|
+
end
|
|
671
|
+
# Ruby errors
|
|
672
|
+
elsif head =~ /Errno::[A-Z]+/
|
|
673
|
+
# Connection refused
|
|
674
|
+
if head =~ /Errno::ECONNREFUSED/
|
|
675
|
+
result["code"] = 502
|
|
676
|
+
result["message"] = 'Bad Gateway'
|
|
677
|
+
# No route to host
|
|
678
|
+
elsif head =~ /Errno::EHOSTUNREACH/
|
|
679
|
+
result["code"] = 502
|
|
680
|
+
result["message"] = 'Bad Gateway'
|
|
681
|
+
# Connection timed out
|
|
682
|
+
elsif head =~ /Errno::ETIMEDOUT/
|
|
683
|
+
result["code"] = 504
|
|
684
|
+
result["message"] = 'Timeout'
|
|
685
|
+
end
|
|
686
|
+
elsif head =~ /(Connection refused|No route to host) - connect\(\d\)/
|
|
687
|
+
# Connection refused
|
|
688
|
+
if head =~ /Connection refused - connect\(\d\)/
|
|
689
|
+
result["code"] = 502
|
|
690
|
+
result["message"] = 'Bad Gateway'
|
|
691
|
+
# No route to host
|
|
692
|
+
elsif head =~ /No route to host - connect\(\d\)/
|
|
693
|
+
result["code"] = 502
|
|
694
|
+
result["message"] = 'Bad Gateway'
|
|
695
|
+
# Connection timed out
|
|
696
|
+
elsif head =~ /Connection timed out - connect\(\d\)/
|
|
697
|
+
result["code"] = 504
|
|
698
|
+
result["message"] = 'Timeout'
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
logger.info "Using HTTP response status: #{result["code"]} #{result["message"]}"
|
|
702
|
+
end
|
|
703
|
+
result["headers"] = "HTTP\/#{result["http_version"]} #{result["code"]} #{result["message"]}\n"
|
|
704
|
+
response.each_header do |header_name, header_value|
|
|
705
|
+
if @strip.include?(header_name.downcase)
|
|
706
|
+
logger.info "Removed response header: #{header_name}"
|
|
707
|
+
next
|
|
708
|
+
end
|
|
709
|
+
result["headers"] << "#{header_name}: #{header_value}\n"
|
|
710
|
+
end
|
|
711
|
+
result["headers"] << "\n"
|
|
712
|
+
result["body"] = "#{response.body}" unless response.body.nil?
|
|
713
|
+
rescue => e
|
|
714
|
+
logger.info("Malformed HTTP response from server")
|
|
715
|
+
result["status"] = 'fail'
|
|
716
|
+
result["code"] = 502
|
|
717
|
+
result["message"] = 'Error'
|
|
718
|
+
result["headers"] = "HTTP\/1.1 502 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
|
719
|
+
end
|
|
720
|
+
return result
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
#
|
|
724
|
+
# @note Guess content type based on file extension
|
|
725
|
+
# - List from: https://stackoverflow.com/questions/1029740/get-mime-type-from-filename-extension
|
|
726
|
+
#
|
|
727
|
+
# @options
|
|
728
|
+
# - ext - String - File extension [with dots] (Example: '.png')
|
|
729
|
+
#
|
|
730
|
+
# @returns String - content-type value
|
|
731
|
+
#
|
|
732
|
+
def guess_mime(ext)
|
|
733
|
+
content_types = %w(
|
|
734
|
+
323,text/h323,
|
|
735
|
+
3g2,video/3gpp2,
|
|
736
|
+
3gp,video/3gpp,
|
|
737
|
+
3gp2,video/3gpp2,
|
|
738
|
+
3gpp,video/3gpp,
|
|
739
|
+
7z,application/x-7z-compressed,
|
|
740
|
+
aa,audio/audible,
|
|
741
|
+
AAC,audio/aac,
|
|
742
|
+
aaf,application/octet-stream,
|
|
743
|
+
aax,audio/vnd.audible.aax,
|
|
744
|
+
ac3,audio/ac3,
|
|
745
|
+
aca,application/octet-stream,
|
|
746
|
+
accda,application/msaccess.addin,
|
|
747
|
+
accdb,application/msaccess,
|
|
748
|
+
accdc,application/msaccess.cab,
|
|
749
|
+
accde,application/msaccess,
|
|
750
|
+
accdr,application/msaccess.runtime,
|
|
751
|
+
accdt,application/msaccess,
|
|
752
|
+
accdw,application/msaccess.webapplication,
|
|
753
|
+
accft,application/msaccess.ftemplate,
|
|
754
|
+
acx,application/internet-property-stream,
|
|
755
|
+
AddIn,text/xml,
|
|
756
|
+
ade,application/msaccess,
|
|
757
|
+
adobebridge,application/x-bridge-url,
|
|
758
|
+
adp,application/msaccess,
|
|
759
|
+
ADT,audio/vnd.dlna.adts,
|
|
760
|
+
ADTS,audio/aac,
|
|
761
|
+
afm,application/octet-stream,
|
|
762
|
+
ai,application/postscript,
|
|
763
|
+
aif,audio/x-aiff,
|
|
764
|
+
aifc,audio/aiff,
|
|
765
|
+
aiff,audio/aiff,
|
|
766
|
+
air,application/vnd.adobe.air-application-installer-package+zip,
|
|
767
|
+
amc,application/x-mpeg,
|
|
768
|
+
application,application/x-ms-application,
|
|
769
|
+
art,image/x-jg,
|
|
770
|
+
asa,application/xml,
|
|
771
|
+
asax,application/xml,
|
|
772
|
+
ascx,application/xml,
|
|
773
|
+
asd,application/octet-stream,
|
|
774
|
+
asf,video/x-ms-asf,
|
|
775
|
+
ashx,application/xml,
|
|
776
|
+
asi,application/octet-stream,
|
|
777
|
+
asm,text/plain,
|
|
778
|
+
asmx,application/xml,
|
|
779
|
+
aspx,application/xml,
|
|
780
|
+
asr,video/x-ms-asf,
|
|
781
|
+
asx,video/x-ms-asf,
|
|
782
|
+
atom,application/atom+xml,
|
|
783
|
+
au,audio/basic,
|
|
784
|
+
avi,video/x-msvideo,
|
|
785
|
+
axs,application/olescript,
|
|
786
|
+
bas,text/plain,
|
|
787
|
+
bcpio,application/x-bcpio,
|
|
788
|
+
bin,application/octet-stream,
|
|
789
|
+
bmp,image/bmp,
|
|
790
|
+
c,text/plain,
|
|
791
|
+
cab,application/octet-stream,
|
|
792
|
+
caf,audio/x-caf,
|
|
793
|
+
calx,application/vnd.ms-office.calx,
|
|
794
|
+
cat,application/vnd.ms-pki.seccat,
|
|
795
|
+
cc,text/plain,
|
|
796
|
+
cd,text/plain,
|
|
797
|
+
cdda,audio/aiff,
|
|
798
|
+
cdf,application/x-cdf,
|
|
799
|
+
cer,application/x-x509-ca-cert,
|
|
800
|
+
chm,application/octet-stream,
|
|
801
|
+
class,application/x-java-applet,
|
|
802
|
+
clp,application/x-msclip,
|
|
803
|
+
cmx,image/x-cmx,
|
|
804
|
+
cnf,text/plain,
|
|
805
|
+
cod,image/cis-cod,
|
|
806
|
+
config,application/xml,
|
|
807
|
+
contact,text/x-ms-contact,
|
|
808
|
+
coverage,application/xml,
|
|
809
|
+
cpio,application/x-cpio,
|
|
810
|
+
cpp,text/plain,
|
|
811
|
+
crd,application/x-mscardfile,
|
|
812
|
+
crl,application/pkix-crl,
|
|
813
|
+
crt,application/x-x509-ca-cert,
|
|
814
|
+
cs,text/plain,
|
|
815
|
+
csdproj,text/plain,
|
|
816
|
+
csh,application/x-csh,
|
|
817
|
+
csproj,text/plain,
|
|
818
|
+
css,text/css,
|
|
819
|
+
csv,text/csv,
|
|
820
|
+
cur,application/octet-stream,
|
|
821
|
+
cxx,text/plain,
|
|
822
|
+
dat,application/octet-stream,
|
|
823
|
+
datasource,application/xml,
|
|
824
|
+
dbproj,text/plain,
|
|
825
|
+
dcr,application/x-director,
|
|
826
|
+
def,text/plain,
|
|
827
|
+
deploy,application/octet-stream,
|
|
828
|
+
der,application/x-x509-ca-cert,
|
|
829
|
+
dgml,application/xml,
|
|
830
|
+
dib,image/bmp,
|
|
831
|
+
dif,video/x-dv,
|
|
832
|
+
dir,application/x-director,
|
|
833
|
+
disco,text/xml,
|
|
834
|
+
dll,application/x-msdownload,
|
|
835
|
+
dll.config,text/xml,
|
|
836
|
+
dlm,text/dlm,
|
|
837
|
+
doc,application/msword,
|
|
838
|
+
docm,application/vnd.ms-word.document.macroEnabled.12,
|
|
839
|
+
docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document,
|
|
840
|
+
dot,application/msword,
|
|
841
|
+
dotm,application/vnd.ms-word.template.macroEnabled.12,
|
|
842
|
+
dotx,application/vnd.openxmlformats-officedocument.wordprocessingml.template,
|
|
843
|
+
dsp,application/octet-stream,
|
|
844
|
+
dsw,text/plain,
|
|
845
|
+
dtd,text/xml,
|
|
846
|
+
dtsConfig,text/xml,
|
|
847
|
+
dv,video/x-dv,
|
|
848
|
+
dvi,application/x-dvi,
|
|
849
|
+
dwf,drawing/x-dwf,
|
|
850
|
+
dwp,application/octet-stream,
|
|
851
|
+
dxr,application/x-director,
|
|
852
|
+
eml,message/rfc822,
|
|
853
|
+
emz,application/octet-stream,
|
|
854
|
+
eot,application/octet-stream,
|
|
855
|
+
eps,application/postscript,
|
|
856
|
+
etl,application/etl,
|
|
857
|
+
etx,text/x-setext,
|
|
858
|
+
evy,application/envoy,
|
|
859
|
+
exe,application/octet-stream,
|
|
860
|
+
exe.config,text/xml,
|
|
861
|
+
fdf,application/vnd.fdf,
|
|
862
|
+
fif,application/fractals,
|
|
863
|
+
filters,Application/xml,
|
|
864
|
+
fla,application/octet-stream,
|
|
865
|
+
flr,x-world/x-vrml,
|
|
866
|
+
flv,video/x-flv,
|
|
867
|
+
fsscript,application/fsharp-script,
|
|
868
|
+
fsx,application/fsharp-script,
|
|
869
|
+
generictest,application/xml,
|
|
870
|
+
gif,image/gif,
|
|
871
|
+
group,text/x-ms-group,
|
|
872
|
+
gsm,audio/x-gsm,
|
|
873
|
+
gtar,application/x-gtar,
|
|
874
|
+
gz,application/x-gzip,
|
|
875
|
+
h,text/plain,
|
|
876
|
+
hdf,application/x-hdf,
|
|
877
|
+
hdml,text/x-hdml,
|
|
878
|
+
hhc,application/x-oleobject,
|
|
879
|
+
hhk,application/octet-stream,
|
|
880
|
+
hhp,application/octet-stream,
|
|
881
|
+
hlp,application/winhlp,
|
|
882
|
+
hpp,text/plain,
|
|
883
|
+
hqx,application/mac-binhex40,
|
|
884
|
+
hta,application/hta,
|
|
885
|
+
htc,text/x-component,
|
|
886
|
+
htm,text/html,
|
|
887
|
+
html,text/html,
|
|
888
|
+
htt,text/webviewhtml,
|
|
889
|
+
hxa,application/xml,
|
|
890
|
+
hxc,application/xml,
|
|
891
|
+
hxd,application/octet-stream,
|
|
892
|
+
hxe,application/xml,
|
|
893
|
+
hxf,application/xml,
|
|
894
|
+
hxh,application/octet-stream,
|
|
895
|
+
hxi,application/octet-stream,
|
|
896
|
+
hxk,application/xml,
|
|
897
|
+
hxq,application/octet-stream,
|
|
898
|
+
hxr,application/octet-stream,
|
|
899
|
+
hxs,application/octet-stream,
|
|
900
|
+
hxt,text/html,
|
|
901
|
+
hxv,application/xml,
|
|
902
|
+
hxw,application/octet-stream,
|
|
903
|
+
hxx,text/plain,
|
|
904
|
+
i,text/plain,
|
|
905
|
+
ico,image/x-icon,
|
|
906
|
+
ics,application/octet-stream,
|
|
907
|
+
idl,text/plain,
|
|
908
|
+
ief,image/ief,
|
|
909
|
+
iii,application/x-iphone,
|
|
910
|
+
inc,text/plain,
|
|
911
|
+
inf,application/octet-stream,
|
|
912
|
+
inl,text/plain,
|
|
913
|
+
ins,application/x-internet-signup,
|
|
914
|
+
ipa,application/x-itunes-ipa,
|
|
915
|
+
ipg,application/x-itunes-ipg,
|
|
916
|
+
ipproj,text/plain,
|
|
917
|
+
ipsw,application/x-itunes-ipsw,
|
|
918
|
+
iqy,text/x-ms-iqy,
|
|
919
|
+
isp,application/x-internet-signup,
|
|
920
|
+
ite,application/x-itunes-ite,
|
|
921
|
+
itlp,application/x-itunes-itlp,
|
|
922
|
+
itms,application/x-itunes-itms,
|
|
923
|
+
itpc,application/x-itunes-itpc,
|
|
924
|
+
IVF,video/x-ivf,
|
|
925
|
+
jar,application/java-archive,
|
|
926
|
+
java,application/octet-stream,
|
|
927
|
+
jck,application/liquidmotion,
|
|
928
|
+
jcz,application/liquidmotion,
|
|
929
|
+
jfif,image/pjpeg,
|
|
930
|
+
jnlp,application/x-java-jnlp-file,
|
|
931
|
+
jpb,application/octet-stream,
|
|
932
|
+
jpe,image/jpeg,
|
|
933
|
+
jpeg,image/jpeg,
|
|
934
|
+
jpg,image/jpeg,
|
|
935
|
+
js,application/x-javascript,
|
|
936
|
+
json,application/json,
|
|
937
|
+
jsx,text/jscript,
|
|
938
|
+
jsxbin,text/plain,
|
|
939
|
+
latex,application/x-latex,
|
|
940
|
+
library-ms,application/windows-library+xml,
|
|
941
|
+
lit,application/x-ms-reader,
|
|
942
|
+
loadtest,application/xml,
|
|
943
|
+
lpk,application/octet-stream,
|
|
944
|
+
lsf,video/x-la-asf,
|
|
945
|
+
lst,text/plain,
|
|
946
|
+
lsx,video/x-la-asf,
|
|
947
|
+
lzh,application/octet-stream,
|
|
948
|
+
m13,application/x-msmediaview,
|
|
949
|
+
m14,application/x-msmediaview,
|
|
950
|
+
m1v,video/mpeg,
|
|
951
|
+
m2t,video/vnd.dlna.mpeg-tts,
|
|
952
|
+
m2ts,video/vnd.dlna.mpeg-tts,
|
|
953
|
+
m2v,video/mpeg,
|
|
954
|
+
m3u,audio/x-mpegurl,
|
|
955
|
+
m3u8,audio/x-mpegurl,
|
|
956
|
+
m4a,audio/m4a,
|
|
957
|
+
m4b,audio/m4b,
|
|
958
|
+
m4p,audio/m4p,
|
|
959
|
+
m4r,audio/x-m4r,
|
|
960
|
+
m4v,video/x-m4v,
|
|
961
|
+
mac,image/x-macpaint,
|
|
962
|
+
mak,text/plain,
|
|
963
|
+
man,application/x-troff-man,
|
|
964
|
+
manifest,application/x-ms-manifest,
|
|
965
|
+
map,text/plain,
|
|
966
|
+
master,application/xml,
|
|
967
|
+
mda,application/msaccess,
|
|
968
|
+
mdb,application/x-msaccess,
|
|
969
|
+
mde,application/msaccess,
|
|
970
|
+
mdp,application/octet-stream,
|
|
971
|
+
me,application/x-troff-me,
|
|
972
|
+
mfp,application/x-shockwave-flash,
|
|
973
|
+
mht,message/rfc822,
|
|
974
|
+
mhtml,message/rfc822,
|
|
975
|
+
mid,audio/mid,
|
|
976
|
+
midi,audio/mid,
|
|
977
|
+
mix,application/octet-stream,
|
|
978
|
+
mk,text/plain,
|
|
979
|
+
mmf,application/x-smaf,
|
|
980
|
+
mno,text/xml,
|
|
981
|
+
mny,application/x-msmoney,
|
|
982
|
+
mod,video/mpeg,
|
|
983
|
+
mov,video/quicktime,
|
|
984
|
+
movie,video/x-sgi-movie,
|
|
985
|
+
mp2,video/mpeg,
|
|
986
|
+
mp2v,video/mpeg,
|
|
987
|
+
mp3,audio/mpeg,
|
|
988
|
+
mp4,video/mp4,
|
|
989
|
+
mp4v,video/mp4,
|
|
990
|
+
mpa,video/mpeg,
|
|
991
|
+
mpe,video/mpeg,
|
|
992
|
+
mpeg,video/mpeg,
|
|
993
|
+
mpf,application/vnd.ms-mediapackage,
|
|
994
|
+
mpg,video/mpeg,
|
|
995
|
+
mpp,application/vnd.ms-project,
|
|
996
|
+
mpv2,video/mpeg,
|
|
997
|
+
mqv,video/quicktime,
|
|
998
|
+
ms,application/x-troff-ms,
|
|
999
|
+
msi,application/octet-stream,
|
|
1000
|
+
mso,application/octet-stream,
|
|
1001
|
+
mts,video/vnd.dlna.mpeg-tts,
|
|
1002
|
+
mtx,application/xml,
|
|
1003
|
+
mvb,application/x-msmediaview,
|
|
1004
|
+
mvc,application/x-miva-compiled,
|
|
1005
|
+
mxp,application/x-mmxp,
|
|
1006
|
+
nc,application/x-netcdf,
|
|
1007
|
+
nsc,video/x-ms-asf,
|
|
1008
|
+
nws,message/rfc822,
|
|
1009
|
+
ocx,application/octet-stream,
|
|
1010
|
+
oda,application/oda,
|
|
1011
|
+
odc,text/x-ms-odc,
|
|
1012
|
+
odh,text/plain,
|
|
1013
|
+
odl,text/plain,
|
|
1014
|
+
odp,application/vnd.oasis.opendocument.presentation,
|
|
1015
|
+
ods,application/oleobject,
|
|
1016
|
+
odt,application/vnd.oasis.opendocument.text,
|
|
1017
|
+
one,application/onenote,
|
|
1018
|
+
onea,application/onenote,
|
|
1019
|
+
onepkg,application/onenote,
|
|
1020
|
+
onetmp,application/onenote,
|
|
1021
|
+
onetoc,application/onenote,
|
|
1022
|
+
onetoc2,application/onenote,
|
|
1023
|
+
orderedtest,application/xml,
|
|
1024
|
+
osdx,application/opensearchdescription+xml,
|
|
1025
|
+
p10,application/pkcs10,
|
|
1026
|
+
p12,application/x-pkcs12,
|
|
1027
|
+
p7b,application/x-pkcs7-certificates,
|
|
1028
|
+
p7c,application/pkcs7-mime,
|
|
1029
|
+
p7m,application/pkcs7-mime,
|
|
1030
|
+
p7r,application/x-pkcs7-certreqresp,
|
|
1031
|
+
p7s,application/pkcs7-signature,
|
|
1032
|
+
pbm,image/x-portable-bitmap,
|
|
1033
|
+
pcast,application/x-podcast,
|
|
1034
|
+
pct,image/pict,
|
|
1035
|
+
pcx,application/octet-stream,
|
|
1036
|
+
pcz,application/octet-stream,
|
|
1037
|
+
pdf,application/pdf,
|
|
1038
|
+
pfb,application/octet-stream,
|
|
1039
|
+
pfm,application/octet-stream,
|
|
1040
|
+
pfx,application/x-pkcs12,
|
|
1041
|
+
pgm,image/x-portable-graymap,
|
|
1042
|
+
pic,image/pict,
|
|
1043
|
+
pict,image/pict,
|
|
1044
|
+
pkgdef,text/plain,
|
|
1045
|
+
pkgundef,text/plain,
|
|
1046
|
+
pko,application/vnd.ms-pki.pko,
|
|
1047
|
+
pls,audio/scpls,
|
|
1048
|
+
pma,application/x-perfmon,
|
|
1049
|
+
pmc,application/x-perfmon,
|
|
1050
|
+
pml,application/x-perfmon,
|
|
1051
|
+
pmr,application/x-perfmon,
|
|
1052
|
+
pmw,application/x-perfmon,
|
|
1053
|
+
png,image/png,
|
|
1054
|
+
pnm,image/x-portable-anymap,
|
|
1055
|
+
pnt,image/x-macpaint,
|
|
1056
|
+
pntg,image/x-macpaint,
|
|
1057
|
+
pnz,image/png,
|
|
1058
|
+
pot,application/vnd.ms-powerpoint,
|
|
1059
|
+
potm,application/vnd.ms-powerpoint.template.macroEnabled.12,
|
|
1060
|
+
potx,application/vnd.openxmlformats-officedocument.presentationml.template,
|
|
1061
|
+
ppa,application/vnd.ms-powerpoint,
|
|
1062
|
+
ppam,application/vnd.ms-powerpoint.addin.macroEnabled.12,
|
|
1063
|
+
ppm,image/x-portable-pixmap,
|
|
1064
|
+
pps,application/vnd.ms-powerpoint,
|
|
1065
|
+
ppsm,application/vnd.ms-powerpoint.slideshow.macroEnabled.12,
|
|
1066
|
+
ppsx,application/vnd.openxmlformats-officedocument.presentationml.slideshow,
|
|
1067
|
+
ppt,application/vnd.ms-powerpoint,
|
|
1068
|
+
pptm,application/vnd.ms-powerpoint.presentation.macroEnabled.12,
|
|
1069
|
+
pptx,application/vnd.openxmlformats-officedocument.presentationml.presentation,
|
|
1070
|
+
prf,application/pics-rules,
|
|
1071
|
+
prm,application/octet-stream,
|
|
1072
|
+
prx,application/octet-stream,
|
|
1073
|
+
ps,application/postscript,
|
|
1074
|
+
psc1,application/PowerShell,
|
|
1075
|
+
psd,application/octet-stream,
|
|
1076
|
+
psess,application/xml,
|
|
1077
|
+
psm,application/octet-stream,
|
|
1078
|
+
psp,application/octet-stream,
|
|
1079
|
+
pub,application/x-mspublisher,
|
|
1080
|
+
pwz,application/vnd.ms-powerpoint,
|
|
1081
|
+
qht,text/x-html-insertion,
|
|
1082
|
+
qhtm,text/x-html-insertion,
|
|
1083
|
+
qt,video/quicktime,
|
|
1084
|
+
qti,image/x-quicktime,
|
|
1085
|
+
qtif,image/x-quicktime,
|
|
1086
|
+
qtl,application/x-quicktimeplayer,
|
|
1087
|
+
qxd,application/octet-stream,
|
|
1088
|
+
ra,audio/x-pn-realaudio,
|
|
1089
|
+
ram,audio/x-pn-realaudio,
|
|
1090
|
+
rar,application/octet-stream,
|
|
1091
|
+
ras,image/x-cmu-raster,
|
|
1092
|
+
rat,application/rat-file,
|
|
1093
|
+
rc,text/plain,
|
|
1094
|
+
rc2,text/plain,
|
|
1095
|
+
rct,text/plain,
|
|
1096
|
+
rdlc,application/xml,
|
|
1097
|
+
resx,application/xml,
|
|
1098
|
+
rf,image/vnd.rn-realflash,
|
|
1099
|
+
rgb,image/x-rgb,
|
|
1100
|
+
rgs,text/plain,
|
|
1101
|
+
rm,application/vnd.rn-realmedia,
|
|
1102
|
+
rmi,audio/mid,
|
|
1103
|
+
rmp,application/vnd.rn-rn_music_package,
|
|
1104
|
+
roff,application/x-troff,
|
|
1105
|
+
rpm,audio/x-pn-realaudio-plugin,
|
|
1106
|
+
rqy,text/x-ms-rqy,
|
|
1107
|
+
rtf,application/rtf,
|
|
1108
|
+
rtx,text/richtext,
|
|
1109
|
+
ruleset,application/xml,
|
|
1110
|
+
s,text/plain,
|
|
1111
|
+
safariextz,application/x-safari-safariextz,
|
|
1112
|
+
scd,application/x-msschedule,
|
|
1113
|
+
sct,text/scriptlet,
|
|
1114
|
+
sd2,audio/x-sd2,
|
|
1115
|
+
sdp,application/sdp,
|
|
1116
|
+
sea,application/octet-stream,
|
|
1117
|
+
searchConnector-ms,application/windows-search-connector+xml,
|
|
1118
|
+
setpay,application/set-payment-initiation,
|
|
1119
|
+
setreg,application/set-registration-initiation,
|
|
1120
|
+
settings,application/xml,
|
|
1121
|
+
sgimb,application/x-sgimb,
|
|
1122
|
+
sgml,text/sgml,
|
|
1123
|
+
sh,application/x-sh,
|
|
1124
|
+
shar,application/x-shar,
|
|
1125
|
+
shtml,text/html,
|
|
1126
|
+
sit,application/x-stuffit,
|
|
1127
|
+
sitemap,application/xml,
|
|
1128
|
+
skin,application/xml,
|
|
1129
|
+
sldm,application/vnd.ms-powerpoint.slide.macroEnabled.12,
|
|
1130
|
+
sldx,application/vnd.openxmlformats-officedocument.presentationml.slide,
|
|
1131
|
+
slk,application/vnd.ms-excel,
|
|
1132
|
+
sln,text/plain,
|
|
1133
|
+
slupkg-ms,application/x-ms-license,
|
|
1134
|
+
smd,audio/x-smd,
|
|
1135
|
+
smi,application/octet-stream,
|
|
1136
|
+
smx,audio/x-smd,
|
|
1137
|
+
smz,audio/x-smd,
|
|
1138
|
+
snd,audio/basic,
|
|
1139
|
+
snippet,application/xml,
|
|
1140
|
+
snp,application/octet-stream,
|
|
1141
|
+
sol,text/plain,
|
|
1142
|
+
sor,text/plain,
|
|
1143
|
+
spc,application/x-pkcs7-certificates,
|
|
1144
|
+
spl,application/futuresplash,
|
|
1145
|
+
src,application/x-wais-source,
|
|
1146
|
+
srf,text/plain,
|
|
1147
|
+
SSISDeploymentManifest,text/xml,
|
|
1148
|
+
ssm,application/streamingmedia,
|
|
1149
|
+
sst,application/vnd.ms-pki.certstore,
|
|
1150
|
+
stl,application/vnd.ms-pki.stl,
|
|
1151
|
+
sv4cpio,application/x-sv4cpio,
|
|
1152
|
+
sv4crc,application/x-sv4crc,
|
|
1153
|
+
svc,application/xml,
|
|
1154
|
+
swf,application/x-shockwave-flash,
|
|
1155
|
+
t,application/x-troff,
|
|
1156
|
+
tar,application/x-tar,
|
|
1157
|
+
tcl,application/x-tcl,
|
|
1158
|
+
testrunconfig,application/xml,
|
|
1159
|
+
testsettings,application/xml,
|
|
1160
|
+
tex,application/x-tex,
|
|
1161
|
+
texi,application/x-texinfo,
|
|
1162
|
+
texinfo,application/x-texinfo,
|
|
1163
|
+
tgz,application/x-compressed,
|
|
1164
|
+
thmx,application/vnd.ms-officetheme,
|
|
1165
|
+
thn,application/octet-stream,
|
|
1166
|
+
tif,image/tiff,
|
|
1167
|
+
tiff,image/tiff,
|
|
1168
|
+
tlh,text/plain,
|
|
1169
|
+
tli,text/plain,
|
|
1170
|
+
toc,application/octet-stream,
|
|
1171
|
+
tr,application/x-troff,
|
|
1172
|
+
trm,application/x-msterminal,
|
|
1173
|
+
trx,application/xml,
|
|
1174
|
+
ts,video/vnd.dlna.mpeg-tts,
|
|
1175
|
+
tsv,text/tab-separated-values,
|
|
1176
|
+
ttf,application/octet-stream,
|
|
1177
|
+
tts,video/vnd.dlna.mpeg-tts,
|
|
1178
|
+
txt,text/plain,
|
|
1179
|
+
u32,application/octet-stream,
|
|
1180
|
+
uls,text/iuls,
|
|
1181
|
+
user,text/plain,
|
|
1182
|
+
ustar,application/x-ustar,
|
|
1183
|
+
vb,text/plain,
|
|
1184
|
+
vbdproj,text/plain,
|
|
1185
|
+
vbk,video/mpeg,
|
|
1186
|
+
vbproj,text/plain,
|
|
1187
|
+
vbs,text/vbscript,
|
|
1188
|
+
vcf,text/x-vcard,
|
|
1189
|
+
vcproj,Application/xml,
|
|
1190
|
+
vcs,text/plain,
|
|
1191
|
+
vcxproj,Application/xml,
|
|
1192
|
+
vddproj,text/plain,
|
|
1193
|
+
vdp,text/plain,
|
|
1194
|
+
vdproj,text/plain,
|
|
1195
|
+
vdx,application/vnd.ms-visio.viewer,
|
|
1196
|
+
vml,text/xml,
|
|
1197
|
+
vscontent,application/xml,
|
|
1198
|
+
vsct,text/xml,
|
|
1199
|
+
vsd,application/vnd.visio,
|
|
1200
|
+
vsi,application/ms-vsi,
|
|
1201
|
+
vsix,application/vsix,
|
|
1202
|
+
vsixlangpack,text/xml,
|
|
1203
|
+
vsixmanifest,text/xml,
|
|
1204
|
+
vsmdi,application/xml,
|
|
1205
|
+
vspscc,text/plain,
|
|
1206
|
+
vss,application/vnd.visio,
|
|
1207
|
+
vsscc,text/plain,
|
|
1208
|
+
vssettings,text/xml,
|
|
1209
|
+
vssscc,text/plain,
|
|
1210
|
+
vst,application/vnd.visio,
|
|
1211
|
+
vstemplate,text/xml,
|
|
1212
|
+
vsto,application/x-ms-vsto,
|
|
1213
|
+
vsw,application/vnd.visio,
|
|
1214
|
+
vsx,application/vnd.visio,
|
|
1215
|
+
vtx,application/vnd.visio,
|
|
1216
|
+
wav,audio/wav,
|
|
1217
|
+
wave,audio/wav,
|
|
1218
|
+
wax,audio/x-ms-wax,
|
|
1219
|
+
wbk,application/msword,
|
|
1220
|
+
wbmp,image/vnd.wap.wbmp,
|
|
1221
|
+
wcm,application/vnd.ms-works,
|
|
1222
|
+
wdb,application/vnd.ms-works,
|
|
1223
|
+
wdp,image/vnd.ms-photo,
|
|
1224
|
+
webarchive,application/x-safari-webarchive,
|
|
1225
|
+
webtest,application/xml,
|
|
1226
|
+
wiq,application/xml,
|
|
1227
|
+
wiz,application/msword,
|
|
1228
|
+
wks,application/vnd.ms-works,
|
|
1229
|
+
WLMP,application/wlmoviemaker,
|
|
1230
|
+
wlpginstall,application/x-wlpg-detect,
|
|
1231
|
+
wlpginstall3,application/x-wlpg3-detect,
|
|
1232
|
+
wm,video/x-ms-wm,
|
|
1233
|
+
wma,audio/x-ms-wma,
|
|
1234
|
+
wmd,application/x-ms-wmd,
|
|
1235
|
+
wmf,application/x-msmetafile,
|
|
1236
|
+
wml,text/vnd.wap.wml,
|
|
1237
|
+
wmlc,application/vnd.wap.wmlc,
|
|
1238
|
+
wmls,text/vnd.wap.wmlscript,
|
|
1239
|
+
wmlsc,application/vnd.wap.wmlscriptc,
|
|
1240
|
+
wmp,video/x-ms-wmp,
|
|
1241
|
+
wmv,video/x-ms-wmv,
|
|
1242
|
+
wmx,video/x-ms-wmx,
|
|
1243
|
+
wmz,application/x-ms-wmz,
|
|
1244
|
+
wpl,application/vnd.ms-wpl,
|
|
1245
|
+
wps,application/vnd.ms-works,
|
|
1246
|
+
wri,application/x-mswrite,
|
|
1247
|
+
wrl,x-world/x-vrml,
|
|
1248
|
+
wrz,x-world/x-vrml,
|
|
1249
|
+
wsc,text/scriptlet,
|
|
1250
|
+
wsdl,text/xml,
|
|
1251
|
+
wvx,video/x-ms-wvx,
|
|
1252
|
+
x,application/directx,
|
|
1253
|
+
xaf,x-world/x-vrml,
|
|
1254
|
+
xaml,application/xaml+xml,
|
|
1255
|
+
xap,application/x-silverlight-app,
|
|
1256
|
+
xbap,application/x-ms-xbap,
|
|
1257
|
+
xbm,image/x-xbitmap,
|
|
1258
|
+
xdr,text/plain,
|
|
1259
|
+
xht,application/xhtml+xml,
|
|
1260
|
+
xhtml,application/xhtml+xml,
|
|
1261
|
+
xla,application/vnd.ms-excel,
|
|
1262
|
+
xlam,application/vnd.ms-excel.addin.macroEnabled.12,
|
|
1263
|
+
xlc,application/vnd.ms-excel,
|
|
1264
|
+
xld,application/vnd.ms-excel,
|
|
1265
|
+
xlk,application/vnd.ms-excel,
|
|
1266
|
+
xll,application/vnd.ms-excel,
|
|
1267
|
+
xlm,application/vnd.ms-excel,
|
|
1268
|
+
xls,application/vnd.ms-excel,
|
|
1269
|
+
xlsb,application/vnd.ms-excel.sheet.binary.macroEnabled.12,
|
|
1270
|
+
xlsm,application/vnd.ms-excel.sheet.macroEnabled.12,
|
|
1271
|
+
xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,
|
|
1272
|
+
xlt,application/vnd.ms-excel,
|
|
1273
|
+
xltm,application/vnd.ms-excel.template.macroEnabled.12,
|
|
1274
|
+
xltx,application/vnd.openxmlformats-officedocument.spreadsheetml.template,
|
|
1275
|
+
xlw,application/vnd.ms-excel,
|
|
1276
|
+
xml,text/xml,
|
|
1277
|
+
xmta,application/xml,
|
|
1278
|
+
xof,x-world/x-vrml,
|
|
1279
|
+
XOML,text/plain,
|
|
1280
|
+
xpm,image/x-xpixmap,
|
|
1281
|
+
xps,application/vnd.ms-xpsdocument,
|
|
1282
|
+
xrm-ms,text/xml,
|
|
1283
|
+
xsc,application/xml,
|
|
1284
|
+
xsd,text/xml,
|
|
1285
|
+
xsf,text/xml,
|
|
1286
|
+
xsl,text/xml,
|
|
1287
|
+
xslt,text/xml,
|
|
1288
|
+
xsn,application/octet-stream,
|
|
1289
|
+
xss,application/xml,
|
|
1290
|
+
xtp,application/octet-stream,
|
|
1291
|
+
xwd,image/x-xwindowdump,
|
|
1292
|
+
z,application/x-compress,
|
|
1293
|
+
zip,application/x-zip-compressed )
|
|
1294
|
+
content_types.each do |type_info|
|
|
1295
|
+
if ext == ".#{type_info.split(',').first}"
|
|
1296
|
+
content_type = type_info.split(',')[1]
|
|
1297
|
+
return content_type
|
|
1298
|
+
end
|
|
1299
|
+
end
|
|
1300
|
+
nil
|
|
1301
|
+
end
|
|
1302
|
+
|
|
1303
|
+
private :parse_http_response,:send_http_request,:run_rules,:encode_ip,:guess_mime
|
|
1304
|
+
|
|
1305
|
+
end
|
|
1306
|
+
end
|