summaryrefslogtreecommitdiff
path: root/lib/webrick/httpservlet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/webrick/httpservlet')
-rw-r--r--lib/webrick/httpservlet/abstract.rb7
-rw-r--r--lib/webrick/httpservlet/cgi_runner.rb6
-rw-r--r--lib/webrick/httpservlet/cgihandler.rb40
-rw-r--r--lib/webrick/httpservlet/erbhandler.rb8
-rw-r--r--lib/webrick/httpservlet/filehandler.rb95
-rw-r--r--lib/webrick/httpservlet/prochandler.rb8
6 files changed, 105 insertions, 59 deletions
diff --git a/lib/webrick/httpservlet/abstract.rb b/lib/webrick/httpservlet/abstract.rb
index 03861e8fc7..f8bf14a330 100644
--- a/lib/webrick/httpservlet/abstract.rb
+++ b/lib/webrick/httpservlet/abstract.rb
@@ -48,8 +48,7 @@ module WEBrick
end
def do_OPTIONS(req, res)
- m = self.methods.grep(/^do_[A-Z]+$/)
- m.collect!{|i| i.sub(/do_/, "") }
+ m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1}
m.sort!
res["allow"] = m.join(",")
end
@@ -58,8 +57,8 @@ module WEBrick
def redirect_to_directory_uri(req, res)
if req.path[-1] != ?/
- location = req.path + "/"
- if req.query_string && req.query_string.size > 0
+ location = WEBrick::HTTPUtils.escape_path(req.path + "/")
+ if req.query_string && req.query_string.bytesize > 0
location << "?" << req.query_string
end
res.set_redirect(HTTPStatus::MovedPermanently, location)
diff --git a/lib/webrick/httpservlet/cgi_runner.rb b/lib/webrick/httpservlet/cgi_runner.rb
index 1069a68d58..dd7325d25c 100644
--- a/lib/webrick/httpservlet/cgi_runner.rb
+++ b/lib/webrick/httpservlet/cgi_runner.rb
@@ -13,7 +13,7 @@ def sysread(io, size)
while size > 0
tmp = io.sysread(size)
buf << tmp
- size -= tmp.size
+ size -= tmp.bytesize
end
return buf
end
@@ -39,7 +39,9 @@ dir = File::dirname(ENV["SCRIPT_FILENAME"])
Dir::chdir dir
if interpreter = ARGV[0]
- exec(interpreter, ENV["SCRIPT_FILENAME"])
+ argv = ARGV.dup
+ argv << ENV["SCRIPT_FILENAME"]
+ exec(*argv)
# NOTREACHED
end
exec ENV["SCRIPT_FILENAME"]
diff --git a/lib/webrick/httpservlet/cgihandler.rb b/lib/webrick/httpservlet/cgihandler.rb
index a35b59edb8..1976ae6948 100644
--- a/lib/webrick/httpservlet/cgihandler.rb
+++ b/lib/webrick/httpservlet/cgihandler.rb
@@ -1,11 +1,11 @@
-#
+#
# cgihandler.rb -- CGIHandler Class
-#
+#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
-#
+#
# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $
require 'rbconfig'
@@ -17,13 +17,11 @@ module WEBrick
module HTTPServlet
class CGIHandler < AbstractServlet
- Ruby = File::join(::Config::CONFIG['bindir'],
- ::Config::CONFIG['ruby_install_name'])
- Ruby << ::Config::CONFIG['EXEEXT']
- CGIRunner = "\"#{Ruby}\" \"#{Config::LIBDIR}/httpservlet/cgi_runner.rb\""
+ Ruby = RbConfig.ruby
+ CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\""
def initialize(server, name)
- super
+ super(server, name)
@script_filename = name
@tempdir = server[:TempDir]
@cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}"
@@ -34,8 +32,10 @@ module WEBrick
status = -1
cgi_in = IO::popen(@cgicmd, "wb")
- cgi_out = Tempfile.new("webrick.cgiout.", @tempdir)
- cgi_err = Tempfile.new("webrick.cgierr.", @tempdir)
+ cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY)
+ cgi_out.set_encoding("ASCII-8BIT")
+ cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY)
+ cgi_err.set_encoding("ASCII-8BIT")
begin
cgi_in.sync = true
meta = req.meta_vars
@@ -46,14 +46,14 @@ module WEBrick
end
dump = Marshal.dump(meta)
- cgi_in.write("%8d" % cgi_out.path.size)
+ cgi_in.write("%8d" % cgi_out.path.bytesize)
cgi_in.write(cgi_out.path)
- cgi_in.write("%8d" % cgi_err.path.size)
+ cgi_in.write("%8d" % cgi_err.path.bytesize)
cgi_in.write(cgi_err.path)
- cgi_in.write("%8d" % dump.size)
+ cgi_in.write("%8d" % dump.bytesize)
cgi_in.write(dump)
- if req.body and req.body.size > 0
+ if req.body and req.body.bytesize > 0
cgi_in.write(req.body)
end
ensure
@@ -63,19 +63,19 @@ module WEBrick
data = cgi_out.read
cgi_out.close(true)
if errmsg = cgi_err.read
- if errmsg.size > 0
+ if errmsg.bytesize > 0
@logger.error("CGIHandler: #{@script_filename}:\n" + errmsg)
end
- end
+ end
cgi_err.close(true)
end
-
+
if status != 0
@logger.error("CGIHandler: #{@script_filename} exit with #{status}")
end
data = "" unless data
- raw_header, body = data.split(/^[\xd\xa]+/on, 2)
+ raw_header, body = data.split(/^[\xd\xa]+/, 2)
raise HTTPStatus::InternalServerError,
"Premature end of script headers: #{@script_filename}" if body.nil?
@@ -85,6 +85,10 @@ module WEBrick
res.status = $1.to_i
header.delete('status')
end
+ if header.has_key?('location')
+ # RFC 3875 6.2.3, 6.2.4
+ res.status = 302 unless (300...400) === res.status
+ end
if header.has_key?('set-cookie')
header['set-cookie'].each{|k|
res.cookies << Cookie.parse_set_cookie(k)
diff --git a/lib/webrick/httpservlet/erbhandler.rb b/lib/webrick/httpservlet/erbhandler.rb
index b9d5e65b65..845db07169 100644
--- a/lib/webrick/httpservlet/erbhandler.rb
+++ b/lib/webrick/httpservlet/erbhandler.rb
@@ -1,11 +1,11 @@
-#
+#
# erbhandler.rb -- ERBHandler Class
-#
+#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
-#
+#
# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
require 'webrick/httpservlet/abstract.rb'
@@ -17,7 +17,7 @@ module WEBrick
class ERBHandler < AbstractServlet
def initialize(server, name)
- super
+ super(server, name)
@script_filename = name
end
diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb
index 410cc6f9a9..daad8abd27 100644
--- a/lib/webrick/httpservlet/filehandler.rb
+++ b/lib/webrick/httpservlet/filehandler.rb
@@ -20,7 +20,7 @@ module WEBrick
class DefaultFileHandler < AbstractServlet
def initialize(server, local_path)
- super
+ super(server, local_path)
@local_path = local_path
end
@@ -32,7 +32,7 @@ module WEBrick
if not_modified?(req, res, mtime, res['etag'])
res.body = ''
raise HTTPStatus::NotModified
- elsif req['range']
+ elsif req['range']
make_partial_content(req, res, @local_path, st.size)
raise HTTPStatus::PartialContent
else
@@ -87,7 +87,7 @@ module WEBrick
content = io.read(last-first+1)
body << "--" << boundary << CRLF
body << "Content-Type: #{mtype}" << CRLF
- body << "Content-Range: #{first}-#{last}/#{filesize}" << CRLF
+ body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF
body << CRLF
body << content
body << CRLF
@@ -107,7 +107,7 @@ module WEBrick
content = io.read(last-first+1)
end
res['content-type'] = mtype
- res['content-range'] = "#{first}-#{last}/#{filesize}"
+ res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
res['content-length'] = last - first + 1
res.body = content
else
@@ -163,6 +163,7 @@ module WEBrick
end
end
end
+ prevent_directory_traversal(req, res)
super(req, res)
end
@@ -198,10 +199,42 @@ module WEBrick
private
+ def trailing_pathsep?(path)
+ # check for trailing path separator:
+ # File.dirname("/aaaa/bbbb/") #=> "/aaaa")
+ # File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb")
+ # File.dirname("/aaaa/bbbb") #=> "/aaaa")
+ # File.dirname("/aaaa/bbbbx") #=> "/aaaa")
+ return File.dirname(path) != File.dirname(path+"x")
+ end
+
+ def prevent_directory_traversal(req, res)
+ # Preventing directory traversal on Windows platforms;
+ # Backslashes (0x5c) in path_info are not interpreted as special
+ # character in URI notation. So the value of path_info should be
+ # normalize before accessing to the filesystem.
+
+ # dirty hack for filesystem encoding; in nature, File.expand_path
+ # should not be used for path normalization. [Bug #3345]
+ path = req.path_info.dup.force_encoding(Encoding.find("filesystem"))
+ if trailing_pathsep?(req.path_info)
+ # File.expand_path removes the trailing path separator.
+ # Adding a character is a workaround to save it.
+ # File.expand_path("/aaa/") #=> "/aaa"
+ # File.expand_path("/aaa/" + "x") #=> "/aaa/x"
+ expanded = File.expand_path(path + "x")
+ expanded.chop! # remove trailing "x"
+ else
+ expanded = File.expand_path(path)
+ end
+ expanded.force_encoding(req.path_info.encoding)
+ req.path_info = expanded
+ end
+
def exec_handler(req, res)
raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
if set_filename(req, res)
- handler = get_handler(req)
+ handler = get_handler(req, res)
call_callback(:HandlerCallback, req, res)
h = handler.get_instance(@config, res.filename)
h.service(req, res)
@@ -211,9 +244,13 @@ module WEBrick
return false
end
- def get_handler(req)
- suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase
- suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase
+ def get_handler(req, res)
+ suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase
+ if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename
+ if @options[:AcceptableLanguages].include?($2.downcase)
+ suffix2 = $1.downcase
+ end
+ end
handler_table = @options[:HandlerTable]
return handler_table[suffix1] || handler_table[suffix2] ||
HandlerTable[suffix1] || HandlerTable[suffix2] ||
@@ -226,15 +263,13 @@ module WEBrick
path_info.unshift("") # dummy for checking @root dir
while base = path_info.first
- check_filename(req, res, base)
break if base == "/"
- break unless File.directory?(res.filename + base)
+ break unless File.directory?(File.expand_path(res.filename + base))
shift_path_info(req, res, path_info)
call_callback(:DirectoryCallback, req, res)
end
if base = path_info.first
- check_filename(req, res, base)
if base == "/"
if file = search_index_file(req, res)
shift_path_info(req, res, path_info, file)
@@ -255,12 +290,10 @@ module WEBrick
end
def check_filename(req, res, name)
- @options[:NondisclosureName].each{|pattern|
- if File.fnmatch("/#{pattern}", name)
- @logger.warn("the request refers nondisclosure name `#{name}'.")
- raise HTTPStatus::NotFound, "`#{req.path}' not found."
- end
- }
+ if nondisclosure_name?(name) || windows_ambiguous_name?(name)
+ @logger.warn("the request refers nondisclosure name `#{name}'.")
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
+ end
end
def shift_path_info(req, res, path_info, base=nil)
@@ -268,7 +301,8 @@ module WEBrick
base = base || tmp
req.path_info = path_info.join
req.script_name << base
- res.filename << base
+ res.filename = File.expand_path(res.filename + base)
+ check_filename(req, res, File.basename(res.filename))
end
def search_index_file(req, res)
@@ -308,9 +342,15 @@ module WEBrick
end
end
+ def windows_ambiguous_name?(name)
+ return true if /[. ]+\z/ =~ name
+ return true if /::\$DATA\z/ =~ name
+ return false
+ end
+
def nondisclosure_name?(name)
@options[:NondisclosureName].each{|pattern|
- if File.fnmatch(pattern, name)
+ if File.fnmatch(pattern, name, File::FNM_CASEFOLD)
return true
end
}
@@ -326,7 +366,8 @@ module WEBrick
list = Dir::entries(local_path).collect{|name|
next if name == "." || name == ".."
next if nondisclosure_name?(name)
- st = (File::stat(local_path + name) rescue nil)
+ next if windows_ambiguous_name?(name)
+ st = (File::stat(File.join(local_path, name)) rescue nil)
if st.nil?
[ name, nil, -1 ]
elsif st.directory?
@@ -365,25 +406,25 @@ module WEBrick
res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> "
res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
res.body << "<HR>\n"
-
- list.unshift [ "..", File::mtime(local_path+".."), -1 ]
+
+ list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
list.each{ |name, time, size|
if name == ".."
dname = "Parent Directory"
- elsif name.size > 25
- dname = name.sub(/^(.{23})(.*)/){ $1 + ".." }
+ elsif name.bytesize > 25
+ dname = name.sub(/^(.{23})(?:.*)/, '\1..')
else
dname = name
end
- s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
- s << " " * (30 - dname.size)
+ s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{HTMLUtils::escape(dname)}</A>"
+ s << " " * (30 - dname.bytesize)
s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
s << (size >= 0 ? size.to_s : "-") << "\n"
res.body << s
}
res.body << "</PRE><HR>"
- res.body << <<-_end_of_html_
+ res.body << <<-_end_of_html_
<ADDRESS>
#{HTMLUtils::escape(@config[:ServerSoftware])}<BR>
at #{req.host}:#{req.port}
diff --git a/lib/webrick/httpservlet/prochandler.rb b/lib/webrick/httpservlet/prochandler.rb
index 783cb27896..2be3c854c1 100644
--- a/lib/webrick/httpservlet/prochandler.rb
+++ b/lib/webrick/httpservlet/prochandler.rb
@@ -1,11 +1,11 @@
-#
+#
# prochandler.rb -- ProcHandler Class
-#
+#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
-#
+#
# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
require 'webrick/httpservlet/abstract.rb'
@@ -16,7 +16,7 @@ module WEBrick
class ProcHandler < AbstractServlet
def get_instance(server, *options)
self
- end
+ end
def initialize(proc)
@proc = proc