fx-tftp 0.7 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +11 -4
- data/.travis.yml +4 -3
- data/README.md +24 -12
- data/bin/tftpd +26 -44
- data/lib/tftp/tftp.rb +12 -22
- data/lib/tftp/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2eb12234e670c55c0c634f86118f62d56ca172a7
|
4
|
+
data.tar.gz: 9e02a93b38f29149e36c3294c751c78581675f13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b2de98e63190ff5fca124b8a4e47fa90ac5a2247ad0e6d506a6457bee3cc0c1f646ed9a003d40fc8d437e2dec2cb14fe71c21e1ff1b883431d01f40c8f7fa29
|
7
|
+
data.tar.gz: b8654091eceed1f70276b2f090e5e228634ab22af969844f6820c58b1cde12e4a60c65e76fa8a22e97e074b647a16d7366c1b28eb8298806cec1c962bb485156
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
* [Homepage](https://github.com/drbig/fx-tftp)
|
4
4
|
* [Documentation](http://rubydoc.info/gems/fx-tftp/frames)
|
5
5
|
|
6
|
+
|
6
7
|
## Description
|
7
8
|
|
8
9
|
FX-TFTP is a slightly over-OO-ed pure-Ruby implementation of plain [RFC1350](https://www.ietf.org/rfc/rfc1350.txt) TFTP *server*. It is very flexible and intended for hacking. Also, and more importantly, it **works**, contrary to other gems that are occupying space at RubyGems.
|
@@ -11,6 +12,28 @@ That flexibility may be useful if you're planning on massive custom TFTP-based b
|
|
11
12
|
|
12
13
|
The included `tftpd` executable gives you a fully-fledged read-write TFTP server that also does logging, daemon mode and does not crap out on `SIGTERM`.
|
13
14
|
|
15
|
+
|
16
|
+
## Quickstart
|
17
|
+
|
18
|
+
Just in case you need an easy to use tftp server...
|
19
|
+
Install via Rubygems:
|
20
|
+
|
21
|
+
$ gem install fx-tftp
|
22
|
+
|
23
|
+
Then start the beast with any directory you want to serve (works for uploads and downloads as well):
|
24
|
+
|
25
|
+
$ sudo tftpd ~/router_firmwares_and_config_files/
|
26
|
+
|
27
|
+
Further options:
|
28
|
+
|
29
|
+
$ tftpd
|
30
|
+
Usage: tftpd [OPTIONS] DIR
|
31
|
+
-V, --version Show version and exit
|
32
|
+
-a, --address ADDRESS Address to listen on (default: 0.0.0.0)
|
33
|
+
-p, --port PORT Port to listen on (default: 69)
|
34
|
+
-v, --verbose Enable verbose output
|
35
|
+
|
36
|
+
|
14
37
|
## Hacking
|
15
38
|
|
16
39
|
Suppose we want to have a TFTP server that only supports reading, but the files served should depend on the IP block the client is connecting from:
|
@@ -28,18 +51,6 @@ Suppose we want to have a TFTP server that only supports reading, but the files
|
|
28
51
|
|
29
52
|
When you combine filename inspection and `#send` and `#recv` methods working on plain `IO` objects you can easily whip up things like serving dynamically built scripts/binaries/archives based on parameters passed as the requested 'filename'.
|
30
53
|
|
31
|
-
## Included executable
|
32
|
-
|
33
|
-
$ tftpd
|
34
|
-
Usage: tftpd [OPTIONS] PORT
|
35
|
-
-v, --version Show version and exit
|
36
|
-
-d, --debug Enable debug output
|
37
|
-
-l, --log PATH Log to file
|
38
|
-
-b, --background Fork into background
|
39
|
-
-m, --mode MODE Run in R/W only mode
|
40
|
-
-h, --host HOST Bind do host
|
41
|
-
-p, --path PATH Serving root directory
|
42
|
-
-o, --overwrite Overwrite existing files
|
43
54
|
|
44
55
|
## Contributing
|
45
56
|
|
@@ -50,6 +61,7 @@ Fell free to contributed patches using the common GitHub model (descried below).
|
|
50
61
|
- Write code and tests, check, commit
|
51
62
|
- Make a Pull Request
|
52
63
|
|
64
|
+
|
53
65
|
## Licensing
|
54
66
|
|
55
67
|
Standard two-clause BSD license, see LICENSE.txt for details.
|
data/bin/tftpd
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
#
|
3
2
|
|
4
3
|
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
5
4
|
|
@@ -7,59 +6,42 @@ require 'logger'
|
|
7
6
|
require 'optparse'
|
8
7
|
require 'tftp'
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
:no_read => false, :no_write => false, :overwrite => false}
|
13
|
-
|
14
|
-
def die!(msg)
|
15
|
-
STDERR.puts msg
|
16
|
-
exit(2)
|
17
|
-
end
|
9
|
+
# Defaults
|
10
|
+
config = { :port => 69, :address => '0.0.0.0', :loglevel => Logger::WARN }
|
18
11
|
|
19
12
|
op = OptionParser.new do |o|
|
20
|
-
o.banner = "Usage: #{$PROGRAM_NAME} [OPTIONS]
|
21
|
-
o.on
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
o.on
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
13
|
+
o.banner = "Usage: #{$PROGRAM_NAME} [OPTIONS] DIR"
|
14
|
+
o.on '-V', '--version', 'Show version and exit' do
|
15
|
+
puts "fx-tftpd v#{TFTP::VERSION} Copyright (c) 2015, Piotr S. Staszewski"
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
o.on '-a', '--address ADDRESS', String,
|
19
|
+
'Address to listen on (default: 0.0.0.0)' do |address|
|
20
|
+
config[:address] = address
|
21
|
+
end
|
22
|
+
o.on '-p', '--port PORT', Integer,
|
23
|
+
'Port to listen on (default: 69)' do |port|
|
24
|
+
abort 'Invalid port' if port < 1 || port > 65535
|
25
|
+
config[:port] = port
|
26
|
+
end
|
27
|
+
o.on '-v', '--verbose', 'Enable verbose output' do
|
28
|
+
config[:loglevel] = Logger::INFO
|
34
29
|
end
|
35
|
-
o.on('-h', '--host HOST', String, 'Bind do host') {|a| config[:host] = a }
|
36
|
-
o.on('-p', '--path PATH', String, 'Serving root directory') {|a| config[:path] = a }
|
37
|
-
o.on('-o', '--overwrite', 'Overwrite existing files') { config[:overwrite] = true }
|
38
|
-
end
|
39
|
-
op.parse! or die!(op)
|
40
|
-
die!(op) if config[:bad_mode]
|
41
|
-
|
42
|
-
if config[:ver]
|
43
|
-
puts "fx-tftpd v#{TFTP::VERSION} Copyright (c) 2015, Piotr S. Staszewski"
|
44
|
-
exit
|
45
30
|
end
|
46
31
|
|
47
|
-
|
32
|
+
config[:path] = op.parse!.first
|
33
|
+
abort 'Last argument must be a directory' unless File.directory? config[:path]
|
48
34
|
|
49
|
-
|
50
|
-
|
35
|
+
if config[:port] < 1024 && !Process.euid.zero?
|
36
|
+
abort 'Please run tftpd as root via sudo!'
|
37
|
+
end
|
51
38
|
|
52
|
-
log = Logger.new(
|
39
|
+
log = Logger.new(STDOUT)
|
53
40
|
log.level = config[:loglevel]
|
54
|
-
log.formatter = lambda
|
55
|
-
|
56
|
-
if config[:fork]
|
57
|
-
log.info 'Detaching from the console'
|
58
|
-
Process.daemon(true)
|
41
|
+
log.formatter = lambda do |s, d, p, m|
|
42
|
+
"#{d.strftime('%Y-%m-%d %H:%M:%S.%3N')} | #{s.ljust(5)} | #{m}\n"
|
59
43
|
end
|
60
|
-
|
61
44
|
config[:logger] = log
|
62
|
-
config[:port] = PORT
|
63
45
|
|
64
46
|
begin
|
65
47
|
log.info "Serving from and to #{config[:path]}"
|
data/lib/tftp/tftp.rb
CHANGED
@@ -255,16 +255,6 @@ module TFTP
|
|
255
255
|
return
|
256
256
|
end
|
257
257
|
log :info, "#{tag} Write request for #{req.filename} (#{req.mode})"
|
258
|
-
if File.exist? path
|
259
|
-
if @opts[:overwrite]
|
260
|
-
log :info, "#{tag} Overwrite existing file #{req.filename}"
|
261
|
-
else
|
262
|
-
log :warn, "#{tag} Refuse to overwrite existing file #{req.filename}"
|
263
|
-
sock.send(Packet::ERROR.new(6, 'File already exists.').encode, 0)
|
264
|
-
sock.close
|
265
|
-
return
|
266
|
-
end
|
267
|
-
end
|
268
258
|
mode = 'w'
|
269
259
|
mode += 'b' if req.mode == :octet
|
270
260
|
io = File.open(path, mode)
|
@@ -288,28 +278,28 @@ module TFTP
|
|
288
278
|
# test it for 10K or something.
|
289
279
|
#
|
290
280
|
# @attr handler [Handler] Session handler
|
291
|
-
# @attr
|
281
|
+
# @attr address [String] Address to listen to
|
292
282
|
# @attr port [Integer] Session dispatcher port
|
293
283
|
# @attr clients [Hash] Current sessions
|
294
284
|
class Base
|
295
|
-
attr_reader :handler, :
|
285
|
+
attr_reader :handler, :address, :port, :clients
|
296
286
|
|
297
287
|
# Initialize the server.
|
298
288
|
#
|
299
289
|
# Options:
|
300
290
|
#
|
301
|
-
# - :
|
302
|
-
# - :port
|
303
|
-
# - :logger
|
291
|
+
# - :address => address to listen to (default: '0.0.0.0')
|
292
|
+
# - :port => dispatcher port (default: 69)
|
293
|
+
# - :logger => logger instance
|
304
294
|
#
|
305
295
|
# @param handler [Handler] Initialized session handler
|
306
296
|
# @param opts [Hash] Options
|
307
297
|
def initialize(handler, opts = {})
|
308
298
|
@handler = handler
|
309
299
|
|
310
|
-
@
|
311
|
-
@port
|
312
|
-
@logger
|
300
|
+
@address = opts[:address] || '0.0.0.0'
|
301
|
+
@port = opts[:port] || 69
|
302
|
+
@logger = opts[:logger]
|
313
303
|
|
314
304
|
@clients = Hash.new
|
315
305
|
@run = false
|
@@ -319,9 +309,9 @@ module TFTP
|
|
319
309
|
#
|
320
310
|
# This is obviously blocking.
|
321
311
|
def run!
|
322
|
-
log :info, "UDP server loop at #{@
|
312
|
+
log :info, "UDP server loop at #{@address}:#{@port}"
|
323
313
|
@run = true
|
324
|
-
Socket.udp_server_loop(@
|
314
|
+
Socket.udp_server_loop(@address, @port) do |msg, src|
|
325
315
|
break unless @run
|
326
316
|
|
327
317
|
addr = src.remote_address
|
@@ -338,7 +328,7 @@ module TFTP
|
|
338
328
|
log :debug, "#{tag} -> PKT: #{pkt.inspect}"
|
339
329
|
tid = get_tid
|
340
330
|
tag = "[#{addr.ip_address}:#{addr.ip_port.to_s.ljust(5)}:#{tid.to_s.ljust(5)}]"
|
341
|
-
sock = addr.connect_from(@
|
331
|
+
sock = addr.connect_from(@address, tid)
|
342
332
|
@clients[tid] = tag
|
343
333
|
|
344
334
|
unless pkt.is_a?(Packet::RRQ) || pkt.is_a?(Packet::WRQ)
|
@@ -363,7 +353,7 @@ module TFTP
|
|
363
353
|
def stop
|
364
354
|
log :info, 'Stopping UDP server loop'
|
365
355
|
@run = false
|
366
|
-
UDPSocket.new.send('break', 0, @
|
356
|
+
UDPSocket.new.send('break', 0, @address, @port)
|
367
357
|
end
|
368
358
|
|
369
359
|
private
|
data/lib/tftp/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fx-tftp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0
|
4
|
+
version: '1.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr S. Staszewski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubygems-tasks
|