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

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 环境来执行本程序。

下载地址在这儿

原文地址:https://www.cnblogs.com/myzony/p/9349578.html

时间: 2024-08-03 18:58:43

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

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

可定时任务检查域名解析,调用alidns.sh更新DNS解析 #!/bin/bash # alidns.sh #https://www.cnblogs.com/elvi/p/11663910.html #阿里云DNS api接口 shell 更改DNS解析 ############################## which dig &>/dev/null || { yum install -y bind-utils ; } || { echo "need to install

基于阿里云Ubuntu14.04 64bit部署WordPress博客系统

环境:基于阿里云Ubuntu14.04  64bit服务器系统 1, 安装apache2+mysql5+php5+php5-mysql sudo apt-get install apache2 sudo apt-get install php5 sudo apt-get install mysql-server sudo apt-get install php5-mysql sudo /etc/init.d/apache2 restart 至此重启了apache后应该就已经配置好服务器了,对此先

基于阿里云ECS的phpwind网站备案前如何远程访问调试?

基于阿里云ECS的phpwind网站部署非常方便,但云主机的外网IP绑定域名却比较复杂.先要申请域名,成功后还需要备案.尤其是企业网站备案,需要提交的资料较多,准备资料以及审批的时间较长.这段时间在外网采用IP访问是不行的,显示的是主机宝服务器默认页. 即使域名已经绑定成功,但若未备案的话,采用域名访问也会出现以下“温馨提示”: 那么有没有办法在网站备案批准之前就能远程访问,以便尽早调试?经过摸索,基于阿里云ECS的phpwind网站可以采用以下步骤,在网站域名备案前修改默认的80端口以便使用I

在基于阿里云服务器CentOS6.5下安装Subversion 1.6.5服务

最近阿里云搞了个1元免费提供云服务器的活动,偶心痒痒就申请了一个. 正好可以作为团队的SVN服务器了,下面就来部署SVN服务吧. 一.安装基础环境 apr-1.5.0.tar.gz apr-util-1.5.3.tar.gz pcre-8.35.tar.gz zlib-1.2.8.tar.gz subversion-1.5.6.tar.gz tar -xzvf apr-1.5.0.tar.gz cd apr-1.5.0 ./configure --prefix=/usr/local/apr ma

基于阿里云数加MaxCompute的企业大数据仓库架构建设思路

摘要: 数加大数据直播系列课程主要以基于阿里云数加MaxCompute的企业大数据仓库架构建设思路为主题分享阿里巴巴的大数据是怎么演变以及怎样利用大数据技术构建企业级大数据平台. 本次分享嘉宾是来自阿里云大数据的技术专家祎休 背景与总体思路 数据仓库是一个面向主题的.集成的.非易失的.反映历史变化的数据集合用于支持管理决策. 原文链接:http://click.aliyun.com/m/43803/ 数加大数据直播系列课程,主要以基于阿里云数加MaxCompute的企业大数据仓库架构建设思路为主

(二)基于阿里云的MQTT远程控制(购买阿里云,在云端安装MQTT,测试MQTT远程通信)

QQ名称为Friday~的网友把他自己买MQTT的过程截图发给了我,今天就说一下如何购买阿里云,安装MQTT可以参考 http://www.cnblogs.com/yangfengwu/p/7764667.html https://blog.csdn.net/frankcheng5143/article/details/52045501 如果有什么问题可以在上面的群里面向他请教 基于阿里云的远程控制,其实就是在云端安装服务器,然后我们的WiFi设备和手机都去连接云端服务器(云端服务器就相当于云端

云上拍客梨视频 基于阿里云的技术实践分享

摘要: 梨视频大部分的业务都选择了阿里云,其中一个主要原因是阿里云提供基于钉钉群构建的24贴身技术支持,刘隽表示,这种服务模式可以更充分.高效的对接需求,快速得到反馈,这也让梨视频的同学有信心去尝试一些新的方案. 在上海云栖大会视频专场中,梨视频CTO刘隽先生分享了梨视频拍客生产全流程及其背后的技术,同时作为业务使用方,向现场嘉宾阿里云产品的使用实践. 云上拍客梨视频 梨视频是全球第一资讯短视频内容生产和消费平台,拥有5万名全球核心拍客,遍布全球七大洲,覆盖525个国际主要城市和2000多个国内

如何基于阿里云搭建适合初创企业的轻量级架构?

摘要: 在项目的初期往往存在很多变数,业务逻辑时刻在变,而且还要保证快速及时,所以,一个灵活多变.快速部署.持续集成并可以适应多种情况的架构便显得尤为重要.本文主要介绍基于阿里云搭建适合项目初期的后端架构 ----基于阿里云搭建的适合初创企业的轻量级架构 前言在项目的初期往往存在很多变数,业务逻辑时刻在变,而且还要保证快速及时,所以,一个灵活多变.快速部署.持续集成并可以适应多种情况的架构便显得尤为重要.本文主要介绍基于阿里云搭建适合项目初期的后端架构,至于细节操作不作描述,比如nginx配置优

阿里云短信验证_基于阿里云OpenAPI实现

阿里云短信服务 背景简介: 短信验证以及短信通知,目前已经应用的非常广泛,最近因项目需要,需要将原来的短信接口换成阿里云的的短信服务,原项目集成的短信服务能够实现短信的发送以及短信的验证整个过程,简单的来说,原来的短息服务,只需应用申请获取短信,短息服务器会发送短信到指定的手机,用户将验证码发送到短信服务商的服务器,服务器做出验证返回是否通过,而阿里云仅提供短信发送服务,需要自己开发短信的验证.下面简单的介绍一下: 1.获取阿里云AccessKey 用户->Accesskeys:需要自己创建一个