· 4 years ago · Aug 27, 2021, 06:14 AM
1#!/usr/bin/env ruby
2
3require 'sinatra'
4require 'logger'
5require 'json'
6require 'resolv'
7
8# curl -w "%{http_code}" -X POST -H 'Content-Type: application/json' -H 'X-Api-Key: secretkeyabc' -d '{ "domain": "aaa.ingipro.ml", "data": "1.1.1.16", "type":"mx" }' https://dns-api.ingipro.ru/dns
9# curl -w "%{http_code}" -X DELETE -H 'Content-Type: application/json' -H 'X-Api-Key: secretkeyabc' -d '{ "domain": "aaa.ingipro.ml", "data": "1.1.1.16", "type":"mx" }' https://dns-api.ingipro.ru/dns
10
11
12# Response codes:
13# 200 - Record succesfully removed or not exists
14# 201 - Record succesfully created
15# 202 - Record already exists
16# 400 - Wrong record type specified
17# 401 - Invalid secret key
18# 409 - DNS check error
19# 500 - Server Error. Record not created/deleted
20
21# IP address for listen sinatra port 4567
22#set :bind, '10.0.1.4'
23set :bind, '0.0.0.0'
24set :dump_errors, true
25set :logger, Logger.new(STDOUT)
26
27# DNS Server
28dns_server = '127.0.0.1'
29
30# default TTL
31default_ttl = '300'
32# default record type (A/AAAA/MX only)
33default_type = 'A'
34# default mx priority
35default_mx_priority = '10'
36
37# Authenticate all requests with an API key
38before do
39 # X-Api-Key
40 error 401 unless env['HTTP_X_API_KEY'] == ENV["DNS_API_KEY"]
41 #error 401 unless env['HTTP_X_API_KEY'] == "secretkey"
42end
43
44# Add DNS record
45post '/dns' do
46 # renew variables
47 ttl = default_ttl
48 type = default_type
49 mx_priority = default_mx_priority
50
51 request_params = JSON.parse(request.body.read)
52 ttl = if request_params["ttl"].nil? then ttl else request_params["ttl"] end
53 type = if request_params["type"].nil? then type else request_params["type"] end
54 mx_priority = if request_params["mx_priority"].nil? then mx_priority else request_params["mx_priority"] end
55
56 # Check local DNS server for record already exist
57 res = Resolv::DNS.new(:nameserver => dns_server)
58 case type.upcase
59 when 'A', 'AAAA'
60 ret = res.getaddresses request_params['domain']
61 ret = ret.join(",")
62 p ret
63 if ret.include? request_params['data'] then error 202 end
64 when 'MX'
65 ret = res.getresources request_params['domain'], Resolv::DNS::Resource::IN::MX
66 ret = ret.map { |r| [r.exchange.to_s, r.preference] }
67 ret = ret.join(",")
68 p ret
69 if ret.include? request_params['data'] then error 202 end
70 when 'TXT'
71 ret = res.getresources request_params['domain'], Resolv::DNS::Resource::IN::TXT
72 ret = ret.map { |r| [r.strings.to_s] }
73 ret = ret.join(",")
74 p ret
75 if ret.include? request_params['data'] then error 202 end
76 end
77
78 # Select supported record
79 case type.upcase
80 when 'A', 'AAAA'
81 nsupdate_command = "update add #{request_params["domain"]} #{ttl} #{type} #{request_params["data"]}"
82 when 'MX'
83 nsupdate_command = "update add #{request_params["domain"]} #{ttl} #{type} #{mx_priority} #{request_params["data"]}"
84 when 'TXT'
85 nsupdate_command = "update add #{request_params["domain"]} #{ttl} #{type} #{request_params["data"]}"
86 else
87 error 400
88 end
89 # Call nsupdate to write record via local connection
90 IO.popen("nsupdate -l",'r+') do |f|
91 f << <<-EOF
92 #{nsupdate_command}
93 send
94 EOF
95 f.close_write
96 end
97 error 500 unless $? == 0
98 # Check local DNS server for record exist
99 res = Resolv::DNS.new(:nameserver => dns_server)
100 case type.upcase
101 when 'A', 'AAAA'
102 ret = res.getaddresses request_params['domain']
103 ret = ret.join(",")
104 p ret
105 if ret.include? request_params['data'] then status 201 else error 409 end
106 when 'MX'
107 ret = res.getresources request_params['domain'], Resolv::DNS::Resource::IN::MX
108 ret = ret.map { |r| [r.exchange.to_s, r.preference] }
109 ret = ret.join(",")
110 p ret
111 if ret.include? request_params['data'] then status 201 else error 409 end
112 when 'TXT'
113 ret = res.getresources request_params['domain'], Resolv::DNS::Resource::IN::TXT
114 #pp ret
115 ret = ret.map { |r| [r.strings.to_s] }
116 ret = ret.join(",")
117 p ret
118 if ret.include? request_params['data'] then status 201 else error 409 end
119 else
120 error 400
121 end
122end
123
124# Remove DNS record
125delete '/dns' do
126 # renew variables
127 type = default_type
128 mx_priority = default_mx_priority
129
130 request_params = JSON.parse(request.body.read)
131 type = if request_params["type"].nil? then type else request_params["type"] end
132 mx_priority = if request_params["mx_priority"].nil? then mx_priority else request_params["mx_priority"] end
133 case type.upcase
134 when 'A', 'AAAA'
135 nsupdate_command = "update delete #{request_params["domain"]} #{type} #{request_params["data"]}"
136 when 'MX'
137 nsupdate_command = "update delete #{request_params["domain"]} #{type} #{mx_priority} #{request_params["data"]}"
138 when 'TXT'
139 nsupdate_command = "update delete #{request_params["domain"]} #{type} #{request_params["data"]}"
140 else
141 error 400
142 end
143 # Call nsupdate to remove record via local connection
144 IO.popen("nsupdate -l",'r+') do |f|
145 f << <<-EOF
146 #{nsupdate_command}
147 send
148 EOF
149 f.close_write
150 end
151 error 500 unless $? == 0
152 # Check local DNS server for record not exist
153 res = Resolv::DNS.new(:nameserver => dns_server)
154 case type.upcase
155 when 'A', 'AAAA'
156 ret = res.getaddresses request_params['domain']
157 ret = ret.join(",")
158 p ret
159 if ret.include? request_params['data'] then status 409 else error 200 end
160 when 'MX'
161 ret = res.getresources request_params['domain'], Resolv::DNS::Resource::IN::MX
162 ret = ret.map { |r| [r.exchange.to_s, r.preference] }
163 ret = ret.join(",")
164 p ret
165 if ret.include? request_params['data'] then status 409 else error 200 end
166 when 'TXT'
167 ret = res.getresources request_params['domain'], Resolv::DNS::Resource::IN::TXT
168 ret = ret.map { |r| [r.strings.to_s] }
169 ret = ret.join(",")
170 p ret
171 if ret.include? request_params['data'] then status 409 else error 200 end
172 else
173 error 400
174 end
175end