当前位置: 首页 > 后端技术 > Java

openwrt使用阿里DDNS更新失败问题

时间:2023-04-02 10:04:41 Java

最近买了一个openwrt软路由器,开始了折腾之旅。首先是做好动态域名映射。还好openwrt集成了阿里的DDNS服务,但是没想到的是:这真是折腾……折腾的前提:我已经申请了中国电信的公网IP,阿里申请的。域名,如果是:foo.bar折腾行程:0希望阿里动态能帮我自动更新如下信息:1、在定时任务中,查询阿里动态的执行命令#crontab-l,结果如下:*/10****/usr/sbin/aliddns>>/var/log/aliddns.log2>&1找到执行脚本:/usr/sbin/aliddns2,启用脚本调试信息输出:add在脚本第一行之前:#输出调试信息(执行的命令)set-x#调试信息格式(打印行号)PS4='+${LINENO}:'调试脚本重复执行后,输出urlencode部分太长,影响阅读,可以在urlencode函数执行前后添加:设置+x关闭调试,设置-x打开调试,这样整个命令执行过程就多了更清晰。3、定位到第一个问题:

NotFound

请求的URL/dyndns/getip没有在这个服务器上找到。获取公网IP地址时使用了如下代码:intelnetip(){tmp_ip=`curl-sL--connect-timeout3members.3322.org/dyndns/getip`if["Z$tmp_ip"=="Z"];然后tmp_ip=`curl-sL--connect-timeout3api-ipv4.ip.sb/ip`fiecho-n$tmp_ip}把命令拿出来单独执行:curlmembers.3322.org/dyndns/getip就可以了原来是members.3322.org网站停止了公网IP服务,返回了404头。但是脚本中是根据返回值是否为空来判断执行成功还是失败,很明显是误判了。这样,只要注释掉这行代码即可。4.继续执行,又遇到错误:curl:(3)Error2022-08-2823:10:00ADDrecord2022-08-2823:10:00#ERROR,PleaseCheckConfig/Time是不是系统时间?问题?查看了系统时间,好像没有问题。无奈之下,只好仔细查看阿里云解析API。同时,为了方便修改bash脚本,将aliddns复制到一个临时目录下,方便修改执行。5、阿里云解析API文档:https://help.aliyun.com/docum...仔细查阅文档,大致有以下规则:以RPC调用规则为例:-请求参数包括公共请求参数和业务请求参数。-将参数按字母顺序排序,将参数名/参数值按照UTF-8进行编码,然后urlencode得到“规范化查询字符串”-添加“规范化查询字符串”:RequestMethod(如GET),Secretkeyinformation(AccessSecret),使用HMAC-SHA1计算消息摘要,作为最后一个参数添加到URL请求中。6.使用Java实现需要用到的3个API:-DescribeSubDomainRecords获取解析记录列表-AddDomainRecord添加解析记录-UpdateDomainRecord修改解析记录继续遇到以下问题:-冷门点之一:HMAC-SHA1messageDigestalgorithm:与一般JDK自带的java.security.MessageDigest用于MD5消息摘要不同,HMAC-SHA1不包含在MessageDigest中,而是由jce扩展包提供:javax.crypto.Mac。(Mac和MessageDigest的API接口几乎一样)-冷门点2:Timestamp需要指定UTC时间-弯路1:JDK自带的URLEncoder没有对'*'字符进行编码。根据阿里文档的要求(按照RFC3986规则进行编码),'*'字符也应该编码为:'%2A',??这是在反复调用和比较请求返回的结果后发现的。-弯路2:UpdateDomainRecord使用两个参数RR和RecordId。一开始以为RecordId参数在前面,RR在后面;调用比较后,发现RR参数排在最前面。原来忽略了字母大小写的问题。所有大写字母的ASCii码都低于小写字母。解除javaURLEncoder对“*”字符的编码如下://javaURLEncoder不提供用户指定和修改“免编码字符”的机会。以下方法的修改具有全局影响,仅用于测试环境。字段字段=URLEncoder.class.getDeclaredField("dontNeedEncoding");field.setAccessible(true);BitSetdontNeedEncoding=(BitSet)field.get(null);dontNeedEncoding.clear('*');几个JavaAPI的写法终于通过Javaapi的实现,调用阿里dns云解析api所需的基本操作也已经明确了。继续返回aliddns脚本故障排除。7、aliddns脚本主要逻辑:openwrt提供了统一的配置接口uci,openwrt的aliddns配置信息通过uci保存在/etc/config/aliddns文件中。脚本主要逻辑如下:-通过uci读取阿里ddns配置-通过第三方API获取本地WAN口公网IP-通过nslookup命令从域名服务中查询域名映射ip-WAN口ip和路由器查询ip,如果不同则调用DescribeSubDomainRecords获取解析记录并删除;然后添加一条新的解析记录8.查询解析记录失败问题:通过uci读取的阿里ddns配置中的子域名不正确(这里子域名配置为“*”)。发现问题在下面的代码中:uci_get_by_name(){localret=$(uciget$NAME.$1.$22>/dev/null)echo${ret:=$3}}在读取子域名的时候居然执行了echo*,在bash中执行后,返回结果类似于ls命令,列出当前目录下的所有文件。当然我得到了错误的结果。只需将其更改为:echo"${ret:=$3}"。发送DescribeSubDomainRecords查询请求时,子域名的“*”没有进行编码,不符合阿里API的要求。query_recordid(){send_request"DescribeSubDomainRecords""SignatureMethod=HMAC-SHA1&SignatureNonce=$timestamp&SignatureVersion=1.0&SubDomain=$sub_dm.$main_dm&Timestamp=$timestamp&Type=A"}核心是&SubDomain=$sub_dm.$main_dm。$sub_dm.$main_dm为urlencode,更改为:&SubDomain=$(urlencode$sub_dm.$main_dm)但是!修改后*.foo.bar编码为%61%74.foo.bar,解码结果为at.foo.bar???谁知道这是怎么回事?9.到现在为止,我已经被折磨得有点崩溃了。阿里ddns里用'*'泛域名解析都快要放弃了。幸运的是!既然读取和编码'*'字符有问题,那么直接配置'*'字符为'%2A'怎么样?应该可以感受到。尝试一下!果然解析成功!好的!总结一下:只需要注意两点:1.注释掉/usr/sbin/aliddns脚本中的members.3322.org这行代码2.在阿里ddns服务中,如果子域名是“*”,可以写成“%2A”(其他通用二级域名可以正常更新)记录下这段从折腾到被折磨的历程。不过,也有一些收获。比如:我之前没有调用过阿里云API,有些API比较冷门用,熟悉bash的潜规则(之前用的很少,没有系统总结)。