Identifying the client during a .NET remoting invocation

Mr. Lame picture Mr. Lame · Feb 9, 2009 · Viewed 7.4k times · Source

Given this MarshalByRef class:

public class MyRemotedClass : MarshalByRef
{
  public void DoThis()
  {
     ...
  }
  public void DoThat()
  {
     ...
  }
}

Client side code:

MyRemotedClass m = GetSomehowMyRemotedClass();
m.DoThis();
m.DoThat();

I can have several clients doing the same thing at a the same time. I would like to distinct the clients. How can I identify inside the remotely accessed methods, by whom the remoting invocation is executed? For example, I could log who did what. (Actually, I do not need to trace back the true client info, I just want to be able to group invocations by clients.)

[Edited to add more background info]

I have enormous amount of code to cover, including properties. Therefore extending the input parameter list is not an option.

Answer

Kev picture Kev · Feb 9, 2009

One of the things you can do is identify a client by IP address by implementing an IServerChannelSinkProvider.

Add this class to your remoting host project:

ClientIPServerSinkProvider.cs

using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels;
using System.Threading;
using System.Net;

namespace MyRemotingEnvironment
{
    public class ClientIPServerSinkProvider : 
        IServerChannelSinkProvider
    {
        private IServerChannelSinkProvider _nextProvider = null;

        public ClientIPServerSinkProvider()
        {
        }

        public ClientIPServerSinkProvider(
            IDictionary properties, 
            ICollection providerData)
        {
        }

        public IServerChannelSinkProvider Next
        {
            get { return _nextProvider; }
            set { _nextProvider = value; }
        }

        public IServerChannelSink CreateSink(IChannelReceiver channel)
        {
            IServerChannelSink nextSink = null;

            if (_nextProvider != null)
            {
                nextSink = _nextProvider.CreateSink(channel);
            }
            return new ClientIPServerSink(nextSink);
        }

        public void GetChannelData(IChannelDataStore channelData)
        {
        }
    }



    public class ClientIPServerSink : 
        BaseChannelObjectWithProperties, 
        IServerChannelSink, 
        IChannelSinkBase
    {

        private IServerChannelSink _nextSink;

        public ClientIPServerSink(IServerChannelSink next)
        {
            _nextSink = next;
        }

        public IServerChannelSink NextChannelSink
        {
            get { return _nextSink; }
            set { _nextSink = value; }
        }

        public void AsyncProcessResponse(
            IServerResponseChannelSinkStack sinkStack, 
            Object state, 
            IMessage message, 
            ITransportHeaders headers, 
            Stream stream)
        {
            IPAddress ip = headers[CommonTransportKeys.IPAddress] as IPAddress;
            CallContext.SetData("ClientIPAddress", ip);
            sinkStack.AsyncProcessResponse(message, headers, stream);
        }

        public Stream GetResponseStream(
            IServerResponseChannelSinkStack sinkStack, 
            Object state, 
            IMessage message, 
            ITransportHeaders headers)
        {

            return null;

        }


        public ServerProcessing ProcessMessage(
            IServerChannelSinkStack sinkStack, 
            IMessage requestMsg, 
            ITransportHeaders requestHeaders, 
            Stream requestStream, 
            out IMessage responseMsg, 
            out ITransportHeaders responseHeaders, 
            out Stream responseStream)
        {
            if (_nextSink != null)
            {
                IPAddress ip = 
                    requestHeaders[CommonTransportKeys.IPAddress] as IPAddress;
                CallContext.SetData("ClientIPAddress", ip);
                ServerProcessing spres = _nextSink.ProcessMessage(
                    sinkStack, 
                    requestMsg, 
                    requestHeaders, 
                    requestStream, 
                    out responseMsg, 
                    out responseHeaders, 
                    out responseStream);
                return spres;
            }
            else
            {
                responseMsg = null;
                responseHeaders = null;
                responseStream = null;
                return new ServerProcessing();
            }
        }


    }
}

Then when you start your remoting host do something like the following:

BinaryServerFormatterSinkProvider bp = new BinaryServerFormatterSinkProvider();
ClientIPServerSinkProvider csp = new ClientIPServerSinkProvider();
csp.Next = bp;
Hashtable ht = new Hashtable();
ht.Add("port", "1234"); // Your remoting port number
TcpChannel channel = new TcpChannel(ht, null, csp);
ChannelServices.RegisterChannel(channel, false);

RemotingConfiguration.RegisterWellKnownServiceType(
    typeof(MyRemotedClass), 
    "MyRemotedClass.rem", 
    WellKnownObjectMode.SingleCall);

In your method calls you can access the IP address of the client by doing:

public class MyRemotedClass : MarshalByref
{
    public void DoThis()
    {
        string clientIP = CallContext.GetData("ClientIPAddress").ToString();
    }
}