# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# USER CONFIGURATION
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# On the first run, leave cfRecordId empty "".
# Once you get the ID from the Log, paste it here (e.g., :local cfRecordId "abcdef123456...")
:local cfRecordId ""
:local cfToken "YOUR_CLOUDFLARE_API_TOKEN"
:local cfZoneId "YOUR_CLOUDFLARE_ZONE_ID"
:local cfRecordName "ddns.domain.com"
:local wanInterface "ether1"
# Specify the DNS server used to resolve your Cloudflare domain IP (e.g., 1.1.1.1 or 8.8.8.8)
:local dnsServer "1.1.1.1"
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# AUTOMATIC PROCESS (Determined by the cfRecordId status above)
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:local httpHeaders "Authorization: Bearer $cfToken"
# If cfRecordId is empty, fetch the ID from Cloudflare and print it to the log
:if ([:len $cfRecordId] = 0) do={
:log warning "CF-DDNS: [STEP 1] cfRecordId is EMPTY. Fetching ID from Cloudflare..."
:local getUrl "https://api.cloudflare.com/client/v4/zones/$cfZoneId/dns_records"
:local fetchResult ""
:do {
:set fetchResult [/tool fetch url=$getUrl http-header-field=$httpHeaders as-value output=user]
} on-error={
:log error "CF-DDNS: Failed to connect to Cloudflare API. Check your Token/Zone ID."
}
:if ($fetchResult != "") do={
:local responseData ($fetchResult->"data")
:local idKeyword "\"id\":\""
:local idPos [:find $responseData $idKeyword]
:if ($idPos > 0) do={
:local idStart ($idPos + [:len $idKeyword])
:local idEnd [:find $responseData "\"" $idStart]
:local fetchedId [:pick $responseData $idStart $idEnd]
# Action required: Print the Record ID to the log for copying
:log error "=========================================================="
:log warning "CF-DDNS: [SUCCESS] Your Record ID is: $fetchedId"
:log warning "CF-DDNS: Please copy this ID and paste it into ':local cfRecordId \"\"' at the top of this script!"
:log error "=========================================================="
} else={
:log error "CF-DDNS: DNS Record Name not found on Cloudflare. Please check your spelling."
}
}
} else={
# If cfRecordId is provided, proceed to check IP and update DDNS
:local currentIp [/ip address get [/ip address find interface=$wanInterface] address]
:set currentIp [:pick $currentIp 0 [:find $currentIp "/"]]
:local cloudflareIp ""
:do {
# Resolve the domain using the specific DNS resolver configured above
:set cloudflareIp [:resolve $cfRecordName server=$dnsServer]
} on-error={
:log info "CF-DDNS: Cannot resolve $cfRecordName via $dnsServer, proceeding to check update."
}
# Only execute API update if the WAN IP has changed
:if ($currentIp != $cloudflareIp) do={
:log info "CF-DDNS: IP changed from $cloudflareIp to $currentIp. Updating Cloudflare..."
:local putUrl ("https://api.cloudflare.com/client/v4/zones/" . $cfZoneId . "/dns_records/" . $cfRecordId)
:local putData "{\"type\":\"A\",\"name\":\"$cfRecordName\",\"content\":\"$currentIp\",\"ttl\":300,\"proxied\":false}"
:do {
/tool fetch url=$putUrl http-method=put http-data=$putData http-header-field=$httpHeaders as-value output=user
:log info "CF-DDNS: Successfully updated $cfRecordName to $currentIp"
} on-error={
:log error "CF-DDNS: Failed to update IP on Cloudflare."
}
}
}