{"id":2443,"date":"2023-06-13T22:13:32","date_gmt":"2023-06-13T14:13:32","guid":{"rendered":"http:\/\/www.jhouseconsulting.com\/?p=2443"},"modified":"2023-07-01T17:30:31","modified_gmt":"2023-07-01T09:30:31","slug":"xdping-c-function","status":"publish","type":"post","link":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/2023\/06\/13\/xdping-c-function-2443","title":{"rendered":"XDPing C# Function"},"content":{"rendered":"<p>I wrote this C# (csharp) function back in 2020 when developing the <a href=\"https:\/\/www.jhouseconsulting.com\/2020\/08\/28\/citrix-self-service-session-reset-tool-2106\" target=\"_blank\">Self-Service Session Reset Tool<\/a>. I have since enhanced it for improved error checking and logging and am happy to share it here for anyone else who wants to leverage it. I released a <a href=\"https:\/\/www.jhouseconsulting.com\/2019\/05\/16\/xdping-powershell-function-1931\" target=\"_blank\">PowerShell version<\/a> back in 2019. I like to integrate these functions into the scripts and tools I create as part of the health checks I do. I believe that by checking that the Broker&#8217;s Registrar service&nbsp;is reachable, listening and processing requests on its configured port, we can only then assume it is in&nbsp;a healthy state.<!--more--><\/p>\n<p>The original code was taken from the&nbsp;VDAAssistant.Backend.dll library which is included with the Citrix Health Assistant Tool. Citrix don&#8217;t obfuscate these libraries, so it&#8217;s easy to use tools like JetBrains decompiler to take a look at how they wrote this. From there I reverse engineer the code to extract what I need and wrap it up into a function that will work for me.<\/p>\n<p>The original Citrix XDPing tool can be found <a href=\"https:\/\/support.citrix.com\/article\/CTX123278\" target=\"_blank\">here<\/a>.&nbsp;The <a href=\"https:\/\/support.citrix.com\/article\/CTX207624\" target=\"_blank\">Citrix Health Assistant<\/a> tool has since replaced XDPing.<\/p>\n<p>Here is the code. It&#8217;s also available as a full project from my <a href=\"https:\/\/github.com\/jeremyts\/XDPing\" target=\"_blank\">GitHub repository<\/a>.&nbsp;Total number of downloads to date: 10.<\/p>\n<pre class=\"brush: csharp; auto-links: false; title: ; toolbar: false; notranslate\" title=\"\">\r\nusing System;\r\nusing System.Text;\r\n\/\/ Required for sockets\r\nusing System.Net.Sockets;\r\n\r\nnamespace XDPing\r\n{\r\n    class Program\r\n    {\r\n        static int Main(string&#x5B;] args)\r\n        {\r\n            string deliverycontroller = string.Empty;\r\n            int port = 80;\r\n\r\n            for (int i = 0; i &lt; args.Length; i++)\r\n            {\r\n                var arg = args&#x5B;i].ToLower();\r\n                if (arg == &quot;-deliverycontroller&quot; || arg == &quot;--deliverycontroller&quot; || arg == &quot;\/deliverycontroller&quot;)\r\n                {\r\n                    if (args.Length &gt;= i + 2)\r\n                    {\r\n                        deliverycontroller = args&#x5B;i + 1];\r\n                    }\r\n                }\r\n\r\n                if (arg == &quot;-port&quot; || arg == &quot;--port&quot; || arg == &quot;\/port&quot;)\r\n                {\r\n                    if (args.Length &gt;= i + 2)\r\n                    {\r\n                        int.TryParse(args&#x5B;i + 1], out port);\r\n                    }\r\n                }\r\n            }\r\n\r\n            if (string.IsNullOrEmpty(deliverycontroller))\r\n            {\r\n                Console.WriteLine(&quot;Valid command line arguments must be supplied:&quot;);\r\n                Console.WriteLine(&quot;-deliverycontroller, --deliverycontroller or \/deliverycontroller is a required flag. This must be followed by the name of a Delivery Controller or Cloud Connector.&quot;);\r\n                Console.WriteLine(&quot;-port, --port or \/port is an optional flag. It will default to 80 if not supplied. This is the port the Broker's Registrar service listens on.&quot;);\r\n                return -1;\r\n            }\r\n\r\n            XDPing(deliverycontroller, port);\r\n\r\n            return 0;\r\n        }\r\n\r\n        \/\/\/ &lt;summary&gt;\r\n        \/\/\/ Performs an XDPing to make sure the Delivery Controller or Cloud Connector is in a healthy state.\r\n        \/\/\/ It test whether the Broker service is reachable, listening and processing requests on its configured port.\r\n        \/\/\/ We do this by issuing a blank HTTP POST requests to the Broker's Registrar service.\r\n        \/\/\/ Including &quot;Expect: 100-continue&quot; in the body will ensure we receive a respose of &quot;HTTP\/1.1 100 Continue&quot;,\r\n        \/\/\/ which is what we use to verify that it's in a healthy state.\r\n        \/\/\/ &lt;\/summary&gt;\r\n        \/\/\/ &lt;param name=&quot;deliverycontroller&quot;&gt;&lt;\/param&gt;\r\n        \/\/\/ &lt;param name=&quot;port&quot;&gt;&lt;\/param&gt;\r\n        \/\/\/ &lt;returns&gt;&lt;\/returns&gt;\r\n        static private bool XDPing(string deliverycontroller, int port)\r\n        {\r\n            \/\/ This code has essentially been taken from the Citrix Health Assistant Tool and improved for reliability and troubleshooting purposes.\r\n            \/\/ I was able to reverse engineer the process by decompiling the VDAAssistant.Backend.dll, which is a component of the Citrix Health\r\n            \/\/ Assistant Tool.\r\n            string service = &quot;http:\/\/&quot; + deliverycontroller + &quot;:&quot; + port +&quot;\/Citrix\/CdsController\/IRegistrar&quot;;\r\n            string s = string.Format(&quot;POST {0} HTTP\/1.1\\r\\nContent-Type: application\/soap+xml; charset=utf-8\\r\\nHost: {1}:{2}\\r\\nContent-Length: 1\\r\\nExpect: 100-continue\\r\\nConnection: Close\\r\\n\\r\\n&quot;, (object)service, (object)deliverycontroller, (object)port);\r\n            StringBuilder stringBuilder = new StringBuilder();\r\n            stringBuilder.AppendLine(&quot;Attempting an XDPing against &quot; + deliverycontroller + &quot; on TCP port number &quot; + port.ToString());\r\n            bool listening = false;\r\n            try\r\n            {\r\n                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);\r\n                try\r\n                {\r\n                    socket.Connect(deliverycontroller, port);\r\n                    if (socket.Connected)\r\n                    {\r\n                        stringBuilder.AppendLine(&quot;- Socket connected&quot;);\r\n                        byte&#x5B;] bytes = Encoding.ASCII.GetBytes(s);\r\n                        \/\/ Send the string as bytes.\r\n                        socket.Send(bytes, bytes.Length, SocketFlags.None);\r\n                        stringBuilder.AppendLine(&quot;- Sent the data&quot;);\r\n                        byte&#x5B;] numArray = new byte&#x5B;21];\r\n                        socket.ReceiveTimeout = 5000;\r\n                        socket.Receive(numArray);\r\n                        stringBuilder.AppendLine(&quot;- Received the following 21 byte array: &quot; + BitConverter.ToString(numArray));\r\n                        \/\/ ASCII conversion - string from bytes\r\n                        string strASCII = Encoding.ASCII.GetString(numArray, 0, numArray.Length);\r\n                        \/\/ UTF conversion - String from bytes  \r\n                        string strUTF8 = Encoding.UTF8.GetString(numArray, 0, numArray.Length);\r\n                        stringBuilder.AppendLine(&quot;- Converting the byte array to an ASCII string we get the output between the quotes: \\&quot;&quot; + strASCII + &quot;\\&quot;&quot;);\r\n                        stringBuilder.AppendLine(&quot;- Converting the byte array to a UTF8 string we get the output between the quotes: \\&quot;&quot; + strUTF8 + &quot;\\&quot;&quot;);\r\n                        \/\/ Send an additional single byte of 32 (space) as 1 byte with no flags.\r\n                        socket.Send(new byte&#x5B;1] { (byte)32 }, 1, SocketFlags.None);\r\n                        stringBuilder.AppendLine(&quot;- Sending the following string as a byte to close the connection: \\&quot;&quot; + BitConverter.ToString(new byte&#x5B;1] { 32 }) + &quot;\\&quot;&quot;);\r\n                        if (strASCII.Trim().IndexOf(&quot;HTTP\/1.1 100 Continue&quot;, StringComparison.CurrentCultureIgnoreCase) == 0)\r\n                        {\r\n                            listening = true;\r\n                            stringBuilder.AppendLine(&quot;- The service is listening and healthy&quot;);\r\n                        }\r\n                        else\r\n                        {\r\n                            stringBuilder.AppendLine(&quot;- The service is not listening&quot;);\r\n                        }\r\n                    }\r\n                    else\r\n                    {\r\n                        stringBuilder.AppendLine(&quot;- Socket failed to connect&quot;);\r\n                    }\r\n                }\r\n                catch (SocketException se)\r\n                {\r\n                    stringBuilder.AppendLine(&quot;- Failed to connect to service&quot;);\r\n                    stringBuilder.AppendLine(&quot;- ERROR: &quot; + se.Message);\r\n                }\r\n                catch (Exception e)\r\n                {\r\n                    stringBuilder.AppendLine(&quot;- Failed with an unexpected error&quot;);\r\n                    stringBuilder.AppendLine(&quot;- ERROR: &quot; + e.Message);\r\n                }\r\n                if (socket.Connected)\r\n                {\r\n                    try\r\n                    {\r\n                        socket.Close();\r\n                        stringBuilder.AppendLine(&quot;- Socket closed&quot;);\r\n                    }\r\n                    catch (SocketException se)\r\n                    {\r\n                        stringBuilder.AppendLine(&quot;- Failed to close the socket&quot;);\r\n                        stringBuilder.AppendLine(&quot;- ERROR: &quot; + se.Message);\r\n                    }\r\n                    catch (Exception e)\r\n                    {\r\n                        stringBuilder.AppendLine(&quot;- Failed with an unexpected error&quot;);\r\n                        stringBuilder.AppendLine(&quot;- ERROR: &quot; + e.Message);\r\n                    }\r\n                }\r\n                socket.Dispose();\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                stringBuilder.AppendLine(&quot;- Failed to create a socket&quot;);\r\n                stringBuilder.AppendLine(&quot;- ERROR: &quot; + e.Message);\r\n            }\r\n            Console.WriteLine(stringBuilder.ToString().Substring(0, stringBuilder.ToString().Length - 1));\r\n            return listening;\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>Notes:<\/p>\n<ul>\n<li>I have verified that the code conforms to HTTP 1.1 standards. It terminates the headers section correctly with an empty line (line break without any visible characters) before sending the request.<\/li>\n<li>I understand that the extra small payload sent as a single byte with a value of 32 (which corresponds to a space character in ASCII encoding) after the initial request is a trigger for the Broker&#8217;s Registrar service to close the connection and\/or perform any necessary clean-up actions.<\/li>\n<\/ul>\n<p>The following screen shot shows the intended output of this code.<\/p>\n<p><a href=\"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-content\/uploads\/2023\/06\/XDPing-csharp-e1686664837736.png\"><img fetchpriority=\"high\" decoding=\"async\" class=\"aligncenter size-full wp-image-2461\" src=\"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-content\/uploads\/2023\/06\/XDPing-csharp-e1686664837736.png\" alt=\"XDPing csharp output\" width=\"680\" height=\"176\"><\/a><\/p>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I wrote this C# (csharp) function back in 2020 when developing the Self-Service Session Reset Tool. I have since enhanced it for improved error checking and logging and am happy to share it here for anyone else who wants to leverage it. I released a PowerShell version back in 2019. I like to integrate these &#8230; <a title=\"XDPing C# Function\" class=\"read-more\" href=\"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/2023\/06\/13\/xdping-c-function-2443\" aria-label=\"Read more about XDPing C# Function\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[612,14,626,5,91,38,242],"tags":[544,657,581,416,658,545,656,543],"class_list":["post-2443","post","type-post","status-publish","format-standard","hentry","category-c","category-citrix","category-cvad","category-scripting","category-vdi","category-xenapp","category-xendesktop","tag-citrixcdscontrolleriregistrar","tag-broker-registrar-service","tag-c","tag-citrix","tag-csharp","tag-http1-1-100-continue","tag-registrar-service","tag-xdping"],"aioseo_notices":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/posts\/2443","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/comments?post=2443"}],"version-history":[{"count":5,"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/posts\/2443\/revisions"}],"predecessor-version":[{"id":2678,"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/posts\/2443\/revisions\/2678"}],"wp:attachment":[{"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/media?parent=2443"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/categories?post=2443"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jhouseconsulting.com\/jhouseconsulting\/wp-json\/wp\/v2\/tags?post=2443"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}