ddns 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|