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