339 lines
12 KiB
Python
339 lines
12 KiB
Python
import json
|
||
|
||
from sslModel.base import sslBase
|
||
|
||
import public
|
||
|
||
import copy
|
||
import sys
|
||
import hashlib
|
||
import hmac
|
||
import binascii
|
||
from datetime import datetime
|
||
import requests
|
||
|
||
|
||
class main(sslBase):
|
||
dns_provider_name = "huaweicloud"
|
||
_type = 0
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.region = "cn-south-1"
|
||
self.BasicDateFormat = "%Y%m%dT%H%M%SZ"
|
||
self.Algorithm = "SDK-HMAC-SHA256"
|
||
self.HeaderXDate = "X-Sdk-Date"
|
||
self.HeaderHost = "host"
|
||
self.HeaderAuthorization = "Authorization"
|
||
self.HeaderContentSha256 = "x-sdk-content-sha256"
|
||
self.url = "https://dns.cn-north-1.myhuaweicloud.com"
|
||
|
||
def __init_data(self, data):
|
||
self.ak = data["AccessKey"]
|
||
self.sk = data["SecretKey"]
|
||
self.project_id = data["project_id"]
|
||
|
||
def sign_to_response(self, dns_id, method="", url="", headers=None, body=""):
|
||
url = self.url + url
|
||
self.__init_data(self.get_dns_data(None)[dns_id])
|
||
if sys.version_info.major < 3:
|
||
body = body
|
||
from urllib import quote, unquote
|
||
|
||
def hmacsha256(keyByte, message):
|
||
return hmac.new(keyByte, message, digestmod=hashlib.sha256).digest()
|
||
|
||
# Create a "String to Sign".
|
||
def StringToSign(canonicalRequest, t):
|
||
bytes = HexEncodeSHA256Hash(canonicalRequest)
|
||
return "%s\n%s\n%s" % (self.Algorithm, datetime.strftime(t, self.BasicDateFormat), bytes)
|
||
|
||
else:
|
||
body = body.encode("utf-8")
|
||
from urllib.parse import quote, unquote
|
||
|
||
def hmacsha256(keyByte, message):
|
||
return hmac.new(keyByte.encode('utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256).digest()
|
||
|
||
# Create a "String to Sign".
|
||
def StringToSign(canonicalRequest, t):
|
||
bytes = HexEncodeSHA256Hash(canonicalRequest.encode('utf-8'))
|
||
return "%s\n%s\n%s" % (self.Algorithm, datetime.strftime(t, self.BasicDateFormat), bytes)
|
||
|
||
def HexEncodeSHA256Hash(data):
|
||
sha256 = hashlib.sha256()
|
||
sha256.update(data)
|
||
return sha256.hexdigest()
|
||
|
||
def findHeader(headers, header):
|
||
for k in headers:
|
||
if k.lower() == header.lower():
|
||
return headers[k]
|
||
return None
|
||
|
||
def CanonicalQueryString(query):
|
||
keys = []
|
||
for key in query:
|
||
keys.append(key)
|
||
keys.sort()
|
||
a = []
|
||
for key in keys:
|
||
k = quote(key, safe='~')
|
||
value = query[key]
|
||
if type(value) is list:
|
||
value.sort()
|
||
for v in value:
|
||
kv = k + "=" + quote(str(v), safe='~')
|
||
a.append(kv)
|
||
else:
|
||
kv = k + "=" + quote(str(value), safe='~')
|
||
a.append(kv)
|
||
return '&'.join(a)
|
||
|
||
def SignStringToSign(stringToSign, signingKey):
|
||
hm = hmacsha256(signingKey, stringToSign)
|
||
return binascii.hexlify(hm).decode()
|
||
|
||
def AuthHeaderValue(signature, AppKey, signedHeaders):
|
||
return "%s Access=%s, SignedHeaders=%s, Signature=%s" % (
|
||
self.Algorithm, AppKey, ";".join(signedHeaders), signature)
|
||
|
||
spl = url.split("://", 1)
|
||
scheme = 'http'
|
||
if len(spl) > 1:
|
||
scheme = spl[0]
|
||
url = spl[1]
|
||
query = {}
|
||
spl = url.split('?', 1)
|
||
url = spl[0]
|
||
if len(spl) > 1:
|
||
for kv in spl[1].split("&"):
|
||
spl = kv.split("=", 1)
|
||
key = spl[0]
|
||
value = ""
|
||
if len(spl) > 1:
|
||
value = spl[1]
|
||
if key != '':
|
||
key = unquote(key)
|
||
value = unquote(value)
|
||
if key in query:
|
||
query[key].append(value)
|
||
else:
|
||
query[key] = [value]
|
||
spl = url.split('/', 1)
|
||
host = spl[0]
|
||
if len(spl) > 1:
|
||
url = '/' + spl[1]
|
||
else:
|
||
url = '/'
|
||
if headers is None:
|
||
headers = {"content-type": "application/json"}
|
||
else:
|
||
headers = copy.deepcopy(headers)
|
||
|
||
headerTime = findHeader(headers, self.HeaderXDate)
|
||
|
||
if headerTime is None:
|
||
t = datetime.utcnow()
|
||
headers[self.HeaderXDate] = datetime.strftime(t, self.BasicDateFormat)
|
||
else:
|
||
t = datetime.strptime(headerTime, self.BasicDateFormat)
|
||
|
||
haveHost = False
|
||
for key in headers:
|
||
if key.lower() == 'host':
|
||
haveHost = True
|
||
break
|
||
if not haveHost:
|
||
headers["host"] = host
|
||
signedHeaders = []
|
||
for key in headers:
|
||
signedHeaders.append(key.lower())
|
||
signedHeaders.sort()
|
||
a = []
|
||
__headers = {}
|
||
for key in headers:
|
||
keyEncoded = key.lower()
|
||
value = headers[key]
|
||
valueEncoded = value.strip()
|
||
__headers[keyEncoded] = valueEncoded
|
||
if sys.version_info.major == 3:
|
||
headers[key] = valueEncoded.encode("utf-8").decode('iso-8859-1')
|
||
for key in signedHeaders:
|
||
a.append(key + ":" + __headers[key])
|
||
canonicalHeaders = '\n'.join(a) + "\n"
|
||
|
||
hexencode = findHeader(headers, self.HeaderContentSha256)
|
||
if hexencode is None:
|
||
hexencode = HexEncodeSHA256Hash(body)
|
||
pattens = unquote(url).split('/')
|
||
CanonicalURI = []
|
||
for v in pattens:
|
||
CanonicalURI.append(quote(v, safe="~"))
|
||
CanonicalURL = "/".join(CanonicalURI)
|
||
if CanonicalURL[-1] != '/':
|
||
CanonicalURL = CanonicalURL + "/" # always end with /
|
||
|
||
canonicalRequest = "%s\n%s\n%s\n%s\n%s\n%s" % (method.upper(), CanonicalURL, CanonicalQueryString(query),
|
||
canonicalHeaders, ";".join(signedHeaders), hexencode)
|
||
|
||
stringToSign = StringToSign(canonicalRequest, t)
|
||
signature = SignStringToSign(stringToSign, self.sk)
|
||
authValue = AuthHeaderValue(signature, self.ak, signedHeaders)
|
||
headers[self.HeaderAuthorization] = authValue
|
||
headers["content-length"] = str(len(body))
|
||
queryString = CanonicalQueryString(query)
|
||
if queryString != "":
|
||
url = url + "?" + queryString
|
||
|
||
res = requests.request(method, scheme + "://" + host + url, headers=headers, data=body)
|
||
return res
|
||
|
||
def create_dns_record(self, get):
|
||
domain_name = get.domain_name
|
||
domain_dns_value = get.domain_dns_value
|
||
record_type = 'TXT'
|
||
if 'record_type' in get:
|
||
record_type = get.record_type
|
||
if record_type == 'TXT':
|
||
domain_dns_value = "\"{}\"".format(domain_dns_value)
|
||
|
||
root_domain, sub_domain, _ = self.extract_zone(domain_name)
|
||
if sub_domain == "@":
|
||
domain_name = root_domain
|
||
|
||
try:
|
||
zone_dic = self.get_zoneid_dict(get.dns_id)
|
||
zone_id = zone_dic.get(root_domain)
|
||
body = json.dumps({
|
||
"name": domain_name,
|
||
"type": record_type,
|
||
"records": [domain_dns_value],
|
||
})
|
||
|
||
res = self.sign_to_response(get.dns_id, "POST", "/v2/zones/{}/recordsets".format(zone_id), body=body)
|
||
if res.status_code != 202:
|
||
return public.returnMsg(False, self.get_error(res.text))
|
||
|
||
return public.returnMsg(True, '添加成功')
|
||
except Exception as e:
|
||
return public.returnMsg(False, self.get_error(str(e)))
|
||
|
||
def delete_dns_record(self, get):
|
||
domain_name = get.domain_name
|
||
RecordId = get.RecordId
|
||
root_domain, _, sub_domain = self.extract_zone(domain_name)
|
||
|
||
zone_dic = self.get_zoneid_dict(get.dns_id)
|
||
zone_id = zone_dic[root_domain]
|
||
try:
|
||
res = self.sign_to_response(get.dns_id, "DELETE", "/v2/zones/{}/recordsets/{}".format(zone_id, RecordId))
|
||
if res.status_code != 202:
|
||
return public.returnMsg(False, self.get_error(res.text))
|
||
return public.returnMsg(True, '删除成功')
|
||
except Exception as e:
|
||
return public.returnMsg(False, self.get_error(str(e)))
|
||
|
||
def get_zoneid_dict(self, dns_id):
|
||
"""
|
||
获取所有域名对应id
|
||
"""
|
||
res = self.sign_to_response(dns_id, "GET", "/v2/zones")
|
||
if res.status_code != 200:
|
||
return {}
|
||
response = res.json()
|
||
data = {i["name"][:-1]: i["id"] for i in response["zones"]}
|
||
|
||
return data
|
||
|
||
def get_dns_record(self, get):
|
||
domain_name = get.domain_name
|
||
root_domain, _, sub_domain = self.extract_zone(domain_name)
|
||
data = {}
|
||
try:
|
||
zone_dic = self.get_zoneid_dict(get.dns_id)
|
||
limit = 100
|
||
offset = 0
|
||
zone_id = zone_dic[root_domain]
|
||
res = self.sign_to_response(get.dns_id, "GET",
|
||
"/v2/zones/{}/recordsets?limit={}&offset={}".format(zone_id, limit, offset))
|
||
if res.status_code != 200:
|
||
return {}
|
||
response = res.json()
|
||
|
||
data["list"] = [
|
||
{
|
||
"RecordId": i["id"],
|
||
"name": i["name"][:-1],
|
||
"value": '\r\n'.join(i["records"]) if i["type"] != "TXT" else '\r\n'.join([j.replace('"', '') for j in i["records"]]),
|
||
"line": "默认",
|
||
"ttl": i["ttl"],
|
||
"type": i["type"],
|
||
"status": "启用"if i["status"] == "ACTIVE" else "暂停" if i["status"] == "DISABLE" else i["status"],
|
||
"mx": "",
|
||
"updated_on": i["update_at"],
|
||
"remark": i.get("description") or "",
|
||
}
|
||
for i in response["recordsets"]
|
||
]
|
||
data["info"] = {
|
||
'record_total': response['metadata']['total_count']
|
||
}
|
||
except Exception as e:
|
||
pass
|
||
self.set_record_data({root_domain: data})
|
||
return data
|
||
|
||
def update_dns_record(self, get):
|
||
domain_name = get.domain_name
|
||
RecordId = get.RecordId
|
||
record_type = get.record_type
|
||
domain_dns_value = get.domain_dns_value
|
||
root_domain, _, sub_domain = self.extract_zone(domain_name)
|
||
|
||
if sub_domain == "@":
|
||
domain_name = root_domain
|
||
if record_type == 'TXT':
|
||
domain_dns_value = "\"{}\"".format(domain_dns_value)
|
||
|
||
zone_dic = self.get_zoneid_dict(get.dns_id)
|
||
zone_id = zone_dic[root_domain]
|
||
try:
|
||
body = json.dumps({
|
||
"name": domain_name,
|
||
"type": record_type,
|
||
"records": [domain_dns_value],
|
||
})
|
||
|
||
res = self.sign_to_response(get.dns_id, "PUT", "/v2/zones/{}/recordsets/{}".format(zone_id, RecordId), body=body)
|
||
if res.status_code != 202:
|
||
return public.returnMsg(False, self.get_error(res.text))
|
||
|
||
return public.returnMsg(True, '修改成功')
|
||
except Exception as e:
|
||
return public.returnMsg(False, self.get_error(str(e)))
|
||
|
||
def get_error(self, error):
|
||
if "DNS.0317" in error:
|
||
return "此记录为系统默认域名记录值,不能删除。"
|
||
elif "DNS.0318" in error:
|
||
return "此记录为系统默认域名记录值,不能更新。"
|
||
elif "DNS.0321" in error:
|
||
return "子域名级别超过限制。"
|
||
elif "DNS.0324" in error:
|
||
return "系统默认解析记录不能操作。"
|
||
elif "DNS.0308" in error:
|
||
return "记录集类型非法。"
|
||
elif "DNS.0307" in error:
|
||
return "记录集的值非法。"
|
||
elif "DNS.0302" in error:
|
||
return "这个华为云账户下面不存在这个域名,请检查dns接口配置后重试"
|
||
elif "APIGW.0301" in error:
|
||
return "dns账号信息有误"
|
||
elif "DNS.0312" in error:
|
||
return "存在同名的主机记录"
|
||
elif "DNS.0313" in error:
|
||
return "不存在这条记录"
|
||
else:
|
||
return error
|