Running with Code Like with scissors, only more dangerous

29Jan/080

The Remoting Mystery… solved!

Posted by Rob

I remarked yesterday that I was having difficulty accessing remoted objects - sometimes I would never break into the methods I was calling, and sometimes calling RemotingServices.Disconnect() would fail.  I was a bit miffed about all this, and to say the documentation was unclear about why precisely an object would intermittently just stop.  I was custom-marshaling the object upon startup of the host program; it wasn't server-activated, and shouldn't have any reason for just going away.  But the failure was intermittent; I couldn't regularly reproduce the problem, and it wasn't raising an exception - it was just returning false.

Well, I went to lunch today and left my remoting host program running.  When I got back I tried to debug the client application, and of course it failed.  When I tried to close the server application, though, I didn't get my regular InvalidOperationException (the one that I was throwing) - I got a RemotingException.  And before I quickly hit enter to close the console window, I noticed it said something about how the object no longer lived on the server and therefore couldn't be shown along the remoting channels.

It turns out that MarshalByRefObject - the class from which I'm deriving my service specification objects - this class provides a method to specify the object's lifetime called InitializeLifetimeService().  This method returns an object that implements the System.Runtime.Remoting.Lifetime.ILease interface.  By changing the InitialLeaseTime value of this object to zero, the object can last forever while marshaled.  I overrode this method in a new base class, and then derived the services from this class:

   1:  public class RemotedServiceObject : MarshalByRefObject
   2:  {
   3:      public override object InitializeLifetimeService()
   4:      {
   5:          ILease lease = (ILease)base.InitializeLifetimeService();
   6:          if (lease.CurrentState == LeaseState.Initial)
   7:          {
   8:              lease.InitialLeaseTime = TimeSpan.Zero;
   9:          }
  10:          return lease;
  11:      }
  12:  }

So great news - it worked out perfectly.  No more returning false, and no more inability to connect to the object after a while.

Finally, remoting behaves like I tell it to!

Tagged as: No Comments
29Jan/080

The Madness of Remoting

Posted by Rob

My latest project has been pretty much my first real distributed application - it involves securely storing and encrypting credit card data in a system that makes it nigh impossible to access the information.  It's actually been really fun, delving into the depths of secure programming and trying to come up with security measures to thwart perceived avenues of attack against the system.

Part of the way that this system works is by encrypting sensitive data as soon as it arrives to the application servers.  However, to prevent recovery of the data should the application servers be compromised, we chose to use a public key infrastructure system - a server elsewhere on the network issues public/private key pairs, and only the public key is stored on the application server.  The private key never actually leaves that server, and specialized trust relationships are configured so that when decryption needs to take place, it happens on the server where the private key was stored, and data exchange requires a secure channel.

The fun part about figuring out how all of this was going to work together was determining where the system lived.  One of the kicks about the application is that the encryption keys aren't allowed to be stored in "plain text" anywhere - they themselves are encrypted, and the encryption key we use to encrypt those is absolutely forbidden from being stored - we keep it in memory throughout the lifetime of the application (it is backed up with a physical backup mechanism that involves division of responsibility).

Well, with the requirement that the key encrypting key could not just disappear, I knew that we couldn't use ASP.NET to manage it - we'd have a catastrophe in the event that the process recycled.  The obvious solution was to use a Windows Service.  But the only way for a Windows Service to communicate with the outside world (in .NET anyway) is via remoting.

I'd played with remoting a small bit in the past - an app I wrote used remoting to activate objects in another AppDomain so that I could inspect the object for supported interfaces - as far as plugin systems go, it was one of my more in-depth ones.  And I'd achieved my MCTS credential in distributed applications.  But I really had (and I still really don't have) no idea what I was headed for.

Because I need to manage object lifetime myself, I am unable to use the automatic .NET Remoting configuration scheme; I need to make sure that the server has access to certain services as soon as it starts and until it ends.  I don't have the luxury of client-activated objects, even singleton ones.  So I need to set up the remoting infrastructure myself; I defined a series of service interfaces in a DLL that will be accessible from both the client and server applications, and then implement the interfaces in the server.  Seems straightforward enough.  Internal to the server-side, I also created an IService interface that defined Start() and Stop() methods - a series of objects that I could control from within the Windows Service and also work with from a factory-style utility class.

Here's sample code for the Start and Stop methods:

   1:   
   2:          public void Start()
   3:          {
   4:              m_wellKnown = RemotingServices.Marshal(this, string.Format("{0}.svc", m_config.Name), typeof(IEncryptionKeyGeneratorService));
   5:          }
   6:   
   7:          public void Stop()
   8:          {
   9:              if (!RemotingServices.Disconnect(this))
  10:                  throw new InvalidOperationException("Error disconnecting this object.");
  11:              m_wellKnown = null;
  12:          }

In this example, the ObjRef specified by the m_wellKnown field is initialized by the call to RemotingServices.Marshal.  The object actually marshals itself, but limits the type hierarchy by specifying exactly which interface it is casting to.  My current class, EncryptionKeyGeneratorService, inherits MarshalByRefObject and implements IEncryptionKeyGeneratorService and IService.  IEncryptionKeyGeneratorService does not inherit from IService; otherwise I would risk allowing a cast to IService, which could compromise the integrity of the application.

The part that I don't get, and really the whole purpose of this, is to explain why the call to RemotingServices.Disconnect(this) fails.

I always look at it and wonder, "Why did I get an InvalidOperationException?"  And then I realize: it's because I threw it.

The RemotingServices class is already obscure enough in that calls to Marshal are paired with calls to Disconnect, and that calls to Connect are paired with calls to Unmarshal (but not always... or something).  What I don't understand is why Disconnect() would ever return false.  It's pretty clear: I'm telling you to stop acting as an object that can be accessed elsewhere.  You're not a two-year-old child; stop telling me no.

The really scary part is that I can close out my client application altogether and still have RemotingServices be stubborn.

WHY?