Ruby TFTP server - 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

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

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

Related

ruby server - issue doing if then else checks on imcoming data from client

Here is my code (and below it is my issue):
require "socket"
server = TCPServer.open("localhost", 2000)
loop {
thread.start(server.accept) do |nodervcr|
msg = nodervcr.gets
puts(msg)
if msg = "codeword"
puts("codeword!")
else
puts("not codeword")
# Note this part works: it sends the server a message and it displays it
# You would think a simple if then else statement could redirect it according to the imcoming message from the client; which is my issue.
end
}
So I tried:
if msg == code
# then do this
elsif msg == code2
# then do this
# etc
But it's not working.
I've tried replacing msg with nodervcr, still nothing.
The strange part is that it's obviously getting the message, and msg does = what the client sends.. but it acts as if that variable dies immediately. I'm new to ruby. please help thanks.
You have a few problems in your code. But the main problem is that when you use something like Telnet, you probally are giving a enter.
So your sending "codeword" + CRLF (codeword\r\n). Thats not the same as your check: msg == "codeword"
What you should do is strip the input from these chars (strip). It removes the CRLF and it then works
Remember. Variable Assignments are = (single) comparing is double ==
Below the working code:
require "socket"
server = TCPServer.open("127.0.0.1", 2000)
loop {
Thread.start(server.accept) do |nodervcr| # Thread, not thread
msg = nodervcr.gets
# puts(msg)
p msg # use this so see all the invisiable chars
if msg.strip == "codeword" # stip the input
puts("codeword!")
else
puts("not codeword")
end
# Note this part works: it sends the server a message and it displays it
# You would think a simple if then else statement could redirect it according to the imcoming message from the client; which is my issue.
end
}

Create a UDP ping packet to fetch mumble(murmur) status

I'd like to send a udp packet to a mumble server using ruby to get status information about how many users are currently connected.
The documentation states there is a way using a UDP packet: http://mumble.sourceforge.net/Protocol#UDP_Ping_packet
However I don't know how to formulate that with ruby and thus get no reply from the server.
require 'socket'
sock = UDPSocket.new
sock.connect("99.99.99.99", 66666)
sock.send("00", 0)
p sock.recvfrom(1) # this does not return
sock.close
How do I format data of the udp packet?
This should work to generate your ping packet:
def ping(identifier)
v = identifier
a = []
while v > 256 # extract bytes from the identifier
a << v % 256
v = v / 256
end
a << v % 256
prefix = [0] * (8-a.length) # pad the identifier
([0,0,0,0] + prefix + a).pack("C*") # pack the message as bytes
end
usage:
# random 8 byte number as a message identifier - compare this to any packet
# received to ensure you're receiving the correct response.
identifier = rand(256**8)
sock.send ping(identifier), 0
# you should get a response here if the mumble server is
# accessible and responding to pings.
sock.recvfrom(1)

Zlib Inflate buffer error

def deflate(string, level)
z = Zlib::Deflate.new(level)
dst = z.deflate(string, Zlib::NO_FLUSH)
z.close
return dst
end
def inflate(string)
zstream = Zlib::Inflate.new
buf = zstream.inflate(string)
zstream.finish
zstream.close
return buf
end
a = deflate("asasasas",6)
p a
p inflate(a)
Gives me a buffer error on line
zstream.finish
Why is that? Ruby 1.8.7 I believe.
The documentation on deflate says:
Normally the parameter flush is set to Z_NO_FLUSH, which allows
deflate to decide how much data to accumulate before producing output,
in order to maximize compression.
By using Zlib::NO_FLUSH the returned value is not the entire deflated stream. Use Zlib::FINISH instead.
The functions you've written are the ones provided in the Zlib::Deflate / Zlib::Inflate documentation. You could replace all the code you've written with:
a = Zlib::Deflate.deflate("asasasas", 6)
p a
p Zlib::Inflate.inflate(a)

How to correctly handle partial write in Ruby Sockets?

TCP sockets are streams, not messages, so berkeley sockets send() function on some systems may send less data than required. Since Ruby Socket is very thin wrapper over berkeley sockets, AFAIK Socket#send will behave exactly like berkeley sockets send(). So what is the correct way to send a complete message via Ruby TCP sockets? In python it's a special function for that called sendall(). But in Ruby i need to manually write code like that:
while (sent = sock.send( data, 0 ) < data.length do
data = data[ sent..-1 ]
end
To expand on what danielnegri says, IO.write ends up calling io_binwrite in io.c
The relevant bit of the ruby source is below (n and len are initially set to the length of your data and offset to 0)
retry:
arg.offset = offset;
arg.length = n;
if (fptr->write_lock) {
r = rb_mutex_synchronize(fptr->write_lock, io_binwrite_string, (VALUE)&arg);
}
else {
long l = io_writable_length(fptr, n);
r = rb_write_internal(fptr->fd, ptr+offset, l);
}
if (r == n) return len;
if (0 <= r) {
offset += r;
n -= r;
errno = EAGAIN;
}
if (rb_io_wait_writable(fptr->fd)) {
rb_io_check_closed(fptr);
if (offset < RSTRING_LEN(str))
goto retry;
}
return -1L;
As you can see, until it has written all the data it will keep on doing goto retry and trying again. rb_io_wait_writable basically checks that the value of errno is such that one should try again (as opposed to something more fatal) and then calls select to avoid busy-waiting.
Recently I used 'write' method:
require "socket"
server = TCPServer.new('localhost', 20000)
loop do
Thread.start(server.accept) do |s|
print(s, " is accepted\n")
server.write(Time.now)
print(server, " is gone\n")
server.close
end
end
Try:
require 'socket'
sock = Socket.open(Socket::PF_INET,Socket::SOCK_STREAM,Socket::IPPROTO_TCP)
#data = "anyThing"
#addr = pack_sockaddr_in(port, host)
sock.connect(#addr) #make the connection
sock.send(#data, 0)
You may also want to try using the TCPSocket class. I haven't used any of this Ruby code, so I'm not used to this particular library; please let me know if I got this all wrong.
require 'socket'
sock = TCPSocket.new(host, port)
#data = "anyThing"
sock.send(#data, 0)

Ruby TCPSocket keeps losing connection

I have a client and server. I start up the server, and run the client, and the first time it works fine. The second time I run the client(without restarting the server), the client appears to hang. Can anyone see what is wrong?
I have a client:
# Code example originated from p069dtclient.rb at http://rubylearning.com/satishtalim/ruby_socket_programming.html
require 'socket'
x = 0;
streamSock = TCPSocket.new( 'localhost', 20000 )
while x &lt 10
streamSock.send( "Hello #{x}",0 )
str = streamSock.recv( 100 )
puts "#{x} " + str
x=x+1
end
streamSock.close
And server:
# p068dtserver.rb
require "socket"
dts = TCPServer.new('localhost', 20000)
s = dts.accept
print(s, " is accepted\n")
loopCount = 0;
loop do
Thread.start(s) do
loopCount = loopCount + 1
lineRcvd = s.recv(1024)
if ( !lineRcvd.empty? )
puts("#{loopCount} Received: #{lineRcvd}")
s.write(Time.now)
end
end
end
s.close
print(s, " is gone\n")
Each connection to the server requires a separate accept call in order to be received. What's happening is that you're accepting the first, working with it, and then effectively terminating while leaving the socket in a listening state. This means connections will be opened, but not accepted, so they hang as you describe.
You might be better off using a more robust server framework. EventMachine (http://rubyeventmachine.com/) is a little tricky to learn, but is far more powerful than a roll your own solution.
Here's a quick fix that might help:
require "socket"
dts = TCPServer.new('localhost', 20000)
while (s = dts.accept)
print(s, " is accepted\n")
loopCount = 0;
loop do
Thread.start(s) do
loopCount = loopCount + 1
lineRcvd = s.recv(1024)
if ( !lineRcvd.empty? )
puts("#{loopCount} Received: #{lineRcvd}")
s.write(Time.now)
end
end
end
s.close
print(s, " is gone\n")
end
Now the accept call is wrapped in a loop so more than one connection can be processed.

Resources