require "net/https" require "optparse" require "json" require "uri" options = {:ttl => "3600", :records => {}} req_headers = {"Content-Type" => "application/json"} OptionParser.new do |opts| opts.banner = "Usage: ddns.rb -a -p -f file/path -t 3600 -r example.com" opts.on("-f", "--token TOKEN_PATH", "Absolute path of file containing a Cloudflare API token.") do |path| if File.file?(path) then if File.readable?(path) then cf_token = File.read(path) cf_token = cf_token.gsub("\s", "") req_headers["Authorization"] = "Bearer " + cf_token else warn "Token file not readable." exit 11 end else warn "Failed to find token file." exit 10 end end opts.on("-t", "--ttl TTL", "TTL") do |ttl| options[:ttl] = ttl end opts.on("-a", "--aaaa", "Update AAAA records as well as A records.") do |aaaa| options[:update_aaaa] = aaaa end opts.on("-p", "--proxy", "Enable proxy on updated DNS records.") do |proxy| options[:enable_proxy] = proxy end opts.on("-r", "--record RECORD", "Add record name to update.") do |record| options[:records][record] = true end end.parse! print "Fetching external IP... " ip_res, external_ip = Net::HTTP.get_response("checkip.amazonaws.com", "/") if ip_res.code == "200" then external_ip = ip_res.body.tr("\n", "") puts "#{external_ip}." else puts "Failed." puts "#{ip_res.code}: #{ip_res.body}" exit 20 end puts "Updating DDNS records." zone_uri = URI.parse("https://api.cloudflare.com/client/v4/zones") https = Net::HTTP.new(zone_uri.host, zone_uri.port) https.use_ssl = true zone_req = Net::HTTP::Get.new(zone_uri, req_headers) zone_res = JSON.parse(https.request(zone_req).body) if zone_res["success"] == true then puts "Found #{zone_res["result"].count()} zone(s) to update." zone_res["result"].each do |zone| puts "Fetching records from zone '#{zone["name"]}'." zone_id = zone["id"] record_uri = URI.parse("https://api.cloudflare.com/client/v4/zones/#{zone_id}/dns_records") https = Net::HTTP.new(record_uri.host, record_uri.port) https.use_ssl = true record_req = Net::HTTP::Get.new(record_uri, req_headers) record_res = JSON.parse(https.request(record_req).body) if record_res["success"] == true then record_res["result"].each do |record| record_name = record["name"] record_type = record["type"] if options[:records][record_name] && (record_type == "A" or (options[:update_aaaa] && record_type == "AAAA")) then print "Updating record '#{record_name}'... " if record["content"] == external_ip then puts "Content doesn't need updating, skipping record." else update = { "type" => record_type, "name" => record_name, "content" => external_ip, "ttl" => options[:ttl], "proxied" => options[:enable_proxy] } update_uri = URI.parse("https://api.cloudflare.com/client/v4/zones/#{zone_id}/dns_records/#{record["id"]}") https = Net::HTTP.new(update_uri.host, update_uri.port) https.use_ssl = true update_req = Net::HTTP::Put.new(update_uri, req_headers) update_req.body = update.to_json update_res = JSON.parse(https.request(update_req).body) if update_res["success"] == true then puts "Updated successfully!" else puts "Update failed!" pp update_res end end end end else puts record_res exit 22 end end else puts zone_res exit 21 end puts "Updated DDNS records successfully."