ddns 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +40 -0
- data/bin/ddns +70 -0
- data/bin/ddnsctl +73 -0
- data/lib/ddns-server.rb +103 -0
- metadata +102 -0
data/README
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
= ddns
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
ddns is a distributed name server.
|
6
|
+
|
7
|
+
== Install
|
8
|
+
|
9
|
+
gem install ddns
|
10
|
+
|
11
|
+
== Example
|
12
|
+
|
13
|
+
ip-10-0-0-1> ddns -H node-01 -P 53
|
14
|
+
I, [2011-05-24T00:10:00.870732 #3389] INFO -- : Name service at 0.0.0.0:53
|
15
|
+
I, [2011-05-24T00:10:00.870914 #3389] INFO -- : Gossip service at 10.0.0.1:10870
|
16
|
+
I, [2011-05-24T00:10:00.870999 #3389] INFO -- : Hostname: node-01
|
17
|
+
I, [2011-05-24T00:10:00.873100 #3389] INFO -- : Starting server...
|
18
|
+
|
19
|
+
ip-10-0-0-1> ddnsctl -L
|
20
|
+
IP Address Timestamp Hostname
|
21
|
+
--------------- -------------------------- ---------------------------
|
22
|
+
10.0.0.1 2011/05/24 00:10:00.517688 node-01
|
23
|
+
|
24
|
+
ip-10-0-0-2> ddns -H node-02 -P 53
|
25
|
+
I, [2011-05-24T00:11:00.870732 #3389] INFO -- : Name service at 0.0.0.0:53
|
26
|
+
I, [2011-05-24T00:11:00.870914 #3389] INFO -- : Gossip service at 10.0.0.2:10870
|
27
|
+
I, [2011-05-24T00:11:00.870999 #3389] INFO -- : Hostname: node-02
|
28
|
+
I, [2011-05-24T00:11:00.873100 #3389] INFO -- : Starting server...
|
29
|
+
|
30
|
+
ip-10-0-0-1> ddnsctl -L
|
31
|
+
IP Address Timestamp Hostname
|
32
|
+
--------------- -------------------------- ---------------------------
|
33
|
+
10.0.0.1 2011/05/24 00:10:00.517688 node-01
|
34
|
+
10.0.0.2 2011/05/24 00:11:00.998678 node-02
|
35
|
+
|
36
|
+
ip-10-0-0-2> ddnsctl -L
|
37
|
+
IP Address Timestamp Hostname
|
38
|
+
--------------- -------------------------- ---------------------------
|
39
|
+
10.0.0.2 2011/05/24 00:11:00.998678 node-02
|
40
|
+
10.0.0.1 2011/05/24 00:10:00.517688 node-01
|
data/bin/ddns
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'drb/drb'
|
5
|
+
require 'ddns-server'
|
6
|
+
require 'optparse'
|
7
|
+
require 'socket'
|
8
|
+
|
9
|
+
LOGGER = Logger.new($stderr)
|
10
|
+
|
11
|
+
options = {
|
12
|
+
:hostname => Socket.gethostname,
|
13
|
+
:address => '0.0.0.0',
|
14
|
+
:port => 10053,
|
15
|
+
:gaddress => IPSocket.getaddress(Socket.gethostname),
|
16
|
+
:gport => 10870,
|
17
|
+
:sock => '/tmp/ddns.sock',
|
18
|
+
:daemon => false,
|
19
|
+
:logger => LOGGER,
|
20
|
+
:loglevel => Logger::INFO
|
21
|
+
}
|
22
|
+
|
23
|
+
opts = OptionParser.new
|
24
|
+
opts.on('-H', '--hostname HOST', "Own hostname (default #{options[:hostname]})") {|v| options[:hostname] = v }
|
25
|
+
opts.on('-A', '--address ADDRESS' "Name service ip address (default #{options[:address]})") {|v| options[:address] = v }
|
26
|
+
opts.on('-P', '--port PORT', Integer, "Name service port (default #{options[:port]})") {|v| options[:port] = v }
|
27
|
+
opts.on('-a', '--gaddress ADDRESS' "Gossip service ip address (default #{options[:gaddress]})") {|v| options[:gaddress] = v }
|
28
|
+
opts.on('-p', '--gport PORT', Integer, "Gossip service port (default #{options[:gport]})") {|v| options[:gport] = v }
|
29
|
+
opts.on('-S', '--socket SOCK', "Socket file path (default #{options[:sock]})") {|v| options[:sock] = v }
|
30
|
+
opts.on('-d', '--daemon COMMAND', "Directive of daemonize (start|stop|restart|status)") {|v| options[:daemon] = v }
|
31
|
+
|
32
|
+
opts.on('-L', '--loglevel LEVEL',
|
33
|
+
[:fatal, :error, :warn, :info, :debug], "Log level (default info)") {|v| options[:loglevel] = Logger.const_get(v.to_s.upcase) }
|
34
|
+
|
35
|
+
opts.on('-i', '--initial NODES', "Initial access node list (ex 10.0.0.1,10.0.0.2)") do |v|
|
36
|
+
options[:initial_nodes] = v.split(/\s*,\s*/)
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on('-h', '--help', "Display this help") { $stdout.puts opts.help ; exit 1 }
|
40
|
+
|
41
|
+
begin
|
42
|
+
opts.parse!(ARGV)
|
43
|
+
rescue => e
|
44
|
+
$stderr.puts e.message
|
45
|
+
$stdout.puts opts.help
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
#RGossip.debug = true
|
50
|
+
RGossip.port = options[:gport]
|
51
|
+
DDNS::Attributes.new(options)
|
52
|
+
|
53
|
+
LOGGER.info("Name service at #{options[:address]}:#{options[:port]}")
|
54
|
+
LOGGER.info("Gossip service at #{options[:gaddress]}:#{options[:gport]}")
|
55
|
+
LOGGER.info("Hostname: #{options[:hostname]}")
|
56
|
+
|
57
|
+
if options[:daemon]
|
58
|
+
ARGV.unshift options[:daemon]
|
59
|
+
DDNS.daemonize
|
60
|
+
else
|
61
|
+
begin
|
62
|
+
at_exit { FileUtils.rm_f(options[:sock]) }
|
63
|
+
$DDNS_GOSSIP = DRb.start_service("drbunix:#{options[:sock]}", $DDNS_ATTR.gossip)
|
64
|
+
rescue => e
|
65
|
+
$stderr.puts(e.message)
|
66
|
+
end
|
67
|
+
|
68
|
+
DDNS.run
|
69
|
+
end
|
70
|
+
|
data/bin/ddnsctl
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'drb/drb'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
def list
|
6
|
+
puts <<-EOS
|
7
|
+
IP Address Timestamp Hostname
|
8
|
+
--------------- -------------------------- ---------------------------
|
9
|
+
EOS
|
10
|
+
|
11
|
+
$gossip.to_a.each do |address, timestamp, data|
|
12
|
+
t = Time.at("#{timestamp.slice 0, 10}.#{timestamp.slice 10..-1}".to_f)
|
13
|
+
puts('%-15s %10s.%-6d %-17s' % [address, t.strftime('%Y/%m/%d %H:%M:%S'), t.usec, data.to_s.slice(0, 17)])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def add(address)
|
18
|
+
puts "Add node: #{address}"
|
19
|
+
$gossip.add_node(address)
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete(address)
|
23
|
+
puts "Delete node: #{address}"
|
24
|
+
$gossip.delete_node(address)
|
25
|
+
end
|
26
|
+
|
27
|
+
def clear
|
28
|
+
puts "Clear dead list"
|
29
|
+
$gossip.clear_dead_list
|
30
|
+
end
|
31
|
+
|
32
|
+
options = {
|
33
|
+
:sock => '/tmp/ddns.sock',
|
34
|
+
}
|
35
|
+
|
36
|
+
opts = OptionParser.new
|
37
|
+
opts.on('-L', "List nodes") {|v| options[:command] = :list }
|
38
|
+
opts.on('-A ADDREDD', "Add node") {|v| options[:command] = :add ; options[:arg] = v }
|
39
|
+
opts.on('-D ADDREDD', "Delete node") {|v| options[:command] = :delete ; options[:arg] = v }
|
40
|
+
opts.on('-C', "Clear dead list") {|v| options[:command] = :clear }
|
41
|
+
opts.on('-S', '--socket SOCK', "Socket file path (default #{options[:sock]})") {|v| options[:sock] = v }
|
42
|
+
opts.on('-h', '--help', "Display this help") { $stdout.puts opts.help ; exit 1 }
|
43
|
+
|
44
|
+
if ARGV.empty?
|
45
|
+
$stdout.puts opts.help
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
raise "invalid arguments: #{ARGV.join ' '}" if 1 < ARGV.select {|i| %w(-L -A -D -C).include?(i) }.length
|
51
|
+
opts.parse!(ARGV)
|
52
|
+
rescue => e
|
53
|
+
$stderr.puts e.message
|
54
|
+
$stdout.puts opts.help
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
unless File.exist?(options[:sock])
|
59
|
+
$stderr.puts "cannot find socket file: #{options[:sock]}"
|
60
|
+
exit 1
|
61
|
+
end
|
62
|
+
|
63
|
+
$gossip = DRbObject.new_with_uri("drbunix:#{options[:sock]}")
|
64
|
+
|
65
|
+
case options[:command]
|
66
|
+
when :list then list
|
67
|
+
when :add then add(options[:arg])
|
68
|
+
when :delete then delete(options[:arg])
|
69
|
+
when :clear then clear
|
70
|
+
else
|
71
|
+
$stdout.puts opts.help
|
72
|
+
exit 1
|
73
|
+
end
|
data/lib/ddns-server.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'drb/drb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'logger'
|
4
|
+
require 'rexec'
|
5
|
+
require 'rexec/daemon'
|
6
|
+
require 'rgossip'
|
7
|
+
require 'rubydns'
|
8
|
+
require 'socket'
|
9
|
+
|
10
|
+
class DDNS < RExec::Daemon::Base
|
11
|
+
class Attributes
|
12
|
+
attr_reader :hostname
|
13
|
+
attr_reader :address
|
14
|
+
attr_reader :port
|
15
|
+
attr_reader :gaddress
|
16
|
+
attr_reader :sock
|
17
|
+
attr_reader :daemon
|
18
|
+
attr_reader :resolver
|
19
|
+
attr_reader :logger
|
20
|
+
attr_reader :loglevel
|
21
|
+
attr_reader :gossip
|
22
|
+
|
23
|
+
def initialize(options = {})
|
24
|
+
@hostname = options[:hostname] || Socket.gethostname
|
25
|
+
@address = options[:address] || ('0.0.0.0')
|
26
|
+
@port = options[:port] || 10053
|
27
|
+
@gaddress = options[:gaddress] || IPSocket.getaddress(Socket.gethostname)
|
28
|
+
@sock = options[:sock]
|
29
|
+
@daemon = options[:daemon]
|
30
|
+
@resolver = options[:resolver] || Resolv::DNS.new
|
31
|
+
@logger = options[:logger] || Logger.new($stderr)
|
32
|
+
@loglevel = options[:loglevel] || Logger::INFO
|
33
|
+
@logger.level = @loglevel
|
34
|
+
|
35
|
+
@gossip = RGossip::Client.new(
|
36
|
+
(options[:initial_nodes] || []), @gaddress, @hostname)
|
37
|
+
|
38
|
+
@gossip.callback = lambda do |action, address, timestamp, data|
|
39
|
+
case action
|
40
|
+
when :add
|
41
|
+
logger.info "Add node: #{address}(#{data})"
|
42
|
+
when :comeback
|
43
|
+
logger.info "Come back node: #{address}(#{data})"
|
44
|
+
when :delete
|
45
|
+
logger.info "Delete node: #{address}(#{data})"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
$DDNS_ATTR = self
|
50
|
+
end
|
51
|
+
|
52
|
+
def lookup_a_record(name)
|
53
|
+
@gossip.any? do |address, timestamp, data|
|
54
|
+
data == name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def lookup_ptr_record(name)
|
59
|
+
name = name.sub(/\.in-addr\.arpa\Z/, '').split('.').reverse.join('.')
|
60
|
+
|
61
|
+
@gossip.any? do |address, timestamp, data|
|
62
|
+
address == name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end # Attributes
|
66
|
+
|
67
|
+
def self.run
|
68
|
+
$DDNS_ATTR.gossip.start
|
69
|
+
|
70
|
+
RubyDNS.run_server(:listen => [[:udp, $DDNS_ATTR.address, $DDNS_ATTR.port]]) do
|
71
|
+
on(:start) do
|
72
|
+
logger.level = $DDNS_ATTR.loglevel
|
73
|
+
|
74
|
+
if $DDNS_ATTR.daemon and $DDNS_ATTR.sock
|
75
|
+
$DDNS_GOSSIP = DRb.start_service("drbunix:#{$DDNS_ATTR.sock}", $DDNS_ATTR.gossip)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
match($DDNS_ATTR.method(:lookup_a_record).to_proc, :A) do |transaction|
|
80
|
+
address, timestamp, data = $DDNS_ATTR.gossip.select {|a, t, d| d == transaction.name }.sort_by{ rand }.first
|
81
|
+
transaction.respond!(address)
|
82
|
+
end
|
83
|
+
|
84
|
+
match($DDNS_ATTR.method(:lookup_ptr_record).to_proc, :PTR) do |transaction|
|
85
|
+
name = transaction.name.sub(/\.in-addr\.arpa\Z/, '').split('.').reverse.join('.')
|
86
|
+
address, timestamp, data = $DDNS_ATTR.gossip.find {|a, t, d| a == name }
|
87
|
+
transaction.respond!(Resolv::DNS::Name.create("#{data}."))
|
88
|
+
end
|
89
|
+
|
90
|
+
otherwise do |transaction|
|
91
|
+
transaction.passthrough!($DDNS_ATTR.resolver)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.shutdown
|
97
|
+
$DDNS_ATTR.gossip.stop
|
98
|
+
|
99
|
+
if $DDNS_ATTR.daemon and $DDNS_ATTR.sock
|
100
|
+
FileUtils.rm_f($DDNS_ATTR.sock)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end # DDNS
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ddns
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- winebarrel
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-05-24 00:00:00 +09:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rubydns
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 17
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 2
|
33
|
+
- 3
|
34
|
+
version: 0.2.3
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rgossip
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 11
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 1
|
49
|
+
- 8
|
50
|
+
version: 0.1.8
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description:
|
54
|
+
email: sgwr_dts@yahoo.co.jp
|
55
|
+
executables:
|
56
|
+
- ddns
|
57
|
+
- ddnsctl
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
files:
|
63
|
+
- README
|
64
|
+
- bin/ddns
|
65
|
+
- bin/ddnsctl
|
66
|
+
- lib/ddns-server.rb
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: https://bitbucket.org/winebarrel/ddns
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
hash: 3
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
requirements: []
|
95
|
+
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.4.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: Distributed name server
|
101
|
+
test_files: []
|
102
|
+
|