#!/usr/bin/env ruby require 'socket' require 'timeout' class JjSrv def rrq len = @socket.gets.to_i @socket.read len end def srq(a) @socket.puts a.length @socket.write a end def initialize(root = '.', host = 'localhost', port = 10987) @lsocket = TCPServer.open host, port @root = root end def main_loop puts Time.now.strftime('%d/%m %H:%M:%S ') + "server ready" loop do a = @lsocket.accept if fork peer = a.peeraddr puts Time.now.strftime('%d/%m %H:%M:%S ') + "new connection from #{peer[2].inspect} (#{peer[3]}:#{peer[1]})" a.close sleep 1 else @lsocket.close @socket = a client_loop @socket.shutdown @socket.close exit end end end def client_loop Timeout.timeout(60) { loop do srq handle_rq(rrq) end } rescue nil end def handle_rq(rq) case rq.split.first when 'read' cmd, off, len, path = rq.split(' ', 4) check_path path File.open(File.join(@root, path), 'rb') { |fd| fd.pos = off.to_i ; fd.read len.to_i } when 'dir' path = rq.split(' ', 2).last check_path path Dir.entries(File.join(@root, path)).map { |d| File.directory?(File.join(@root, path, d)) ? d+'/' : d }.join("\n") # parse error if filename == "foo\nbar" ... when 'size' path = rq.split(' ', 2).last check_path path File.size(File.join(@root, path)).to_s end end def check_path(path) raise 'Directory traversal' if path.split('/').include? '..' end end if $0 == __FILE__ trap('INT') { exit } JjSrv.new(*ARGV).main_loop rescue puts($!, "Usage: #$0 [rootdir] [bindhost] [port]") end