Skip to main content

Initial RDP connection on a remote machine over Relay Hybrid Connection and Web Sockets - Azure Relay

In one of my latest post I presented how we can establish a PuTTy connection between two different machines through Azure Relay Hybrid Connection using web sockets.
Today, we will take a look over a Node.JS sample that initiate the tunnel that can be used for an RDP connection between two machines over Azure Relay.

Before jumping to the solution let's take a look on RDP Connection Sequence (
As we can see in the connection sequence flow, there are multiple connections that are open during a RDP session. The initial session that is open between client and server is used for the initial handshake and credentials validation. Once the credential are validated, the current connection is closed and other socket connection are open automatically.
In the current sample, we will update the original one that was written for Telnet. The first 5 steps from the flow will be supported, until in the moment when the initial socket connection is close and a new one is open.
More information on what we should do after this steps are presented at the end of the post.

The implementation is straightforward and similar with the one that was used for PuTTy connection. There are only some small things that we need to take into account.
GitHub Source code:

Server.js needs to run on the machine that we want to access. In our code, we will need to open the socket and redirect all the content that is send through Azure Relay Hybrid Connection to the local socket.
function (socket) {
    relaysocket = socket;
    console.log('New connection from client');
    relaysocket.onmessage = function (event) {
      // Send data to local socket (local port)
    relaysocket.on('close', function () {
      console.log('Relay connectin was closed');

The second step needs to be done on our local socket, where is necessary to redirect the content from our local socket to our web socket.
  myLocalHost = localsocket;
  myLocalHost.on('data', function(d) {
    myLocalHost.on('error', function(err) {console.log("Socket close: " + err.stack)});

On the other machine, where we run client.js we will do a similar thing. Listen to web socket that is communication with Azure Relay Hybrid Connection and redirect content to local port and redirect all content from the local port to our web socket.
var relayclient = webrelay.relayedConnect(
        webrelay.createRelaySendUri(ns, path),
        webrelay.createRelayToken('http://'+ns, keyrule, key),
        function (socket) {
            // Create local socket to the given port                               
            console.log("Connected to relay")
            relayclient.onmessage = function (event) {
                if(typeof localsocket === "undefined")
                    localsocket = net.connect(sourceport,function(socket)  {
                            console.log("New socket");
                    localsocket.on('data', function(data) {
                    localsocket.on('error', function(err) {console.log("Socket close: " + err.stack)});

Next steps
What if we would like to extend the current solution to be able to do a full RDP connection over Azure Relay Hybrid Connection? There are two clear steps that need to be done.

1. Support multiple connection
We shall extend client.js and server.js to be able to send through the web sockets multiple socket connections. This would required that on one side to mark each package that we send over Azure Relay with a flag that would allow us to know on the other side on what socket we need to redirect the content.

2. Buffering
Even if the solution will work, it is pretty clear that we need a buffering mechanism that is able to stream uniformly all content that is send over Azure Relay. If we would have only one connection open, this would not be necessary. Having multiple open connection that goes over the same web socket, then it is required to have a buffering  mechanism.
Without it, the solution will work, but the connection will not be stable enough.

Yes, it is possible to tunnel a RDP connection over Azure Relay. We have all the functionality and tools available already. The support for multiple connection and buffering are two features that are necessary for any kind of remote connection that we will want to establish.
Once we will do this, we will be able to tunnel a VNC or a FTP connection without any problems.


  1. Works great! Thank you for the good work.

  2. am working on RDP(Remote Desktop Sharing) project in which the remote desktop and client are firewall protected , so one can not directly use VNC viewer and VNC connect directly as the devices doesn't have their public IP exposed , I came to know such kind of situation is handled using Service Relay (Azure) which act as a tunnel between the devices to allow remote sharing .It will be very thankful if somebody can help me in solving me this situation of Sevice relay,by providing some useful links of the projects ,or such concepts ,aur any site that had implemented this logic .Thanks in advance .I am using c/c++ for implementation


Post a Comment

Popular posts from this blog

How to check in AngularJS if a service was register or not

There are cases when you need to check in a service or a controller was register in AngularJS.
For example a valid use case is when you have the same implementation running on multiple application. In this case, you may want to intercept the HTTP provider and add a custom step there. This step don’t needs to run on all the application, only in the one where the service exist and register.
A solution for this case would be to have a flag in the configuration that specify this. In the core you would have an IF that would check the value of this flag.
Another solution is to check if a specific service was register in AngularJS or not. If the service was register that you would execute your own logic.
To check if a service was register or not in AngularJS container you need to call the ‘has’ method of ‘inhector’. It will return TRUE if the service was register.
if ($injector.has('httpInterceptorService')) { $httpProvider.interceptors.push('httpInterceptorService&#…

ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded

Today blog post will be started with the following error when running DB tests on the CI machine:
threw exception: System.InvalidOperationException: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See for more information. at System.Data.Entity.Infrastructure.DependencyResolution.ProviderServicesFactory.GetInstance(String providerTypeName, String providerInvariantName) This error happened only on the Continuous Integration machine. On the devs machines, everything has fine. The classic problem – on my machine it’s working. The CI has the following configuration:

TeamCity.NET 4.51EF 6.0.2VS2013
It seems that there …

Entity Framework (EF) TransactionScope vs Database.BeginTransaction

In today blog post we will talk a little about a new feature that is available on EF6+ related to Transactions.
Until now, when we had to use transaction we used ‘TransactionScope’. It works great and I would say that is something that is now in our blood.
using (var scope = new TransactionScope(TransactionScopeOption.Required)) { using (SqlConnection conn = new SqlConnection("...")) { conn.Open(); SqlCommand sqlCommand = new SqlCommand(); sqlCommand.Connection = conn; sqlCommand.CommandText = ... sqlCommand.ExecuteNonQuery(); ... } scope.Complete(); } Starting with EF6.0 we have a new way to work with transactions. The new approach is based on Database.BeginTransaction(), Database.Rollback(), Database.Commit(). Yes, no more TransactionScope.
In the followi…