Skip to content

Instantly share code, notes, and snippets.

@xiaohui-zhangxh
Last active March 15, 2020 15:28
Show Gist options
  • Save xiaohui-zhangxh/9f15257d915d679cf0ac6a1b818d8da5 to your computer and use it in GitHub Desktop.
Save xiaohui-zhangxh/9f15257d915d679cf0ac6a1b818d8da5 to your computer and use it in GitHub Desktop.
通过 QCloud/DnsPod API 动态更新域名解析
# frozen_string_literal: true
require 'json'
require 'net/http'
class DnspodAPI
API_HOST = 'https://dnsapi.cn'
# API requires UserAgent contains app name/version and email
def initialize(token, app_name:, app_version:, email:)
@token = token
@app_name = app_name
@app_version = app_version
@email = email
end
def domain_records(domain, length: 100, **params)
method(__method__).parameters.each do |(type, name)|
params[name] = binding.local_variable_get(name) if type != :keyrest
end
request_api('/Record.List', params)['records']
end
def update_record(domain, sub_domain, value:, **params)
method(__method__).parameters.each do |(type, name)|
params[name] = binding.local_variable_get(name) if type != :keyrest
end
params[:record_type] ||= 'A'
params[:record_line] ||= '默认'
unless params[:record_id]
record = domain_records(domain, params.slice(:sub_domain, :record_type)).first
raise 'record not found' if record.nil?
return record if record['value'] == value
params[:record_id] = record['id']
end
request_api('/Record.Modify', params)
end
def request_api(path, params)
params = params.merge(login_token: @token, format: :json)
resp = Net::HTTP.post(
URI(API_HOST).merge(path),
URI.encode_www_form(params),
'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8',
'User-Agent' => user_agent
)
raise resp unless resp.is_a?(Net::HTTPOK)
JSON.parse(resp.body)
end
def user_agent
"#{@app_name}/#{@app_version} (#{@email})"
end
end
if $PROGRAM_NAME == __FILE__
require 'socket'
def current_ip
s = TCPSocket.new 'ns1.dnspod.net', 6666
ip = s.gets
s.close
ip
end
token = ARGV[0]
client = DnspodAPI.new(
token,
app_name: 'Ruby Client',
app_version: '0.1.0',
email: '[email protected]'
)
pp client.domain_records('tanmer.com', sub_domain: 'debug')
pp client.update_record('tanmer.com', 'debug', value: ARGV[2] || current_ip)
end
#! /usr/bin/env ruby
require 'active_support/core_ext/string'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/object/to_query'
require 'openssl'
require 'base64'
require 'open-uri'
require 'json'
require 'logger'
class QCloudAPI
attr_reader :secret_id, :secret
def initialize(secret_id, secret, logger: Logger.new(STDOUT))
@secret_id = secret_id
@secret = secret
@logger = logger
end
def log(method=:puts, message)
@logger.send(method, message)
end
def common_args(
action: ,
timestamp: ,
nonce: ,
secret_id: ,
signature: nil,
region: nil,
signature_method: 'HmacSHA256',
token: nil
)
%w[action timestamp nonce secret_id signature region signature_method token].reduce({}) { |h, k|
v = eval(k)
h[k.classify] = v if v
h
}
end
# https://cloud.tencent.com/document/product/302/7310
def build_signature(method, host, path, args, secret)
args = args.stringify_keys
args_str = args.keys.sort.map{ |k| "#{k}=#{args[k]}" }.join('&')
str = "#{method.to_s.upcase}#{host}#{path}?#{args_str}"
mac = OpenSSL::HMAC.digest("SHA256", secret, str)
Base64.encode64(mac).strip
end
def request_api(host, path, action, args)
final_args = common_args(action: action, timestamp: Time.now.to_i, nonce: SecureRandom.hex(4), secret_id: secret_id).merge(args)
signature = build_signature('GET', host, path, final_args, secret)
params = final_args.merge('Signature' => signature).to_query
response = open("https://#{host}#{path}?#{params}") and nil
body = JSON.parse(response.read)
raise "#{body['code']} - #{body['codeDesc']} - #{body['message']}" unless body['code'] == 0
body
end
def domain_lists
response = request_api('cns.api.qcloud.com', '/v2/index.php', 'DomainList', length: 200)
response['data']['domains'].map { |domain| domain.slice('id', 'name') }
end
def domain_records(domain)
response = request_api('cns.api.qcloud.com', '/v2/index.php', 'RecordList', length: 200, domain: domain)
response['data']['records'].map { |record| record.slice('id', 'name', 'status', 'line', 'type', 'value') }
end
# https://cloud.tencent.com/document/product/302/8511
def update_domain_record(
domain: ,
sub_domain: ,
value: ,
record_id: nil,
record_type: nil,
record_line: nil)
old_value = nil
if record_id.nil? || record_type.nil?
record = domain_records(domain).find { |domain| domain['name'] == sub_domain }
raise "Domain #{sub_domain}.#{domain} not found" if record.nil?
old_value = record['value']
return false if value == old_value
record_id = record['id']
record_type = record['type']
record_line = record['line']
end
log :print, "Updating DNS #{sub_domain}.#{domain} from #{old_value} to #{value} ... "
response = request_api(
'cns.api.qcloud.com',
'/v2/index.php',
'RecordModify',
domain: domain,
recordId: record_id,
subDomain: sub_domain,
recordType: record_type,
recordLine: record_line,
value: value
)
log :puts, 'Done.'
return true
end
end
require 'socket'
def current_ip
s = TCPSocket.new 'ns1.dnspod.net', 6666
ip = s.gets
s.close
ip
end
# ========================= update below ======================
api = QCloudAPI.new('secret key', 'secret')
api.update_domain_record(
domain: 'c456.com',
sub_domain: 'tanmerdev',
value: current_ip
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment