基于阿里云 DNS API 实现的 DDNS 工具 (2)

因为阿里云 DNS 的 API 基本上都是 GET 请求,所以通过这个方法可以将之前的 SortedDictionary<string,string> 字典构建成请求字符串。

/// <summary> /// 根据字典构建请求字符串 /// </summary> /// <param>参数字典</param> /// <returns></returns> public static string BuildRequestString(this SortedDictionary<string, string> parameters) { var sb = new StringBuilder(); foreach (var kvp in parameters) { sb.Append("&"); sb.Append(HttpUtility.UrlEncode(kvp.Key)); sb.Append("="); sb.Append(HttpUtility.UrlEncode(kvp.Value)); } return sb.ToString().Substring(1); }

核心就是遍历这个字典,通过 StringBuilder 来构建这个请求字符串。

2.2.3 生成请求签名

这一步也是最重要的一步,因为阿里云所有的 API 接口都需要传递签名参数,这个签名参数是根据你提交的参数集合 AccessKey 来进行计算的。

/// <summary> /// 生成请求签名 /// </summary> /// <param>请求体</param> /// <returns>HMAC-SHA1 的 Base64 编码</returns> public static string GenerateSignature(this string srcStr) { var signStr = $"GET&{HttpUtility.UrlEncode("http://www.likecs.com/")}&{HttpUtility.UrlEncode(srcStr)}"; // 替换已编码的 URL 字符为大写字符 signStr = signStr.Replace("%2f", "%2F").Replace("%3d", "%3D").Replace("%2b", "%2B") .Replace("%253a", "%253A"); var hmac = new HMACSHA1(Encoding.UTF8.GetBytes($"{config.access_key}&")); return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(signStr))); }

这里之前我是按照阿里云 API 来进行开发的,不过有一点需要注意的是,返回的 Signature 值是不需要进行 URL 编码的。就因为这一点,我白白浪费了 3 个小时来排查问题,看看官方 API 文档说的:

基于阿里云 DNS API 实现的 DDNS 工具

说需要将签名值编码之后再提交,扯淡,如果编码之后再提交的话,接口会一直返回:

Specified signature is not matched with our calculation.

这里直接返回 HMACSHA1 加密结果的 Base64 字符串即可。

2.2.4 发送请求

构建好一切之后我们就需要发送请求了,这里统一是使用的 SendRequest() 方法来进行处理,可以看到我们先获得签名,然后将获取到的签名追加到请求体内部,一起进行请求。

/// <summary> /// 追加签名参数 /// </summary> /// <param>参数列表</param> public static string AppendSignature(this SortedDictionary<string, string> parameters, string sign) { parameters.Add("Signature", sign); return parameters.BuildRequestString(); } /// <summary> /// 对阿里云 API 发送 GET 请求 /// </summary> public static async Task<string> SendGetRequest(IRequest request) { var sign = request.Parameters.BuildRequestString().GenerateSignature(); var postUri = $"http://alidns.aliyuncs.com/?{request.Parameters.AppendSignature(sign)}"; using (var client = new HttpClient()) { using (var resuest = new HttpRequestMessage(HttpMethod.Get, postUri)) { using (var response = await client.SendAsync(resuest)) { return await response.Content.ReadAsStringAsync(); } } } }

这里传入的 IRequest 接口,是有具体实现的,可以转到 Main 方法里面看一下:

await Utils.SendGetRequest(new DescribeDomainRecordsRequest(config.domain)); await Utils.SendGetRequest(new UpdateDomainRecordRequest(rrId, config.sub_domain, config.type, currentIP, config.interval.ToString()));

这里的 DescribeDomainRecordsRequest 与 UpdateDomainRecordRequest 就是具体的请求体,定义很简单,就是实现了 IRequest 接口而已,然后在各自的内部添加一些特殊的参数。

2.2 异步 Main 方法

异步的 Main 方法需要 C# 7.1 以上版本才能支持,你只需要右键你的项目选择属性,左侧栏选择生成,找到高级按钮,更改当前 C# 语言版本即可。

基于阿里云 DNS API 实现的 DDNS 工具

效果如下:

static async Task<int> Main(string[] args) { // 代码.... return await Task.FromResult(0); } 2.3 好用的 CommandLine 库

编写控制台程序,最主要的是接受参数然后处理,而 Microsoft.Extensions.CommandLineUtils 库提供了方便快捷的方式来为我们处理用户输入的参数。

使用方法如下:

using System; using McMaster.Extensions.CommandLineUtils; public class Program { public static int Main(string[] args) { var app = new CommandLineApplication(); app.HelpOption(); var optionSubject = app.Option("-s|--subject <SUBJECT>", "The subject", CommandOptionType.SingleValue); var optionRepeat = app.Option<int>("-n|--count <N>", "Repeat", CommandOptionType.SingleValue); // 启动时执行的委托 app.OnExecute(() => { // 接收参数 var subject = optionSubject.HasValue() ? optionSubject.Value() : "world"; var count = optionRepeat.HasValue() ? optionRepeat.ParsedValue : 1; for (var i = 0; i < count; i++) { Console.WriteLine($"Hello {subject}!"); } // 执行完毕返回状态 0 return 0; }); // 真正启动控制台程序 return app.Execute(args); } } 3.GITHUB 开源地址:

https://github.com/GameBelial/AliDDNSNet

有兴趣的朋友可以 star 关注一下。

4.二进制程序下载地址

程序打包了 Linux-x64 与 Linux arm 环境的二进制可执行文件,你可以直接下载对应的压缩包解压到你的路由器或者 NAS 里面进行运行。

如果你的设备支持 Docker 环境,建议通过 Docker 运行 .NET Core 2.1 环境来执行本程序。

下载地址在这儿

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdgsw.html