ddnsupdate 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|