From cb5f5340032fbee687b909a7bc7412edba818cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B8=E5=BE=92=E7=8E=9F=E7=90=85?= Date: Sun, 16 Aug 2020 16:17:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=AA=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E7=BA=BF=E8=B7=AF=E7=9A=84DNS=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E8=AE=B0=E5=BD=95=E7=9A=84=E5=8A=9F=E8=83=BD=E3=80=82?= =?UTF-8?q?=20(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ReadMe.md | 11 ++- aliyun-ddns/DomainUpdater.cs | 150 ++++++++++++++++++++++++----------- aliyun-ddns/Options.cs | 2 +- 3 files changed, 109 insertions(+), 54 deletions(-) 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 = "执行域名更新的间隔,单位秒。")]