class TFTP::Handler::RWSimple

Basic read-write session over a 'physical' directory.

Public Class Methods

new(path, opts = {}) click to toggle source

Initialize the handler.

Options:

- :no_read  => deny read access if true
- :no_write => deny write access if true

@param path [String] Path to serving root directory @param opts [Hash] Options

Calls superclass method TFTP::Handler::Base::new
# File lib/tftp/tftp.rb, line 212
def initialize(path, opts = {})
  @path = path
  super(opts)
end

Public Instance Methods

run!(tag, req, sock, src) click to toggle source

Handle a session.

Has to close the socket (and any other resources). Note that the current version 'guards' against path traversal by a simple substitution of '..' with '__'.

@param tag [String] Tag used for logging @param req [Packet] The initial request packet @param sock [UDPSocket] Connected socket @param src [UDPSource] Initial connection information

# File lib/tftp/tftp.rb, line 227
def run!(tag, req, sock, src)
  name = req.filename.gsub('..', '__')
  path = File.join(@path, name)

  case req
  when Packet::RRQ
    if @opts[:no_read]
      log :info, "#{tag} Denied read request for #{req.filename}"
      sock.send(Packet::ERROR.new(2, 'Access denied.').encode, 0)
      sock.close
      return
    end
    log :info, "#{tag} Read request for #{req.filename} (#{req.mode})"
    unless File.exist? path
      log :warn, "#{tag} File not found"
      sock.send(Packet::ERROR.new(1, 'File not found.').encode, 0)
      sock.close
      return
    end
    mode = 'r'
    mode += 'b' if req.mode == :octet
    io = File.open(path, mode)
    send(tag, sock, io)
    sock.close
    io.close
  when Packet::WRQ
    if @opts[:no_write]
      log :info, "#{tag} Denied write request for #{req.filename}"
      sock.send(Packet::ERROR.new(2, 'Access denied.').encode, 0)
      sock.close
      return
    end
    log :info, "#{tag} Write request for #{req.filename} (#{req.mode})"
    mode = 'w'
    mode += 'b' if req.mode == :octet
    io = File.open(path, mode)
    ok = recv(tag, sock, io)
    sock.close
    io.close
    unless ok
      log :warn, "#{tag} Removing partial file #{req.filename}"
      File.delete(path)
    end
  end
end