ddnsupdate 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 (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: []