diff --git a/ReadMe.md b/ReadMe.md
index 8501f6b..5cb1b22 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -8,11 +8,10 @@
[![GitHub stars](https://img.shields.io/github/stars/sanjusss/aliyun-ddns.svg)](https://github.com/sanjusss/aliyun-ddns/stargazers)
# 功能
-- 通过在线API获取公网IPv4地址,更新到域名A记录。
-- 通过在线API获取公网IPv6地址,更新到域名AAAA记录。
-- 通过本地网卡获取公网或内网IPv4地址,更新到域名A记录。
-- 通过本地网卡获取公网或内网IPv6地址,更新到域名AAAA记录。
+- 通过在线API获取公网IPv4/v6地址,更新到域名A/AAAA记录。
+- 通过本地网卡获取公网或内网IPv4/v6地址,更新到域名A/AAAA记录。
- 支持更新多个域名的记录。
+- 支持更新指定线路的记录。
- 支持Docker容器,支持x64、ARMv7和ARMv8。
- IP发生变化时,使用WebHook通知。
@@ -34,7 +33,7 @@ docker run -d --restart=always --net=host \
| :---- | :----- | :--- |
|AKID|阿里云的Access Key ID。[获取阿里云AccessToken](https://usercenter.console.aliyun.com/)|access key id|
|AKSCT|阿里云的Access Key Secret。|access key secret|
-|DOMAIN|需要更新的域名,多个域名需要“,”分隔。|my.domain.com|
+|DOMAIN|需要更新的域名,可以用“,”隔开。
可以指定线路,用“:”分隔线路和域名([线路名说明](https://help.aliyun.com/document_detail/29807.html?spm=a2c4g.11186623.2.14.42405eb4boCsnd))。
例如:“baidu.com,telecom:dianxin.baidu.com”。|my.domain.com|
|REDO|更新间隔,单位秒。建议大于等于TTL/2。|300|
|TTL|服务器缓存解析记录的时长,单位秒,普通用户最小为600。|600|
|TIMEZONE|输出日志时的时区,单位小时。|8|
@@ -73,7 +72,7 @@ dotnet aliyun-ddns.dll \
| :---- | :----- | :--- |
|u|阿里云的Access Key ID。[获取阿里云AccessToken](https://usercenter.console.aliyun.com/)|access key id|
|p|阿里云的Access Key Secret。|access key secret|
-|d|需要更新的域名,多个域名需要“,”分隔。|my.domain.com|
+|d|需要更新的域名,可以用“,”隔开。
可以指定线路,用“:”分隔线路和域名([线路名说明](https://help.aliyun.com/document_detail/29807.html?spm=a2c4g.11186623.2.14.42405eb4boCsnd))。
例如:“baidu.com,telecom:dianxin.baidu.com”。|my.domain.com|
|i|更新间隔,单位秒。建议大于等于TTL/2。|300|
|t|服务器缓存解析记录的时长,单位秒,普通用户最小为600。|600|
|timezone|输出日志时的时区,单位小时。|8|
diff --git a/aliyun-ddns/DomainUpdater.cs b/aliyun-ddns/DomainUpdater.cs
index 70cd34d..c03ec1f 100644
--- a/aliyun-ddns/DomainUpdater.cs
+++ b/aliyun-ddns/DomainUpdater.cs
@@ -124,7 +124,7 @@ private void Update(string[] domains, HashSet targetTypes)
{
if (typedRecords.Count > 1)
{
- DeleteRecords(typedRecords[0].DomainName, typedRecords[0].RR, type);
+ DeleteRecords(typedRecords);
}
success = AddRecord(j.Key, i, j.Value);
@@ -257,13 +257,15 @@ private IList GetRecords(string domain)
{
var client = GetNewClient();
long pageNumber = 1;
+ ParseSubDomainAndLine(domain, out string subDomain, out string line);
do
{
DescribeSubDomainRecordsRequest request = new DescribeSubDomainRecordsRequest
{
- SubDomain = domain,
+ SubDomain = subDomain,
PageSize = _pageSize,
- PageNumber = pageNumber
+ PageNumber = pageNumber,
+ Line = line
};
var response = client.GetAcsResponse(request);
@@ -288,41 +290,78 @@ private IList GetRecords(string domain)
return records;
}
+ /////
+ ///// 删除所有指定类型的记录。
+ /////
+ ///// 域名
+ ///// 主机记录
+ ///// 解析记录类型
+ ///// 是否删除成功。
+ //private bool DeleteRecordsByType(string domain, string rr, string type)
+ //{
+ // try
+ // {
+ // var client = GetNewClient();
+ // DeleteSubDomainRecordsRequest request = new DeleteSubDomainRecordsRequest
+ // {
+ // DomainName = domain,
+ // RR = rr,
+ // Type = type,
+ // };
+ // var response = client.GetAcsResponse(request);
+ // if (response.HttpResponse.isSuccess())
+ // {
+ // Log.Print($"成功清理{ type }记录{ rr }.{ domain }。");
+ // return true;
+ // }
+ // else
+ // {
+ // Log.Print($"清理{ type }记录{ rr }.{ domain }失败。");
+ // return false;
+ // }
+ // }
+ // catch (Exception e)
+ // {
+ // Log.Print($"删除{ type }记录{ rr }.{ domain }记录时出现异常:{ e }");
+ // return false;
+ // }
+ //}
+
///
- /// 删除所有指定类型的记录。
+ /// 删除所有记录。
///
- /// 域名
- /// 主机记录
- /// 解析记录类型
- /// 是否删除成功。
- private bool DeleteRecords(string domain, string rr, string type)
+ /// 记录
+ /// 是否成功删除
+ private bool DeleteRecords(IEnumerable records)
{
- try
+ foreach (var rd in records)
{
- var client = GetNewClient();
- DeleteSubDomainRecordsRequest request = new DeleteSubDomainRecordsRequest
- {
- DomainName = domain,
- RR = rr,
- Type = type
- };
- var response = client.GetAcsResponse(request);
- if (response.HttpResponse.isSuccess())
+ try
{
- Log.Print($"成功清理{ type }记录{ rr }.{ domain }。");
- return true;
+ var client = GetNewClient();
+ DeleteDomainRecordRequest request = new DeleteDomainRecordRequest
+ {
+ RecordId = rd.RecordId
+ };
+ var response = client.GetAcsResponse(request);
+ if (response.HttpResponse.isSuccess())
+ {
+ Log.Print($"成功清理{ rd.Type }记录{ rd.RR }.{ rd.DomainName }({ rd.Line })记录{ rd.RecordId }。");
+ }
+ else
+ {
+ Log.Print($"清理{ rd.Type }记录{ rd.RR }.{ rd.DomainName }({ rd.Line })记录{ rd.RecordId }失败。");
+ return false;
+ }
}
- else
+ catch (Exception e)
{
- Log.Print($"清理{ type }记录{ rr }.{ domain }失败。");
+ Log.Print($"删除{ rd.Type }记录{ rd.RR }.{ rd.DomainName }({ rd.Line })记录{ rd.RecordId }时出现异常:{ e }");
return false;
}
}
- catch (Exception e)
- {
- Log.Print($"删除{ type }记录{ rr }.{ domain }记录时出现异常:{ e }");
- return false;
- }
+
+ return true;
}
///
@@ -332,25 +371,17 @@ private bool DeleteRecords(string domain, string rr, string type)
/// 是否完全删除成功。
private bool ClearCName(ref IList rds)
{
- bool found = false;
- string domain = null;
- string rr = null;
+ List cnameRecords = new List();
for (int i = rds.Count - 1; i >= 0; --i)
{
if (rds[i].Type == "CNAME")
{
- if (found == false)
- {
- found = true;
- domain = rds[i].DomainName;
- rr = rds[i].RR;
- }
-
+ cnameRecords.Add(rds[i]);
rds.RemoveAt(i);
}
}
- return found == false || DeleteRecords(domain, rr, "CNAME");
+ return cnameRecords.Count == 0 || DeleteRecords(cnameRecords);
}
///
@@ -364,9 +395,10 @@ private bool AddRecord(IpType type, string domain, string ip)
{
try
{
+ ParseSubDomainAndLine(domain, out string subDomain, out string line);
string pattern = @"^(\S*)\.(\S+)\.(\S+)$";
Regex regex = new Regex(pattern);
- var match = regex.Match(domain);
+ var match = regex.Match(subDomain);
string domainName;
string rr;
if (match.Success)
@@ -377,7 +409,7 @@ private bool AddRecord(IpType type, string domain, string ip)
else
{
rr = "@";
- domainName = domain;
+ domainName = subDomain;
}
var client = GetNewClient();
@@ -387,7 +419,8 @@ private bool AddRecord(IpType type, string domain, string ip)
RR = rr,
Type = type.ToString(),
_Value = ip,
- TTL = Options.Instance.TTL
+ TTL = Options.Instance.TTL,
+ Line = line
};
var response = client.GetAcsResponse(request);
if (response.HttpResponse.isSuccess())
@@ -422,7 +455,7 @@ private bool UpdateRecord(Record rd, string ip)
}
else if (ip == rd._Value)
{
- Log.Print($"{ rd.Type }记录{ rd.RR }.{ rd.DomainName }不需要更新。");
+ Log.Print($"{ rd.Type }记录{ rd.RR }.{ rd.DomainName }({ rd.Line })不需要更新。");
return false;
}
@@ -435,25 +468,48 @@ private bool UpdateRecord(Record rd, string ip)
RR = rd.RR,
Type = rd.Type,
_Value = ip,
- TTL = Options.Instance.TTL
+ TTL = Options.Instance.TTL,
+ Line = rd.Line
};
var response = client.GetAcsResponse(request);
if (response.HttpResponse.isSuccess())
{
- Log.Print($"成功更新{ rd.Type }记录{ rd.RR }.{ rd.DomainName }为{ ip }。");
+ Log.Print($"成功更新{ rd.Type }记录{ rd.RR }.{ rd.DomainName }({ rd.Line })为{ ip }。");
return true;
}
else
{
- Log.Print($"更新{ rd.Type }记录{ rd.RR }.{ rd.DomainName }失败。");
+ Log.Print($"更新{ rd.Type }记录{ rd.RR }.{ rd.DomainName }({ rd.Line })失败。");
return false;
}
}
catch (Exception e)
{
- Log.Print($"更新{ rd.Type }记录{ rd.RR }.{ rd.DomainName }时出现异常:{ e }。");
+ Log.Print($"更新{ rd.Type }记录{ rd.RR }.{ rd.DomainName }({ rd.Line })时出现异常:{ e }。");
return false;
}
}
+
+ private static void ParseSubDomainAndLine(string domain, out string subDomain, out string line)
+ {
+ var words = domain.Split(':', StringSplitOptions.RemoveEmptyEntries);
+ if (words.Length > 2 || words.Length == 0)
+ {
+ line = "default";
+ subDomain = domain;
+ return;
+ }
+
+ if (words.Length == 2)
+ {
+ line = words[0];
+ subDomain = words[1];
+ }
+ else
+ {
+ line = "default";
+ subDomain = words[0];
+ }
+ }
}
}
diff --git a/aliyun-ddns/Options.cs b/aliyun-ddns/Options.cs
index 9287d9f..908996e 100644
--- a/aliyun-ddns/Options.cs
+++ b/aliyun-ddns/Options.cs
@@ -14,7 +14,7 @@ public class Options
//[Option('e', "endpoint", Required = false, Default = "cn-hangzhou", HelpText = "Region Id,详见https://help.aliyun.com/document_detail/40654.html?spm=a2c4e.11153987.0.0.6d85366aUfTWbG")]
//public string ENDPOINT { get; set; } = "cn-hangzhou";
- [Option('d', "domain", Required = false, Default = "my.domain.com", HelpText = "需要更新的域名,可以用“,”隔开。")]
+ [Option('d', "domain", Required = false, Default = "my.domain.com", HelpText = "需要更新的域名,可以用“,”隔开。可以指定线路,用“:”分隔线路和域名(线路名说明见https://help.aliyun.com/document_detail/29807.html?spm=a2c4g.11186623.2.14.42405eb4boCsnd)。例如:“baidu.com,telecom:dianxin.baidu.com”。")]
public string Domain { get; set; } = "my.domain.com";
[Option('i', "interval", Required = false, Default = 300, HelpText = "执行域名更新的间隔,单位秒。")]