diff --git a/cfddns/cfddns/scripts/cfddns_config.sh b/cfddns/cfddns/scripts/cfddns_config.sh
index 41012c89..797742b8 100755
--- a/cfddns/cfddns/scripts/cfddns_config.sh
+++ b/cfddns/cfddns/scripts/cfddns_config.sh
@@ -6,16 +6,36 @@ alias echo_date='echo 【$(TZ=UTC-8 date -R +%Y年%m月%d日\ %X)】:'
CONFIG_FILE="/tmp/cfddns_status.json"
LOG_FILE="/tmp/upload/cfddns_log.txt"
LOGTIME=$(TZ=UTC-8 date -R "+%Y-%m-%d %H:%M:%S")
+MAX_RECORDS=10
IPv4Regex="^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$"
IPv6Regex="^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
[ "$cfddns_ttl" = "" ] && cfddns_ttl="1"
+[ "$cfddns_method_v4" = "" ] && cfddns_method_v4="curl ifconfig.me"
+[ "$cfddns_method_v6" = "" ] && cfddns_method_v6="curl -6 ifconfig.me"
-if [ "$cfddns_name" = "@" ];then
- cfddns_name_domain=$cfddns_domain
-else
- cfddns_name_domain=$cfddns_name.$cfddns_domain
-fi
+trim_text(){
+ echo "$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
+}
+
+build_fqdn(){
+ if [ "$1" = "@" ];then
+ echo "$2"
+ else
+ echo "$1.$2"
+ fi
+}
+
+append_status_line(){
+ if [ -z "$1" ];then
+ return
+ fi
+ if [ -z "$status_lines" ];then
+ status_lines="$1"
+ else
+ status_lines="${status_lines}
$1"
+ fi
+}
get_bol() {
case "$cfddns_proxied" in
@@ -29,76 +49,173 @@ get_bol() {
}
get_record_response() {
- curl -kLsX GET "https://api.cloudflare.com/client/v4/zones/${cfddns_zid}/dns_records?type=${record_type}&name=${cfddns_name_domain}&order=type&direction=desc&match=all" \
+ local current_fqdn="$1"
+ curl -kLsG "https://api.cloudflare.com/client/v4/zones/${cfddns_zid}/dns_records" \
-H "Authorization: Bearer ${cfddns_akey}" \
- -H "Content-type: application/json"
+ -H "Content-type: application/json" \
+ --data-urlencode "type=${record_type}" \
+ --data-urlencode "name=${current_fqdn}" \
+ --data-urlencode "order=type" \
+ --data-urlencode "direction=desc" \
+ --data-urlencode "match=all"
}
update_record() {
+ local current_fqdn="$1"
curl -kLsX PUT "https://api.cloudflare.com/client/v4/zones/${cfddns_zid}/dns_records/${cfddns_id}" \
-H "Authorization: Bearer ${cfddns_akey}" \
-H "Content-Type: application/json" \
- --data '{"type":"'${record_type}'","name":"'${cfddns_name_domain}'","content":"'${update_to_ip}'","ttl":'${cfddns_ttl}',"proxied":'$(get_bol)'}'
+ --data '{"type":"'${record_type}'","name":"'${current_fqdn}'","content":"'${update_to_ip}'","ttl":'${cfddns_ttl}',"proxied":'$(get_bol)'}'
}
get_info(){
- cfddns_result=`get_record_response`
- if [ $(echo $cfddns_result | grep -c "\"success\":true") -gt 0 ];then
+ local current_fqdn="$1"
+ cfddns_result=`get_record_response "$current_fqdn"`
+ if [ $(echo "$cfddns_result" | grep -c "\"success\":true") -gt 0 ];then
+ if [ $(echo "$cfddns_result" | grep -c "\"result\":\\[\\]") -gt 0 ];then
+ record_error="【$LOGTIME】:${current_fqdn} 未找到IP${ip_type}解析记录!"
+ return 1
+ fi
# CFDDNS的RECORD ID
- cfddns_id=`echo $cfddns_result | awk -F"","" '{print $1}' | sed 's/{.*://g' | sed 's/\"//g'`
+ cfddns_id=`echo "$cfddns_result" | grep -o '"id":"[^"]*"' | head -n1 | cut -d'"' -f4`
# CFDDNS的RECORD IP
- record_ip=`echo $cfddns_result | awk -F"","" '{print $6}' | sed -e 's/\"//g' -e 's/content://'`
- echo_date CloudFlare IP${ip_type}为 $record_ip
+ record_ip=`echo "$cfddns_result" | grep -o '"content":"[^"]*"' | head -n1 | cut -d'"' -f4`
+ if [ -z "$cfddns_id" -o -z "$record_ip" -o $(echo "$cfddns_id" | grep -Ec '^[0-9a-fA-F]{32}$') -eq 0 ];then
+ record_error="【$LOGTIME】:${current_fqdn} 获取IP${ip_type}解析记录错误!"
+ return 1
+ fi
+ echo_date "${current_fqdn} CloudFlare IP${ip_type}为 $record_ip"
else
- dbus set cfddns_status_${ip_type}="【$LOGTIME】:获取IP${ip_type}解析记录错误!"
- exit 1
+ record_error="【$LOGTIME】:${current_fqdn} 获取IP${ip_type}解析记录错误!"
+ return 1
fi
-
- localip=`$cfddns_method` 2>&1
- if [ $(echo $localip | grep -Ec $IPv4Regex) -gt 0 -o $(echo $localip | grep -Ec $IPv6Regex) -gt 0 ];then
- echo_date 本地IP${ip_type}为 $localip
+
+ localip_raw=`$cfddns_method` 2>&1
+ if [ "$ip_type" = "v4" ];then
+ localip=`echo "$localip_raw" | tr '\r\t ' '\n\n\n' | grep -E "$IPv4Regex" | head -n1`
else
- dbus set cfddns_status_${ip_type}="【$LOGTIME】:获取本地IP${ip_type}错误!"
- exit 1
+ localip=`echo "$localip_raw" | tr '\r\t ' '\n\n\n' | grep -E "$IPv6Regex" | head -n1`
+ fi
+ if [ -n "$localip" ];then
+ echo_date "${current_fqdn} 本地IP${ip_type}为 $localip"
+ else
+ echo_date "${current_fqdn} 获取本地IP${ip_type}错误,命令:$cfddns_method"
+ echo_date "${current_fqdn} 命令输出:$localip_raw"
+ record_error="【$LOGTIME】:${current_fqdn} 获取本地IP${ip_type}错误!"
+ return 1
fi
}
update_ip(){
- update_result=`update_record`
- if [ $(echo $update_result | grep -c "\"success\":true") -gt 0 ];then
- echo_date 更新成功!
+ local current_fqdn="$1"
+ update_result=`update_record "$current_fqdn"`
+ if [ $(echo "$update_result" | grep -c "\"success\":true") -gt 0 ];then
+ echo_date "${current_fqdn} 更新成功!"
else
- echo_date 更新失败!请检查设置!
- echo $update_result
- exit 1
+ echo_date "${current_fqdn} 更新失败!请检查设置!"
+ echo "$update_result"
+ record_error="【$LOGTIME】:${current_fqdn} 更新失败!请检查设置!"
+ return 1
fi
}
check_update(){
- if [ $1 -eq 4 ];then
+ local update_target=$1
+ local current_fqdn="$2"
+ record_status_line=""
+ if [ "$update_target" -eq 4 ];then
record_type="A"
ip_type="v4"
cfddns_method=$cfddns_method_v4
- [ "cfddns_method" == "" ] && cfddns_method="curl -s --interface ppp0 v4.ipip.net"
+ [ "$cfddns_method" = "" ] && cfddns_method="curl ifconfig.me"
else
record_type="AAAA"
cfddns_method=$cfddns_method_v6
- [ "cfddns_method" == "" ] && cfddns_method="curl -s --interface ppp0 v6.ipip.net"
+ [ "$cfddns_method" = "" ] && cfddns_method="curl -6 ifconfig.me"
ip_type="v6"
fi
- echo_date "CloudFlare DDNS更新启动!"
- get_info
- if [ "$localip" == "$record_ip" ];then
- echo_date 两个IP${ip_type}相同,跳过更新!
- dbus set cfddns_status_${ip_type}="【$LOGTIME】:IP${ip_type}地址:$localip 未发生变化,跳过!"
+ echo_date "${current_fqdn} CloudFlare DDNS更新启动!"
+ if ! get_info "$current_fqdn"; then
+ echo_date "$record_error"
+ record_status_line="$record_error"
+ echo_date "======================================"
+ return 1
+ fi
+ if [ "$localip" = "$record_ip" ];then
+ echo_date "${current_fqdn} 两个IP${ip_type}相同,跳过更新!"
+ record_status_line="【$LOGTIME】:${current_fqdn} IP${ip_type}地址:$localip 未发生变化,跳过!"
else
update_to_ip=$localip
- echo_date 两个IP${ip_type}不相同,开始更新!
- update_ip
- dbus set cfddns_status_${ip_type}="【$LOGTIME】:IP${ip_type}地址:$localip 更新成功!"
+ echo_date "${current_fqdn} 两个IP${ip_type}不相同,开始更新!"
+ if update_ip "$current_fqdn"; then
+ record_status_line="【$LOGTIME】:${current_fqdn} IP${ip_type}地址:$localip 更新成功!"
+ else
+ record_status_line="$record_error"
+ fi
fi
echo_date "======================================"
}
+
+run_updates_for_ip(){
+ local update_target=$1
+ local records_raw
+ local records_count=0
+ local records_overflow=0
+ local record_name
+ local record_domain
+ local record_fqdn
+ records_raw=$(printf '%b' "$cfddns_records" | tr '\r' '\n' | sed '/^[[:space:]]*$/d')
+ status_lines=""
+ if [ -z "$records_raw" ];then
+ status_lines="【$LOGTIME】:未配置域名记录!"
+ if [ "$update_target" -eq 4 ];then
+ dbus set cfddns_status_v4="$status_lines"
+ else
+ dbus set cfddns_status_v6="$status_lines"
+ fi
+ echo_date "未配置域名记录,跳过IP${update_target}更新!"
+ return 1
+ fi
+
+ while IFS='|' read -r record_name record_domain; do
+ [ -z "$record_name$record_domain" ] && continue
+ records_count=$((records_count + 1))
+ if [ "$records_count" -gt "$MAX_RECORDS" ];then
+ records_overflow=1
+ break
+ fi
+
+ record_name=$(trim_text "$record_name")
+ record_domain=$(trim_text "$record_domain")
+ if [ -z "$record_name" -o -z "$record_domain" ];then
+ echo_date "第${records_count}组域名记录格式错误,已跳过!"
+ append_status_line "【$LOGTIME】:第${records_count}组域名记录格式错误,已跳过!"
+ continue
+ fi
+
+ record_fqdn=$(build_fqdn "$record_name" "$record_domain")
+ check_update "$update_target" "$record_fqdn"
+ append_status_line "$record_status_line"
+ done <> $LOG_FILE
echo_date "检测到网络拨号..." >> $LOG_FILE
- check_update 4 >> $LOG_FILE
+ run_updates_for_ip 4 >> $LOG_FILE
if [ "$cfddns_ipv6" == "1" ];then
- check_update 6 >> $LOG_FILE
+ run_updates_for_ip 6 >> $LOG_FILE
fi
else
logger "[软件中心]: CloudFlare DDNS未设置开机启动,跳过!"
fi
;;
update)
- check_update >> $LOG_FILE
+ if [ "$cfddns_enable" == "1" ];then
+ echo_date "======================================" >> $LOG_FILE
+ run_updates_for_ip 4 >> $LOG_FILE
+ if [ "$cfddns_ipv6" == "1" ];then
+ run_updates_for_ip 6 >> $LOG_FILE
+ fi
+ else
+ echo_date "CloudFlare DDNS未启用,跳过更新!" >> $LOG_FILE
+ fi
;;
esac
# ====================================submit by web====================================
@@ -133,9 +258,9 @@ case $2 in
if [ "$cfddns_enable" == "1" ];then
[ ! -L "/koolshare/init.d/S99cfddns.sh" ] && ln -sf /koolshare/scripts/cfddns_config.sh /koolshare/init.d/S99cfddns.sh
echo_date "======================================" >> $LOG_FILE
- check_update 4 >> $LOG_FILE
+ run_updates_for_ip 4 >> $LOG_FILE
if [ "$cfddns_ipv6" == "1" ];then
- check_update 6 >> $LOG_FILE
+ run_updates_for_ip 6 >> $LOG_FILE
fi
else
echo_date "关闭CloudFlare DDNS!" >> $LOG_FILE
diff --git a/cfddns/cfddns/version b/cfddns/cfddns/version
index 9459d4ba..7e32cd56 100644
--- a/cfddns/cfddns/version
+++ b/cfddns/cfddns/version
@@ -1 +1 @@
-1.1
+1.3
diff --git a/cfddns/cfddns/webs/Module_cfddns.asp b/cfddns/cfddns/webs/Module_cfddns.asp
index d4357df0..bee3b8a7 100644
--- a/cfddns/cfddns/webs/Module_cfddns.asp
+++ b/cfddns/cfddns/webs/Module_cfddns.asp
@@ -86,13 +86,15 @@ var dbus = {};
var _responseLen;
var noChange = 0;
var x = 5;
-var params_inp = ['cfddns_email', 'cfddns_akey', 'cfddns_zid', 'cfddns_name', 'cfddns_domain', 'cfddns_ttl', 'cfddns_method_v4', 'cfddns_method_v6'];
+var params_inp = ['cfddns_email', 'cfddns_akey', 'cfddns_zid', 'cfddns_ttl', 'cfddns_method_v4', 'cfddns_method_v6', 'cfddns_records'];
var params_chk = ['cfddns_enable', 'cfddns_proxied', 'cfddns_ipv6'];
+var MAX_RECORDS = 10;
function init(){
show_menu(menu_hook);
get_dbus_data();
conf2obj();
+ render_records();
toggle_func();
update_visibility();
get_status();
@@ -111,6 +113,130 @@ function conf2obj(){
}
}
+function trim_text(val){
+ return (val || "").replace(/^\s+|\s+$/g, "");
+}
+
+function get_fqdn(name, domain){
+ if (name == "@"){
+ return domain;
+ }
+ return name + "." + domain;
+}
+
+function parse_records(raw){
+ var records = [];
+ if (!raw){
+ return records;
+ }
+ var lines = raw.replace(/\\n/g, "\n").replace(/\r/g, "\n").split("\n");
+ for (var i = 0; i < lines.length; i++) {
+ var line = trim_text(lines[i]);
+ if (!line){
+ continue;
+ }
+ var idx = line.indexOf("|");
+ if (idx === -1){
+ continue;
+ }
+ var name = trim_text(line.substring(0, idx));
+ var domain = trim_text(line.substring(idx + 1));
+ if (!name || !domain){
+ continue;
+ }
+ records.push({"name": name, "domain": domain});
+ if (records.length >= MAX_RECORDS){
+ break;
+ }
+ }
+ return records;
+}
+
+function update_record_counter(){
+ var rows = $("#cfddns_records_container .cfddns_record_row").length;
+ E("cfddns_record_count").innerHTML = rows + "/" + MAX_RECORDS;
+ E("add_record_btn").disabled = rows >= MAX_RECORDS;
+}
+
+function add_record_row(name, domain){
+ var rows = $("#cfddns_records_container .cfddns_record_row").length;
+ if (rows >= MAX_RECORDS){
+ alert("最多支持" + MAX_RECORDS + "组域名记录!");
+ return;
+ }
+ var row = document.createElement("div");
+ row.className = "cfddns_record_row";
+ row.style.marginBottom = "6px";
+ row.innerHTML = ' . ';
+ row.getElementsByClassName("cfddns_record_name")[0].value = name || "";
+ row.getElementsByClassName("cfddns_record_domain")[0].value = domain || "";
+ row.getElementsByClassName("cfddns_record_remove")[0].onclick = function(){ remove_record_row(this); };
+ E("cfddns_records_container").appendChild(row);
+ update_record_counter();
+}
+
+function remove_record_row(btn){
+ var rows = $("#cfddns_records_container .cfddns_record_row");
+ if (rows.length <= 1){
+ $(rows[0]).find(".cfddns_record_name").val("");
+ $(rows[0]).find(".cfddns_record_domain").val("");
+ update_record_counter();
+ return;
+ }
+ btn.parentNode.parentNode.removeChild(btn.parentNode);
+ update_record_counter();
+}
+
+function render_records(){
+ var records = parse_records(dbus["cfddns_records"] || "");
+ E("cfddns_records_container").innerHTML = "";
+ if (records.length === 0){
+ add_record_row("", "");
+ return;
+ }
+ for (var i = 0; i < records.length; i++) {
+ add_record_row(records[i].name, records[i].domain);
+ }
+ update_record_counter();
+}
+
+function collect_records(){
+ var records = [];
+ var seen = {};
+ var rows = $("#cfddns_records_container .cfddns_record_row");
+ for (var i = 0; i < rows.length; i++) {
+ var name = trim_text($(rows[i]).find(".cfddns_record_name").val());
+ var domain = trim_text($(rows[i]).find(".cfddns_record_domain").val());
+ if (!name && !domain){
+ continue;
+ }
+ if (!name || !domain){
+ alert("第" + (i + 1) + "组域名未填写完整!");
+ return null;
+ }
+ if (name.indexOf("|") !== -1 || domain.indexOf("|") !== -1){
+ alert("第" + (i + 1) + "组域名包含非法字符“|”!");
+ return null;
+ }
+ var fqdn = get_fqdn(name, domain).toLowerCase();
+ if (seen[fqdn]){
+ alert("存在重复域名记录:" + fqdn);
+ return null;
+ }
+ seen[fqdn] = 1;
+ records.push(name + "|" + domain);
+ }
+ if (records.length === 0){
+ alert("请至少填写1组域名记录!");
+ return null;
+ }
+ if (records.length > MAX_RECORDS){
+ alert("最多支持" + MAX_RECORDS + "组域名记录!");
+ return null;
+ }
+ return records.join("\n");
+}
+
function get_dbus_data(){
$.ajax({
type: "GET",
@@ -157,6 +283,11 @@ function get_status1(){
function save(){
setInterval("get_status();",2000);
+ var records_data = collect_records();
+ if (records_data === null){
+ return;
+ }
+ E("cfddns_records").value = records_data;
var dbus_new = {}
$("#show_btn2").trigger("click");
// collect data from input and checkbox
@@ -415,11 +546,14 @@ function reload_Soft_Center(){
- | 域名(Domain Name) |
+ 域名记录(最多10组)[?] |
-
- .
-
+
+
+
+ 0/10
+
+
|
@@ -443,13 +577,13 @@ function reload_Soft_Center(){
| 获得IPv4命令(get ip)[?] |
-
+
|
| 获得IPv6命令(get ip)[?] |
-
+
|
@@ -491,4 +625,3 @@ function reload_Soft_Center(){