ddnsupdate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/HISTORY +5 -0
  2. data/LICENSE +13 -0
  3. data/README.md +1 -0
  4. data/bin/ddnsupdate +86 -0
  5. data/lib/ddnsupdate.rb +76 -0
  6. metadata +50 -0
data/HISTORY ADDED
@@ -0,0 +1,5 @@
1
+ DDNS Update History
2
+ ===================
3
+
4
+ Version 0.1.0 (2011-09-08)
5
+ * First release.
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ Comming soon
data/bin/ddnsupdate ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $0 = 'ddnsupdate' # hide arguments from ps and top
4
+
5
+ require 'rubygems'
6
+ require 'trollop'
7
+
8
+ require 'ddnsupdate'
9
+
10
+ module DDNSUpdate
11
+
12
+ SUB_COMMANDS = %w(gen up ip)
13
+ Trollop::options do
14
+ banner <<-EOS
15
+ ddnsupdate update your dynamic dns zone with your current local or remote IP
16
+
17
+ Usage:
18
+ ddnsupdate <command> [options]
19
+ ddnsupdate gen -p <pass>
20
+ ddnsupdate up -h <host> -k <key> [options]
21
+ ddnsupdate ip [options]
22
+ EOS
23
+ stop_on SUB_COMMANDS
24
+ end
25
+ cmd = ARGV.shift
26
+ case cmd
27
+ when "up"
28
+ cmd_opts = Trollop::options do
29
+ banner <<-EOS
30
+ ddnsupdate update your dynamic dns zone with your current local or remote IP
31
+
32
+ ddnsupdate up -h <host> -k <key> [-r] [-w]
33
+ EOS
34
+ opt :key, "DNS Key", :short => "-k", :type => :string
35
+ opt :host, "Hostname to update", :short => "-h", :type => :string
36
+ opt :remote, "Use remote IP (default: local ip)", :short => "-r"
37
+ opt :wild, "Add a wildcard on the same IP (i.e. *.host IN A ip)", :short => "-w"
38
+ end
39
+ Trollop::die "missing host" if cmd_opts[:host].nil?
40
+ Trollop::die "missing key" if cmd_opts[:key].nil?
41
+
42
+ ip = (cmd_opts[:remote] && determine_remote_ip) || determine_local_ip
43
+ update(cmd_opts[:host], ip, cmd_opts[:key], cmd_opts[:wild])
44
+ puts "Host updated with ip #{ip}"
45
+ when "gen"
46
+ cmd_opts = Trollop::options do
47
+ banner <<-EOS
48
+ ddnsupdate update your dynamic dns zone with your current local or remote IP
49
+
50
+ ddnsupdate gen -p <pass> [-b host]
51
+ EOS
52
+ opt :pass, "Password to hash", :short => "-p", :type => :string
53
+ opt :bind, "Output as a bind key definition", :short => "-b", :type => :string
54
+ end
55
+ Trollop::die "missing password" if cmd_opts[:pass].nil?
56
+ key = keygen(cmd_opts[:pass]).strip
57
+ if cmd_opts[:bind].nil?
58
+ puts "Key: #{key}"
59
+ else
60
+ bind = <<-EOS
61
+ key "#{cmd_opts[:bind]}." {
62
+ algorithm hmac-md5;
63
+ secret "#{key}";
64
+ };
65
+ EOS
66
+ puts bind
67
+ end
68
+ when "ip"
69
+ cmd_opts = Trollop::options do
70
+ banner <<-EOS
71
+ ddnsupdate update your dynamic dns zone with your current local or remote IP
72
+
73
+ ddnsupdate ip [-r]
74
+ EOS
75
+ opt :remote, "Display remote ip instead of local", :short => "-r"
76
+ end
77
+ if cmd_opts[:remote]
78
+ puts determine_remote_ip
79
+ else
80
+ puts determine_local_ip
81
+ end
82
+ else
83
+ cmd_opts = Trollop::die "unknown subcommand #{cmd.inspect}"
84
+ end
85
+
86
+ end
data/lib/ddnsupdate.rb ADDED
@@ -0,0 +1,76 @@
1
+ module DDNSUpdate
2
+ require 'open3'
3
+ require 'digest/md5'
4
+ require 'base64'
5
+ require 'socket'
6
+ require 'net/http'
7
+ require 'uri'
8
+
9
+ class UpdateError < StandardError; end
10
+
11
+ def self.keygen(input)
12
+ Base64.encode64(Digest::MD5.hexdigest(input.downcase))
13
+ end
14
+
15
+ def self.determine_soa(zone)
16
+ soa = %x{dig -t SOA #{zone.gsub(/\.$/, "")} +noquestion +nostats +nocmd +noqr +nocomments +noadditional +nottlid}
17
+ #Split lines into an array, filtering out comments and blanks
18
+ soa = soa.split("\n").delete_if { |el| el.start_with?(";") || el.empty? }
19
+ #Split remaining line into whitespace delimited fields
20
+ soa = soa[0].split(/\s/)
21
+ #Find the field we actually want, stripping the trailing dot
22
+ soa[soa.index("SOA") + 1].gsub(/\.$/, "")
23
+ end
24
+
25
+ def self.determine_current_ip(zone, soa=nil)
26
+ soa = determine_soa(zone) unless !soa.nil?
27
+ %x{dig @#{soa} #{zone} A +short }
28
+ end
29
+
30
+ def self.determine_local_ip
31
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
32
+
33
+ UDPSocket.open do |s|
34
+ s.connect '64.233.187.99', 1
35
+ s.addr.last
36
+ end
37
+ ensure
38
+ Socket.do_not_reverse_lookup = orig
39
+ end
40
+
41
+ def self.determine_remote_ip
42
+ Net::HTTP.get(URI.parse('http://www.whatismyip.org/'))
43
+ end
44
+
45
+ def self.update(zone, ip, key, wild = false)
46
+ soa = determine_soa(zone)
47
+ curip = determine_current_ip(zone, soa)
48
+
49
+ if curip != ip
50
+ delete_seq = <<-";"
51
+ server #{soa}
52
+ key #{zone}. #{key}
53
+ prereq yxdomain #{zone}.
54
+ update delete #{zone}. A
55
+ ;
56
+ delete_seq += " update delete *.#{zone}. A\n" if wild
57
+ delete_seq += " send\n"
58
+
59
+ create_seq = <<-";"
60
+ server #{soa}
61
+ key #{zone}. #{key}
62
+ prereq nxdomain #{zone}.
63
+ update add #{zone}. 300 A #{ip}
64
+ ;
65
+ create_seq += " update add *.#{zone}. 300 A #{ip}\n" if wild
66
+ create_seq += " send\n"
67
+
68
+ Open3.popen3("nsupdate") do |stdin, stdout, stderr|
69
+ stdin << delete_seq << create_seq
70
+ stdin.close_write
71
+ err = stderr.read
72
+ raise UpdateError, err unless err.empty?
73
+ end
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ddnsupdate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Hugues Lismonde
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-08 00:00:00.000000000Z
13
+ dependencies: []
14
+ description:
15
+ email: hugues@epic.net
16
+ executables:
17
+ - ddnsupdate
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - HISTORY
22
+ - LICENSE
23
+ - README.md
24
+ - bin/ddnsupdate
25
+ - lib/ddnsupdate.rb
26
+ homepage: https://github.com/epicagency/ddnsupdate/
27
+ licenses: []
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.8.6
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 1.8.10
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: Performs dynamic dns updates
50
+ test_files: []