-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathiwantmyname-hook.rb
executable file
·132 lines (102 loc) · 3.32 KB
/
iwantmyname-hook.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env ruby
load File.dirname(__FILE__) + '/iwantmyname-secrets.rb'
require 'mechanize'
require 'date'
require 'pp'
require 'resolv'
require 'json'
DEBUG = false
hook_stage = ARGV[0]
domain = ARGV[1]
txt_challenge = ARGV[3]
exit unless %[deploy_challenge clean_challenge].include?(hook_stage)
hostname = ""
agent = Mechanize.new
agent.get('https://iwantmyname.com/') do |page|
page = page.form_with(:action => '/signin') do |form|
form.username = @username
form.password = @password
end.click_button
page = agent.click(page.link_with(:text => /Domains/))
domains = page.links_with(:href => /^\/dashboard\/domains\/edit\//).map(&:text)
while ! domains.include?(domain)
puts "Splitting #{domain}"
start, domain = domain.split(/\./, 2)
hostname += "#{"." unless hostname.empty?}#{start}"
exit if domain.empty?
end
puts "Top domain is #{domain}"
hostname = "_acme-challenge#{"." unless hostname.empty?}#{hostname}"
page = agent.get("/dashboard/dns/#{domain}")
# Required otherwise add will just remove everything but this!
rrset_response = agent.get("/dashboard/dns/list/#{domain}/#{DateTime.now.strftime("%Q")}")
rrset = JSON.parse(rrset_response.body)["rr"]
pp rrset if DEBUG
id = rrset.index { |rr| rr["type"] == "TXT" && rr["name"] == hostname }
csrf_token = page.at('meta').attributes['content'].value
headers = {
"X-CSRF-Token" => csrf_token,
}
if hook_stage == "deploy_challenge"
if id
old_value = rrset[id]["value"]
data = {
"id" => id
}
puts "Removing TXT record for #{hostname}, was \"#{old_value}\""
response = agent.post("/dashboard/dns/delete/#{domain}", data, headers)
end
data = {
"name" => hostname,
"type" => "TXT",
"value" => txt_challenge,
"prio" => "",
"ttl" => 3600,
}
puts "Adding TXT record for #{hostname}, value of \"#{txt_challenge}\""
response = agent.post("/dashboard/dns/add/#{domain}", data, headers)
elsif hook_stage == "clean_challenge" && id
old_value = rrset[id]["value"]
data = {
"id" => id
}
puts "Removing TXT record for #{hostname}, was \"#{old_value}\""
response = agent.post("/dashboard/dns/delete/#{domain}", data, headers)
end
# Commit the data
data = {
"csrf_token" => csrf_token,
"commit" => "",
}
puts "Committing changes"
response = agent.post("/dashboard/dns/commit/#{domain}", data)
end
if hook_stage == "deploy_challenge"
dns = Resolv::DNS.new
nameservers = [
dns.getaddress('ns1.iwantmyname.net').to_s,
dns.getaddress('ns2.iwantmyname.net').to_s,
dns.getaddress('ns3.iwantmyname.net').to_s,
dns.getaddress('ns4.iwantmyname.net').to_s,
]
resolved = false
until resolved
dns = Resolv::DNS.new({:nameserver => [nameservers.sample], :search => '', :ndots => 1})
pp dns if DEBUG
puts "Trying to resolve #{hostname}.#{domain}"
dns.each_resource("#{hostname}.#{domain}", Resolv::DNS::Resource::IN::TXT) do |resp|
if resp.strings[0] == txt_challenge
puts "Found #{resp.strings[0]}. match."
resolved = true
else
puts "Found #{resp.strings[0]}. no match."
end
end
if !resolved
puts "Didn't find a match for #{txt_challenge}"
puts "Waiting to retry"
15.times { sleep 1; putc "."; }
puts
end
end
end