0.简要介绍

0.1 思路说明

AliDDNSNet 是基于 .NET Core 开发的动态 DNS 解析工具,借助于阿里云的 DNS API 来实现域名与动态 IP 的绑定功能。工具核心就是调用了阿里云 DNS 的两个 API ,一个 API 获取指定域名的所有解析记录,然后通过比对与当前公网 IP 是否一致,一致则不进行更改,不一致则通过另外一个修改 API 来修改指定子域名的修改记录。

0.2 使用说明

使用时请更改同目录下的 settings.json.examplesettings.json 文件,同时也可以显示通过 -f 参数来制定配置文件路径。例如:

dotnet ./AliDDNSNet.dll -f ./settings.json2
./AliDDNSNet -f ./settings.json3

NAS 运行效果图:

0.3.配置说明

通过更改 settings.json/settings.json.example 的内容来实现 DDNS 更新。

{
// 阿里云的 Access Id
"access_id": "",
// 阿里云的 Access Key
"access_key": "",
// TTL 时间
"interval": 600,
// 主域名
"domain": "example.com",
// 子域名前缀
"sub_domain": "test",
// 记录类型
"type": "A"
}

其中 Access Id 与 Access Key 可以登录阿里云之后在右上角可以得到。

1.代码说明

1.1 主程序流程

主要流程代码在 Program.cs 文件当中编写,这里依次讲解一下。

首先加载配置文件,如果用户传入了 -f 参数,则使用用户传入的配置文件路径,否则的话直接使用当前目录的默认 settings.json 配置文件,读取成功之后存放到 Utils.config 属性当中以便 Utils 使用。

// 加载配置文件:
var filePath = attachments.HasValue()
? attachments.Value()
: $"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}settings.json"; if (!File.Exists(filePath))
{
Console.WriteLine("当前目录没有配置文件,或者配置文件位置不正确。");
return -1;
} var config = await Utils.ReadConfigFile(filePath);
Utils.config = config;

之后通过 Utils.GetCurentPublicIP() 方法获取到当前设备的公网 IP,再判断指定的二级域名解析是否存在,如果不存在的话,则直接返回,这里并没有做新增解析操作,后续版本可能会加上。

// 获得当前 IP
var currentIP = (await Utils.GetCurentPublicIP()).Replace("\n", "");
var subDomains = JObject.Parse(await Utils.SendGetRequest(new DescribeDomainRecordsRequest(config.domain))); if (subDomains.SelectToken($"$.DomainRecords.Record[?(@.RR == '{config.sub_domain}')]") == null)
{
Console.WriteLine("指定的子域名不存在,请新建一个子域名解析。");
return 0;
}

如果找到了对应二级域名的解析,则输出当前解析的记录值,然后进行比较,如果当前主机的公网 IP 与记录值一样则无需进行变更。

Console.WriteLine("已经找到对应的域名与解析");
Console.WriteLine("======================");
Console.WriteLine($"子域名:{config.sub_domain}{config.domain}"); var dnsIp = subDomains.SelectToken($"$.DomainRecords.Record[?(@.RR == '{config.sub_domain}')].Value").Value<string>(); Console.WriteLine($"目前的 A 记录解析 IP 地址:{dnsIp}");
if (currentIP == dnsIp)
{
Console.WriteLine("解析地址与当前主机 IP 地址一致,无需更改.");
return 0;
}

当阿里云 DNS 解析记录与当前主机公网 IP 不一致的时候调用更新 API,传入之前的域名的 rrId 去进行变更,完成即退出。

Console.WriteLine("检测到 IP 地址不一致,正在更改中......");
var rrId = subDomains.SelectToken($"$.DomainRecords.Record[?(@.RR == '{config.sub_domain}')].RecordId").Value<string>(); var response = await Utils.SendGetRequest(new UpdateDomainRecordRequest(rrId, config.sub_domain, config.type, currentIP, config.interval.ToString())); var resultRRId = JObject.Parse(response).SelectToken("$.RecordId").Value<string>(); if (resultRRId == null || resultRRId != rrId)
{
Console.WriteLine("更改记录失败,请稍后再试。");
}
else
{
Console.WriteLine("更改记录成功。");
} return 0;

1.2 Utils 详解

Utils.cs 主要存放一些功能性方法,比如说将 SortedDictionary 字典转为请求字符串,还有就是加密方法,请求方法等。

1.2.1 生成通用参数字典

因为 API 请求的时候有很多共有参数,所以这里单独用了一个静态方法来生成这个公有请求参数的字典。

/// <summary>
/// 生成通用参数字典
/// </summary>
public static SortedDictionary<string, string> GenerateGenericParameters()
{
var dict = new SortedDictionary<string, string>(StringComparer.Ordinal)
{
{"Format", "json"},
{"AccessKeyId", config.access_id},
{"SignatureMethod", "HMAC-SHA1"},
{"SignatureNonce", Guid.NewGuid().ToString()},
{"Version", "2015-01-09"},
{"SignatureVersion", "1.0"},
{"Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")}
}; return dict;
}

可以看到这里使用了 SortedDictionary<string,string> 来处理,这是因为阿里云 API 必须要求按大小写敏感来排序请求参数,所以这里直接使用了 ``````SortedDictionary``` 来处理这种情况。

1.2.2 根据字典构建请求字符串

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

/// <summary>
/// 根据字典构建请求字符串
/// </summary>
/// <param name="parameters">参数字典</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 来构建这个请求字符串。

1.2.3 生成请求签名

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

/// <summary>
/// 生成请求签名
/// </summary>
/// <param name="srcStr">请求体</param>
/// <returns>HMAC-SHA1 的 Base64 编码</returns>
public static string GenerateSignature(this string srcStr)
{
var signStr = $"GET&{HttpUtility.UrlEncode("/")}&{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 文档说的:

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

Specified signature is not matched with our calculation.

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

1.2.4 发送请求

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

/// <summary>
/// 追加签名参数
/// </summary>
/// <param name="parameters">参数列表</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()));

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

1.3 异步 Main 方法

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

效果如下:

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

1.4 好用的 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);
}
}

2.GITHUB 开源地址

https://github.com/GameBelial/AliDDNSNet

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

3.二进制程序下载地址

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

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

下载地址在这儿

基于阿里云 DNS API 实现的 DDNS 工具的更多相关文章

  1. 阿里云DNS api接口 shell 更改DNS解析

    可定时任务检查域名解析,调用alidns.sh更新DNS解析 #!/bin/bash # alidns.sh #https://www.cnblogs.com/elvi/p/11663910.html ...

  2. 通过python将阿里云DNS解析作为DDNS使用

    通过python将阿里云DNS解析作为DDNS使用 脚本需要Python2.x运行 安装alidns python sdk sudo pip install aliyun-python-sdk-ali ...

  3. C#调用阿里云CDN API刷新缓存

    使用CDN必须要解决CDN缓存的问题,要么在每次更新文件时生成不同的URL,要么在每次更新文件时刷新CDN缓存.我们在一个实际应用场景中用到了后者,所以需要调用阿里云CDN的API进行缓存刷新的操作. ...

  4. 构建基于阿里云OSS文件上传服务

    转载请注明来源:http://blog.csdn.net/loongshawn/article/details/50710132 <构建基于阿里云OSS文件上传服务> <构建基于OS ...

  5. 【原】命令行增删改查阿里云 DNS

    命令行解析阿里云 DNS 项目地址:https://github.com/liyongjian5179/alidns 首先需要获取阿里云账号账号的AccessKeyID及AccessKeySecret ...

  6. go程序基于阿里云CodePipeline的一次devops实践

    背景 最近朋友有个项目代码托管用的码云,测试服务器(阿里云ECS)只有一台,三四个人开发,于是想基于阿里云的CodePipeline快速打造一套自动化cicd的流程,使用docker来进行多套环境部署 ...

  7. JAVA实现对阿里云DNS的解析管理

    1.阿里云DNS的SDK依赖 <dependency> <groupId>com.aliyun</groupId> <artifactId>tea-op ...

  8. 基于阿里云容器服务用docker容器运行ASP.NET 5示例程序

    小试阿里云容器服务 之后,接下来有一个挡不住的小试冲动--用docker容器运行程序.首先想到的程序是 ASP.NET 5示例程序,于是参考msdn博客中的这篇博文 Running ASP.NET 5 ...

  9. 一·创建Linux服务器(基于阿里云)

    本系统是基于阿里云服务器,购买请前往https://www.aliyun.com/?spm=5176.8142029.388261.1.taXish ,由于经济能力的限制,本人购买的是最低配置如下 其 ...

随机推荐

  1. liunx一键安装禅道

    一定要把压缩吧放在/opt下面,然后tar解压,启动 https://blog.csdn.net/sinat_23957257/article/details/82697458

  2. Apache Sling

    最新打算研究下 Apache Sling ,在此记录下过程~ 官网地址 Apache Sling - Bringing Back the Fun! 带回快乐 Apache Sling™ is an i ...

  3. ArrayList 和LinkedList的区别?

    ArrayList底层使用时数组.LinkedList使用的是链表. ArrayList:  数组查询具有所有查询特定元素比较快.而插入和删除和修改比较慢(数组在内存中是一块连续的内存,如果插入或删除 ...

  4. Python从入门到精通之Sixth!

    补充:enumerate 函数用于遍历序列(元组tuple.列表list.字典dict)中的元素以及它们的下标: >>> for i,j in enumerate(('a','b', ...

  5. #2019-2020-4 《Java 程序设计》第七周总结

    2019-2020-4 <Java 程序设计>第七周知识总结 第八章:常用实用类 一.String类 String类的构造方法 public String(byte[] bytes); p ...

  6. oracle存储过程 out cursor

    create or replace procedure BUILDEMPLID(emp_cursor out sys_refcursor) is n_emplid number; n_emplid1 ...

  7. Java基础之一

    移位操作符 移位操作符只可用来处理整数类型. <<:左移位操作符,按照操作符右侧指定的位数将操作符左边的操作数向左移动,在低位补0. >>:“有符号”右移位操作符,按照操作符右 ...

  8. Codeforces 884 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 传送门 A题 传送门 题意简述: 一个人要完成一件事总共需要ttt秒,现在有nnn天,每天有aia_iai​不能做事,问他可以在第几天做完. 思路:按照题 ...

  9. 添加sqljdbc的maven依赖JAVA环境配置

    sqljdbc是微软sql server的jdbc驱动 使用sqljdbc需要从微软的官方网站下载jar包: http://www.microsoft.com/en-us/download/detai ...

  10. 弹性盒子模型属性之flex-shrink

    上一次,我们已经了解过flex-grow的具体用法后,这周,让我们一起来见一下flex-basis这个属性. flex-shrink 定义项目的缩小比例,默认值为1,注意前提是空间不足的情况下,项目缩 ...