- Has good connection management - groups, saved credentials, etc.
- Captures special keys correctly (Windows key, Alt+Tab, Ctrl+Shift+Esc, etc.)
- Supports Windows Azure
Terminals comes close, because it handles the first two very well, but unfortunately, it does not support Windows Azure. However, since it is open-source and written in C#.Net, I thought I would just modify it myself to add support.
If you've worked with Windows Azure, you'll be familiar with the Connect link at the bottom of the Azure Portal when viewing information about a service instance. That link gives you an .RDP file to download which can be used to connect to the instance. If you type in the public instance name (name.cloudapp.net) into an RDP session without going through the .RDP file download, you'll quickly discover that it doesn't work. So what is different about generating the .RDP file directly from the portal?
When you open the file, there is a special property value unique to Windows Azure that is required for the connection. Without it, you get no response whatsoever, and no idea why your connection is not working. I guess that's a security feature by design in Azure. Here's an example file (with values redacted):
full address:s:subdomain.cloudapp.netYou'll notice there is a special value called "LoadBalanceInfo" which is not present on typical RDP sessions. So I added the property to the UI of the Terminals program and modified all the places that generate RDP sessions to make use of the property as well. However, every time I would try to connect, still no response. After doing a little research, I became convinced that I was just missing a small detail and WireShark (network capture software) would provide the answer.
username:s:bobbytest
LoadBalanceInfo:s:Cookie: mstshash=subdomain#subdomain_IN_0
With WireShark I quickly discovered exactly what the issue was - my strings were getting sent over the wire as UTF-16, which means every character was 2 bytes. Here is an example of what the TCP conversation dump looks like:
C.o.o.k.i.e.:. .m.s.t.s.h.a.s.h.=.s.u.b.d.o.m.a.i.n.#.s.u.b.d.o.m.a.i.n._.I.N._.0.
The dots in between each character are 0-byte characters making up the other half of the 2-byte unicode characters. Since the class being used for the RDP connections is an ActiveX component provided by Microsoft, actually modifying the class was out of the question. But there is a way - re-encode the string as UTF-8 by squishing two characters together into each 2-byte character. It becomes a really weird string if you try to interpret as UTF-16 (the default in .NET), but the ActiveX class which reads as 1-byte characters works beautifully:
var b = Encoding.UTF8.GetBytes(temp);
var newLBI = Encoding.Unicode.GetString(b);
The only catch to this solution is that if the original string has an odd number of characters, the last character will still be a 0-byte and be rejected. Simple solution is to pad with an extra space at the end, which seems to work okay. Add to that the requirement that LoadBalanceInfo always be suffixed by "\r\n", and the full working solution is below:
var temp = LoadBalanceInfo;
if (temp.Length % 2 == 1) temp += " ";
temp += "\r\n";
var b = Encoding.UTF8.GetBytes(temp);
var newLBI = Encoding.Unicode.GetString(b);
Magically, it works! I created and uploaded a patch that adds support for Windows Azure to the Terminals website, so you can try it for yourself. Happy programming!
No comments:
Post a Comment