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.
- data/HISTORY +5 -0
- data/LICENSE +13 -0
- data/README.md +1 -0
- data/bin/ddnsupdate +86 -0
- data/lib/ddnsupdate.rb +76 -0
- metadata +50 -0
data/HISTORY
ADDED
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: []
|