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.
Files changed (5) hide show
  1. data/README +40 -0
  2. data/bin/ddns +70 -0
  3. data/bin/ddnsctl +73 -0
  4. data/lib/ddns-server.rb +103 -0
  5. 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
@@ -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
+