How to Force a Dynamic DNS Update After Previous Failure on IPFire 2.17

Sep 17, 2015 18:00 ipfire dns

Overview

IPFire uses a Python script located at /usr/lib/python2.7/site-packages/ddns/providers.py to handle interfacing with many of the dynamic DNS providers that the project supports. The script and the web UI from which it’s normally launched try to provide as generic of an interface as possible for the administrator of the firewall to input the DDNS configuration stuffs. Getting it right for your particular provider can take several attempts as some DDNS providers expect a username and password in the request, some just a password, yet others may expect some weird combination of the username and password into an obfuscated string in either the username or password field. Unfortunately, IPFire will only attempt to update a record twice (from what I can tell in the logs) before marking it as “failed” and then preventing any further attempts for 16 hours. My guess is that this is to prevent you from potentially inundating your DDNS provider with failed update attempts.

Failures
Sep 16 19:55:00 myipfire ddns[12345]: An update has not been performed because earlier updates failed for my-broken-site.net 
Sep 16 19:55:00 myipfire ddns[12345]: Last failure message:
Sep 16 19:55:00 myipfire ddns[12345]: DDNSRequestError: Request error
Sep 16 19:55:00 myipfire ddns[12345]: Further updates will be withheld until 2015-09-17 11:55:00.086977

SQLite

The fact that the script was able to track state immediately tipped me off to the presence of a state file in the form of either a CSV file or a SQLite database. Since only a masochist would want to track state in a CSV file, I assumed SQLite was in play. Tracking down the SQLite database in question took me a few minutes as I’m not particularly fluent in Python and IPFire doesn’t ship with strace. In hindsight, I could have used lsof to assist me. At any rate, the SQLite database is located at /var/lib/ddns.db.

There were only 2 tables in the database: “settings” and “updates”:

# sqlite3 /var/lib/ddns.db 
SQLite version 3.8.7.4 2014-12-09 01:34:36
Enter ".help" for usage hints.
sqlite> .tables
settings  updates

And after examining the schema, I knew “updates” was my target:

sqlite> .schema updates
CREATE TABLE updates (
					hostname  TEXT NOT NULL,
					status    TEXT NOT NULL,
					message   TEXT,
					timestamp timestamp NOT NULL
				);
CREATE INDEX idx_updates_hostname ON updates(hostname);
Some success, some failure
sqlite> SELECT * FROM updates;
my-site.org|success||2015-09-17 00:52:56.718985
my-other-site.com|success||2015-09-17 00:52:56.718985
my-broken-site.net|failure|DDNSUpdateError: The update could not be performed|2015-09-17 11:55:00.086977

Breaking the chains

sqlite> DELETE FROM updates WHERE status = 'failure';
sqlite> .quit

Huzzah!

Now go back into the web UI: “Services” -> “Dynamic DNS” and click “Instant Update”.

The End