May 15

I today received an email from Dyn (previously DynDNS), stating:

Starting now, if you would like to maintain your free Dyn account, you must log into your account once a month. Failure to do so will result in expiration and loss of your hostname. Note that using an update client will no longer suffice for this monthly login.

(emphasis theirs)

Now, if this were a service which requires interaction then this would be an unfriendly but potentially fair way to weed-out inactive accounts. This isn’t one of those cases, though – I can happily go for months or even years where my only interaction with Dyn(DNS) is via auto-update clients. And this is the heart of the problem – many routers and embedded devices have built-in DynDNS clients, frequently with no option to switch to an alternative service. Possibly this is worth $25/year, possibly it isn’t. Personally, I’m not paying a penny to a company trying to hold its users to ransom like this. For my usage, there are a handful for hostnames in a Dyn(DNS) domain – and therefore these cannot to transferred to a different provider. I keep them going purely so that historic links will still work.

And, to resolve the immediate problem, I wrote this script which can be scheduled to run every 15 days in order to keep my account active – enjoy!

#!/bin/bash

LOGIN="****"
PASSWORD="****"

COOKIES="/tmp/.dynsdns.cookies.txt"
AL="en-gb"
#UA="Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/0.0.0 (KHTML, like Gecko) Version/0.0.0 Safari/0.0.0"

LOGINURL="https://account.dyn.com/entrance/"
POSTURL="$LOGINURL"
CHKURL="https://account.dyn.com/"

(( DEBUG )) && DST="-" || DST="/dev/null"

[[ -w "$( dirname "$COOKIES" )" ]] || { echo >&2 "FATAL: Cannot write to directory '$( dirname "$COOKIES" )'" ; exit 1; }

# Ensure no broken session caching...
if [[ -s "$COOKIES" ]]; then
	[[ -w "$COOKIES" ]] || { echo >&2 "FATAL: Cannot write to file '$COOKIES'" ; exit 1 ; }
	rm -f "$COOKIES" >/dev/null 2>&1
fi

(( DEBUG )) && echo >&2 "DEBUG: Fetching initial headers to pre-load cookies..."
curl -b $COOKIES -c $COOKIES -Ikso "$DST" -A "$UA" --url "$LOGINURL"

(( DEBUG )) && echo >&2 "DEBUG: Fetching UID..."
VALUE="$(
	   curl -b $COOKIES -c $COOKIES -kso - -A "$UA" --url "$LOGINURL" | \
	   grep -m 1 "multiform" | \
	   cut -d"'" -f 6
)"

(( DEBUG )) && echo >&2 "DEBUG: Read UID as '$VALUE' - posting data..."
curl -b $COOKIES -c $COOKIES -d "username=$LOGIN" -d "password=$PASSWORD" -d "iov_id" -d "multiform=$VALUE" -e "$LOGINURL" -kso "$DST" -A "$UA" --url "$POSTURL"

(( DEBUG )) && echo >&2 "DEBUG: Response received - verifying result..."
curl -b $COOKIES -c $COOKIES -e "$POSTURL" -kso - -A "$UA" -H "Accept-Language: $AL" --url "$CHKURL" | \
	   grep -qE "<span>(Welcome|Hi)&nbsp;<b>$LOGIN</b></span>" \
	&& echo "Login successful" \
	|| { echo >&2 "Login failed" ; exit 1 ; }

exit 0

Download this script from http://files.stuart.shelton.me/unix/dyndns-login.sh.

47 Responses to “Dyn SLA Update – or, How To Lose Friends and Alienate Customers”

  1. Trent Says:

    Thank you so much for this.

    I don’t know much about this, but could you please explain how one initialises this fantastic script you have created please? I’d like to use it myself, but am unsure as to how.

    Thank you kindly
    Trent

  2. Martin Says:

    Awesome script, thanks! Sadly, DYN is more and more unfriendly to legacy free accounts. I also have one with hostnames I would hate to lose.

  3. Noone Says:

    great job, i would be pelased if you could publish a windows dos batch version with cURL of this.

  4. Martin Says:

    @Trent. You schedule the script to run from crontab. Obviously, it assumes a Linux box or unix-like OS (like apple).

  5. fermulator Says:

    Hey it looks like your script was truncated? (the last line seems to just … stop … without the closing double quote)

    $ ./dynDns.sh 
    ./dynDns.sh: line 25: unexpected EOF while looking for matching `"'
    ./dynDns.sh: line 26: syntax error: unexpected end of file
  6. Kril Says:

    Thanks man.
    Works perfect, now scheduled on dd-wrt

  7. Stuart Says:

    @fermulator – Looks okay to me… perhaps a copy/paste problem?

    Try downloading from this link:

    http://files.stuart.shelton.me/unix/dyndns-login.sh

  8. fermulator Says:

    Thanks! My mistake, I had scripts disabled so I couldn’t see the full script.

    Note, since the impact to this /failing/ is so drastic, I strongly recommend, as part of the cron job people create, that they ALSO send an e-mail on FAIL.

    For example:

    RESULT=$(curl -b $COOKIES -c $COOKIES -e "$POSTURL" -kso - -A "$UA" --url "$CHKURL")
    echo "DEBUG: URL POST $RESULT" | grep "Welcome&nbsp;<b>$LOGIN</b>"
    
    if [ $? -ne 0 ] ; then
      echo "Subject: DynDns Login/Update Failed\n\n $RESULT" | msmtp -d your_email@domain.com
    else
      echo "Subject: DynDns Login/Update Success\n\n YEYE!" | msmtp -d your_email@domain.com
    fi
    
  9. fermulator Says:

    (if you need help setting up msmtp from a linux server, I have it documented here: http://ubuntuforums.org/showthread.php?t=1185134 — only do the /first/ section of the how to)

  10. Stuart Says:

    Personally, I’ve always like ‘ssmtp‘ as a simple one-line-configuration MTA…

  11. Stuart Says:

    Oops…

    When I pasted the link above, I somehow forgot to remove my credentials from the script!

    I’ve now performed a password-reset, but could anyone who retrieved the above script please edit/delete their copy? If nothing else, it shouldn’t authenticate any longer ;)

    Many thanks,

    Stuart

  12. silverdulcet Says:

    Just tried the script and I get Login Failed every time. Not sure what the problem is. Perhaps they’ve already made a change to their site?

  13. Stuart Says:

    @silverdulcet (After updating my credentials) – it still looks good to me…

  14. fermulator Says:

    Still working for me:

    fermulator@fermmy-server:~/scripts$ ./dynDns.sh 
    Login successful
    -------- Original Message --------
    From: Fermulator Fermulator 
    Sent: Tue 21 May 2013 12:56:05 AM EDT
    To: Fermulator Fermulator 
    Cc:
    Subject: DynDns Login/Update Success\n YEYE!
  15. Martin Says:

    @silverdulcet
    I hope you are not trying to authenticate with :P

    LOGIN="****"
    PASSWORD="****"
  16. silverdulcet Says:

    Nope. I filled in the proper credentials. Tried it with 2 different accounts. One password has special characters & one doesn’t.

  17. boo Says:

    Great script, will be running it as a cronjob on my NAS.

    The only thing I had to change was the grep “-m 1” parameter. My system spits out a “grep: invalid option -- m” when using it.

    Instead I used “head -n 1“, example:

    VALUE="$( curl -b $COOKIES -c $COOKIES -kso - -A "$UA" --url "$LOGINURL" | grep "multiform" | cut -d"'" -f 6 | head -n 1 )"
  18. yaztromo Says:

    Thank you for this. It is working fine for me.

    The only weakness I see is that it will be easy for Dyn to break the script, and they will probably do just that as often as possible.

  19. Peterkneter Says:

    It doesn’t work for me. I Always get 302 Found.
    Here my Debug Information:

    DEBUG: Fetching initial headers to pre-load cookies...
    HTTP/1.1 200 OK
    Date: Tue, 21 May 2013 16:31:38 GMT
    Server: Apache
    Cache-control: no-cache
    Content-Type: text/html; charset=ISO-8859-1
    Expires: Tue, 21 May 2013 16:31:38 GMT
    Set-Cookie: id=/caEcIu/FvC14c6gHKY6kwciz+o; domain=account.dyn.com; path=/; expires=Mon, 19-Aug-2013 16:31:38 GMT
    Accept-Ranges: none
    Vary: Accept-Encoding
    
    DEBUG: Fetching UID...
    DEBUG: Read UID as '7DA5CE1862B7568C0145A5A7CF660002B4' - posting data...
    
    
    302 Found
    
    Found
    The document has moved <a HREF="https://account.dyn.com/" rel="nofollow">here</a>.
    
    DEBUG: Response received - verifying result...
    Login failed
  20. Ruediger Says:

    I have tried with 3 accounts. One is working fine, the other two fail. Manual login works fine for all three accounts. None of the credentials contains special characters. The only difference of the failing one is a much longer login name. Might be a coincidence, but this is the only obvious difference…

  21. Sean Says:

    I experience the same thing, using the proper credentials. It’s a great idea, and I’m much appreciative for the script, if I can figure out what’s going wrong.

  22. Stuart Says:

    @silverdulcet You might need to change the double-quotes for single-quotes, e.g.:

    LOGIN='myu$sername'
    PASSWORD='c4@2yP@ssw0rd!'

    … to make sure that nothing tries to interpret it. Dollar symbols especially could be problematic.

  23. Stuart Says:

    @boo: Your NAS could well be using busybox rather than GNU coreutils grep, in which case it may be lacking the ‘match <n> option – but using ‘head‘ is definitely a perfectly good work-around.

  24. Stuart Says:

    @yaztromo: … or they might just disable free accounts. That seems to be the way they’re headed.

    Step 1: Make free accounts a pain to maintain;
    Step 2: Point out the drop-off in the number of free accounts;
    Step 3: Because clearly no-one wants a free account, cancel them;
    Step 4: ???
    Step 5: Profit!

  25. Stuart Says:

    @Peterkneter: ‘Found’ *is* the correct response at this stage – the final check is a dumb string-match against the accounts page. It could be that your accounts page is localised (e.g. not reading “Welcome”) or it could be that it your username ($LOGIN) is interesting and/or long that it has been truncated/altered.

    @silverdulcet, @Ruediger: Thinking about it, this could be the issue affecting you too.

    Could anyone with a language *not* set to en-GB or en-US please login manually and then look at the accounts page for the equivalent of “Welcome <username>” and post what they find here?

  26. silverdulcet Says:

    I see the DEBUG lines in the script. How would I enable them to see if I can find where the error is.

    The usernames of the 2 accounts I tried are 9 and 12 characters, just regular letters no numbers or special characters. Language is set to en-US.

  27. Stuart Says:

    @silverdulcet:

    DEBUG=1 ./dyndns-login.sh
  28. Ruediger Says:

    @Stuart
    used debug for successful account and failing one. Here only debug info:
    ———————————

    DEBUG: Fetching UID...
    DEBUG: Read UID as '4C963E4A20D55590832C8FE2878B545698' - posting data...
    
    
    302 Found
    
    Found
    The document has moved <a HREF="https://account.dyn.com/" rel="nofollow">here</a>.
    
    DEBUG: Response received - verifying result...
    Login successful

    ———————–
    ———————–

    DEBUG: Fetching UID...
    DEBUG: Read UID as '3069D920308CF60A496C87B9DCCBC83CAC' - posting data...
    
    
    302 Found
    
    Found
    The document has moved <a HREF="https://account.dyn.com/" rel="nofollow">here</a>.
    
    DEBUG: Response received - verifying result...
    Login failed

    ——————-

  29. Peterkneter Says:

    I found my problem. I used my E-Mail address instead of the username. Now it is working fine.

    @Stuart:
    Where can I change the language at dyn.com?

  30. Stuart Says:

    To everyone who’s having problems:

    It does look as if the authentication has actually been successful, but the check afterwards is failing.

    To help to diagnose this, please update the final line of the original script to:

    FILE="$( mktemp -t "$0.XXXXXXXX" )" || { echo >&2 "mktemp failed: $?" ; exit 1 ; }
    curl -b $COOKIES -c $COOKIES -e "$POSTURL" -kso - -A "$UA" --url "$CHKURL" -o "$FILE"
    if grep -q "<span>Welcome&nbsp;<b>$LOGIN</b></span>"; then
        echo "Login successful"
    else
        echo >&2 "Login failed"
        grep -q "<span>Welcome&nbsp;" "$FILE" && echo "Page contains 'welcome' string"
        grep -q "<b>$LOGIN</b>" "$FILE" && echo "Page contains username"
        PATTERN="<span>[^&]+&nbsp;<b>[^<]+</b></span>"
        grep -Eq "$PATTERN" "$FILE" && echo "Page contains welcome pattern $( grep -Eo "$PATTERN" "$FILE" )"
        echo "Checks complete"
    fi
    rm "$FILE"
  31. Stuart Says:

    @Peterkneter: D’oh!

    I don’t actually know that you can change the language – it just struck me that if “Welcome” is localised then it might explain why people are having problems ;)

  32. Ruediger Says:

    sorry, does not work for me:

    DEBUG: Fetching UID...
    DEBUG: Read UID as 'EDE102E60F53B36775683E66566F5307B2' - posting data...
    
    302 Found
    
    Found
    The document has moved <a HREF="https://account.dyn.com/" rel="nofollow":gt;here</a>.
    
    DEBUG: Response received - verifying result...
    mktemp: invalid template, `./dyndns.sh.XXXXXXXX', contains directory separator
    mktemp failed: 1
    
  33. Stuart Says:

    @Ruediger: My bad, I was thinking of the GNU ‘--tmpdir‘ option (which you’ll likely be missing if you’re on Red Hat or not-Linux). Change the first line to:

    FILE="$( mktemp -t "$( basename "$0" ).XXXXXXXX" )" || { echo >&2 "mktemp failed: $?" ; exit 1 ; }
  34. Dirk Says:

    First to say: Nice script – thanks for that.

    I maintain multiple accounts on dyndns so i have modified the script to be able to handle multiple accounts and added a sleep which hopefully helps to disguise it a bit :) Further i wanted to have an email alert as this accounts are very important for me. For one account I figured out the same problem which was described by Stuart. One account has the welcome message “Welcome”, the other one shows “Hi”. I have modified this as well.

    Find my code below and feel free to use and modify it

    #!/usr/bin/env bash
    
    LOGIN=(     'account1'   'account2'   'accountn' )
    PASSWORD=(  'password1'  'password2'  'passwordn' )
    #UA="Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/0.0.0 (KHTML, like Gecko) Version/0.0.0 Safari/0.0.0"
    
    EMAIL='myemail@example.com'
    
    COOKIES="/tmp/.dynsdns.cookies.txt"
    
    LOGINURL="https://account.dyn.com/entrance/"
    POSTURL="$LOGINURL"
    CHKURL="https://account.dyn.com/"
    
    (( DEBUG )) && DST="-" || DST="/dev/null"
    
    for i in ${!LOGIN[*]};
    do (
            # Ensure no broken session caching...
            [[ -s "$COOKIES" ]] && rm "$COOKIES"
    
            (( DEBUG )) && echo >&2 "DEBUG: Fetching initial headers to pre-load cookies..."
            curl -b $COOKIES -c $COOKIES -Ikso "$DST" -A "$UA" --url "$LOGINURL"
            (( DEBUG )) && echo >&2 "DEBUG: Fetching UID..."
            VALUE="$( curl -b $COOKIES -c $COOKIES -kso - -A "$UA" --url "$LOGINURL" | grep -m 1 "multiform" | cut -d"'" -f 6 )"
            (( DEBUG )) && echo >&2 "DEBUG: Read UID as '$VALUE' - posting data..."
            curl -b $COOKIES -c $COOKIES -d "username=${LOGIN[$i]}" -d "password=${PASSWORD[$i]}" -d "iov_id" -d "multiform=$VALUE" -e "$LOGINURL" -kso "$DST" -A "$UA" --url "$POSTURL"
            (( DEBUG )) && echo >&2 "DEBUG: Response received - verifying result..."
            curl -b $COOKIES -c $COOKIES -e "$POSTURL" -kso - -A "$UA" --url "$CHKURL" | grep -qE "<span>(Welcome|Hi)&nbsp;<b>${LOGIN[$i]}</b></span>" && (echo "Login successful" | mutt -s "DynDNS ${LOGIN[$i]} successful" -- $EMAIL) || (echo "Login failed" | mutt -s "DynDNS ${LOGIN[$i]} failed" -- $EMAIL)
            sleep 5$i.$RANDOM );
    done
    
  35. Ruediger Says:

    Hello Stuart,
    Thank you for your support. I am using OpenSuse. Does not work for me. The page is fetched, but then the script does not continue. Final output see below:

    (function(){
    var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
    g.type = 'text/javascript'; g.defer = true; g.async = true;
    g.src = document.location.protocol + '//dyn3.apptegic.com/scripts/apptegic-tw.min.js';
    s.parentNode.insertBefore(g, s);
    })();
    </script>
    </body>
    </html>
    <!-- page generated successfully -->
    

    Then it stops and I have to cancel script via ctrl-c.

  36. Stuart Says:

    @Ruediger: That’s pretty much the end save for the login check, and the best way to confirm that is probably to add ‘set -o xtrace‘ near the top of the script – from that point onwards, there should be very verbose output detailing the exact commands being invoked. The last of these output is likely where the problem lies…

  37. Ruediger Says:

    Done: Following output (replaced plan text of login name):

    (function(){
    var d = document, g = d.createElement(‘script’), s = d.getElementsByTagName(‘script’)[0];
    g.type = ‘text/javascript’; g.defer = true; g.async = true;
    g.src = document.location.protocol + ‘//dyn3.apptegic.com/scripts/apptegic-tw.min.js’;
    s.parentNode.insertBefore(g, s);
    })();


    + grep -q ‘Welcome MyLogin

  38. Stuart Says:

    @Ruediger: Hmm – I think that angle-brackets in your comment have confused WordPress, and it’s swallowed most of your comment :(

    Could you email the result to me or try pasting it in a comment again surrounded by ‘<pre>’/’</pre>’ tags?

  39. Stuart Says:

    Update: Script updated (and download link added to top of page). By setting an ‘Accept-Language’ header, I’m hoping that the login screen will always show ‘Welcome’, overriding whatever default language the account may be set to. This assumes that Dyn does track different languages, and pays any attention to this header. Please let me know if this has made any difference…

  40. Ruediger Says:

    Hello Stuart,
    grate work! Now it is working fine for all 3 accounts. I have also made negative tests with wrong login data and it worked as expected. Thank you very much for your effort and sharing it! Thumbs up!

    Greetings
    Ruediger

  41. Gerry Says:

    Hello,
    great work and many thanks to the creators of this script !

    To use this feature with a Fritzbox (with freetz+curl), I modified the code a little bit and scheduled it with cron.

    #!/bin/sh
    
    #DEBUG=1
    
    LOGIN="***"
    PASSWORD="***"
    EMAIL="***"
    
    COOKIES="/tmp/.dynsdns.cookies.txt"
    AL="en-gb"
    UA="Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"
    LOGINURL="https://account.dyn.com/entrance/"
    POSTURL="$LOGINURL"
    CHKURL="https://account.dyn.com/"
    
    [[ $DEBUG ]] && DST="-" || DST="/dev/null"
    
    # random sleep time 1-6000
    DELAY=$(($(hexdump -n 2 -e '"%u"' /dev/urandom) % 6000 + 1))
    echo >&2 "Wait '$DELAY' sec..."
    sleep $DELAY
    
    [[ -w "$( dirname "$COOKIES" )" ]] || { echo >&2 "FATAL: Cannot write to directory '$( dirname "$COOKIES" )'" ; exit 1; }
    
    # Ensure no broken session caching...
    if [[ -s "$COOKIES" ]]; then
    	[[ -w "$COOKIES" ]] || { echo >&2 "FATAL: Cannot write to file '$COOKIES'" ; exit 1 ; }
    	rm -f "$COOKIES" >/dev/null 2>&1
    fi
    
    [[ $DEBUG ]] && echo >&2 "DEBUG: Fetching initial headers to pre-load cookies..."
    curl -b $COOKIES -c $COOKIES -Ikso "$DST" -A \"$UA\" --url "$LOGINURL"
    
    [[ $DEBUG ]] && echo >&2 "DEBUG: Fetching UID..."
    VALUE="$(
    	   curl -b $COOKIES -c $COOKIES -kso - -A \"$UA\" --url "$LOGINURL" | \
    	   grep -m 1 "multiform" | \
    	   cut -d"'" -f 6
    )"
    
    [[ $DEBUG ]] && echo >&2 "DEBUG: Read UID as '$VALUE' - posting data..."
    curl -b $COOKIES -c $COOKIES -d "username=$LOGIN" -d "password=$PASSWORD" -d "iov_id" -d "multiform=$VALUE" -e "$LOGINURL" -kso "$DST" -A \"$UA\" --url "$POSTURL"
    
    [[ $DEBUG ]] && echo >&2 "DEBUG: Response received - verifying result..."
    curl -b $COOKIES -c $COOKIES -e "$POSTURL" -kso - -A \"$UA\" -H "Accept-Language: $AL" --url "$CHKURL" | \
    	   grep -qE "(Welcome|Hi) <b>$LOGIN</b>" \
    	&& echo "Login successful" \
    	|| { echo >&2 "Login failed" ; mail send -s "Dyndns web login failed !" -f "fritz.box@home.de" -t "\"$EMAIL\" ; exit 1 ; }
    
    exit 0
    
  42. Gerry Says:

    Sorry,

    there is an error in line 48 near EMAIL.
    To late to keep a clear mind ;-)


    || { echo >&2 "Login failed" ; mail send -s "Dyndns web login failed !" -f "fritz.box@home.de" -t "\"$EMAIL\" ; exit 1 ; }

    should be:


    || { echo >&2 "Login failed" ; mail send -s "Dyndns web login failed !" -f "fritz.box@home.de" -t \"$EMAIL\" ; exit 1 ; }

    Greeting from Germany.

  43. Titus Says:

    Stuart, I appreciate the time and effort you put into this. I’m not very adept with code, can you tell me how I’d get this to run on my Mac? Do I just save it in a text file with a certain extension?

  44. Stuart Says:

    @Titus: No problem – if you follow the instructions at parezcoydigo.wordpress.com then you should be able to copy-and-paste the above shell-script into the Apple ‘Automator’ utility supplied with OS X, and it will create an application for you. Running this application just like any other will then execute the script.

    Please note that I’ve not actually tried this, so please let us know how you get on ;)

  45. 2D Says:

    The login check is case-sensitive, but the actual login to dyndns is not. You could use grep -iqE in stead of grep -qE .

  46. noam Says:

    Dyndns is stopping free account.
    First there was no limit, then 5 domains, then 2, and now you have to pay ….

    I’m now using ovh (6€/year) for my domains. They have a ddns option include …

  47. Stuart Says:

    Well, that’s that then. Would the last person to leave dyn.com please turn off the lights :(

    I’m certainly not planning on paying dyn a penny, as this would only reward and encourage this kind of bait-and-switch behaviour. On the other hand, the domains I have (had?) with dyn were ones such as ‘.homeunix.net’ which aren’t portable to elsewhere… so I guess those sites are going dark. Congratulations, dyn, you’re killing off a small part of the internet.

Leave a Reply

%d bloggers like this: