跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

阿里云DNS API调用详解:从零实现一个DDNS客户端

前言

动态域名解析(DDNS)的核心问题在于:如何让一个没有固定公网IP的设备(如家庭树莓派、NAS)能够通过一个固定的域名被外网访问。解决这个问题的钥匙,就是阿里云提供的DNS API。

本文将详细解析阿里云DNS API的调用机制,并以一个C++实现的DDNS客户端为例,展示如何通过这两个API完成域名解析记录的动态更新。

一、核心API介绍

阿里云DNS服务(Alibaba Cloud DNS)提供了完整的API接口,用于管理域名解析记录。在我们的DDNS实现中,主要使用了以下两个API:

1. DescribeDomainRecords – 查询解析记录

作用:获取指定域名下的所有DNS解析记录列表。

为什么需要它:要更新一条DNS记录,必须先知道它的唯一标识符——RecordId。这个API正是用来获取这个ID的。

关键请求参数:

参数说明示例
ActionAPI名称,固定值DescribeDomainRecords
DomainName要查询的主域名example.com
RRKeyWord主机记录关键字,用于过滤www
TypeKeyWord记录类型关键字,用于过滤A

返回数据结构:

{
  "RequestId": "536E9CAD-DB30-4647-AC87-AA5CC38C5382",
  "DomainRecords": {
    "Record": [
      {
        "RecordId": "1234567890123456",
        "RR": "www",
        "Type": "A",
        "Value": "1.2.3.4",
        "TTL": 600
      }
    ]
  }
}

2. UpdateDomainRecord – 更新解析记录

作用:修改一条已存在的DNS解析记录的值。

关键请求参数:

参数说明示例
ActionAPI名称,固定值UpdateDomainRecord
RecordId要修改的记录ID1234567890123456
RR主机记录www
Type记录类型(A为IPv4)A
Value新的记录值(新的IP地址)203.0.113.1
TTL缓存时间(秒)600

成功返回:

{
  "RequestId": "536E9CAD-DB30-4647-AC87-AA5CC38C5382",
  "RecordId": "1234567890123456"
}

二、API调用流程

整个DDNS更新过程可以概括为下图所示的流程:

三、API调用的核心难点:签名机制

阿里云API要求每个请求都必须附带签名,这是调用过程中最复杂的环节。签名的作用是:

  • 身份验证:证明请求是由合法的AccessKey持有者发起的
  • 请求防篡改:确保请求在传输过程中未被修改

签名算法步骤

待签名字符串 = HTTP方法 + "&" + URI编码 + "&" + 参数串编码
签名 = Base64( HMAC-SHA1(待签名字符串, AccessKeySecret + "&") )

详细分解

第一步:准备参数

将所有请求参数(包括API的Action参数和公共参数)放入一个集合:

Action=UpdateDomainRecord
RecordId=1234567890123456
RR=www
Type=A
Value=203.0.113.1
AccessKeyId=LTAI5txxxxx
Format=JSON
SignatureMethod=HMAC-SHA1
SignatureVersion=1.0
SignatureNonce=20240609123456
Timestamp=2024-06-09T12:00:00Z
Version=2015-01-09

第二步:参数排序与规范化

按参数名升序排序,然后拼接成 key=value&key=value 格式:

AccessKeyId=LTAI5txxxxx&Action=UpdateDomainRecord&Format=JSON&RR=www&RecordId=1234567890123456&SignatureMethod=HMAC-SHA1&SignatureNonce=20240609123456&SignatureVersion=1.0&TTL=600&Timestamp=2024-06-09T12:00:00Z&Type=A&Value=203.0.113.1&Version=2015-01-09

第三步:URL编码

对上一步的字符串进行URL编码,得到参数串。

第四步:构造待签名字符串

stringToSign = "GET&%2F&" + URL编码后的参数串

其中:

  • GET 是HTTP方法
  • %2F 是 / 的URL编码

第五步:计算签名

使用HMAC-SHA1算法,以 AccessKeySecret + "&" 作为密钥,对待签名字符串进行加密,然后对加密结果进行Base64编码:

signature = Base64( HMAC-SHA1(stringToSign, AccessKeySecret + "&") )

第六步:发送请求

将签名作为Signature参数附加到URL末尾,发送HTTP GET请求:

https://alidns.aliyuncs.com/?[参数串]&Signature=[计算出的签名]

四、代码实现要点

1. 签名计算的C++实现

std::string hmacSha1(const std::string& data, const std::string& key) {
    unsigned char digest[20];
    HMAC(EVP_sha1(), key.c_str(), key.length(),
         (unsigned char*)data.c_str(), data.length(), digest, nullptr);
    return base64_encode(digest, 20);
}

std::string buildSignature(const std::string& method, 
                           const std::string& uri,
                           const std::string& queryString,
                           const std::string& secret) {
    std::string stringToSign = method + "&" + urlEncode(uri) + "&" + urlEncode(queryString);
    return hmacSha1(stringToSign, secret + "&");
}

2. JSON解析(不依赖第三方库)

由于只需要提取少数几个字段,可以用简单的字符串查找代替完整的JSON解析库:

std::string extractJsonString(const std::string& json, const std::string& key) {
    std::string search = "\"" + key + "\"";
    size_t keyPos = json.find(search);
    if (keyPos == std::string::npos) return "";

    size_t colonPos = json.find(":", keyPos);
    if (colonPos == std::string::npos) return "";

    size_t startQuote = json.find("\"", colonPos);
    if (startQuote == std::string::npos) return "";

    size_t endQuote = json.find("\"", startQuote + 1);
    if (endQuote == std::string::npos) return "";

    return json.substr(startQuote + 1, endQuote - startQuote - 1);
}

3. 完整的API调用流程

bool updateDDNS(const std::string& rr, const std::string& newIP) {
    // 第一步:查询获取RecordId
    std::string recordId = describeDomainRecords(rr);
    if (recordId.empty()) return false;

    // 第二步:更新记录
    return updateDomainRecord(recordId, rr, newIP);
}

五、公共参数说明

每次调用阿里云API时,除了业务参数外,还需要携带以下公共参数:

参数说明生成方式
AccessKeyId访问密钥ID从阿里云控制台获取
Format返回格式固定为JSON
SignatureMethod签名方法固定为HMAC-SHA1
SignatureVersion签名版本固定为1.0
SignatureNonce唯一随机数时间戳+随机数,确保每次请求不同
Timestamp请求时间戳UTC时间,格式YYYY-MM-DDThh:mm:ssZ
VersionAPI版本2015-01-09

六、常见错误码及处理

错误码含义处理方法
InvalidAccessKeyIdAccessKeyId不存在检查AK是否正确
SignatureDoesNotMatch签名不匹配检查签名算法和参数编码
DomainRecordNotBelongToUser记录不属于该用户检查域名是否在账号下
InvalidRR主机记录无效检查RR参数格式
InvalidValue记录值无效检查IP格式是否正确

七、总结

通过阿里云DNS API实现DDNS,本质上是一个“查询-更新”的两步操作:

  1. 用DescribeDomainRecords获取记录ID
  2. 用UpdateDomainRecord更新记录值

而这一切的核心难点在于签名机制——它是阿里云API安全体系的基础,也是调用任何阿里云OpenAPI时必须掌握的知识。

本文实现的DDNS客户端,完整展示了如何不依赖第三方库,从零开始构建符合阿里云规范的API请求。这套方法不仅适用于DNS服务,也同样适用于ECS、OSS等其他阿里云产品的API调用。

作者

老丹

关注我
其他文章
上一个

curl库完全指南:从命令行到libcurl开发

下一个

阿里云前缀列表 API 实战:如何精准更新指定的 CIDR 地址块

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

搜索

近期文章

  • 阿里云前缀列表权限设置完全指南 2026年6月9日
  • 阿里云前缀列表 API 实战:如何精准更新指定的 CIDR 地址块 2026年6月9日
  • 阿里云DNS API调用详解:从零实现一个DDNS客户端 2026年6月9日
  • curl库完全指南:从命令行到libcurl开发 2026年6月8日
  • FastCGI 协议详解:从 CGI 的瓶颈到高性能 Web 应用 2026年6月7日

文章分类

  • C/C++开发 (9)
  • Linux服务配置 (3)
  • 计算机理论 (4)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号