#!/usr/bin/env sh # shellcheck disable=SC2034 # Global variables for returning results (avoid stdout pollution from logging) _BAIDU_FIND_RESULT="" _BAIDU_BCE_AUTH_RESULT="" : "${BAIDU_LOG_LEVEL:=2}" _baidu_log_ts() { date } _baidu_log_ge() { _want="$1" [ "${BAIDU_LOG_LEVEL:-0}" -ge "$_want" ] } _baidu_log() { _lvl="$1" _tag="$2" _msg="$3" if [ "$_lvl" = "0" ] || _baidu_log_ge "$_lvl"; then printf -- "[%s] %s %s\n" "$(_baidu_log_ts)" "$_tag" "$_msg" fi } _baidu_err() { _baidu_log 0 "baidu_bcd.err" "$1" return 1 } _baidu_info() { _baidu_log 1 "baidu_bcd.info" "$1" return 0 } _baidu_debug() { _baidu_log 2 "$1" "$2" return 0 } dns_baidu_info='Baidu Cloud BCD DNS Site: cloud.baidu.com Docs: https://cloud.baidu.com/doc/BCD/ Signature: https://cloud.baidu.com/doc/Reference/s/njwvz1yfu Options: Baidu_AK AccessKeyId Baidu_SK SecretAccessKey OptionsAlt: Baidu_BCD_Host API host, default: bcd.baidubce.com Baidu_BCD_Version API version number, default: 1 Baidu_BCD_Expire Signature expiration seconds, default: 3600 Baidu_View Resolve view, default: DEFAULT Baidu_TTL Resolve ttl seconds, default: 300 Baidu_RM_Max Max records to delete in one run, default: 20 ' BAIDU_BCD_DEFAULT_HOST="bcd.baidubce.com" # --- Public API --- dns_baidu_add() { fulldomain=$(_idn "$1") txtvalue=$2 if ! _baidu_prepare_record "$fulldomain"; then _baidu_err "baidu_prepare_record failed for add: $fulldomain" return 1 fi if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then _baidu_err "baidu_find_record_ids failed for add: $_record_domain.$_zone_name" return 1 fi _existing_ids="$_BAIDU_FIND_RESULT" if [ "$_existing_ids" ]; then _baidu_info "txt exists, skip add: $_record_domain.$_zone_name" return 0 fi _ttl="${Baidu_TTL:-300}" _ttl="$(_baidu_trim_ws "$_ttl")" case "$_ttl" in "" | *[!0-9]*) _ttl="300" ;; esac _view="$(_baidu_trim_ws "${Baidu_View:-DEFAULT}")" txtvalue="$(_baidu_trim_ws "$txtvalue")" _record_domain="$(_baidu_trim_ws "$_record_domain")" _zone_name="$(_baidu_trim_ws "$_zone_name")" _body="$(_baidu_payload_add_txt "$_zone_name" "$_record_domain" "$txtvalue" "$_ttl" "$_view")" if ! _baidu_bcd_post "/domain/resolve/add" "$_body"; then _baidu_err "baidu_bcd_post failed: add record" return 1 fi if _baidu_is_api_error "$response"; then _baidu_err "$response" return 1 fi return 0 } dns_baidu_rm() { fulldomain=$(_idn "$1") txtvalue=$2 if ! _baidu_prepare_record "$fulldomain"; then _baidu_err "baidu_prepare_record failed for delete: $fulldomain" return 1 fi if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then _baidu_err "baidu_find_record_ids failed for delete: $_record_domain.$_zone_name" return 1 fi _ids="$_BAIDU_FIND_RESULT" if [ -z "$_ids" ]; then _baidu_info "no matching txt to delete: $_record_domain.$_zone_name" return 0 fi _rm_max="${Baidu_RM_Max:-20}" _rm_max="$(_baidu_trim_ws "$_rm_max")" case "$_rm_max" in "" | *[!0-9]*) _rm_max="20" ;; esac _rm_cnt="$(printf "%s\n" "$_ids" | sed '/^$/d' | wc -l | tr -d ' ')" if [ "$_rm_cnt" ] && [ "$_rm_cnt" -gt "$_rm_max" ]; then _baidu_err "Refusing to delete $_rm_cnt records (limit: $_rm_max)" return 1 fi for _rid in $_ids; do _body="$(_baidu_payload_delete "$_zone_name" "$_rid")" if ! _baidu_bcd_post "/domain/resolve/delete" "$_body"; then _baidu_err "baidu_bcd_post failed: delete recordId=$_rid" return 1 fi if _baidu_is_api_error "$response"; then _baidu_err "$response" return 1 fi done if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then _baidu_err "baidu_find_record_ids failed for delete verify: $_record_domain.$_zone_name" return 1 fi _left_ids="$_BAIDU_FIND_RESULT" if [ -z "$_left_ids" ]; then return 0 fi if [ -n "$_left_ids" ]; then _baidu_err "delete verification failed: $_record_domain.$_zone_name still has TXT records" return 1 fi return 0 } # --- Config / Record Context --- _baidu_load_credentials() { Baidu_AK="${Baidu_AK:-$(_readaccountconf_mutable Baidu_AK)}" Baidu_SK="${Baidu_SK:-$(_readaccountconf_mutable Baidu_SK)}" Baidu_AK="$(_baidu_trim_ws "$Baidu_AK")" Baidu_SK="$(_baidu_trim_ws "$Baidu_SK")" if [ -z "$Baidu_AK" ] || [ -z "$Baidu_SK" ]; then _baidu_err "Baidu_AK and Baidu_SK are required" return 1 fi _saveaccountconf_mutable Baidu_AK "$Baidu_AK" _saveaccountconf_mutable Baidu_SK "$Baidu_SK" BAIDU_BCD_HOST="${Baidu_BCD_Host:-$BAIDU_BCD_DEFAULT_HOST}" BAIDU_BCD_VERSION="${Baidu_BCD_Version:-1}" return 0 } _baidu_prepare_record() { _fulldomain="$1" if ! _baidu_load_credentials; then _baidu_err "baidu_load_credentials failed" return 1 fi if ! _baidu_get_root "$_fulldomain"; then _baidu_err "Could not find zone for $_fulldomain" return 1 fi _record_domain="$_sub_domain" _zone_name="$_domain" return 0 } # --- Zone / Records --- _baidu_get_root() { domain=$1 i=1 p=1 while true; do h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) if [ -z "$h" ]; then _baidu_err "invalid domain: $domain" return 1 fi if ! _baidu_bcd_post "/domain/resolve/list" "$(_baidu_payload_list "$h" 1 1)"; then _baidu_err "baidu_bcd_post failed: list zones" return 1 fi if ! _baidu_is_api_error "$response" && (_contains "$response" "\"totalCount\"" || _contains "$response" "\"result\""); then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") _domain=$h if [ "$_sub_domain" = "$_domain" ]; then _sub_domain="@" fi _baidu_info "zone matched: $_domain (host: $_sub_domain)" return 0 fi p=$i i=$(_math "$i" + 1) done } _baidu_find_record_ids() { _zone_name="$1" _record_domain="$2" _rdtype="$3" _rdata="$4" # Reset global result variable _BAIDU_FIND_RESULT="" _zone_name_e="$(_baidu_json_escape "$_zone_name")" _record_domain_e="$(_baidu_json_escape "$_record_domain")" _rdtype_e="$(_baidu_json_escape "$_rdtype")" _rdata_e="$(_baidu_json_escape "$_rdata")" _page=1 _page_size=100 _ids="" _max_page="" while true; do if ! _baidu_bcd_post "/domain/resolve/list" "$(_baidu_payload_list "$_zone_name" "$_page" "$_page_size")"; then _baidu_err "baidu_bcd_post failed: list records" return 1 fi if _baidu_is_api_error "$response"; then _baidu_err "baidu_bcd error: $(_baidu_json_get_str "$response" "code") $(_baidu_json_get_str "$response" "message")" return 1 fi _normalized="$( printf "%s" "$response" | _normalizeJson )" if [ -z "$_max_page" ]; then _total="$(_baidu_parse_totalcount "$_normalized")" _max_page="$(_baidu_calc_max_page "$_total" "$_page_size")" fi _records=$(printf "%s" "$_normalized" | sed 's/},{/}\n{/g') while IFS= read -r _line; do _id="$(_baidu_match_record_id "$_line" "$_record_domain_e" "$_rdtype_e" "$_rdata_e")" if [ "$_id" ]; then _ids="$_ids $_id" fi done <