阿里云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的。
关键请求参数:
| 参数 | 说明 | 示例 |
|---|---|---|
| Action | API名称,固定值 | 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解析记录的值。
关键请求参数:
| 参数 | 说明 | 示例 |
|---|---|---|
| Action | API名称,固定值 | UpdateDomainRecord |
| RecordId | 要修改的记录ID | 1234567890123456 |
| 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 |
| Version | API版本 | 2015-01-09 |
六、常见错误码及处理
| 错误码 | 含义 | 处理方法 |
|---|---|---|
| InvalidAccessKeyId | AccessKeyId不存在 | 检查AK是否正确 |
| SignatureDoesNotMatch | 签名不匹配 | 检查签名算法和参数编码 |
| DomainRecordNotBelongToUser | 记录不属于该用户 | 检查域名是否在账号下 |
| InvalidRR | 主机记录无效 | 检查RR参数格式 |
| InvalidValue | 记录值无效 | 检查IP格式是否正确 |
七、总结
通过阿里云DNS API实现DDNS,本质上是一个“查询-更新”的两步操作:
- 用
DescribeDomainRecords获取记录ID - 用
UpdateDomainRecord更新记录值
而这一切的核心难点在于签名机制——它是阿里云API安全体系的基础,也是调用任何阿里云OpenAPI时必须掌握的知识。
本文实现的DDNS客户端,完整展示了如何不依赖第三方库,从零开始构建符合阿里云规范的API请求。这套方法不仅适用于DNS服务,也同样适用于ECS、OSS等其他阿里云产品的API调用。