diff --git a/app/lib/DnsHelper.php b/app/lib/DnsHelper.php index 8123edd..cb576d8 100644 --- a/app/lib/DnsHelper.php +++ b/app/lib/DnsHelper.php @@ -442,6 +442,41 @@ class DnsHelper 'page' => true, 'add' => false, ], + 'henet' => [ + 'name' => 'HE DNS', + 'icon' => 'he.ico', + 'note' => '', + 'config' => [ + 'username' => [ + 'name' => '用户名/邮箱', + 'type' => 'input', + 'placeholder' => '', + 'required' => true, + ], + 'password' => [ + 'name' => '密码', + 'type' => 'input', + 'placeholder' => '', + 'required' => true, + ], + 'proxy' => [ + 'name' => '使用代理服务器', + 'type' => 'radio', + 'options' => [ + '0' => '否', + '1' => '是', + ], + 'value' => '0' + ], + ], + 'remark' => 0, + 'status' => false, + 'redirect' => false, + 'log' => false, + 'weight' => false, + 'page' => true, + 'add' => false, + ], 'spaceship' => [ 'name' => 'Spaceship', 'icon' => 'spaceship.ico', @@ -699,6 +734,7 @@ class DnsHelper 'qingcloud' => ['DEF' => '0', 'CT' => '2', 'CU' => '3', 'CM' => '4', 'AB' => '8'], 'cloudflare' => ['DEF' => '0'], 'namesilo' => ['DEF' => 'default'], + 'henet' => ['DEF' => 'default'], 'powerdns' => ['DEF' => 'default'], 'spaceship' => ['DEF' => 'default'], 'aliyunesa' => ['DEF' => '0'], diff --git a/app/lib/dns/henet.php b/app/lib/dns/henet.php new file mode 100644 index 0000000..5d579af --- /dev/null +++ b/app/lib/dns/henet.php @@ -0,0 +1,562 @@ +username = $config['username']; + $this->password = $config['password']; + $this->domain = $config['domain']; + $this->domainid = $config['domainid']; + $this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false; + $this->cacheKey = 'henet_cookie_' . md5($this->username . '|' . $this->password); + $this->loadCachedSession(); + } + + public function getError() + { + return $this->error; + } + + public function check() + { + return $this->getDomainList() !== false; + } + + public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20) + { + $html = $this->login(); + if ($html === false) { + return false; + } + + $list = $this->parseDomains($html); + if (!isNullOrEmpty($KeyWord)) { + $list = array_values(array_filter($list, function ($row) use ($KeyWord) { + return stripos($row['Domain'], $KeyWord) !== false; + })); + } + + $total = count($list); + $offset = max(0, ($PageNumber - 1) * $PageSize); + $list = array_slice($list, $offset, $PageSize); + return ['total' => $total, 'list' => $list]; + } + + public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null) + { + $zoneid = $this->getZoneId(); + if ($zoneid === false) { + return false; + } + + $html = $this->request('GET', 'index.cgi?' . http_build_query([ + 'hosted_dns_zoneid' => $zoneid, + 'menu' => 'edit_zone', + 'hosted_dns_editzone' => '', + ])); + if ($html === false) { + return false; + } + + $list = $this->parseRecords($html); + if (!isNullOrEmpty($SubDomain)) { + $list = array_values(array_filter($list, function ($row) use ($SubDomain) { + return strcasecmp($row['Name'], $SubDomain) === 0; + })); + } else { + if (!isNullOrEmpty($KeyWord)) { + $list = array_values(array_filter($list, function ($row) use ($KeyWord) { + return stripos($row['Name'], $KeyWord) !== false || stripos($row['Value'], $KeyWord) !== false; + })); + } + if (!isNullOrEmpty($Value)) { + $list = array_values(array_filter($list, function ($row) use ($Value) { + return $row['Value'] == $Value; + })); + } + if (!isNullOrEmpty($Type)) { + $list = array_values(array_filter($list, function ($row) use ($Type) { + return strcasecmp($row['Type'], $Type) === 0; + })); + } + if (!isNullOrEmpty($Status)) { + $list = array_values(array_filter($list, function ($row) use ($Status) { + return $row['Status'] == $Status; + })); + } + } + + $total = count($list); + $offset = max(0, ($PageNumber - 1) * $PageSize); + $list = array_slice($list, $offset, $PageSize); + return ['total' => $total, 'list' => $list]; + } + + public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null) + { + if ($SubDomain == '') $SubDomain = '@'; + return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line); + } + + public function getDomainRecordInfo($RecordId) + { + $records = $this->getDomainRecords(1, 1000); + if ($records === false) { + return false; + } + foreach ($records['list'] as $row) { + if ($row['RecordId'] == $RecordId) { + return $row; + } + } + $this->setError('解析记录不存在'); + return false; + } + + public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $zoneid = $this->getZoneId(); + if ($zoneid === false) { + return false; + } + + $params = $this->buildRecordParams($zoneid, '', $Name, $Type, $Value, $TTL, $MX); + $params['hosted_dns_editrecord'] = 'Submit'; + + $html = $this->request('POST', 'index.cgi', $params); + if ($html !== false && $this->isSuccess($html, 'Successfully added new record')) { + $recordid = $this->findRecordInHtml($html, $Name, $Type, $Value); + return $recordid ? $recordid : true; + } + + $this->setError($this->extractMessage($html) ?: '添加解析记录失败'); + return false; + } + + public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null) + { + $zoneid = $this->getZoneId(); + if ($zoneid === false) { + return false; + } + + $params = $this->buildRecordParams($zoneid, $RecordId, $Name, $Type, $Value, $TTL, $MX); + $params['hosted_dns_editrecord'] = 'Update'; + + $html = $this->request('POST', 'index.cgi', $params); + if ($html !== false && $this->isSuccess($html, 'Successfully updated record')) { + return true; + } + + $this->setError($this->extractMessage($html) ?: '修改解析记录失败'); + return false; + } + + public function updateDomainRecordRemark($RecordId, $Remark) + { + return false; + } + + public function deleteDomainRecord($RecordId) + { + $zoneid = $this->getZoneId(); + if ($zoneid === false) { + return false; + } + + $html = $this->request('POST', 'index.cgi', [ + 'menu' => 'edit_zone', + 'hosted_dns_zoneid' => $zoneid, + 'hosted_dns_recordid' => $RecordId, + 'hosted_dns_editzone' => '1', + 'hosted_dns_delrecord' => '1', + 'hosted_dns_delconfirm' => 'delete', + ]); + if ($html !== false && $this->isSuccess($html, 'Successfully removed record')) { + return true; + } + + $this->setError($this->extractMessage($html) ?: '删除解析记录失败'); + return false; + } + + public function setDomainRecordStatus($RecordId, $Status) + { + return false; + } + + public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null) + { + return false; + } + + public function getRecordLine() + { + return ['default' => ['name' => '默认', 'parent' => null]]; + } + + public function getMinTTL() + { + return 300; + } + + public function addDomain($Domain) + { + return false; + } + + private function login() + { + if ($this->loggedIn) { + return $this->request('GET', ''); + } + + $this->request('GET', '', null, false); + $html = $this->request('POST', '', [ + 'email' => $this->username, + 'pass' => $this->password, + ], false); + if ($html === false) { + return false; + } + if (stripos($html, 'incorrect') !== false || stripos($html, 'Invalid') !== false) { + $this->setError($this->extractMessage($html) ?: '登录失败,请检查用户名和密码'); + return false; + } + + $domains = $this->parseDomains($html); + if (empty($domains) && stripos($html, 'hosted_dns_zoneid') === false) { + $this->setError($this->extractMessage($html) ?: '登录失败,未找到域名列表'); + return false; + } + + $this->loggedIn = true; + $this->saveCachedSession(); + return $html; + } + + private function getZoneId() + { + if (!isNullOrEmpty($this->domainid)) { + return $this->domainid; + } + if (isNullOrEmpty($this->domain)) { + $this->setError('未指定域名'); + return false; + } + + $domains = $this->getDomainList($this->domain, 1, 1000); + if ($domains === false) { + return false; + } + foreach ($domains['list'] as $row) { + if (strcasecmp($row['Domain'], $this->domain) === 0) { + $this->domainid = $row['DomainId']; + return $this->domainid; + } + } + + $this->setError('域名不存在或无权限访问'); + return false; + } + + private function buildRecordParams($zoneid, $recordid, $Name, $Type, $Value, $TTL, $MX) + { + return [ + 'account' => '', + 'menu' => 'edit_zone', + 'Type' => strtoupper($Type), + 'hosted_dns_zoneid' => $zoneid, + 'hosted_dns_recordid' => $recordid, + 'hosted_dns_editzone' => '1', + 'Priority' => strtoupper($Type) == 'MX' ? intval($MX) : '', + 'Name' => $this->toFullName($Name), + 'Content' => $this->convertValue($Value, $Type), + 'TTL' => intval($TTL), + ]; + } + + private function parseDomains($html) + { + $list = []; + $patterns = [ + '/onclick="delete_dom\(this\);"[^>]*name="([^"]+)"[^>]*value="(\d+)"/i', + '/name="([^"]+)"[^>]*value="(\d+)"[^>]*onclick="delete_dom\(this\);"/i', + ]; + foreach ($patterns as $pattern) { + if (!preg_match_all($pattern, $html, $matches, PREG_SET_ORDER)) { + continue; + } + foreach ($matches as $match) { + $domain = html_entity_decode($match[1], ENT_QUOTES); + $zoneid = $match[2]; + $list[$zoneid] = [ + 'DomainId' => $zoneid, + 'Domain' => $domain, + 'RecordCount' => 0, + ]; + } + } + if (preg_match_all('/hosted_dns_zoneid=(\d+)[^"\']*["\'][^>]*>\s*([^<]+)\s* $match[1], + 'Domain' => $domain, + 'RecordCount' => 0, + ]; + } + } + } + return array_values($list); + } + + private function parseRecords($html) + { + $list = []; + if (!preg_match_all('/