Skip to content

Instantly share code, notes, and snippets.

@corny
Last active August 2, 2024 03:18
Show Gist options
  • Save corny/7a07f5ac901844bd20c9 to your computer and use it in GitHub Desktop.
Save corny/7a07f5ac901844bd20c9 to your computer and use it in GitHub Desktop.
Update script for dynv6.com to set your IPv4 address and IPv6 prefix
#!/bin/sh -e
hostname=$1
device=$2
file=$HOME/.dynv6.addr6
[ -e $file ] && old=`cat $file`
if [ -z "$hostname" -o -z "$token" ]; then
echo "Usage: token=<your-authentication-token> [netmask=64] $0 your-name.dynv6.net [device]"
exit 1
fi
if [ -z "$netmask" ]; then
netmask=128
fi
if [ -n "$device" ]; then
device="dev $device"
fi
address=$(ip -6 addr list scope global $device | grep -v " fd" | sed -n 's/.*inet6 \([0-9a-f:]\+\).*/\1/p' | head -n 1)
if [ -e /usr/bin/curl ]; then
bin="curl -fsS"
elif [ -e /usr/bin/wget ]; then
bin="wget -O-"
else
echo "neither curl nor wget found"
exit 1
fi
if [ -z "$address" ]; then
echo "no IPv6 address found"
exit 1
fi
# address with netmask
current=$address/$netmask
if [ "$old" = "$current" ]; then
echo "IPv6 address unchanged"
exit
fi
# send addresses to dynv6
$bin "http://dynv6.com/api/update?hostname=$hostname&ipv6=$current&token=$token"
$bin "http://ipv4.dynv6.com/api/update?hostname=$hostname&ipv4=auto&token=$token"
# save current address
echo $current > $file
@Beaving
Copy link

Beaving commented Dec 10, 2018

#!/bin/bash

#Configure
Hostname="example.dynv6.net"
IPVERSION="6" #IPV4=4, IPV6=6
UpdateURL="http://dynv6.com/api/update?hostname=$Hostname&ipv6=auto&token=YOUR_TOKEN"

RecordType=$([ "$IPVERSION" == 4 ] && echo "A" || echo "AAAA")

while true; do
 SavedIP=$(dig -$IPVERSION +short $RecordType $Hostname) #check IP which is stored on dynamic DNS
 ActualIP=$(dig -$IPVERSION +short $RecordType myip.opendns.com @resolver1.opendns.com) #get actual IP

 if [ "$SavedIP" != "$ActualIP" ] ; then
	wget -qO- $UpdateURL &> /dev/null
	if [[ $? -ge 0 ]]; then
	 SavedIP=$ActualIP
	fi
 fi

 sleep 5s #run every 5 seconds
done

Very simple and neat script that runs every 5 seconds in a loop. Has some advantages over the script above. Should work on most clients, includes a reliable way to get the IP and can be adapted to most dynamic DNS providers.

Configure hostname, record type and update URL, done.

For dynv6 you have to add your token in the URL. If you want to use IPV4, change IPVERSION to 4 and change url to ipv4 version of the URL. Then, simply copy it into a file like ddnsupdate.sh, chmod +x ddnsupdate.sh to make it executable and execute it from a startup script like rc.local: nohup /home/ddnsupdate.sh > /dev/null 2>&1&

@xbulat
Copy link

xbulat commented Jan 18, 2019

There is example of systemd unit

[Unit]
After=miredo.service
[Service]
ExecStart=/usr/local/bin/ddnsupdate.sh
[Install]
WantedBy=default.target

@pulsar256
Copy link

pulsar256 commented Mar 15, 2019

Adapted findings and requirements from this thread into a n updated (and currently working) script. Depends on ns1.google.com ability to return a TXT record for o-o.myaddr.l.google.com. @Beaving did a similar thing with opendns but it did stop working as the "myip service" has been discontinued.

Script

#!/bin/bash

if [ -z "$DYNV6_HOSTNAME" -o -z "$DYNV6_TOKEN" -o -z "$DYNV6_PROTO" ]; then
  echo "Usage:"
  echo "DYNV6_HOSTNAME=hostname DYNV6_TOKEN=token DYNV6_PROTO=[4|6] $0"
  exit 1
fi;

record=$([ "$DYNV6_PROTO" == 4 ] && echo "A" || echo "AAAA")
resolved=$(dig +short $record $DYNV6_HOSTNAME )
actual=$(dig -$DYNV6_PROTO TXT +short o-o.myaddr.l.google.com @ns1.google.com | tr -d '"')

if [ "$resolved" != "$actual" ] ; then
  if [ "$DYNV6_PROTO" == "4" ] ; then
          curl "http://ipv4.dynv6.com/api/update?hostname=$DYNV6_HOSTNAME&ipv4=$actual&token=$DYNV6_TOKEN"
  fi

  if [ "$DYNV6_PROTO" == "6" ] ; then
          curl "http://ipv6.dynv6.com/api/update?hostname=$DYNV6_HOSTNAME&ipv6=$actual&token=$DYNV6_TOKEN"
  fi
fi

Example:

DYNV6_HOSTNAME=ramalamadingdong.dynv6.net DYNV6_TOKEN=muchSecureSoSecret DYNV6_PROTO=6 ./update_dns.sh

Please note that if you want to update v6 and v4 records, you will have to call the script twice.

@elbosso
Copy link

elbosso commented May 7, 2019

like some other people before me realized: when the ipv6 has not changed, then the ipv4 address is not updated even if it has changed. This is due to 27, 32,40 where the script just exits.

I would open an issue for that if it were not only a gist but a full-fledged github repo. In the meantime - could i just suggest to actually put this script into a full-fledged github repo and to encourage the many people linking to their own versions in the comments here to make an effort and merge their diffs into the script so it actually becoms usable for noobs?

Many thanks for considering this!

@lemmy04
Copy link

lemmy04 commented Aug 14, 2019

I would open an issue for that if it were not only a gist but a full-fledged github repo. In the meantime - could i just suggest to actually put this script into a full-fledged github repo and to encourage the many people linking to their own versions in the comments here to make an effort and merge their diffs into the script so it actually becoms usable for noobs?

+1

@juergenhoetzel
Copy link

Nice service!

I use the following systemd timer/service templates to enable the update on my hosts:

# /etc/systemd/system/dyndns@.service
[Unit]
Description=Update dynv6 DNS Entries

[Service]
Type=oneshot
EnvironmentFile=/etc/conf.d/dynv6
ExecStart=/usr/local/bin/dynv6.sh %I.dynv6.net

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/dyndns@.timer
[Unit]
Description=Minutely dynv6 update

[Timer]
OnCalendar=minutely
Persistent=true

[Install]
WantedBy=timers.target

then enable/start:

sudo systemctl enable dyndns@MYHOSTNAME.timer
sudo systemctl start dyndns@MYHOSTNAME.timer

@nename0
Copy link

nename0 commented Nov 1, 2019

@corny

Since the 2019-10-31 update this does no longer work.

There is a new 'zone' parameter. I tried a few things like myhostname.dynv6.net, dynv6.net or empty, but each time I get:

HTTP/1.1 422 Unprocessable Entity
...

Error 1146: Table 'dynv6.hosts' doesn't exist

What should the zone parameter be set to? The api doc just says: 'Your DynV6 zone (mandatory) '

@VolkmarWillert
Copy link

Use the zones ipv4.dynv6.com or ipv6.dynv6.com to enforce a specific IP protocol version.

Bit I also felt

@pasdVn
Copy link

pasdVn commented Nov 1, 2019

I have the same problem. Since yesterday the update does not work anymore.
The documentation on dynv6.com is weird and confusing. I think what they want to say is, that you should use "ipv4.dynv6.com" as the url to query - not as zone parameter - to enforce ipv4 (ipv4.dynv6.com has only A records).
If you replace the foreign "hostname" parameter by "zone" (as the documentation says) it complains about a missing hostname. The API seems buggy to me...

@jbob
Copy link

jbob commented Nov 1, 2019

Same issue here. Also https://dynv6.com/docs/apis currently is borked.

@ZeaKyX
Copy link

ZeaKyX commented Nov 2, 2019

Just got into the dynv6 here and same problem.

@nename0
Copy link

nename0 commented Nov 2, 2019

Seems to be fixed now, without changing the url.

@corny
Copy link
Author

corny commented Nov 3, 2019

Sorry, we introduced a bug that has been fixed in the meanwhile.

@tomkhlr
Copy link

tomkhlr commented Feb 5, 2020

I have a raspi behind a fritzbox acting as owncloud server. That worked well until Monday morning, when the IPv4 address got updated to
127.0.0.1. I changed the script instead of ipv4=auto in the api update command i use the current external IPv4 address. That fixes the problem.
Any idea why this suddenly happens? (no hardware or software changes/updates)

@jbob
Copy link

jbob commented Feb 7, 2020

Same here

@andi34
Copy link

andi34 commented Mar 29, 2020

You can use address=$(wget -O- "https://api6.ipify.org")
(For ipv4 use https://api.ipify.org)

Or better place it below

if [ -e /usr/bin/curl ]; then
  bin="curl -fsS"
elif [ -e /usr/bin/wget ]; then
  bin="wget -O-"
else
  echo "neither curl nor wget found"
  exit 1
fi

To allow using curl or wget, depending on your setup.

if [ -e /usr/bin/curl ]; then
  bin="curl -fsS"
elif [ -e /usr/bin/wget ]; then
  bin="wget -O-"
else
  echo "neither curl nor wget found"
  exit 1
fi

address=$($bin "https://api6.ipify.org")

@hiifeng
Copy link

hiifeng commented May 13, 2021

Because we don't want to use crontab to execute the script, we hope to execute the update script through the /etc/ppp/ipv6-up script of the system after establishing the pppoe session. But I found that after my router established pppoe session, the IPv6 address acquisition would be delayed, resulting in the update failure when $address was empty. Therefore, the base script is modified. When it is found that $address is empty, wait for 1 minute and then assign the value again, The loop ends after 30 times.

Note: crontab is not recommended for this script, which will increase the operating system load.
https://gist.github.com/hiifeng/0afc189bb79b3a6cc239d45af3ec6dbe

# When the address is empty, wait for 1 minute and then assign the value again. The loop ends after 30 times.
i=1
while [ $i -lt 30 ]
do
 address=$(ip -6 addr list scope global $device | grep -v " fd" | sed -n 's/.*inet6 \([0-9a-f:]\+\).*/\1/p' | head -n 1)
 if [ -z "$address" ]; then
  sleep 1m
  ((i++))
  else
   break
 fi
done

@flid0
Copy link

flid0 commented Jul 21, 2021

Here's a Python updater for IPv4 that updates to the IP of the PC the script is run on.
-Prerequisites
Python (duh)
requests the python module
pip install requests

Once done with the prerequisites, edit the script so the variables match your zone (hostname) and token.
Then run the script and the IPv4 address for your zone should update every 30 seconds!

I created two versions of the updater; an auto and a manual.

Manual Version:
https://gist.github.com/flid0/8784baff35ec061f00432a9db3525c20

Auto Version:
https://gist.github.com/flid0/91bd9a3e15b61a8fd5797c325b3451a0

DM me on Discord if you have any problems:
flido#0001

@Terutoki
Copy link

Terutoki commented Nov 15, 2021

Only for IPV6 DDNS

filename: dynv6.sh
path:/usr/bin
chmod +x dynv6.sh


#!/bin/sh -e
GETIPV6="https://ip.ddnspod.com"
hostname="xxxxx.v6.rocks"
token="xxxxxxxxxxxxxxxxxxxxxxxxxx"

file=$HOME/.dynv6.addr6
[ -e $file ] && OldIP=cat $file
echo "[Old IP]:$OldIP"

NewIP=$(curl -6 -s -k $GETIPV6)
echo "[New IP]:$NewIP"

if [ "$OldIP" == "$NewIP" ];then
echo "IP IS SAME,SKIP UPDATE."
exit
fi

#send addresses to dynv6
curl "http://dynv6.com/api/update?hostname=$hostname&ipv6=$NewIP&token=$token"
#curl "http://ipv4.dynv6.com/api/update?hostname=$hostname&ipv4=auto&token=$token"

#save current address
echo $NewIP > $file


[root@C921 ~]# crontab -e
*/1 * * * * dynv6.sh &>/dev/null 2>&1

@tolshao
Copy link

tolshao commented Nov 29, 2021

@andi34
the following script was out of work
address=$(wget -O- "https://api6.ipify.org")
"api64.ipify.org " always returns ipv4 address

@andi34
Copy link

andi34 commented Nov 29, 2021

@tolshao nope, works fine. Typo on your end? api64 vs api6?

@tm-107
Copy link

tm-107 commented Feb 16, 2022

I don't like caching the "old" IP in a file because if it was changed manually on dynv6.com or the update went wrong, the script doesn't notice.
So this variant works very well for me:

#!/bin/sh

HOSTNAME_DYNV6="****.dynv6.net"
TOKEN_DYNV6="****"

IP4ADDR=$(curl -s http://ipecho.net/plain)
IP6ADDR=`ip addr show eth0 | grep 'scope global dynamic' | grep -Po 'inet6 \K[0-9a-fA-F:]+'`

if [ "$IP4ADDR" = "" ]
then
        echo "Error: unable to determine IPv4 address" 1>&2
fi

if [ "$IP6ADDR" = "" ]
then
        echo "Error: unable to determine IPv6 address" 1>&2
fi

if [ "$IP4ADDR" != "" ]
then
	ping $HOSTNAME_DYNV6 -4 -c 1 > null # a little dirty - needed to update dns-cache
	IP4ADDR_DYNV6=$(dig $HOSTNAME_DYNV6 A +short)

	if [ "$IP4ADDR" != "$IP4ADDR_DYNV6" ]
	then
		echo "IPv4 adress has changed -> update ..."
		curl -s "https://ipv4.dynv6.com/api/update?hostname=$HOSTNAME_DYNV6&token=$TOKEN_DYNV6&ipv4=auto"
		echo "---"
	fi
fi

if [ "$IP6ADDR" != "" ]
then
        ping $HOSTNAME_DYNV6 -6 -c 1 > null # a little dirty - needed to update dns-cache
		IP6ADDR_DYNV6=$(dig $HOSTNAME_DYNV6 AAAA +short)

	if [ "$IP6ADDR" != "$IP6ADDR_DYNV6" ]
	then
		echo "IPv6 adress has changed -> update ..."
		curl -s "https://ipv6.dynv6.com/api/update?hostname=$HOSTNAME_DYNV6&token=$TOKEN_DYNV6&ipv6prefix=auto"
		echo "---"
	fi
fi

@vinnymac
Copy link

vinnymac commented May 31, 2022

@tm-107 thanks for the script. While this did most of the work for me, I had to modify it to the following for it to work for my needs. For some reason auto was setting the IPV6 address to one that ended in :: and was missing the last three characters of my address. I made some small modifications to use the current address, including the netmask.

#!/bin/sh

HOSTNAME_DYNV6="****.dynv6.net"
TOKEN_DYNV6="****"
DEVICE_INTERFACE=eno1

IP4ADDR=$(curl -s http://ipecho.net/plain)
IP6ADDR_WITH_MASK=`ip -6 addr list scope global dynamic $DEVICE_INTERFACE | grep -Po 'inet6 \K[0-9a-fA-F:\/]+' | head -n1 | tr -d '\n'`
IP6ADDR=`echo $IP6ADDR_WITH_MASK | tr '/' '\n' | head -n1`

if [ "$IP4ADDR" = "" ]
then
        echo "Error: unable to determine IPv4 address" 1>&2
fi

if [ "$IP6ADDR" = "" ]
then
        echo "Error: unable to determine IPv6 address" 1>&2
fi

if [ "$IP4ADDR" != "" ]
then
	ping $HOSTNAME_DYNV6 -4 -c 1 > null # a little dirty - needed to update dns-cache
	IP4ADDR_DYNV6=$(dig $HOSTNAME_DYNV6 A +short)

	if [ "$IP4ADDR" != "$IP4ADDR_DYNV6" ]
	then
		echo "IPv4 adress has changed -> update ..."
		curl -s "https://ipv4.dynv6.com/api/update?hostname=$HOSTNAME_DYNV6&token=$TOKEN_DYNV6&ipv4=auto"
		echo "---"
	fi
fi

if [ "$IP6ADDR" != "" ]
then
        ping $HOSTNAME_DYNV6 -6 -c 1 > null # a little dirty - needed to update dns-cache
		IP6ADDR_DYNV6=$(dig $HOSTNAME_DYNV6 AAAA +short)

	if [ "$IP6ADDR" != "$IP6ADDR_DYNV6" ]
	then
		echo "IPv6 adress has changed -> update ..."
		curl -s "https://ipv6.dynv6.com/api/update?hostname=$HOSTNAME_DYNV6&token=$TOKEN_DYNV6&ipv6prefix=$IP6ADDR_WITH_MASK"
		echo "---"
	fi
fi

@tolshao and @andi34
curl https://api6.ipify.org will actually return an IPV4 address, but only if you have disabled IPV6. I imagine that was what happened above.

@slyfunky
Copy link

I made a simple batch file script for my Windows need, hope it can help someone too.

https://github.com/slyfunky/Pango/blob/main/DNS.bat

@andredasilvapinto
Copy link

You should use https

@nilsbentlage
Copy link

Made my shell script like this. Think it's leightweight and easy to read

#!/bin/bash

# dynDNS with dynV6 & ipv6 done right

# Initial values

newIpV6=$(ip -6 addr show scope global | grep inet6 | sed -e 's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d')
oldIpV6='No data in cachefile'

# Change these parameters as you want to

logfile=~/dynv6.log
cachefile=~/.dynv6cache
token=<your-token>
zone=<your-zone>

if [[ -f $cachefile ]]; then
    oldIpV6=$(cat $cachefile)
fi

if [[ $newIpV6 != $oldIpV6 ]]; then
    echo "$(date): Updating the DNS. Output can be found in ${logfile}"
    echo "$(date): $(curl -k "https://dynv6.com/api/update?token=${token}&zone=${zone}&ipv6=${newIpV6}")" | tee -a $logfile
    echo $newIpV6 > $cachefile
else
    echo "$(date): IP did not Change. Skipping the update" | tee -a $logfile
fi

Use this as a cron job as often as you need and you should be good to go.

@HandisableL
Copy link

Made my shell script like this. Think it's leightweight and easy to read

#!/bin/bash

# dynDNS with dynV6 & ipv6 done right

# Initial values

newIpV6=$(ip -6 addr show scope global | grep inet6 | sed -e 's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d')
oldIpV6='No data in cachefile'

# Change these parameters as you want to

logfile=~/dynv6.log
cachefile=~/.dynv6cache
token=<your-token>
zone=<your-zone>

if [[ -f $cachefile ]]; then
    oldIpV6=$(cat $cachefile)
fi

if [[ $newIpV6 != $oldIpV6 ]]; then
    echo "$(date): Updating the DNS. Output can be found in ${logfile}"
    echo "$(date): $(curl -k "https://dynv6.com/api/update?token=${token}&zone=${zone}&ipv6=${newIpV6}")" | tee -a $logfile
    echo $newIpV6 > $cachefile
else
    echo "$(date): IP did not Change. Skipping the update" | tee -a $logfile
fi

Use this as a cron job as often as you need and you should be good to go.

I test it on my RPI4b,Which
ip -6 addr show scope global | grep inet6 | sed -e 's/^.inet6 ([^ ])/.*$/\1/;t;d'
will output two AAAA addr,and this script output error like this:
curl: (3) URL using bad/illegal format or missing URL
Sun 05 Feb 2023 09:45:55 AM CST:

can be fix by adding | head -n 1)

@nibbsification
Copy link

nibbsification commented Feb 7, 2023

can be fix by adding | head -n 1)

Hi there, could you please tell a noob, at which position? I added it behind the URI and it didnt work
dynv6.sh: line 23: ` echo "$(date): $(curl -k "https://dynv6.com/api/update?token=${token}&zone=${zone}&ipv6=${newIpV6}")" | tee -a $logfile | head -n 1)'

Edit: I've read your comment with more care and figured it out. it should be like this:
newIpV6=$(ip -6 addr show scope global | grep inet6 | sed -e 's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)

@aacunha
Copy link

aacunha commented Apr 25, 2023

This is my script (bash), free to use:


#!/bin/bash

bin="curl -fsS"

fqdn=<your_domain>
token='<Your_token>'

ipv4=dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | cut -f2 -d'"'
ipv6=dig -6 TXT +short o-o.myaddr.l.google.com @ns1.google.com | cut -f2 -d'"'

echo "My Ipv6: "$ipv6
echo ""

if [[ $ipv6 =~ ^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$ ]]
then
echo "Updating $fqdn (ipv6)..."
$bin "http://dynv6.com/api/update?hostname=$fqdn&ipv6=$ipv6&token=$token"
echo ""
else
echo "Invalid IPv6 address"
fi

echo "My IPv4: "$ipv4
echo ""

if [[ $ipv4 =~ ^[0-9]+.[0-9]+.[0-9]+.[0-9]+$ ]]
then
echo "Updating $fqdn (ipv4)..."
$bin "http://dynv6.com/api/update?hostname=$fqdn&token=$token&ipv4=$ipv4"
else
echo "Invalid IPv4 address"
fi

echo "End of DNS records update!"

exit 0
`

`

@coolirisme
Copy link

coolirisme commented Mar 19, 2024

Here is a node.js implementation of the script, that can be built into an executable with pkg and scheduled using Systemd/Windows Task Scheduler

const os = require('os');

const ip6addr = (os.networkInterfaces())['Ethernet'][1]['address'];
const domain = '<domain>';
const username = '<token>';
const url = `https://dynv6.com/api/update?hostname=${domain}&token=${username}&ipv6=${ip6addr}`;

fetch(url, {
  method: 'GET'
}).then((response) => {
  response.text().then((data) => {
    console.log(data);
  });
}).catch((error) => {
  console.error(error);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment