Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby TFTP server

Tags:

ruby

I have the following code that I put together for a simple Ruby TFTP server. It works fine in that it listens to port 69 and my TFTP client connects to it and I am able to write the packets to the test.txt, but instead of just writing packets, I want to be able to TFTP a file from my client to the /temp directory.

Thanks in advance for your help!

require 'socket.so'

class TFTPServer
  def initialize(port)
    @port = port
  end

  def start
    @socket = UDPSocket.new
    @socket.bind('', @port)
    while true
      packet = @socket.recvfrom(1024)
      puts packet

      File.open('/temp/test.txt', 'w') do |p|
            p.puts packet   
        end 
    end
  end
end

server = TFTPServer.new(69)
server.start
like image 678
rahrahruby Avatar asked May 17 '11 23:05

rahrahruby


2 Answers

Instead of writing to the /temp/test.txt you can use ruby's Tempfile class

So in your example:

require 'socket.so'
require 'tempfile'

class TFTPServer
  def initialize(port)
   @port = port
  end

  def start
    @socket = UDPSocket.new
    @socket.bind('', @port)
    while true
      packet = @socket.recvfrom(1024)
      puts packet

      Tempfile.new('tftpserver') do |p|
        p.puts process_packet( packet )
      end 
    end
  end
end

server = TFTPServer.new(69)
server.start

This will create a guaranteed unique temporary file in your /tmp directory with a name based off of 'tftpserver'.

EDIT: I noticed you wanted to write to /temp (not /tmp) to do this you can do Tempfile.new('tftpserver', '/temp') to specify a specific temporary directory.

Edit 2: For anyone interested there is a library that will do this https://github.com/spiceworks/net-tftp

like image 143
diedthreetimes Avatar answered Sep 28 '22 18:09

diedthreetimes


you'll not get it so easily, the tftp protocol is relatively easy, but put/get is not stateless, or at least if the file does not fit in a single packet, that is something like 512, but some extensions allow a bigger packet

the file on the wire is splited and you'll get a sequence of packets each packet has a sequence number so the other end can send error for a specific packet

you should take a look at wikipedia page:

http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol

here a sample code I wrote in 2005 but id does the specular thing (it sends a file) it's python but reasonably similar to ruby :D

def send_file(self, addr, filesend, filesize, blocksize):
    print '[send_file] Sending %s (size: %d - blksize: %d) to %s:%d' % (filesend, filesize, blocksize, addr[0], addr[1])
    fd = open(filesend, 'rb')

    for c in range(1, (filesize / blocksize) + 2):
        hdr = pack('!H', DATA) + pack('!H', c)
        indata = fd.read(blocksize)
        if debug > 5: print '[send_file] [%s] Sending block %d size %d' % (filesend, c, len(indata))
        self.s.sendto(hdr + indata, addr)
        data, addr = self.s.recvfrom(1024)
        res = unpack('!H', data[:2])[0]
        data = data[2:]
        if res != ACK:
            print '[send_file] Transfer aborted: %s' % errors[res]
            break
        if debug > 5:
            print '[send_file] [%s] Received ack for block %d' % (filesend, unpack('>H', data[:2])[0] + 1)
    fd.close()

    ## End Transfer
    pkt = pack('!H', DATA) + pack('>H', c) + NULL
    self.s.sendto(pkt, addr)
    if debug: print '[send_file] File send Done (%d)' % c

you can find constants in arpa/tftp.h (you need a unix or search online)

the sequence number is a big endian (network order) short !H format for struct pack ruby has something like python struct in String class

like image 23
sherpya Avatar answered Sep 28 '22 18:09

sherpya