openwrt支持dnspod动态解析(IPV4/IPV6)
文章目录
openwrt带的ddns不支持dnspod.然后弄了这个脚本,用于更新dnspod的动态域名解析.
如果想要纯IPV6解析,或者IPV4+IPV6双解析.必须要AAAA解析.
弄了一个脚本,主要是IPV6,当然脚本也支持IPV4.
在贴脚本之前,先提供一个临时方案.
中转方案
dynv6.net这家运营商在ddns支持列表.
且同时支持IPV6+IPV4双解析.
演示域名
windows下
ping -4 t2.getce.cn
ping -6 t2.getce.cn
非windows
ping t2.getce.cn
ping6 t2.getce.cn
添加dynv6.net解析
先用dynv6.net添加IPV4 和IPV6双解析的ddns设置.
这个luci界面可以简单处理.
cname解析到dynv6.net
在dnspod中添加cname解析到dynv6.net即可.
优点
解析方便.设置简单.一般场景足够了.
缺点
cname解析和mx解析冲突.
用以下解析才不会.
- A+MX (IPV4解析 +邮箱)
- AAAA+MX (IPV6解析 +邮箱)
- A+AAAA+MX (IPV4 + IPV6 +邮箱)
DNSPOD直解方案
也就是直接路由器脚本更新DNSPOD的AAAA和A记录.
我这里演示主要是AAAA解析.A记录只需要将脚本复制一份,稍微修改下即可.
新建一个AAAA解析
AAAA解析就是IPV6的解析.
这步不说明,我用的是DNSPOD,也就现在的腾讯云
以下域名部分的说明都是dnspod.
路由器添加解析脚本
要求
首先要保证你wget 支持https,
如果wget不支持https,安装一个"ca-"开头的软件包,有两个. 路由容量够就都装.
解析脚本
这个脚本我稍微修改过,适应路由器wget.
脚本上有原作者信息.
将文件保存到 /root/dnspod.sh
#!/bin/bash
#################################################
# AnripDdns v5.08
# Dynamic DNS using DNSPod API
# Original by anrip<mail@anrip.com>, http://www.anrip.com/ddnspod
# Edited by ProfFan
#################################################
#################################################
# 2018-11-06
# support LAN / WAN / IPV6 resolution
# 2019-05-24
# Support Ipv6 truly (Yes, it was just claimed to, but actually not = =!)
# Add another way resolving IPv6, for machines without nvram.
#if you have any issues, please let me know.
# https://blog.csdn.net/Imkiimki/article/details/83794355
# Daleshen mailto:gf@gfshen.cn
#################################################
#Please select IP type
IPtype=3 #1.WAN 2.LAN 3.IPv6
#---------------------
if [ $IPtype = '3' ]; then
record_type='AAAA'
else
record_type='A'
fi
echo Type: ${record_type}
# OS Detection
case $(uname) in
'Linux')
echo "OS: Linux"
arIpAddress() {
case $IPtype in
'1')
curltest=`which curl`
if [ -z "$curltest" ] || [ ! -s "`which curl`" ]
then
#根据实际情况选择使用合适的网址
#wget --no-check-certificate --quiet --output-document=- "https://www.ipip.net" | grep "IP地址" | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
wget --no-check-certificate --secure-protocol=TLSv1_2 --quiet --output-document=- "http://members.3322.org/dyndns/getip" | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
#wget --no-check-certificate --secure-protocol=TLSv1_2 --quiet --output-document=- "ip.6655.com/ip.aspx" | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
#wget --no-check-certificate --secure-protocol=TLSv1_2 --quiet --output-document=- "ip.3322.net" | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
else
curl -k -s "http://members.3322.org/dyndns/getip" | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
#curl -L -k -s "https://www.ipip.net" | grep "IP地址" | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
#curl -k -s ip.6655.com/ip.aspx | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
#curl -k -s ip.3322.net | grep -E -o '([0-9]+\.){3}[0-9]+' | head -n1 | cut -d' ' -f1
fi
;;
'2')
ip -o -4 addr list | grep -Ev '\s(docker|lo)' | awk '{print $4}' | cut -d/ -f1
;;
'3')
# 因为一般ipv6没有nat ipv6的获得可以本机获得
#ifconfig $(nvram get wan0_ifname_t) | awk '/Global/{print $3}' | awk -F/ '{print $1}'
ip addr show dev br-lan |grep "global dynamic" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d;'| awk 'NR==1' #如果没有nvram,使用这条,注意将eth0改为本机上的网口设备 (通过 ifconfig 查看网络接口)
;;
esac
}
;;
'FreeBSD')
echo 'FreeBSD'
exit 100
;;
'WindowsNT')
echo "Windows"
exit 100
;;
'Darwin')
echo "Mac"
arIpAddress() {
ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}'
}
;;
'SunOS')
echo 'Solaris'
exit 100
;;
'AIX')
echo 'AIX'
exit 100
;;
*) ;;
esac
echo "Address: $(arIpAddress)"
# Get script dir
# See: http://stackoverflow.com/a/29835459/4449544
rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.
target=$1 fname= targetDir= CDPATH=
{ \unalias command; \unset -f command; } >/dev/null 2>&1
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
while :; do # Resolve potential symlinks until the ultimate target is found.
[ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
fname=$(command basename -- "$target") # Extract filename.
[ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
if [ -L "$fname" ]; then
# Extract [next] target path, which may be defined
# *relative* to the symlink's own directory.
# Note: We parse `ls -l` output to find the symlink target
# which is the only POSIX-compliant, albeit somewhat fragile, way.
target=$(command ls -l "$fname")
target=${target#* -> }
continue # Resolve [next] symlink target.
fi
break # Ultimate target reached.
done
targetDir=$(command pwd -P) # Get canonical dir. path
# Output the ultimate target's canonical path.
# Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
if [ "$fname" = '.' ]; then
command printf '%s\n' "${targetDir%/}"
elif [ "$fname" = '..' ]; then
# Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
# AFTER canonicalization.
command printf '%s\n' "$(command dirname -- "${targetDir}")"
else
command printf '%s\n' "${targetDir%/}/$fname"
fi
)
DIR=$(dirname -- "$(readlink "$0")")
# Global Variables:
# Token-based Authentication
arToken=""
# Account-based Authentication
arMail=""
arPass=""
# Load config
#. $DIR/dns.conf
# Get Domain IP
# arg: domain
arDdnsInfo() {
local domainID recordID recordIP
# Get domain ID
domainID=$(arApiPost "Domain.Info" "domain=${1}")
domainID=$(echo $domainID | sed 's/.*{"id":"\([0-9]*\)".*/\1/')
# Get Record ID
recordID=$(arApiPost "Record.List" "domain_id=${domainID}&sub_domain=${2}&record_type=${record_type}")
recordID=$(echo $recordID | sed 's/.*\[{"id":"\([0-9]*\)".*/\1/')
# Last IP
recordIP=$(arApiPost "Record.Info" "domain_id=${domainID}&record_id=${recordID}&record_type=${record_type}")
recordIP=$(echo $recordIP | sed 's/.*,"value":"\([0-9a-z\.:]*\)".*/\1/')
# Output IP
case "$recordIP" in
[1-9a-z]*)
echo $recordIP
return 0
;;
*)
echo "Get Record Info Failed!"
return 1
;;
esac
}
# Get data
# arg: type data
# see Api doc: https://www.dnspod.cn/docs/records.html#
arApiPost() {
local agent="AnripDdns/5.07(mail@anrip.com)"
#local inter="https://dnsapi.cn/${1:?'Info.Version'}"
local inter="https://dnsapi.cn/${1}"
if [ "x${arToken}" = "x" ]; then # undefine token
local param="login_email=${arMail}&login_password=${arPass}&format=json&${2}"
else
local param="login_token=${arToken}&format=json&${2}"
fi
wget --quiet --no-check-certificate -O - --post-data=$param $inter
}
# Update
# arg: main domain sub domain
arDdnsUpdate() {
local domainID recordID recordRS recordCD recordIP myIP
# Get domain ID
domainID=$(arApiPost "Domain.Info" "domain=${1}")
domainID=$(echo $domainID | sed 's/.*{"id":"\([0-9]*\)".*/\1/')
#echo $domainID
# Get Record ID
recordID=$(arApiPost "Record.List" "domain_id=${domainID}&record_type=${record_type}&sub_domain=${2}")
recordID=$(echo $recordID | sed 's/.*\[{"id":"\([0-9]*\)".*/\1/')
#echo $recordID
# Update IP
myIP=$(arIpAddress)
recordRS=$(arApiPost "Record.Modify" "domain_id=${domainID}&sub_domain=${2}&record_type=${record_type}&record_id=${recordID}&record_line=默认&value=${myIP}")
recordCD=$(echo $recordRS | sed 's/.*{"code":"\([0-9]*\)".*/\1/')
recordIP=$(echo $recordRS | sed 's/.*,"value":"\([0-9a-z\.:]*\)".*/\1/')
# Output IP
if [ "$recordIP" = "$myIP" ]; then
if [ "$recordCD" = "1" ]; then
echo $recordIP
return 0
fi
# Echo error message
echo $recordRS | sed 's/.*,"message":"\([^"]*\)".*/\1/'
return 1
else
echo $recordIP #"Update Failed! Please check your network."
return 1
fi
}
# DDNS Check
# Arg: Main Sub
arDdnsCheck() {
local postRS
local lastIP
local hostIP=$(arIpAddress)
echo "Updating Domain: ${2}.${1}"
echo "hostIP: ${hostIP}"
lastIP=$(arDdnsInfo $1 $2)
if [ $? -eq 0 ]; then
echo "lastIP: ${lastIP}"
if [ "$lastIP" != "$hostIP" ]; then
postRS=$(arDdnsUpdate $1 $2)
if [ $? -eq 0 ]; then
echo "update to ${postRS} successed."
return 0
else
echo ${postRS}
return 1
fi
fi
echo "Last IP is the same as current, no action."
return 1
fi
echo ${lastIP}
return 1
}
# DDNS
#echo ${#domains[@]}
#for index in ${!domains[@]}; do
# echo "${domains[index]} ${subdomains[index]}"
# arDdnsCheck "${domains[index]}" "${subdomains[index]}"
#done
. $DIR/dns.conf
配置文件
保存到脚本同目录下,文件名是上面脚本指定的.
所以保存到:
/root/dns.conf
# 指定你DNSPODtoken
arToken="10xxxx,dbxxxxxxxxxxxxxxxxxxxxxxxxxx"
# 2. 添加你要ddns解析的域名,一行一个
# 下面记录就是更新 ipv6.getce.cn
# yge.me 是你域名
# ipv6 是子域名
# 解析是固定AAAA解析,前面脚本固定了.
arDdnsCheck "yge.me" "ipv6"
测试脚本
root@OpenWrt:~# sh /root/dnspod.sh
# 输出
Type: AAAA
OS: Linux
Address: 240e:370:3f0c:ce0::1
Updating Domain: ipv6.getce.cn
hostIP: 240e:370:3f0c:ce0::1
lastIP: 240e:370:3f08:cba0::1
update to 240e:370:3f0c:ce0::1 successed.
# 如果IP没有变化会提示no action.
Type: AAAA
OS: Linux
Address: 240e:370:3f0c:ce0::1
Updating Domain: ipv6.getce.cn
hostIP: 240e:370:3f0c:ce0::1
lastIP: 240e:370:3f0c:ce0::1
Last IP is the same as current, no action.
看到update成功就是对的了.如果看到wget错误,极有可能就是ssl支持问题. 参考前面说明安装 "ca-"开头的软件包.
::1 结尾的一般就是路由器ip.
添加cron
*/5 * * * * /bin/sh /root/dnspod.sh >>/tmp/dnspod.log 2>&1
# tmp生成一个日志文件.自己可以查看
每5分钟执行一次.
如果要求高一些可以设置短一些.
脚本做了校验,只有IP变动才会上传新IP到DNSPOD.
所以时间间隔短一些也没问题.
A解析
如果需要A解析,只需要将前面两个文件复制一份,稍微修改.
其他照葫芦画瓢即可.
主要修改解析脚本这两行.
IPtype=3 #1.WAN 2.LAN 3.IPv6
. $DIR/dns.conf
IPtyep = 1
配置文件另外指定一个,比如:
dns_v4.conf
改完自己测试下,没问题再添加到cron中执行即可.
热拔插事件
LINUX系统有个热拔插事件,无论是USB或者网络断开连接都会自动触发.
不作过多介绍.想了解更多 -- > https://github.com/wywincl/hotplug
vi /etc/hotplug.d/iface/98-dnspod-up
新建并保存
#!/bin/bash
[ "$ACTION" = ifup ] || exit 0
[ "$INTERFACE" = vwan1 ] || exit 0
/bin/sh /root/dnspod.sh >>/tmp/dnspod_up.log 2>&1
我这里是多播,所以我的是vwan1 一般如果不是多播的用户这里是wan
参考
https://isnimitz.com/posts/ddns-with-openwrt-and-dnspod/
https://my.oschina.net/kmwzjs/blog/687408