ddns 0.1.0

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