Some time ago I had the great fun of "debugging" a NMSConnectionException that contained a SocketException with the value of 0xFFFFFFFE and an unreadable stacktrace
It happened in the Apache ActiveMQ NMS library which boldly claims on the webpage "The NMS API This allows you to build .NET applications in C#, VB, or any other .NET language, using a single API to connect to multiple different providers using a JMS style API."
So let us dive into how exactly this library does connect:
protected Socket DoConnect(string host, int port, string localAddress, int localPort)
{
Socket socket = null;
IPAddress ipaddress;
try
{
if(TryParseIPAddress(host, out ipaddress))
{
socket = TryConnectSocket(ipaddress, port, localAddress, localPort);
}
else
{
// Looping through the AddressList allows different type of connections to be tried
// (IPv6, IPv4 and whatever else may be available).
IPHostEntry hostEntry = GetIPHostEntry(host);
if(null != hostEntry)
{
// Prefer IPv6 first.
ipaddress = GetIPAddress(hostEntry, AddressFamily.InterNetworkV6);
socket = TryConnectSocket(ipaddress, port, localAddress, localPort);
if(null == socket)
{
// Try IPv4 next.
ipaddress = GetIPAddress(hostEntry, AddressFamily.InterNetwork);
socket = TryConnectSocket(ipaddress, port, localAddress, localPort);
if(null == socket)
{
// Try whatever else there is.
foreach(IPAddress address in hostEntry.AddressList)
{
if(AddressFamily.InterNetworkV6 == address.AddressFamily
|| AddressFamily.InterNetwork == address.AddressFamily)
{
// Already tried these protocols.
continue;
}
socket = TryConnectSocket(ipaddress, port, localAddress, localPort);
if(null != socket)
{
ipaddress = address;
break;
}
}
}
}
}
}
if(null == socket)
{
const int RTSSL_HANDSHAKE_FAILURE = -2;
throw new SocketException(RTSSL_HANDSHAKE_FAILURE);
}
}
catch(Exception ex)
{
throw new NMSConnectionException(String.Format("Error connecting to {0}:{1}.", host, port), ex);
}
Tracer.DebugFormat("Connected to {0}:{1} using {2} protocol.", host, port, ipaddress.AddressFamily.ToString());
return socket;
}
Great, its a connect loop that attempts ipv6, then ipv4, then other protocols, dunno maybe your message broker is serving on an IPX link?
we dive deeper, what does TryConnectSocket() do:
private Socket TryConnectSocket(IPAddress address, int port, string localAddress, int localPort)
{
if(null != address)
{
try
{
Socket socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
if(null != socket)
{
if(!String.IsNullOrEmpty(localAddress))
{
DoBind(socket, localAddress, localPort);
}
IAsyncResult result = socket.BeginConnect(new IPEndPoint(address, port), null, null);
result.AsyncWaitHandle.WaitOne(ConnectTimeout, true);
if(!socket.Connected)
{
socket.Close();
}
else
{
return socket;
}
}
}
catch
{
}
}
return null;
}
Just catch everything and return null on error, it's called TryConnectSocket after all. Never mind the fact that it also attempts to create the socket it connects.
In the end this was great Information hiding. SocketException with a value of -2 because we have no socket, which obviously must be because of an RTSSL_HANDSHAKE_FAILURE wrap that into a NMSConnectionException and the user will know what went wrong
The actual hidden exception was port exhaustion, because someone decided to send ~60k messages without reusing the tcp socket to the broker.
But sure NMSConnectionException(SocketException(0xFFFFFFFE)) told me exactly that...