diff --git a/dnsapi/dns_opusdns.sh b/dnsapi/dns_opusdns.sh index b9337b89..19205256 100755 --- a/dnsapi/dns_opusdns.sh +++ b/dnsapi/dns_opusdns.sh @@ -18,7 +18,6 @@ OPUSDNS_TTL_Default=60 ######## Public functions ########### # Add DNS TXT record -# Usage: dns_opusdns_add _acme-challenge.example.com "token_value" dns_opusdns_add() { fulldomain=$1 txtvalue=$2 @@ -27,44 +26,17 @@ dns_opusdns_add() { _debug fulldomain "$fulldomain" _debug txtvalue "$txtvalue" - # Load and validate credentials - OPUSDNS_API_Key="${OPUSDNS_API_Key:-$(_readaccountconf_mutable OPUSDNS_API_Key)}" - if [ -z "$OPUSDNS_API_Key" ]; then - _err "OPUSDNS_API_Key not set. Please set it and try again." - _err "You can create an API key at your OpusDNS dashboard." + if ! _opusdns_init; then return 1 fi - # Save credentials for future use - _saveaccountconf_mutable OPUSDNS_API_Key "$OPUSDNS_API_Key" - - # Load optional configuration - OPUSDNS_API_Endpoint="${OPUSDNS_API_Endpoint:-$(_readaccountconf_mutable OPUSDNS_API_Endpoint)}" - if [ -z "$OPUSDNS_API_Endpoint" ]; then - OPUSDNS_API_Endpoint="$OPUSDNS_API_Endpoint_Default" - fi - _saveaccountconf_mutable OPUSDNS_API_Endpoint "$OPUSDNS_API_Endpoint" - - OPUSDNS_TTL="${OPUSDNS_TTL:-$(_readaccountconf_mutable OPUSDNS_TTL)}" - if [ -z "$OPUSDNS_TTL" ]; then - OPUSDNS_TTL="$OPUSDNS_TTL_Default" - fi - _saveaccountconf_mutable OPUSDNS_TTL "$OPUSDNS_TTL" - - _debug "API Endpoint: $OPUSDNS_API_Endpoint" - _debug "TTL: $OPUSDNS_TTL" - - # Detect zone from FQDN if ! _get_zone "$fulldomain"; then - _err "Failed to detect zone for domain: $fulldomain" return 1 fi - _info "Detected zone: $_zone" - _debug "Record name: $_record_name" + _info "Zone: $_zone, Record: $_record_name" - # Add the TXT record - if ! _opusdns_add_record "$_zone" "$_record_name" "$txtvalue"; then + if ! _opusdns_api PATCH "/v1/dns/$_zone/records" "{\"ops\":[{\"op\":\"upsert\",\"record\":{\"name\":\"$_record_name\",\"type\":\"TXT\",\"ttl\":$OPUSDNS_TTL,\"rdata\":\"\\\"$txtvalue\\\"\"}}]}"; then _err "Failed to add TXT record" return 1 fi @@ -74,7 +46,6 @@ dns_opusdns_add() { } # Remove DNS TXT record -# Usage: dns_opusdns_rm _acme-challenge.example.com "token_value" dns_opusdns_rm() { fulldomain=$1 txtvalue=$2 @@ -83,38 +54,19 @@ dns_opusdns_rm() { _debug fulldomain "$fulldomain" _debug txtvalue "$txtvalue" - # Load credentials - OPUSDNS_API_Key="${OPUSDNS_API_Key:-$(_readaccountconf_mutable OPUSDNS_API_Key)}" - OPUSDNS_API_Endpoint="${OPUSDNS_API_Endpoint:-$(_readaccountconf_mutable OPUSDNS_API_Endpoint)}" - OPUSDNS_TTL="${OPUSDNS_TTL:-$(_readaccountconf_mutable OPUSDNS_TTL)}" - - if [ -z "$OPUSDNS_API_Endpoint" ]; then - OPUSDNS_API_Endpoint="$OPUSDNS_API_Endpoint_Default" - fi - - if [ -z "$OPUSDNS_TTL" ]; then - OPUSDNS_TTL="$OPUSDNS_TTL_Default" - fi - - if [ -z "$OPUSDNS_API_Key" ]; then - _err "OPUSDNS_API_Key not found" + if ! _opusdns_init; then return 1 fi - # Detect zone from FQDN if ! _get_zone "$fulldomain"; then - _err "Failed to detect zone for domain: $fulldomain" - # Don't fail cleanup - best effort + _err "Zone not found, cleanup skipped" return 0 fi - _info "Detected zone: $_zone" - _debug "Record name: $_record_name" + _info "Zone: $_zone, Record: $_record_name" - # Remove the TXT record (need to pass txtvalue) - if ! _opusdns_remove_record "$_zone" "$_record_name" "$txtvalue"; then - _err "Warning: Failed to remove TXT record (this is usually not critical)" - # Don't fail cleanup - best effort + if ! _opusdns_api PATCH "/v1/dns/$_zone/records" "{\"ops\":[{\"op\":\"remove\",\"record\":{\"name\":\"$_record_name\",\"type\":\"TXT\",\"ttl\":$OPUSDNS_TTL,\"rdata\":\"\\\"$txtvalue\\\"\"}}]}"; then + _err "Warning: Failed to remove TXT record" return 0 fi @@ -124,132 +76,83 @@ dns_opusdns_rm() { ######## Private functions ########### -# Detect zone from FQDN by checking against OpusDNS API -# Iterates through domain parts until a valid zone is found -# Sets global variables: _zone, _record_name -_get_zone() { - domain=$1 - _debug "Detecting zone for: $domain" - - # Remove trailing dot if present - domain=$(echo "$domain" | sed 's/\.$//') - - export _H1="X-Api-Key: $OPUSDNS_API_Key" - - # Start from position 2 (skip first part like _acme-challenge) - i=2 - p=1 - while true; do - # Extract potential zone (domain parts from position i onwards) - h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) - _debug "Trying zone: $h" - - if [ -z "$h" ]; then - # No more parts to try - _err "Could not find a valid zone for: $domain" - return 1 - fi - - # Check if this zone exists in OpusDNS - response=$(_get "$OPUSDNS_API_Endpoint/v1/dns/$h") - - if _contains "$response" '"name"'; then - # Zone found - _record_name=$(printf "%s" "$domain" | cut -d . -f 1-"$p") - _zone="$h" - _debug "Found zone: $_zone" - _debug "Record name: $_record_name" - return 0 - fi +# Initialize and validate configuration +_opusdns_init() { + OPUSDNS_API_Key="${OPUSDNS_API_Key:-$(_readaccountconf_mutable OPUSDNS_API_Key)}" + OPUSDNS_API_Endpoint="${OPUSDNS_API_Endpoint:-$(_readaccountconf_mutable OPUSDNS_API_Endpoint)}" + OPUSDNS_TTL="${OPUSDNS_TTL:-$(_readaccountconf_mutable OPUSDNS_TTL)}" - _debug "$h not found, trying next" - p="$i" - i=$(_math "$i" + 1) - done + if [ -z "$OPUSDNS_API_Key" ]; then + _err "OPUSDNS_API_Key not set" + return 1 + fi - return 1 -} + [ -z "$OPUSDNS_API_Endpoint" ] && OPUSDNS_API_Endpoint="$OPUSDNS_API_Endpoint_Default" + [ -z "$OPUSDNS_TTL" ] && OPUSDNS_TTL="$OPUSDNS_TTL_Default" - if [ -z "$_record_name" ]; then - _record_name="@" - fi + _saveaccountconf_mutable OPUSDNS_API_Key "$OPUSDNS_API_Key" + _saveaccountconf_mutable OPUSDNS_API_Endpoint "$OPUSDNS_API_Endpoint" + _saveaccountconf_mutable OPUSDNS_TTL "$OPUSDNS_TTL" + _debug "Endpoint: $OPUSDNS_API_Endpoint" return 0 } -# Add TXT record using OpusDNS API -_opusdns_add_record() { - zone=$1 - record_name=$2 - txtvalue=$3 +# Make API request +# Usage: _opusdns_api METHOD PATH [DATA] +_opusdns_api() { + method=$1 + path=$2 + data=$3 - _debug "Adding TXT record: $record_name.$zone = $txtvalue" - - # Escape all JSON special characters in txtvalue - # Order matters: escape backslashes first, then other characters - escaped_value=$(printf '%s' "$txtvalue" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/ /\\t/g' | sed ':a;N;$!ba;s/\n/\\n/g') - - # Build JSON payload - # Note: TXT records need quotes around the value in rdata - json_payload="{\"ops\":[{\"op\":\"upsert\",\"record\":{\"name\":\"$record_name\",\"type\":\"TXT\",\"ttl\":$OPUSDNS_TTL,\"rdata\":\"\\\"$escaped_value\\\"\"}}]}" - - _debug2 "JSON payload: $json_payload" - - # Send PATCH request export _H1="X-Api-Key: $OPUSDNS_API_Key" export _H2="Content-Type: application/json" - response=$(_post "$json_payload" "$OPUSDNS_API_Endpoint/v1/dns/$zone/records" "" "PATCH") - status=$? - - _debug2 "API Response: $response" + url="$OPUSDNS_API_Endpoint$path" + _debug2 "API: $method $url" + [ -n "$data" ] && _debug2 "Data: $data" - if [ $status -ne 0 ]; then - _err "Failed to add TXT record" - _err "API Response: $response" - return 1 + if [ -n "$data" ]; then + response=$(_post "$data" "$url" "" "$method") + else + response=$(_get "$url") fi - # Check for error in response (OpusDNS returns JSON error even on failure) - # Use anchored pattern to avoid matching field names like "error_count" - if echo "$response" | grep -q '"error":'; then - _err "API returned error: $response" + if [ $? -ne 0 ]; then + _err "API request failed" + _debug "Response: $response" return 1 fi + _debug2 "Response: $response" return 0 } -# Remove TXT record using OpusDNS API -_opusdns_remove_record() { - zone=$1 - record_name=$2 - txtvalue=$3 - - _debug "Removing TXT record: $record_name.$zone = $txtvalue" - - # Escape all JSON special characters in txtvalue (same as add) - escaped_value=$(printf '%s' "$txtvalue" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/ /\\t/g' | sed ':a;N;$!ba;s/\n/\\n/g') - - # Build JSON payload for removal - needs complete record specification - json_payload="{\"ops\":[{\"op\":\"remove\",\"record\":{\"name\":\"$record_name\",\"type\":\"TXT\",\"ttl\":$OPUSDNS_TTL,\"rdata\":\"\\\"$escaped_value\\\"\"}}]}" - - _debug2 "JSON payload: $json_payload" - - # Send PATCH request - export _H1="X-Api-Key: $OPUSDNS_API_Key" - export _H2="Content-Type: application/json" +# Detect zone from FQDN +# Sets: _zone, _record_name +_get_zone() { + domain=$(echo "$1" | sed 's/\.$//') + _debug "Finding zone for: $domain" - response=$(_post "$json_payload" "$OPUSDNS_API_Endpoint/v1/dns/$zone/records" "" "PATCH") - status=$? + i=2 + p=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) - _debug2 "API Response: $response" + if [ -z "$h" ]; then + _err "No valid zone found for: $domain" + return 1 + fi - if [ $status -ne 0 ]; then - _err "Failed to remove TXT record" - _err "API Response: $response" - return 1 - fi + _debug "Trying: $h" + if _opusdns_api GET "/v1/dns/$h" && _contains "$response" '"name"'; then + _zone="$h" + _record_name=$(printf "%s" "$domain" | cut -d . -f 1-"$p") + [ -z "$_record_name" ] && _record_name="@" + return 0 + fi - return 0 + p="$i" + i=$(_math "$i" + 1) + done }