Dean's Blog http://www.codeka.com.au/blog Development blog of Dean Harding. en-us Tue, 09 Sep 2014 13:59:00 GMT A battery widget with support for monitoring your wearable battery level http://www.codeka.com.au/blog/2014/09/a-battery-widget-with-support-for-monitoring-your-wearable-battery-level Tue, 09 Sep 2014 13:59:00 GMT http://www.codeka.com.au/blog/2014/09/a-battery-widget-with-support-for-monitoring-your-wearable-battery-level <p>I released the first version of &quot;<a href="https://play.google.com/store/apps/details?id=au.com.codeka.advbatterygraph">Advanced Battery Monitor</a>&quot; towards the end of last year, to not much fanfare at all. I used it and a couple of my friends used it, and that&#39;s about it.</p><p>But since getting my hands on an Android Wear watch (Moto 360, to be precise), I added a new feature to my graph to also monitor the battery <em>on the watch</em>.</p><p>Here you can see my phone + watch battery level thoughout the day today:</p><p style="text-align: center;"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg2LmdncGh0LmNvbS9lWnpxTFRLWVNUOEk1UHJlTFV1MzZwTnNNSDdHOGh0eXZBLVYyMWxHZnN6ZEFldml4TlF0MENCbkpZUFB2bEZyYVRQYlczNEVUN1o0bFhXdzdRa3M9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NVoyckN3a1hsVEFNclF3MkFKVUpqSjA3SDRfbm44LTdlMy14VVpYc0hYXzlUa2E1ZW15MEd5MVVBSDUyeHFOU0duZmtVWXVKM3RMaTBOMjJBd1RFYkxhanRJUXlBQk5oTW5FLWljS2RRZVFrZnJmOUdDQmpqZFJLcmJYLV82U1RaZFVjVndEWmExVUozQ2p3WFdOSXhqbGZJeXJ3IiwiaGVpZ2h0IjoyNjQsIndpZHRoIjoxMDgwLCJmaWxlbmFtZSI6IlNjcmVlbnNob3RfMjAxNC0wOS0wOS0yMy0zMC0yMS5wbmciLCJzaXplIjo1MzA3NzJ9" src="http://lh6.ggpht.com/eZzqLTKYST8I5PreLUu36pNsMH7G8htyvA-V21lGfszdAevixNQt0CBnJYPPvlFraTPbW34ET7Z4lXWw7Qks=s800" /></p><p>The green line represents my phone&#39;s battery, the blue my watch&#39;s. I use both devices fairly heavily throughout the day, so I&#39;ll let you make of these graphs as you will.</p><p>The other feature I&#39;ve added is the ability to export your battery data. The app only keeps battery data for up to a week, but you can export the whole data by tapping the widget to get into the &quot;settings&quot; and using the Export option.</p><p style="text-align: center;"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg2LmdncGh0LmNvbS9ha3BqQ0VSTUhjT1ZLandBUUoyQzAtOHJhQ20xY00tRUxWbVltUWloQlVLcEl2RmM0OENoUG9tVm1jbHpTbU4yckFpOWVYN2x4Vm9oSy1ieV9sbzRjdz1zMTAwIiwiYmxvYl9rZXkiOiJBTUlmdjk0U1hyMjJPNXpjRzdVdXk0ekM0NERTVGZOdWRmUXN1NHp2ZlZWOWszUlVzSkJZX1dKcHp4YUJrTTJDaXNzazBGQVpoSThydG1LVmlhQVRnTF9ybkdSUkJVNU9QT29OaUkwTWRyVjc5TU1peTQyVkxSdTV4Tjc5ZUpkd3dGWlQxQzlDWW1mUGZPaTlFYzFEb0Q0WElrYzdlX19nY3ciLCJoZWlnaHQiOjEyODAsIndpZHRoIjo3NjgsImZpbGVuYW1lIjoiU2NyZWVuc2hvdF8yMDE0LTA5LTA5LTIzLTU2LTE2LnBuZyIsInNpemUiOjEwODQxOH0=" src="http://lh6.ggpht.com/akpjCERMHcOVKjwAQJ2C0-8raCm1cM-ELVmYmQihBUKpIvFc48ChPomVmclzSmN2rAi9eX7lxVohK-by_lo4cw=s400" /></p><p>The source code for the widget is available on github:&nbsp;<a href="https://github.com/codeka/advbatterygraph">https://github.com/codeka/advbatterygraph</a></p><p>And of course, the widget itself can be downloaded from the <a href="https://play.google.com/store/apps/details?id=au.com.codeka.advbatterygraph">Play Store</a>:</p><p style="text-align: center;"><a href="https://play.google.com/store/apps/details?id=au.com.codeka.advbatterygraph"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGgzLmdncGh0LmNvbS8zdjJ3MGl2STU3VUhKSExILVlxYTVKQTVOeDhHSmpYcmdHSlN6QUlMMWE4WTQ0czRPREpaWXpFUm5fLW96T0U0bkd3RFdlYkt3d1FwYWg5UC1OTVRZUkE9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NXpFR0RvVS1ZODkzbDNKTW45OE1IelBET3Vncmc2U1JNbnF2SUt6VmtEQTJrWkUzTjZMY0Z6NlpZclVjSVpoTHNpdjNNWmpudGM2aEFCMkhqX005UXJDcl9YSUhLaVVyOURTS3BUVTExTmliZ3BsWFd4U2FmbzBsYUF1X0JsZDY0U1BfNmJaZjlLY3RRTWNQRnozbzcxZnZCYk5RIiwiaGVpZ2h0Ijo0NSwid2lkdGgiOjEyOSwiZmlsZW5hbWUiOiJlbl9nZW5lcmljX3JnYl93b180NS5wbmciLCJzaXplIjo4MTcxfQ==" src="http://lh3.ggpht.com/3v2w0ivI57UHJHLH-Yqa5JA5Nx8GJjXrgGJSzAIL1a8Y44s4ODJZYzERn_-ozOE4nGwDWebKwwQpah9P-NMTYRA=s400" /></a></p><p>&nbsp;</p> Playing around with a random level generator http://www.codeka.com.au/blog/2014/08/playing-around-with-a-random-level-generator Mon, 25 Aug 2014 11:09:00 GMT http://www.codeka.com.au/blog/2014/08/playing-around-with-a-random-level-generator <p>So, one of the things I&#39;ve been toying with recently is the idea of a Diablo-style hack &amp; slash. One of the most important aspects of the hack &amp; slash is level generation. There&#39;s lots of ways you can generate levels and lots of different &quot;looks&quot; you can give your levels. I plan to explore a few different techniques over the next few weeks. My first attempt, which I&#39;ve included below, gives a sort of cavernous look that I think would be good for boss levels:</p><script src="/blob/AMIfv94WoAkp7jGjPq2RvIBgpJ5q5L7ScKOlWgYXTSJ2TBKJjzo_gYWcDv8kslnr_Hau-UBSb_FxYS5-q7EZOHmyTPiK-LrFJmSejrjc7bZ0U6tcMgYbdJUhN2a96s4R9kcQTf11jtqF68sEL1OWZRaS7Ht_Au3YhA"></script><p>The technique is quite simple, I &quot;borrowed&quot; the idea from <a href="http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels">this page</a>. Basically, the algorithm runs as:</p><ol><li>Fill the board with random walls. Each square starts with a 45% chance of being filled.</li><li>Each iteration, for each square, count the number of walls around that square. If there is more than 5, fill the wall in a new version of the map.</li><li>Repeat step 2 until the map is &quot;stable&quot; (that is, until two iterations produce the same map).</li></ol><p>In the example above, you just let it run normally, it&#39;ll do a few iterations, then complete the run in one go and pause for a few seconds so you can see the outcome. You can click &quot;stop&quot; then use the &quot;reset&quot; and &quot;step&quot; buttons to manually progress through the iterations.</p><p>There&#39;s a couple of problems with this, though. Firstly, it tends to make very open levels. I guess depending on the game, that may or may not be OK, but for our purposes, I think I&#39;d like something a little more close-quarters. Another problem is that it produces overly &quot;smooth&quot; walls, that is, there are no sharp corners and no sharp points. Again, depending on your level design, that may or may not be OK, but I&#39;d like to do something about it.</p><p>For the second problem, the solution is actually quite simple: just don&#39;t run the algorithm so many times. If we cap the number of iterations to, say, 10, then we end up with more jagged walls. But that introduces an extra problem: sometimes it will leave behind very tiny &quot;islands&quot; of walls, which the old alorithm would have smoothed away. That can be fixed by a &quot;manual&quot; cleanup afterward where we simply remove the small islands.</p><p>For the first problem, we modify the algorithm slightly:</p><pre class="brush: js"> Map.prototype.iterate = function() { var newMap = new Map(this.width, this.height); newMap.clearMap(); for (var x = 0; x &lt; this.width; x++) { for (var y = 0; y &lt; this.height; y++) { if (this.numWallsWithinSteps(x, y, 1) &gt;= 5) { newMap.setCell(x, y, 1); } else if (this.stepNo &lt; 6 &amp;&amp; this.numWallsWithinSteps(x, y, 2) &lt; 2) { newMap.setCell(x, y, 1); } } } newMap.stepNo = this.stepNo + 1; return newMap; };</pre><p><span style="font-size: 12pt;">The above algorithm can be seen in the map below:</span></p><script src="/blob/AMIfv96VjhYKBIbPWrx-x3ymC5un3cwHUGZxA9mA24gv2atCpzBDuA9R1YUmCc3YuQ35g_MGiMskeW-2PMXONeBg2irdRbI5KzfEpE15qbeNLU59szNRU3zXP2L62VbjzBh4onxaOSmK0eqezFdsc2i70MwD99lTFQ"></script><p>As you can see, this version results in much more &quot;cramped&quot; level than the first version. You can download the full code for the final version <a href="http://www.codeka.com.au/blob/AMIfv96gW71Xfu3gbw1XEGhLHK3r27_SZsuwA3XVTPFNTWHFZWwwbjCMVHazMpXrA6pJxWJBaj0cDi4hnVx1YDXWPJH3zf2XOgKzMSxz5Lq73EKMeZxuiBoUqmqOc1RV6hjkJ4IY6F-eMnfqlP18glCaZRVT8D52Xw/download">here</a>.</p><p>This is a pretty simple algorithm and produces nice levels for boss fights, but next time, we&#39;ll want to try a slightly more complicated algorithm for generating the room/corridor style levels that you come to expect.</p> Detecting leaked Activities in Android http://www.codeka.com.au/blog/2014/06/detecting-leaked-activities-in-android Sun, 22 Jun 2014 13:27:00 GMT http://www.codeka.com.au/blog/2014/06/detecting-leaked-activities-in-android <p>So <a href="http://war-worlds.com">War Worlds</a>&nbsp;has, for quite some time, been plagued with various memory issues. One of the problems is that Android in general is pretty stingy with the heap memory it gives to programs, but the other is that it&#39;s actually very easy to &quot;leak&quot; large chunks of memory by accidentally leaving references to Activity objects in static variables.</p><p>There&#39;s not much that can be done about the former, but the latter can be fairly easily debugged using a couple of tools provided by Android and Java.</p><h2>Detecting leaked Activties</h2><p>The first step is figuring out when Activties are actually leaked. In API Level 11 (Honeycomb), Android added the&nbsp;detectActivityLeaks()&nbsp;method to StrictMode.VmPolicy.Builder class. All this method does is cause Android to keep an internal count of the number of instances of each Activity class that exist on the heap, and let you know whenever it hits 2.</p><p>When you get a StrictMode violation, there&#39;s a few things you can do, you can have Android kill your process, you can log a message, or you can do both. To be honest, neither of those things is particularly helpful because when you&#39;ve got a leak what you&nbsp;<em>really</em>&nbsp;want is a dump of the heap so that you can analyse it later.</p><p>Luckily for us, the StrictMode class writes a message to System.err just before it kills the process, so we can do this little hack to hook in just before that happens and get a HPROF dump just in time:</p><pre class="brush: java">private static void enableStrictMode() { try { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectActivityLeaks().detectLeakedClosableObjects() .detectLeakedRegistrationObjects().detectLeakedSqlLiteObjects() .penaltyLog().penaltyDeath().build()); // Replace System.err with one that&#39;ll monitor for StrictMode &nbsp; // killing us and perform a hprof heap dump just before it does. System.setErr (new PrintStreamThatDumpsHprofWhenStrictModeKillsUs( &nbsp; System.err)); } catch(Exception e) { // ignore errors } } private static class PrintStreamThatDumpsHprofWhenStrictModeKillsUs &nbsp; extends PrintStream { public PrintStreamThatDumpsHprofWhenStrictModeKillsUs(OutputStream outs) { super (outs); } @Override public synchronized void println(String str) { super.println(str); if (str.startsWith(&quot;StrictMode VmPolicy violation with POLICY_DEATH;&quot;)) { // StrictMode is about to terminate us... do a heap dump! try { File dir = Environment.getExternalStorageDirectory(); File file = new File(dir, &quot;strictmode-violation.hprof&quot;); super.println(&quot;Dumping HPROF to: &quot; + file); Debug.dumpHprofData(file.getAbsolutePath()); } catch (Exception e) { e.printStackTrace(); } } } }</pre><p><span style="font-size: 12pt;">Essentially, what we do is, we set up StrictMode to kill us, the replace System.err with a hack subclass of PrintSteam that checks for the message StrictMode prints and generates a heap dump right then and there.</span></p><p><span style="font-size: 12pt;">It&#39;s ugly, but we only have to do it in debug mode anyway, so I think it&#39;s OK.</span></p><h2><span style="font-size: 12pt;">Analysing the dump</span></h2><p>Next, we have to copy the .hprof file off the device (you could use adb pull for that, but I like <a href="https://play.google.com/store/apps/details?id=com.estrongs.android.pop&amp;hl=en">ES File Explorer</a>). One important step is converting the .hprof file from Dalvik format to normal Java format. There&#39;s a handy-dandy tool called <a href="http://developer.android.com/tools/help/hprof-conv.html">hprof-conv</a> in the Android SDK which does the conversion for you.</p><p>Once you&#39;ve got a HPROF file a format that can be read by the standard Java tools, the next step is actually analysing it. I was using the <a href="http://www.eclipse.org/mat/">Eclipse Memory Analyzer</a> tool, but anything will do, really. The first thing we need to do is find out which objects are leaking. Luckily for us, StrictMode prints out which activity is leaking, so we can start there:</p><pre>01-15 17:24:23.248: E/StrictMode(13867): class au.com.codeka.warworlds.game.EmpireActivity; instances=2; limit=1 01-15 17:24:23.248: E/StrictMode(13867): android.os.StrictMode$InstanceCountViolation: class au.com.codeka.warworlds.game.EmpireActivity; instances=2; limit=1</pre><p><span style="font-size: 12pt;">So it&#39;s detected two copies of the EmpireActivity class. We fire up the Memory Analysis tool in Eclipse, and go to &quot;List objects ... with incoming references&quot; and enter our offending class, &quot;au.com.codeka.warworlds.game.EmpireActivity&quot;. And just as StrictMode said, there&#39;s our two instances:</span></p><p style="text-align: center;"><a title="" class="lightbox" href="http://lh4.ggpht.com/sfVe6iAYo47oeo0s3Yg9OSBA-BKZOgCNpwk4hu4RxEjCE8I-uYt1TDPQEDoQiyyn3wowM-gmUmJviqHIJuwYHQ=s926"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg0LmdncGh0LmNvbS9zZlZlNmlBWW80N29lbzBzM1lnOU9TQkEtQktaT2dDTnB3azRodTRSeEVqQ0U4SS11WXQxVERQUUVEb1FpeXluM3dvd00tZ21VbUp2aXFISUp1d1lIUT1zMTAwIiwiYmxvYl9rZXkiOiJBTUlmdjk3Q1VzZm4yc01lMTRzYzJXZ0k4SzM1Vzd4QVJjQ2VIcnFqd295bGduR3BOM3ZubkFMN3JTRGYwWFBJR2dYRU1DM0g1WUtEdDVxZDR2WUhzUGxTWVlJRkRHd2hyYm8zVGFJbk14NnZnNzhlTHhScHR2LVh5eDd0QnZldnB2YjdGUDV1MnoyTVBmazJELTEyY0c0M3RMLWdVdlFrcFEiLCJoZWlnaHQiOjIyNSwid2lkdGgiOjkyNiwiZmlsZW5hbWUiOiJ0d28taW5zdGFuY2VzLnBuZyIsInNpemUiOjI4OTUzfQ==" src="http://lh4.ggpht.com/sfVe6iAYo47oeo0s3Yg9OSBA-BKZOgCNpwk4hu4RxEjCE8I-uYt1TDPQEDoQiyyn3wowM-gmUmJviqHIJuwYHQ=s700" /></a></p><p><span style="font-size: 12pt;">Now, how do we determine where they&#39;re being held? Right-click one of them and select &quot;Path to GC Roots ... with all references&quot;, which will bring up something like this:</span></p><p style="text-align: center;"><a title="" class="lightbox" href="http://lh4.ggpht.com/jG3HgTooQGAHsQderV94CuAVQUyCz88WXIzdpem4aOCY-5B_5646OXBfSkCKh79pNT3Idgph_KHK09BpVEPt_Q=s927"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg0LmdncGh0LmNvbS9qRzNIZ1Rvb1FHQUhzUWRlclY5NEN1QVZRVXlDejg4V1hJemRwZW00YU9DWS01Ql81NjQ2T1hCZlNrQ0toNzlwTlQzSWRncGhfS0hLMDlCcFZFUHRfUT1zMTAwIiwiYmxvYl9rZXkiOiJBTUlmdjk2bTZxQVRSeExOdDQ5Wm1VSjc2V2xNcFptM3llZF9qbzJKaG1oQXVZbVpXdHJUZ2VGWnRHZ0VfaFdzZ1BFWklkc3VUcWhlb3FYM3NqdW5iYXhlZ1hzM2FBNFBwOGloS05sX21BcnRKYmxCOGM5cE1VNmtqZ0QxdS1hVjdNcFY1RWNwSUhLbkYtNzczRl9kWDRPMHBaZ0N0czg0Z2ciLCJoZWlnaHQiOjQzNiwid2lkdGgiOjkyNywiZmlsZW5hbWUiOiJpbnRlcmVzdGluZy5wbmciLCJzaXplIjo2OTc1NX0=" src="http://lh4.ggpht.com/jG3HgTooQGAHsQderV94CuAVQUyCz88WXIzdpem4aOCY-5B_5646OXBfSkCKh79pNT3Idgph_KHK09BpVEPt_Q=s800" /></a></p><p><span style="font-size: 12pt;">If we go with the assumption that the leak happens in our code and not in the framework (usually a pretty good assumption) the only object holding a reference to this activity is our FleetList class. If we expand that out a few times, we can follow the references all the way back to the mSpriteGeneratedListeners member of StarImageManager.</span></p><p><span style="font-size: 12pt;">What the StarImageManager class does is it generates (or loads from disk) the bitmaps used to display stars. It has a list of objects that are interested in knowing when the star images are updated (generating them can take a while). What was happening here is that the FleetListAdapter class was adding itself to the list of classes interested in receiving updates from StarImageManager, but <em>never removing itself from that list</em>.</span></p><p><span style="font-size: 12pt;">By fixing that bug, I managed to squash one the biggest memory leaks in the game.</span></p><h2>Further work</h2><p>One thing this has highlighted to me is that my system of highly manual event pub/sub is probably not a great idea. Scattered all throughout the code are lots of these &quot;lists of classes who want to subscribe to my event&quot;. Management of those lists is all manual, firing the events is manual, and it&#39;s actually been a bit of a source of problems in the past (e.g. with events firing on random threads and whatnot).</p><p>So the next big chunk of work will be replacing all of these with some kind of unified <a href="https://code.google.com/p/guava-libraries/wiki/EventBusExplained">EventBus pattern</a>. I quite like the implimentation of <a href="https://code.google.com/p/simpleeventbus/">code.google.com/p/simpleeventbus</a>, in particular I like how it uses WeakReferences to hold the event subscribers (which would make the above memory leak non-existent).</p> Unicode support in MySQL is ... 👎 http://www.codeka.com.au/blog/2014/02/unicode-support-in-mysql-is-- Thu, 27 Feb 2014 12:10:00 GMT http://www.codeka.com.au/blog/2014/02/unicode-support-in-mysql-is-- <p>For the last few days, I&#39;ve been getting some strange error reports from the&nbsp;War Worlds&nbsp;server. Messages like this:</p><pre class="brush: text">java.sql.SQLException: Incorrect string value: &#39;\xF0\x9F\x98\xB8. ...&#39; for column &#39;message&#39; at row 1 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1078) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2815) at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359) at com.jolbox.bonecp.PreparedStatementHandle.executeUpdate(PreparedStatementHandle.java:203) at au.com.codeka.warworlds.server.data.SqlStmt.update(SqlStmt.java:117) at au.com.codeka.warworlds.server.ctrl.ChatController.postMessage(ChatController.java:120) . . .</pre><p>Now, that string which MySQL complains is &quot;incorrect&quot; is actually the Unicode codepoint U+1F638 GRINNING CAT FACE WITH SMILING EYES, aka&nbsp;😸 -- a perfectly valid Emoji character. Why was MySQL rejecting it? All my columns are defined to accept UTF-8, so there should not be a problem, right?</p><h2>When is UTF-8 not UTF-8?</h2><p>When it&#39;s used in MySQL, apparently.</p><p>For reasons that completely escape me, MySQL 5.x limits UTF-8 strings to U+FFFF and smaller. That is, the &quot;BMP&quot;. Why they call this encoding &quot;UTF-8&quot; is beyond me, it most definitely is&nbsp;not&nbsp;UTF-8.</p><p>The trick, apparently, is to use a slightly different encoding which MySQL calls &quot;utf8mb4&quot; which supports up to 4-byte UTF-8 characters.</p><p>So the &quot;fix&quot; was simple, just run:</p><pre class="brush: sql;">ALTER TABLE chat_messages &nbsp; MODIFY message TEXT CHARACTER SET utf8mb4 &nbsp; COLLATE utf8mb4_unicode_ci NOT NULL;</pre><p>And so on, on basically every column in the database which could possibly include characters outside the BMP.&nbsp;But that&#39;s not enough!&nbsp;You also need to tell the server to use &quot;utf8mb4&quot; internally as well, by including the following line in your my.cnf:</p><pre class="brush: text;">[mysqld] ​character-set-server = utf8mb4</pre><p>Now presumably there is some drawback from doing this, otherwise &quot;utf8mb4&quot; would be the default (right?) but I&#39;ll be damned if I can figure out what the drawback is. I guess will just moniter things and see where it takes us. But as of now, War Worlds support Emoji emoticons in chat messages, yay!</p><h2>Addendum</h2><p>If you&#39;re just seeing squares for the emoji characters in this post, you&#39;ll need a font that supports the various unicode emoji blocks. I&#39;ve used the Symbola font (<a href="http://users.teilar.gr/~g1951d/">which you can get here</a>) with good results.</p> Why you should use a reputable DNS registrar http://www.codeka.com.au/blog/2014/02/why-you-should-use-a-reputable-dns-registrar Sun, 16 Feb 2014 10:00:00 GMT http://www.codeka.com.au/blog/2014/02/why-you-should-use-a-reputable-dns-registrar <p>I&#39;ve had a bit of a crazy weekend this weekend.&nbsp;<span style="font-size: 12pt;">It all started while I was checking out some of my websites and I discovered their DNS was not resolving! That&#39;s always scary, and after a few minutes I realised that the DNS servers were unreachable from some (but not all) networks. For example, this was a traceroute to one of the DNS servers from my home computer:</span></p><pre class="brush: bash;">$ traceroute 103.30.213.5 traceroute to 103.30.213.5 (103.30.213.5), 30 hops max, 60 byte packets 1 home.gateway.home.gateway (192.168.1.254) 0.745 ms 1.041 ms 1.334 ms 2 lns20.syd7.on.ii.net (150.101.199.219) 18.276 ms 19.705 ms 20.402 ms 3 te3-1-120.cor2.syd6.on.ii.net (150.101.199.241) 22.902 ms 23.762 ms 24.736 ms 4 ae5.br1.syd7.on.ii.net (150.101.33.50) 138.154 ms 139.785 ms ae0.cr1.syd4.on.ii.net (150.101.33.16) 29.454 ms 5 ae5.br1.syd4.on.ii.net (150.101.33.48) 30.446 ms ae0.br1.syd4.on.ii.net (150.101.33.14) 36.895 ms ae5.br1.syd4.on.ii.net (150.101.33.48) 37.306 ms 6 te0-2-0.bdr1.hkg2.on.ii.net (150.101.33.199) 145.732 ms 139.403 ms 140.845 ms 7 hostvirtual-RGE.hkix.net (202.40.160.179) 159.664 ms 132.746 ms 133.402 ms 8 vhk.vr.org (208.111.42.5) 134.369 ms 135.393 ms 136.574 ms 9 * * * 10 * * * 11 * * * 12 * * * 13 * * * 14 * * * 15 * * * 16 * * * . . .</pre><p>It was getting stuck at vhk.vr.org, which seems to be some transit provider or other. But from other networks it was OK, here&#39;s the traceroute from my Google Compute Engine instance:</p><pre class="brush: bash;">$ traceroute 103.30.213.5 traceroute to 103.30.213.5 (103.30.213.5), 30 hops max, 60 byte packets 1 216.239.46.192 (216.239.46.192) 1.168 ms 216.239.43.216 (216.239.43.216) 1.429 ms 216.239.46.192 (216.239.46.192) 1.105 ms 2 216.239.46.192 (216.239.46.192) 1.383 ms 1.072 ms 216.239.43.218 (216.239.43.218) 1.335 ms 3 216.239.46.190 (216.239.46.190) 1.477 ms 216.239.43.218 (216.239.43.218) 1.367 ms 216.239.46.192 (216.239.46.192) 1.287 ms 4 216.239.43.216 (216.239.43.216) 1.656 ms 216.239.46.190 (216.239.46.190) 1.434 ms 216.239.43.218 (216.239.43.218) 1.914 ms 5 216.239.43.216 (216.239.43.216) 1.910 ms 1.886 ms 1.868 ms 6 216.239.43.218 (216.239.43.218) 1.841 ms 1.257 ms 216.239.46.190 (216.239.46.190) 1.470 ms 7 216.239.46.192 (216.239.46.192) 1.196 ms 216.239.43.218 (216.239.43.218) 1.289 ms 216.239.43.216 (216.239.43.216) 1.496 ms 8 216.239.43.218 (216.239.43.218) 1.643 ms 216.239.43.216 (216.239.43.216) 1.457 ms 216.239.43.218 (216.239.43.218) 1.586 ms 9 209.85.248.215 (209.85.248.215) 11.936 ms 72.14.232.140 (72.14.232.140) 11.761 ms 209.85.248.229 (209.85.248.229) 14.151 ms 10 209.85.254.239 (209.85.254.239) 15.746 ms 72.14.237.132 (72.14.237.132) 11.702 ms 72.14.237.131 (72.14.237.131) 14.216 ms 11 209.85.255.133 (209.85.255.133) 14.355 ms 209.85.255.27 (209.85.255.27) 14.187 ms 13.759 ms 12 * * * 13 db-transit.Gi9-12.br01.rst01.pccwbtn.net (63.218.125.26) 39.572 ms 37.646 ms 39.849 ms 14 viad-vc.as36236.net (209.177.157.8) 39.499 ms 37.372 ms 39.243 ms 15 pdns1.terrificdns.com (103.30.213.5) 40.454 ms 39.940 ms 41.335 ms</pre><p>So that seems kind of scary! I quickly composed an email to the support address with what I&#39;d seen. An hour later and no response. At this point I was worried, because I had no idea how many people were unable to contact my websites. I tried logging in to the management console, but that was also a no-go: the DNS for the management console was hosted on the same DNS servers that were not responding! I managed to log in by hard-coding the IP address (which I got from my GCE server that was able to connect to the DNS servers) in my /etc/hosts file.</p><p>Just trying to get something up &amp; running again, I exported all my DNS records and imported them into a friend&#39;s DNS server. Then I was able to change the nameservers configured in the management console to point to my friend&#39;s DNS server. For now, we were back up again. But a few hours later and still no response from my support request.</p><h2>Who are NameTerrific?</h2><p>I first heard about NameTerrific in <a href="https://news.ycombinator.com/item?id=4743455">this post on HackerNews</a>. The website was well-done, the interface was easy to use. So I decided to give it a go with some of my more throwaway domains. I had a minor issue early on, and the support was quite good and over the next year or so, I started to move a couple more domains over.</p><p>Then, I stopped thinking about it. It worked well for the next year or so, and DNS tends to be one of those things you don&#39;t really think about. Until it&nbsp;<em>stops</em>&nbsp;working.</p><p>It was probably my fault. I didn&#39;t put much effort into researching NameTerrific&#39;s founder, <a href="https://www.zhoutong.com/">Ryan Zhou</a>. He seems to be a serial entrepreneur who dropped out of school to persue his dream. That&#39;s all well and good, but when you&#39;re hosting a service for people, it doesn&#39;t do much for your reputation as a reliable investment when you abandon your previous business for who-knows-what-reason.</p><h2>What do I think happened?</h2><p>&nbsp;I think he discovered his website had bugs and people&#39;s domains were being transferred to the wrong people. Why do I suspect that? Because it&#39;s <em>happening to me right now</em>. Check this out. I go into my control panel for one of my domains, war-worlds.com:</p><p style="text-align: center;"><a title="" class="lightbox" href="http://lh6.ggpht.com/irhFZ2RBJcpD-HCIxfyhUSHriCYPQPJOdRAnkfKyBQ-Y2J5I7dC6VqcMk0RIHHN2xpbjiDESigPqNtjJfIDE0gc=s1026"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg2LmdncGh0LmNvbS9pcmhGWjJSQkpjcEQtSENJeGZ5aFVTSHJpQ1lQUVBKT2RSQW5rZkt5QlEtWTJKNUk3ZEM2VnFjTWswUklISE4yeHBiamlERVNpZ1BxTnRqSmZJREUwZ2M9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NHpILV9KMjZLalhfSHB5TVhVdjhHZFpoQnNEMHQ4YjVVZ1BOTC1RdUxhQjhVNFZpSXNUOEpreGpJNlpfSG5BSHp6dUtFZkx1UHdiY1gwWFl4ckxVajJ6SXY3WVZEV2JsZDE4d3RXd1BzSkdRcDVBRXdBaTBHM3dYeVBXaGxDdjBIa2tXajVDZWFuUTNQSWhsX1JQV3lFX3ctc0tBIiwiaGVpZ2h0Ijo2NzEsIndpZHRoIjoxMDI2LCJmaWxlbmFtZSI6IlNjcmVlbnNob3QgZnJvbSAyMDE0LTAyLTE3IDIyOjMzOjI1LnBuZyIsInNpemUiOjYyNzkwfQ==" src="http://lh6.ggpht.com/irhFZ2RBJcpD-HCIxfyhUSHriCYPQPJOdRAnkfKyBQ-Y2J5I7dC6VqcMk0RIHHN2xpbjiDESigPqNtjJfIDE0gc=s600" /></a></p><p>I click on &quot;Transfer Away&quot;, it prompts me to confirm, I click OK and I receive the following email:</p><p style="text-align: center;"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg1LmdncGh0LmNvbS9tUHZxdk1ZVjZQOWoxdVhsaldsOVotM3l6cTN2aUtVeEpaVkFiYW95MWY1bVVHUkF6ckc0QkxYTDN3V2F2U21ZWnNrVXJZeU5EMTM1OEFWN01zalI9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NlNiVERIRkhrMWhhZFBkdVBackMyMExQMHRMQTdUOTRsSFhZOWxrZEdCZm1ZS1NwUER3X0U3bVZMWjdkaWJZWFVydmxaS0NFcDIzdktCOFotU1hUR1JuQ1hsdkhsaFNxZC1aNXJNbkJDYkRhb1FKT1pXY2pmQVp3b0dTZ2drZXVVV3ZXeGRMLTREamxIN0lPV2xEYnNhMzBySUp3IiwiaGVpZ2h0IjozMSwid2lkdGgiOjY0MCwiZmlsZW5hbWUiOiJTY3JlZW5zaG90IGZyb20gMjAxNC0wMi0xNyAyMjo0NToyMi5wbmciLCJzaXplIjo0NjA3fQ==" src="http://lh5.ggpht.com/mPvqvMYV6P9j1uXljWl9Z-3yzq3viKUxJZVAbaoy1f5mUGRAzrG4BLXL3wWavSmYZskUrYyND1358AV7MsjR=s640" /></p><p>It&#39;s an authorization code <em>for a domain I don&#39;t even own</em>. What&#39;s worse, if I do a whois on mitchortenburg.com, I find <em>myself listed in the contact information</em>! I seem to be the owner of a domain I never purchased (and, to be honest, don&#39;t really want) because of some bizarre mixup with the management website.</p><p>Even worse still: I have no way to generate an auth code for war-worlds.com (the domain I <em>do</em> care about) and I&#39;m terrified that some other customer of NameTerrific&#39;s is somehow able to do what I&#39;ve managed to do and gain ownership of my domain!</p><p>I&#39;m not the only one having problems. Their facebook page is full of people who have also apparently realized they&#39;ve been abandoned:</p><p style="text-align: center;"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg0LmdncGh0LmNvbS9Wc0RGVEdCeWpFaFZMckt1NW1nbUVJUEF3bmNlekQ3bVVtZGphazVheGRLREtwY05wa3RsQTltUWxLUXpwcGFoOE9qaV8wRzNnblRSSllWbnBXaFRhdz1zMTAwIiwiYmxvYl9rZXkiOiJBTUlmdjk3aGZFQ2twLTFYWGt2U0tqX052WXRfNWlnYUs5UEtrSTFWVEdORnZPWkJ3V0RrVjFoN3liNFpKRENudmVrSDNzX0RidkhWbkpQaWEwam9WZ3JGRndxMHh4a2E4VEtRWlhHbm9WMWhJZWpONm4tM2ozbEh4a3o5QWxnTGk1WkR3U1hlU2RqbXh6LTYyLU5ncFVxRW9Ld1l1dTFfZlEiLCJoZWlnaHQiOjEwNzQsIndpZHRoIjo1NDcsImZpbGVuYW1lIjoiU2NyZWVuc2hvdCBmcm9tIDIwMTQtMDItMTcgMjI6NTc6NTMucG5nIiwic2l6ZSI6MTMxNTg0fQ==" src="http://lh4.ggpht.com/VsDFTGByjEhVLrKu5mgmEIPAwncezD7mUmdjak5axdKDKpcNpktlA9mQlKQzppah8Oji_0G3gnTRJYVnpWhTaw=s1074" /></p><p>This is not how you run a busines. If you find bugs in your management console, you don&#39;t abandon your business and leave your customer in the lurch.</p><p>Now it seems Ryan has at least learnt from this failure to not let people post on his business&#39; facebook wall. His &quot;CoinJar&quot; business has disabbled wall posts entirely.</p><h2>What are my options?</h2><p>So I currently have a ticket open with eNom (NameTerrific were an eNom reseller) and I hope I can get control of my domain back again. And if Mitch Ortenburg is reading this, I&#39;m also quite happy to return your domain to you, if I only knew how to contact you...</p><p>And of course, I have already transferred every domain I can out of NameTerrific and into a reputable registrar. Lesson learnt!</p>