阿里云DDNS PHP5封装类
/**
* 阿里云DDNS PHP5封装类
* 官方SDK过于臃肿庞大,利用accessKeyId和accessKeySecret获取API;
* 自动获取域名所有列表RecordId;
* 支持手动提交IPv6和IPv4地址;
* 默认自动获取支持IPv6和IPv4;
* 保存IPv6和IPv4地址,有变化才提交更新;
* 获取IP地址不可用时, 支持轮询;
*/
<?php
date_default_timezone_set('Asia/Shanghai');
@header("Content-type: application/json;charset=UTF-8");
function ipUrl(){
$ipList = array(
'IPv4' => array(
array('ip-api.io/json' => 180),
array('myip.ipip.net/json' => 30),
array('ipv4.ip159.com/getip' => 49),
array('ipv4.icanhazip.com' => 53),
array('v4.myip.la' => 170),
array('api-ipv4.ip.sb/geoip' => 200),
array('ipv4-overseas.itdog.plus' => 166),
array('ipv4_ct.itdog.cn' => 12),
array('ipv4_cu.itdog.cn' => 11),
array('ipv4_cm.itdog.cn' => 11),
array('4.ipshudi.com' => 33),
array('4.ipw.cn/api/ip/myip?json' => 33),
),
'IPv6' => array(
array('ipv6.ip159.com/getip' => 76),
array('ipv6.icanhazip.com' => 170),
array('v6.myip.la' => 202),
array('api-ipv6.ip.sb/geoip' => 140),
array('ipv6-overseas.itdog.plus' => 178),
array('ipv6_ct.itdog.cn' => 17),
array('ipv6_cu.itdog.cn' => 20),
array('ipv6_cm.itdog.cn' => 20),
array('6.ipshudi.com' => 30),
array('6.ipw.cn/api/ip/myip?json' => 33),
)
);
$result = [
'IPv4' => null,
'IPv6' => null
];
// 处理IPv4
$minIPv4 = PHP_INT_MAX;
foreach($ipList['IPv4'] as $ipArray){
foreach($ipArray as $url => $time){
if($time < $minIPv4) {
$minIPv4 = $time;
$result['IPv4'] = $url;
}
}
}
// 处理IPv6
$minIPv6 = PHP_INT_MAX;
foreach($ipList['IPv6'] as $ipArray){
foreach($ipArray as $url => $time){
if($time < $minIPv6){
$minIPv6 = $time;
$result['IPv6'] = $url;
}
}
}
return $result;
}
/**
* 阿里云DDNS PHP5封装类
* 官方SDK过于臃肿庞大,利用accessKeyId和accessKeySecret获取API;
* 自动获取域名所有列表RecordId;
* 支持手动提交IPv6和IPv4地址;
* 默认自动获取支持IPv6和IPv4;
* 保存IPv6和IPv4地址,有变化才提交更新;
* 获取IP地址不可用时, 支持轮询;
*/
class AliyunDDNS{
private $endpoint = 'https://alidns.aliyuncs.com';
private $version = '2015-01-09';
private $accessKeyId = 'LT********************4j';
private $accessKeySecret = 'S2**************************D5';
private $domain = 'xxxxxx.com';
private $rr = '*';
private $ipv4;
private $ipv6;
private $ipv4Type = 'A';
private $ipv6Type = 'AAAA';
/**
* 构造函数
* @param string $accessKeyId 阿里云AccessKey ID
* @param string $accessKeySecret 阿里云AccessKey Secret
*/
public function __construct($accessKeyId = null, $accessKeySecret = null, $domain = null, $rr = null){
$this->accessKeyId = $accessKeyId ? $accessKeyId : $this->accessKeyId;
$this->accessKeySecret = $accessKeySecret ? $accessKeySecret : $this->accessKeySecret;
$this->domain = $domain ? $domain : $this->domain;
$this->rr = $rr ? $rr : $this->rr;
}
// 手动设置IPv4, 将不再自动获取IPv4;未设置,将从网络自动获取
public function setIpv4($str){
$this->ipv4 = $str;
return ;
}
// 手动设置IPv6, 将不再自动获取IPv4;未设置,将从网络自动获取
public function setIpv6($str){
$this->ipv6 = $str;
return ;
}
// 设置 如:@、A、CNAME、MX、NS、TXT、SRV、SOA、PTR
public function setIpv4Type($str){
$this->ipv4Type = $str;
return ;
}
// 设置 如:@、AAAA、CNAME、MX、NS、TXT、SRV、SOA、PTR
public function setIpv6Type($str){
$this->ipv6Type = $str;
return ;
}
// 阿里云AccessKey,报错,注意给权限
public function setAccessKeyId($str){
$this->accessKeyId = $str;
return ;
}
// 阿里云AccessKeySecret
public function setAccessKeySecret($str){
$this->accessKeySecret = $str;
return ;
}
// 设置域名, 如:www.zhouein.com中的zhouein.com
public function setDomain($str){
$this->domain = $str;
return ;
}
// 设置二级域名, 如:api.zhouein.com中的'api'或'www'或'*'或'@'
public function setRr($str){
$this->rr = $str;
return ;
}
// 循环得到有效的IP地址, 当URL不可用时, 跳到下一个.
private function getLoopRes($domains, $ipType){
foreach($domains[$ipType] as $domain){
try {
$res = $this->cURL($domain);
if(is_array($res)){
$ip = $res['ip'];
//判断获取到的IP是IPv4或IPv6
$checkFunc = ($ipType === 'ipv4') ? 'isPublicIPv4' : 'isPublicIPv6';
if($this->{$checkFunc}($ip)){
return ['domain' => $domain, 'ip' => $ip];
}
}
} catch (Exception $e){
continue; // 发生异常时尝试下一个域名
}
}
return false; // 所有域名都失败时返回false
}
private function getIP(){
$ips = array(
'ipv4' => array(
'ipv4_ct.itdog.cn',
'ipv4_cu.itdog.cn',
'ipv4_cm.itdog.cn'
),
'ipv6' => array(
'ipv6_ct.itdog.cn',
'ipv6_cu.itdog.cn',
'ipv6_cm.itdog.cn'
)
);
$result = [
'ipv4' => $this->getLoopRes($ips, 'ipv4'),
'ipv6' => $this->getLoopRes($ips, 'ipv6')
];
return $result;
}
// 是否指定IP地址, 没有则从列表中获取, 如果服务器中保存的IP地址未变化, 则不更新
private function getCurrentIp($type){
$publicipFile = 'json/' . $this->domain . '.json';
if(!$this->$type){
$this->$type = $this->getIP()[$type]['ip'];
}
if (!$this->$type || !$this->validateIp($type, $this->$type)) {
return false;
}
$last_update = date('Y-m-d H:i:s');
// 检查目录是否存在,不存在则创建
if (!is_dir('json')) {
mkdir('json', 0755, true);
}
$newIPArr = [
'ipv4' => ['ip' => null, 'last_update' => $last_update],
'ipv6' => ['ip' => null, 'last_update' => $last_update]
];
$newIPArr[$type]['ip'] = $this->$type;
// 检查文件是否存在
if (!file_exists($publicipFile)) {
file_put_contents($publicipFile, json_encode($newIPArr));
return ['type' => $type, 'ip' => $this->$type];
}
// 读取现有IP数据
$saved_ips = json_decode(file_get_contents($publicipFile), true);
// 比较IP是否有变化
if ($saved_ips[$type]['ip'] === $this->$type) {
return false;
}
$saved_ips[$type]['ip'] = $this->$type;
$saved_ips[$type]['last_update'] = $last_update;
file_put_contents($publicipFile, json_encode($saved_ips));
return ['type' => $type, 'ip' => $this->$type];
}
// 验证IPv6地址是否有效
private function isPublicIPv6($ip){
// 排除私有地址(fc00::/7)
$ipBin = inet_pton($ip);
if(($ipBin[0] == "\xfc") || ($ipBin[0] == "\xfd") ||
($ipBin[0] == "\xfe" && ($ipBin[1] & "\xc0") == "\x80")) {
return false;
}
$result = filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV6 |
FILTER_FLAG_NO_PRIV_RANGE |
FILTER_FLAG_NO_RES_RANGE
);
return $result; // 返回标准化后的IPv6地址
}
// 验证IPv4地址是否有效
private function isPublicIPv4($ip){
// 排除多播地址(224.0.0.0/4)
$octets = explode('.', $ip);
if(isset($octets[0]) && $octets[0] >= 224 && $octets[0] <= 239) {
return false;
}
$result = filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 |
FILTER_FLAG_NO_PRIV_RANGE |
FILTER_FLAG_NO_RES_RANGE
);
return $result; // 返回标准化后的IPv4地址
}
private function validateIp($type, $ip) {
return $type === 'ipv4' ? $this->isPublicIPv4($ip) : $this->isPublicIPv6($ip);
}
// 生成签名
private function generateSignature($params){
ksort($params);
$canonicalizedQuery = '';
foreach ($params as $key => $value) {
$canonicalizedQuery .= '&' . rawurlencode($key) . '=' . rawurlencode($value);
}
$stringToSign = 'GET&%2F&' . rawurlencode(substr($canonicalizedQuery, 1));
return base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret . '&', true));
}
// 封装cURL 网络请求GET方法
private function cURL($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
if(curl_errno($ch)){
throw new Exception(curl_error($ch));
}
curl_close($ch);
$result = json_decode($response, true);
return $result;
}
// 请求阿里云DDNS公共参数
private function publicParams($params){
$publicParams = array(
'Format' => 'JSON',
'Version' => $this->version,
'AccessKeyId' => $this->accessKeyId,
'SignatureMethod' => 'HMAC-SHA1',
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'SignatureVersion' => '1.0',
'SignatureNonce' => md5(uniqid(mt_rand(), true))
);
return array_merge($publicParams, $params);
}
// 获取解析记录ID
private function getRecordId(){
$arr = array(
'Action' => 'DescribeDomainRecords',
'DomainName' => $this->domain,
);
$params = $this->publicParams($arr);
$params['Signature'] = $this->generateSignature($params);
$url = 'http://alidns.aliyuncs.com/?' . http_build_query($params);
$response = $this->cURL($url);
$yumingList = $response['DomainRecords']['Record'];
$result = array();
foreach($yumingList as $key => $val){
if($val['RR'] == $this->rr){
$result[] = $val;
}
}
return $result;
}
// 更新域名解析记录
private function updateDNS($RecordId,$Type,$newIP){
$arr = array(
'Action' => 'UpdateDomainRecord', //AddDomainRecord
'RecordId' => $RecordId,
'RR' => $this->rr,
'Type' => $Type,
'Value' => $newIP,
);
$params = $this->publicParams($arr);
$params['Signature'] = $this->generateSignature($params);
$url = 'http://alidns.aliyuncs.com/?' . http_build_query($params);
$response = $this->cURL($url);
return $response;
}
public function main(){
$result = [];
$getIpv4 = $this->getCurrentIp('ipv4');
$getIpv6 = $this->getCurrentIp('ipv6');
if(!$getIpv4 && !$getIpv6) {
return [
'code' => 200,
'msg' => '未更新',
'data' => [
'ipv4' => $this->ipv4,
'ipv6' => $this->ipv6
]
];
}
foreach($this->getRecordId() as $key => $record){
$result[$key] = $record;
if($record['Type'] === 'A' && !empty($getIpv4['ip'])) {
if(filter_var($getIpv4['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
$result[$key]['req'] = $this->ipv4;
$result[$key]['res'] = $this->updateDNS(
$record['RecordId'],
$record['Type'],
$getIpv4['ip']
);
}
}
if($record['Type'] === 'AAAA' && !empty($getIpv6['ip'])){
if(filter_var($getIpv6['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
$result[$key]['req'] = $this->ipv6;
$result[$key]['res'] = $this->updateDNS(
$record['RecordId'],
$record['Type'],
$getIpv6['ip']
);
}
}
}
return $result;
}
private function logError($message) {
error_log("[DNS Helper] ".date('Y-m-d H:i:s')." - {$message}\n", 3, 'dns_error.log');
}
// 废弃
private function percentEncode($str) {
$res = urlencode($str);
$res = str_replace(array('+', '*'), array('%20', '%2A'), $res);
$res = preg_replace('/%7E/', '~', $res);
return $res;
}
}
// 使用示例
$ddns = new AliyunDDNS(
$accessKeyId = 'LT********************4j',
$accessKeySecret = 'S2**************************D5',
$domain = 'xxxxxx.com', // 你的主域名
$rr = '*' // 子域名
);
//$ddns->setIpv4('111.111.111.111'); // 固定公网IPv4地址
//$ddns->setIpv6('2400:3200::1'); // 固定公网IPv6地址
//$ddns->setIpv4Type('@'); // 如:@、AAAA、CNAME、MX、NS、TXT、SRV、SOA、PTR
//$result = $ddns->updateDomainRecord($domain, $rr);
$result = $ddns->main();
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
?>

浙公网安备 33010602011771号