Overview
Our company is about to distribute a C# .NET / WPF app to customers who use Citrix XenApp (or whatever it has been renamed to). This app runs multiple times on one Citrix server, once for each user, as part of their Citrix session. Each instance of the app must respond to browser JavaScript that is fetching from localhost:xxxx
, with xxxx
always being the same port number.
Citrix supplies something called Virtual Loopback for this, and also discusses Windows' Virtual IP for it. For the moment we are trying Virtual Loopback. Virtual Loopback basically allows a different "local host" for each session, in the form 127.0.0.1, 127.0.0.2, 127.0.0.3, and so on. Citrix describes both at length here: https://docs.citrix.com/en-us/xenapp-and-xendesktop/7-15-ltsr/manage-deployment/virtual-ip-virtual-loopback.html.
And basically, it doesn't work for us with .NET. In contrast, a colleague worked with the Citrix approach using a Python test app as an experiment, and that worked fine.
This is a two-part policy in Citrix (setting it up in general, and naming the apps that can use the redirection), and both were set up properly. This isn't a problem of the browser not finding the right address either; that would be set up with the Citrix policy, but the fail is occurring when our app starts and only the first instance can bind to the socket (naturally).
History and latest steps
In our code, using localhost
as a literal resolves to 0:0:0:0
, which isn't part of the 127.x.x.x series and so that fails. That part makes sense to me now, basically. However, when I tried using 127.0.0.1 in the code, thinking the settings might redirect transparently despite it being spelled out, that didn't work at all.
So now I am trying workarounds now that might help or not, such as using IPAddress.Loopback
and ensuring that the IP address is IPv4 (which was found to make a difference for the similar Java problem years ago, seen in this StackOverflow Q/A:
Java get REAL loopback address programmatically).
BTW, the local port can't just be changed programmatically for each instance because the REST call is used to get the session's specific app instance, and so if ports were varied, the JavaScript would have to know the right one in advance, defeating the purpose.
Speculations
My colleague speculates that the problem is that the app we created runs as System, but as far as I can tell, that is a byproduct of using the .NET web stack, since the test app is actually running under my user name. It's also apparently impossible to change, since it's a byproduct. Perhaps I've missed something there though?
Has anyone seen this problem, and what was the answer you found for it?
CodePudding user response:
Sigh, I end up answering my own question. But for the future person who finds this problem in their work...
One of the two Citrix virtual loopback policies required needs a list of names of apps that will get the redirected addresses, each on their own lines, for example
SomeApp.exe
OtherApp.exe
and those must match exactly to the names of the processes that are using TCP/IP.
However, when you use handy types like HttpListener
in C#, you're actually using Windows' built-in HTTP.sys
subsystem (the same one IIS uses, apparently), and that sub-system is in kernel mode, so it always runs as System
. And there's no way to add either System or HTTP.sys to that Citrix list.
So in order to make Citrix and your own .NET HTTP listening play nicely with each other, you can't use HttpListener
or similar objects (which I suspect includes similar things like TcpListener
). Instead, you have to either roll your own HTTP using Socket
in System.Net.Sockets
, which doesn't involve HTTP.sys
at all, or else use a NuGet package that doesn't make use of HTTP.sys as a dependency.
For most purposes, using a NuGet package is definitely better. In my case, I'm rolling my own, at least for the moment, because the listening approach that I inherited from others responds to only one request and only with one thing.
Finding an answer to this problem has been impossible in a single search in Google till now. However, there are lots of references on the web for individual pieces of the puzzle.
To put it all together: Use the two Citrix policies for virtual loopback, and don't use HttpListener
or anything else in .NET that ends up utilizing HTTP.sys
internally. You can tell if something in .NET or from NuGet does use that by looking in Microsoft's TCPView (or other tooling) and seeing if your chosen port comes up with System as its process (even if your app shows up in Task Manager under your user name).
And finally, whatever app is calling your .NET app must also be in that Citrix list, for instance
chrome.exe
.
I hope this helps someone else someday!