Tuesday, May 14, 2013

DYNDNS Auto Login - A programmatic approach

So DynDns.org have decided to make their precious users work for their free accounts by forcing users to login periodically. What a pain in the ass! How can we automate this process programmatically? Lets explore the inner workings of the DynDns login page:


Apart from the obvious fields we need to send back (username, password...),  you can see by the highlighted sections there are 2 hidden fields. Thankfully the first one (iov1) is not necessary for a login - this field is rather complexly generated by javascript and does not render except in a browser. This means we are only interested in the "multiform" hidden field which we need to send back in our request.

That sorts out the fields we need to populate: username, password, submit and multiform. What about cookies? Yes there are cookies but that is easily captured in the header request message (set-cookie) and we can simply attach the same cookie back into our response header unmodified.

I have since setup a cron job on Google App Engine that runs my code once every month. This should help alleviate the headache of logging in manually every month. Here is the code if anyone wants to use it:


URL url = new URL("https://account.dyn.com/entrance/");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
//Spoof as if from a web browser
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31");
con.setDoOutput(true);
con.connect();
// give it 15 seconds to respond
con.setReadTimeout(15 * 1000);

//Get cookie id
String headerName = null;
String cookie = null;
for(int i = 1; (headerName = con.getHeaderFieldKey(i)) != null; i++) 
{
 if (headerName.equalsIgnoreCase("Set-Cookie")) 
 {                  
  cookie = con.getHeaderField(i);
  break;
 }
}

//Read the website into a string so we can parse with Jsoup
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String temp = "";
String line = null;
while((line = reader.readLine()) != null)
{
 temp += line + "\r\n";
}
reader.close();

Document doc = Jsoup.parse(temp);
Elements els = doc.getElementsByTag("input");
String multiform = null;
for(Element e : els)
{
 String name = e.attr("name");
 String value = e.attr("value");
 //Get the hidden field required for login
 if(name.equalsIgnoreCase("multiform"))
 {
  multiform = value;
  break;
 }
}

//Open the connection again with the correct headers
con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Cookie", cookie);
con.setRequestProperty("Host", "account.dyn.com");
con.setRequestProperty("Origin", "https://account.dyn.com");
con.setRequestProperty("Referer", "https://account.dyn.com/entrance/");
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31");
con.setRequestMethod("POST");
con.setDoOutput(true);

String username = "your_username_here";
String password = "your_password_here";

//Set the login fields
OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
String data = 
 URLEncoder.encode("username", "UTF-8")
 + "="
 + URLEncoder.encode(username, "UTF-8");
 data += "&"
 + URLEncoder.encode("password", "UTF-8")
 + "="
 + URLEncoder.encode(password, "UTF-8");
 data += "&"
 + URLEncoder.encode("submit", "UTF-8")
 + "="
 + URLEncoder.encode("Log in", "UTF-8");
 data += "&"
 + URLEncoder.encode("multiform", "UTF-8")
 + "="
 + URLEncoder.encode(multiform, "UTF-8");
out.write(data);
out.flush();
out.close();

//Read the response message
reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
boolean success = false;
while((line = reader.readLine()) != null)
{
 //If the title says we are logged in everything worked
 if(line.contains("My Dyn Account"))
 {
  success = true;
  break;
 }
}

if(success)
{
 System.out.println("Succesfully logged in!");
}
else
{
 System.out.println("Login failed!");
}

reader.close();

13 comments:

  1. Looks like a great solution! I am not much of a coder, but would love to implement something like this. Do you have thoughts on how I could run that as a local CRON job?

    ReplyDelete
    Replies
    1. Thanks for the comment. Are you referring to a local cron job in a unix OS? If so I am not too familiar with setting that up. However to be able to invoke this Java code locally would require a JVM which your computer should have and possibly adding the code to a self-executing JAR file, which you then invoke periodically.

      Delete
    2. Hi, many thanks for your solution. To run it on a local machine you must compile it as a regular java file, with class headers and footers, import declarations, and have a correct classpath with jsoup.

      Here are some instructions to adapt your script to run it on a local machine : http://www.lprp.fr/wiki/doku.php/dyndns_autologin

      Delete
    3. Remi,

      Exception in thread "main" java.lang.UnsupportedClassVersionError: DynDnsAutoLogin : Unsupported major.minor version 51.0
      at java.lang.ClassLoader.defineClass1(Native Method)
      at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
      at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
      at java.net.URLClassLoader.defineClass(URLClassLoader.java:277)
      at java.net.URLClassLoader.access$000(URLClassLoader.java:73)
      at java.net.URLClassLoader$1.run(URLClassLoader.java:212)
      at java.security.AccessController.doPrivileged(Native Method)
      at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
      at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
      Could not find the main class: DynDnsAutoLogin. Program will exit.

      Delete
    4. Hi David, please check your java environnement, and that your jdk and jre have the same version number (javac -version and java -version)

      Delete
    5. Cheers Remi, I would assume some knowledge of Java for those that would want to use it. I simply use Eclipse to compile and load the correct imports for me.

      Delete
    6. Hi Remi
      Is that website on vacation for Bastille Day?? - it doesn't respond
      or
      Is it only available at certain times of the day, it responds to ping and tracert finds it ok but it won't open

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hi.

    This looks very good indeed. However I have no clue about java, and have been trying to get it to do something. But so far no luck.

    Would it not be possible to just make a compiled file where you can pass username and password as arguments?

    Many more would be able to use the script then I believe.

    ReplyDelete
    Replies
    1. Have a look at Rémi Peyronnet posts above who has made a working example of the above script. You can visit his link to see how to run it step by step.

      Delete
  4. Created a new spreadsheet in Drive,
    Added a new blank script with a simple function skeleton.

    Pasted your code with my username/password, but I keep getting a syntax error on the very first line of code after the function. If I comment that line out... same syntax error appears on the very next line... any ideas? The file is called "code.gs"

    Missing ; before statement. (line 2, file "Code")Dismiss

    ReplyDelete
  5. Nevermind... I managed with the standalone solution running as a cron job on my web host.

    Thanks anyways

    ReplyDelete
  6. Hi. I created a Python version with gmail(smtp) notify on failed login attempt: https://github.com/evrenesat/logindyn/

    ReplyDelete