Friday, May 01, 2009

Marshaling tips

I was trying to solve an interesting problem in C# where I was marshaling a structure from the OpenSSL library.  The X509 structure has lots of parameters, but one called "references" is what I was interested in for this challenge.  I have managed objects that wrap the native pointer from OpenSSL, but needed to manage the lifetime of the native pointers when managed objects were finalized and whether they should destroy or release a reference to the native pointer.

I tried a few methods, and decided that the best way to solve the problem would be to utilize the reference counting implemented in OpenSSL.  The tricky part is...  Most OpenSSL structures have a "references" integer that is incremented/decremented by the CRYPTO_add() method which takes a pointer to the "references" integer from the structure.  So, my challenge was to find a way to get a pointer to the "references" integer in the native OpenSSL structure, and pass it from managed code to the CRYPTO_add() method via p/invoke.

Here's how I made it work.

First, provide the structure definition in managed code for the X509 strucuture:

        [StructLayout(LayoutKind.Sequential)]
        private struct X509
        {
            public IntPtr cert_info;
            public IntPtr sig_alg;
            public IntPtr signature;
            public int valid;
            public int references;
            public IntPtr name;
            #region CRYPTO_EX_DATA ex_data
            public IntPtr ex_data_sk;
            public int ex_data_dummy;
            #endregion
            public int ex_pathlen;
            public uint ex_flags;
            public uint ex_kusage;
            public uint ex_xkusage;
            public uint ex_nscert;
            public IntPtr skid;
            public IntPtr akid;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = Native.SHA_DIGEST_LENGTH)]
            public byte[] sha1_hash;
            public IntPtr aux;
        }

Next, implement an Addref method that gets the offset of the "references" member, and then create an IntPtr to that location to pass into the CRYPTO_add() method.

        public override void Addref()
        {
            int offset = (int)Marshal.OffsetOf(typeof(X509), "references");
            IntPtr offset_ptr = new IntPtr((int)ptr + offset);
            Native.CRYPTO_add_lock(offset_ptr, 1, Native.CryptoLockTypes.CRYPTO_LOCK_X509, "X509Certificate.cs", 0);
        }

The Marshal.OffsetOf() method returns an IntPtr that can safely be cast to an integer.  This provides the offset to the member in the native structure.

The offset_ptr is a new IntPtr object that takes the native X509 pointer and adds the offset to the references member.  Once the value is added, the resulting IntPtr object contains a pointer to the "references" member in the native structure, and can be passed into the CRYPTO_add() method.

Hope this helps someone!

Blogged with the Flock Browser

Labels: , , , , , , , , ,

0 Comments:

Post a Comment

<< Home