Discussion in 'Tomato Firmware' started by B.S., May 6, 2012.

  1. B.S.

    B.S. Serious Server Member

    I wanted to share my experience with setting up DDNS so that others could benefit from it.

    I started out setting up a home server and purchasing a domain name (actually three since I wanted .com, .net, and .org TLDs) with NameCheap since they came with a free DDNS service and I wanted to centralize all my domain activity through one service.

    It seemed ideal to have my router (a Linksys E3000) update DDNS since it is the component that receives the WAN change events rather than running a separate process that would have to poll for that information on a machine behind the router.

    I looked at the stock Linksys firmware and the only two services listed didn't include the one that I had selected, so I investigated and eventually flashed DD-WRT as it supported my chosen provider. Its implementation, however, turned out to be geared to allow multiple subdomain updates rather than multiple TLDs. With the NameCheap DDNS update mechanism expecting a password to be provided for any updates, and different passwords for different TLDs, I thought that https was a good idea. The stock answer for this problem involved another service, DNS-O-Matic, which is a fine solution for those that can deal with myriad internet accounts.

    I liked the goal of OpenWRT, but couldn't figure out whether I could get it to support my router. Tomato seemed to be something I could work with -- it supported my router, NameCheap, https, and though only providing a GUI for two TLDs, seemed flexible enough that I could write a script to deal with my particular requirements. Then I was supposed to pick between Toastman, Shibby, Victek, and Teaman. I don't believe that it mattered much to me, but I ended up building Toastman from source that I managed to fetch from the git repo.

    I flashed my build (Tomato Firmware v1.28.7498 MIPSR2-Toastman-VLAN-RT K26 USB VPN) and it worked! I know I shouldn't have been surprised, but confidence comes with experience, and I didn't have any. For my first attempt at setting up DDNS updates, I thought that I would write a script (put in "/tmp/var/wwwext/cgi-bin/", named manyddns.cgi and marked executable) to do all of the individual updates and point the Dynamic DDNS 1 Custom URL at it. Since I set the router web access to https, I had to call it as "https://username:password@LAN.I.P.ADDR/ext/cgi-bin/manyddns.cgi". That worked, but I had to write another script to copy it over from storage (jffs in my case as "manyddns.autorun") so that it could survive being turned off. I couldn't figure out how to get any return status to the GUI, would have to remember to change the call's LAN IP address if I set the router's to something else, didn't especially like my username and password showing in clear text on the GUI, and was not sure that if the web admin access was disabled, that this method would work.

    Since I built it myself and had to look at the source anyway to figure out how to write the CGI script, it then occurred to me that with a couple of simple modifications, I could solve all the problems. The basic idea is adding "file://" mime-type support to the Custom URL service of the Dynamic DNS feature, and point it at an executable shell script in storage. I provide a sanitized script in addition to my changes in the hope that something like this would be incorporated into all of the mainstream branches. Please respond if this works for you or you think it should be done differently.

    I want to thank all those who have worked on, and those that still do work on Tomato.

    ****begin GUI entry****
    ****end GUI entry****

    # -- add DDNS URIs below between the lines marked "--------HERE--------"
    #+ success variable below should be adjusted appropriately according to your provider
    ipaddr="$(nvram get wan_ipaddr)"
    newline="$(printf '\na')"
    set -- $(cat <<-"--------HERE--------"

    log () { logger -p "user.${1}" -t "${rootname}[$$]" "${2}"; }
    notice () { log "notice" "${1}"; }
    info () { log "info" "${1}"; }
    mduck () {
        if [ ! -f "${conffile}" ]; then
            touch "${conffile}"
        if [ ! -w "${conffile}" ]; then
            notice "File ${conffile} not writable."
            return 1
        return 0
    process () {
        echo "url ${1}" > "${conffile}"
        msgreturn=$(mdu --service "custom" --conf "${mdufile}")
        if [ "$(echo ${msgreturn} | grep -Fi -e ${success})" ]; then
            notice "Update ${1} unsuccessful."
    if [ mduck ]; then
        for uri in "${@}"; do
            process "${uri/"${ipsubst}"/"${ipaddr}"}"
    if [ -z "${oldparams}" ]; then
        set --
        set -- "${oldparams}"
    if [ "${anysuccess}" -eq 1 -a "${anyfailure}" -eq 1 ]; then
        notice "Update partially successful."
    elif [ "${anysuccess}" -eq 1 ]; then
        info "Update completely successful."
    elif [ "${anyfailure}" -eq 1 ]; then
        notice "Update completely unsuccessful."
        info "Update completely ineffective."
    rm -f "${conffile}"
    exit "${anyreturn}"
    --- mdu.c.original    2012-04-21 19:03:51.000000000 -0700
    +++ mdu.c    2012-05-01 18:41:55.000000000 -0700
    @@ -1540,6 +1540,7 @@
        char he[256];
        char *header;
        int https;
    +    int afile;
        char *host;
        char path[256];
        char *p;
    @@ -1550,11 +1551,15 @@
        strcpy(url, get_option_required("url"));
        https = 0;
    +    afile = 0;
        host = url + 7;
        if (strncasecmp(url, "https://", 8) == 0) {
            https = 1;
    +    else if (strncasecmp(url, "file://", 7) == 0) {
    +        afile = 1;
    +    }
        else if (strncasecmp(url, "http://", 7) != 0) {
            error(M_INVALID_PARAM__S, "url");
    @@ -1563,6 +1568,16 @@
            error(M_INVALID_PARAM__S, "url");
        strcpy(path, p);
    +    if (afile == 1) {
    +        if (access(path, X_OK) == 0) {
    +            r = system(path);
    +            if (r == 0)
    +                success();
    +            else
    +                error(M_UNKNOWN_RESPONSE__D, r);
    +        }
    +        error(M_INVALID_PARAM__S, "url");
    +    }
        *p = 0;
        if ((c = strstr(path, "@IP")) != NULL) {
    --- basic-ddns.asp.original    2012-05-02 01:27:44.000000000 -0700
    +++ basic-ddns.asp    2012-05-02 01:41:52.000000000 -0700
    @@ -162,7 +162,7 @@
                    if (e.value == '') {
                        e.value = 'http://';
    -                if (\/\/./) != 0)  {
    +                if ((\/\/./) != 0) && (\/\/./) != 0)) {
                        ferror.set(e, 'Expecting a URL -- http://... or https://...', quiet)
                        r = 0;
  2. quietsy

    quietsy Network Guru Member

    This can be done without any scripts using DNS-O-Matic, I personally use freedns which has an option to bind all of your records together so that if there is a change it will change them all.
    (great job with the scripts tho)
  3. B.S.

    B.S. Serious Server Member

    Thanks, quietsy. I found out I could use code blocks rather than quote blocks and the edit should make this more usable.

    Despite how nice, convenient and free services like FreeDNS and DNS-O-Matic are, they are still middlemen. For most, probably, it is worth it, but for me, it is another password for me to forget in addition to being another link in a chain that could break (however unlikely).

    Any takers on including the DDNS "file://" mime-type in the code? It would make supporting new or changed DDNS providers possible without a rebuild.
  4. Icarus

    Icarus Reformed Router Member

    Could you post some instructions on how to do this?

    I have a Windows/DOS background, and I'm new to linux. I've already created the with my namecheap hosts in it and stored it in /jffs

    I'm not sure how to use the diff -u mdu.c or the diff -u basic-ddns.asp
  5. B.S.

    B.S. Serious Server Member

    diff commands show what is different between two files. I had hoped that the maintainers of Tomato might have incorporated the changes indicated by the results of the diff commands -- they know how to use it, I'm sure.

    Since I haven't looked at the source code for a year now, I'm not sure if those changes remain appropriate in later versions. It is conceivable that they have even incorporated the changes so that people would not have to compile their own firmware to gain this capability.

    If you are using the old version indicated, however, you can still make the line additions (shown by a + in the first column) and subtractions (shown by a - in the first column) to the original mdu.c and basic-ddns.asp source files at the right places (shown starting at the line numbers enclosed by @@) before compiling your own firmware. I think the linux patch command will make those changes automatically from the original file and the diff file to produce the new changed file. If you are using a newer version, you may need to interpret where and how to make similar edits, if they are still necessary.

    If you have already compiled your own firmware from source code (even without any changes) and successfully flashed it to your router, I could help to verify what needs to be changed. If not, I'm not sure that I would recommend this path. Even though the changes to the Tomato source code are relatively minor, compiling your own firmware to flash on your router is no mean feat. Good luck.
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice