[DRE-commits] r1831 - in packages-wip: . libnet-tftp-ruby libnet-tftp-ruby/branches libnet-tftp-ruby/branches/upstream libnet-tftp-ruby/branches/upstream/current libnet-tftp-ruby/branches/upstream/current/lib libnet-tftp-ruby/branches/upstream/current/lib/net
deepak-guest at alioth.debian.org
deepak-guest at alioth.debian.org
Mon Aug 20 15:13:40 UTC 2007
Author: deepak-guest
Date: 2007-08-20 15:13:40 +0000 (Mon, 20 Aug 2007)
New Revision: 1831
Added:
packages-wip/libnet-tftp-ruby/
packages-wip/libnet-tftp-ruby/branches/
packages-wip/libnet-tftp-ruby/branches/upstream/
packages-wip/libnet-tftp-ruby/branches/upstream/current/
packages-wip/libnet-tftp-ruby/branches/upstream/current/lib/
packages-wip/libnet-tftp-ruby/branches/upstream/current/lib/net/
packages-wip/libnet-tftp-ruby/branches/upstream/current/lib/net/tftp.rb
packages-wip/libnet-tftp-ruby/tags/
Log:
[svn-inject] Installing original source of libnet-tftp-ruby
Added: packages-wip/libnet-tftp-ruby/branches/upstream/current/lib/net/tftp.rb
===================================================================
--- packages-wip/libnet-tftp-ruby/branches/upstream/current/lib/net/tftp.rb (rev 0)
+++ packages-wip/libnet-tftp-ruby/branches/upstream/current/lib/net/tftp.rb 2007-08-20 15:13:40 UTC (rev 1831)
@@ -0,0 +1,294 @@
+#! /usr/bin/ruby
+
+#--
+# Copyright (c) 2004, Guillaume Marcais (guillaume.marcais at free.fr)
+# All rights reserved.
+# This file is distributed under the Ruby license.
+# http://net-tftp.rubyforge.org
+#++
+#
+# == Description
+# TFTP is used by many devices to upload or download their configuration,
+# firmware or else. It is a very simple file transfer protocol built on top
+# of UDP. It transmits data by chunck of 512 bytes. It waits for an ack after
+# each data packet but does not do any data integrety checks.
+# There is no authentication mechanism nor any way too list the content of
+# the remote directories. It just sends or retrieves files.
+#
+# == Usage
+# Using TFTP is fairly trivial:
+# <pre>
+# <code>
+# require 'net/tftp'
+# t = Net::TFTP.new('localhost')
+# t.getbinaryfile('remote_file', 'local_file')
+# t.putbinaryfile('local_file', 'remote_file')
+# </code>
+# </pre>
+#
+# That's pretty much it. +getbinaryfile+ and +putbinaryfile+ can take a
+# block which will be called every time a block is sent/received.
+#
+# == Known limitations
+# * RFC 1350 mention a net-ascii mode. I am not quite sure what transformation
+# on the data should be done and it is not (yet) implemented.
+# * None of the extensions of TFTP are implemented (RFC1782, RFC1783, RFC1784,
+# RFC1785, RFC2347, RFC2348, RFC2349).
+
+require 'socket'
+require 'timeout'
+
+module Net # :nodoc:
+
+ class TFTPError < StandardError; end
+ class TFTPTimeout < TFTPError; end
+ class TFTPProtocol < TFTPError
+ attr_reader :code
+ def initialize(msg, code)
+ super(msg)
+ @code = code
+ end
+ end
+
+
+ class TFTP
+ VERSION = "0.1.0"
+ DEFAULTS = {
+ :port => (Socket.getservbyname("tftp", "udp") rescue 69),
+ :timeout => 5,
+ }
+
+ MINSIZE = 4
+ MAXSIZE = 516
+ DATABLOCK = 512
+
+ # Errors
+ ERROR_DESCRIPTION = [
+ "Custom error",
+ "File not found",
+ "Access violation",
+ "Disk full",
+ "Illegal TFP operation",
+ "Unknown transfer ID",
+ "File already exists",
+ "No such user",
+ ]
+ ERROR_UNDEF = 0
+ ERROR_FILE_NOT_FOUND = 1
+ ERROR_ACCESS_VIOLATION = 2
+ ERROR_DISK_FULL = 3
+ ERROR_ILLEGAL_OPERATION = 4
+ ERROR_UNKNOWN_TRANSFER_ID = 5
+ ERROR_FILE_ALREADY_EXISTS = 6
+ ERROR_NO_SUCH_USER = 7
+
+ # Opcodes
+ OP_RRQ = 1
+ OP_WRQ = 2
+ OP_DATA = 3
+ OP_ACK = 4
+ OP_ERROR = 5
+
+ class << self
+ # Alias for new
+ def open(host)
+ new(host)
+ end
+
+ # Return the number of blocks to send _size_ bytes.
+ def size_in_blocks(size)
+ s = size / DATABLOCK
+ s += 1 unless (size % DATABLOCK) == 0
+ s
+ end
+ end
+
+ attr_accessor :timeout, :host
+
+ # Create a TFTP connection object to a host. Note that no actual
+ # network connection is made. This methods never fails.
+ # Parameters:
+ # [:port] The UDP port. See DEFAULTS
+ # [:timeout] Timeout in second for each ack packet. See DEFAULTS
+ def initialize(host, params = {})
+ @host = host
+ @port = params[:port] || DEFAULTS[:port]
+ @timeout = params[:timeout] || DEFAULTS[:timeout]
+ end
+
+ # Retrieve a file using binary mode.
+ # If the localfile name is omitted, it is set to the remotefile.
+ # The optional block receives the data in the block and the sequence number
+ # of the block starting at 1.
+ def getbinaryfile(remotefile, localfile = nil, &block) # :yields: data, seq
+ localfile ||= File.basename(remotefile)
+ open(localfile, "w") do |f|
+ getbinary(remotefile, f, &block)
+ end
+ end
+
+ # Retrieve a file using binary mode and send content to an io object
+ # The optional block receives the data in the block and the sequence number
+ # of the block starting at 1.
+ def getbinary(remotefile, io, &block) # :yields: data, seq
+ s = UDPSocket.new
+ begin
+ peer_ip = IPSocket.getaddress(@host)
+ rescue
+ raise TFTPError, "Cannot find host '#{@host}'"
+ end
+
+ peer_tid = nil
+ seq = 1
+ from = nil
+ data = nil
+
+ # Initialize request
+ s.send(rrq_packet(remotefile, "octet"), 0, peer_ip, @port)
+ Timeout::timeout(@timeout, TFTPTimeout) do
+ loop do
+ packet, from = s.recvfrom(MAXSIZE, 0)
+ next unless peer_ip == from[3]
+ type, block, data = scan_packet(packet)
+ break if (type == OP_DATA) && (block == seq)
+ end
+ end
+ peer_tid = from[1]
+
+ # Get and write data to io
+ loop do
+ io.write(data)
+ s.send(ack_packet(seq), 0, peer_ip, peer_tid)
+ yield(data, seq) if block_given?
+ break if data.size < DATABLOCK
+
+ seq += 1
+ Timeout::timeout(@timeout, TFTPTimeout) do
+ loop do
+ packet, from = s.recvfrom(MAXSIZE, 0)
+ next unless peer_ip == from[3]
+ if peer_tid != from[1]
+ s.send(error_packet(ERROR_UNKNOWN_TRANSFER_ID),
+ 0, from[3], from[1])
+ next
+ end
+ type, block, data = scan_packet(packet)
+ break if (type == OP_DATA) && (block == seq)
+ end
+ end
+ end
+
+ return true
+ end
+
+ # Send a file in binary mode. The name of the remotefile is set to
+ # the name of the local file if omitted.
+ # The optional block receives the data in the block and the sequence number
+ # of the block starting at 1.
+ def putbinaryfile(localfile, remotefile = nil, &block) # :yields: data, seq
+ remotefile ||= File.basename(localfile)
+ open(localfile) do |f|
+ putbinary(remotefile, f, &block)
+ end
+ end
+
+ # Send the content read from io to the remotefile.
+ # The optional block receives the data in the block and the sequence number
+ # of the block starting at 1.
+ def putbinary(remotefile, io, &block) # :yields: data, seq
+ s = UDPSocket.new
+ peer_ip = IPSocket.getaddress(@host)
+
+ peer_tid = nil
+ seq = 0
+ from = nil
+ data = nil
+
+ # Initialize request
+ s.send(wrq_packet(remotefile, "octet"), 0, peer_ip, @port)
+ Timeout::timeout(@timeout, TFTPTimeout) do
+ loop do
+ packet, from = s.recvfrom(MAXSIZE, 0)
+ next unless peer_ip == from[3]
+ type, block, data = scan_packet(packet)
+ break if (type == OP_ACK) && (block == seq)
+ end
+ end
+ peer_tid = from[1]
+
+ loop do
+ data = io.read(DATABLOCK) || ""
+ seq += 1
+ s.send(data_packet(seq, data), 0, peer_ip, peer_tid)
+
+ Timeout::timeout(@timeout, TFTPTimeout) do
+ loop do
+ packet, from = s.recvfrom(MAXSIZE, 0)
+ next unless peer_ip == from[3]
+ if peer_tid != from[1]
+ s.send(error_packet(ERROR_UNKNOWN_TRANSFER_ID),
+ 0, from[3], from[1])
+ next
+ end
+ type, block, void = scan_packet(packet)
+ break if (type == OP_ACK) && (block == seq)
+ end
+ end
+
+ yield(data, seq) if block_given?
+ break if data.size < DATABLOCK
+ end
+
+ return true
+ end
+
+ ####################
+ # Private methods #
+ ####################
+ private
+ def rrq_packet(file, mode)
+ [OP_RRQ, file, mode].pack("na#{file.size + 1}a#{mode.size + 1}")
+ end
+
+ def wrq_packet(file, mode)
+ [OP_WRQ, file, mode].pack("na#{file.size + 1}a#{mode.size + 1}")
+ end
+
+ def data_packet(block, data)
+ [OP_DATA, block, data].pack("nna*")
+ end
+
+ def ack_packet(block)
+ [OP_ACK, block].pack("nn")
+ end
+
+ def error_packet(code, message = nil)
+ message ||= ERROR_DESCRIPTION[code] || ""
+ [OP_ERROR, code, message].pack("nna#{message.size + 1}")
+ end
+
+ # Check if the packet is malformed (unknown opcode, too big, etc.),
+ # in which case it returns nil.
+ # If it is an error packet, raise an TFTPProtocol error.
+ # Returns scanned values otherwise.
+ def scan_packet(packet)
+ return nil if packet.size < MINSIZE || packet.size > MAXSIZE
+ opcode, block_err, rest = packet.unpack("nna*")
+ return nil if opcode.nil? || block_err.nil?
+ case opcode
+ when OP_RRQ, OP_WRQ
+ return nil
+ when OP_DATA
+ return [opcode, block_err, rest]
+ when OP_ACK
+ return [opcode, block_err]
+ when OP_ERROR
+ err_msg = "%s: %s"
+ err_msg %= [ERROR_DESCRIPTION[block_err] || "", rest.chomp("\000")]
+ raise TFTPProtocol.new(err_msg, block_err)
+ else
+ return nil
+ end
+ end
+ end
+end
More information about the Pkg-ruby-extras-commits
mailing list