<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/sodnpoo.xsl"?>
<xml>
<post>
  <title>zen dyndns (the VIEWSTATE problem)</title>
  <date>
  28 Aug 2011
  </date>
  <p>
  Due to the complete failure of my previous name host provider (freeparking.co.uk - not only did they fail to renew my domain for the second year in a row but also didn't respond to any of my support tickets leaving my domain unavailable for 3 days. The situation was only resolved when I contacted their upstream registrar..&lt;sigh&gt;) I moved my name hosting to <a href="http://www.zen.co.uk">Zen Internet</a>. Zen's support is excellent - highly recommended.
  </p>
  <p>
  The only downside is that I can no longer set the @ record to a CNAME (seems to work fine for the subdomains however). Previously I'd been pointing sodnpoo.com to sodnpoo.ath.cx, this is in turn updated from the dyndns client on my router.
  </p>
  <p>
  I thought the easy way to solve this would be to just use curl to form post to Zen's customer portal. Login was easy but when I tried to post to the DNS management form it failed with an error, upon closer examination I noticed a huge (several K) parameter being passed called VIEWSTATE.
  </p>
  <p>
  After doing some research I discovered that VIEWSTATE is an opaque data blob produced by ASP.NET forms to hold the persistent state of the page. I pulled the page with curl and used some simple sed to extract the VIEWSTATE value and posted it back. It still didn't work so I started to add more parameters and soon hit the "html isn't regular so you can't parse it with a regex" problem, so I looked for another solution.
  </p>
  <p>
  I decided I should parse the html properly and thought that XSLT was really the best solution - I just needed to generate post data as expected by curl (i.e. uri encoded name=values pairs). I tried to run it through <a href="http://xmlsoft.org/XSLT/xsltproc2.html">xlstproc</a> but the html was too dirty, so I ran it through <a href="http://tidy.sourceforge.net/">tidy</a> which cleaned it up enough to not trip up xsltproc. The following XSLT was matched against all input tags to build the post data.
  </p>
  <pre>
  &lt;xsl:template match="xhtml:input"&gt;
    &lt;xsl:value-of select="@name"/&gt;=&lt;xsl:value-of select="str:encode-uri(@value ,true())"/&gt;&amp;#38;
  &lt;/xsl:template&gt;
  </pre>
  <p>
  Extracting the form data and posting it back still failed though. I looked again at what the browser was doing and saw that some of the parameters were being modified, some dropped and some added by the javascript in the page. I also notice one parameter didn't exist at all in the form when using curl's default user agent - setting it to Firefox's made the parameter appear.
  </p>
  <p>
  I fixed up XLST to modify what was needed to match what the browser was sending and posted the results again - this time there were no errors and a html page was returned. I modified the XSLT to change the IP it was sending and ran through it again - I refreshed the page in my browser and the IP had been changed - success!
  </p>
  <p>
  The code is implemented as a shell script, a template for the login part and a couple of XSLT's. It requires that the zen friendly name is set to the domains name e.g. 'sodnpoo.com'.
  </p>
  <p>
  Autozen.sh is the main script - don't forget to set the USERNAME/PASSWORD/DOMAINS and probably the method of getting the latest IP (IPADDR). (You might need to change the DOMLISTURL - this is the page in the Zen portal where you select the domain to modify).
  </p>
  <p>
  Zen.login.post is a quick and dirty static template - we just poke the USERNAME/PASSWORD values in there with sed.
  </p>
  <p>
  Getdomains.xml is an XSLT to extract the POST URLs of all domains with their friendly name set.
  </p>
  <p>
  Zenasp.xsl is another XSLT that takes the four octets of the desired IP and pokes them into the form data to be sent to the POST URLs.
  </p>
  <p>
  Code can be found on <a href="https://github.com/sodnpoo/autozen">github</a>.
  </p>
</post>
</xml>


