// WinsockSockAddr takes an IPAddress of IPEndpoint on creation, then provides a
// pinned PSOCKADDR to be used for async PInvoke calls. The owner and pending
// async calls may reference the same instance at the same time, and have
// different independent needs with regards to the lifetime of the pinned
// PSOCKADDR. So WinsockSockAddr guarantees the pinned copy is available so
// long as the WinsockSockAddr instance owning that pinned copy is referenced.
// NOTENOTE: System.Net.Endpoint.Serialize will provide a System.Net.SocketAddress
// which mostly fulfills the same use as the class below. It does not pin the
// result though. Consider passing the result of System.Net.Endpoint.Serialize()
// as X to GCHandle.Alloc(X, GCHandleType.Pinned) instead of picking up the
// entire class below.
class WinsockSockAddr
{
const Int16 AF_INET = 2;
const Int16 AF_INET6 = 23;
[StructLayout(LayoutKind.Sequential)]
struct SOCKADDR_IN
{
public Int16 _family;
public Int16 _port;
public Byte _addr0;
public Byte _addr1;
public Byte _addr2;
public Byte _addr3;
public Int32 _nothing;
}
static readonly int SIZEOF_SOCKADDR_IN = Marshal.SizeOf(typeof(SOCKADDR_IN));
[StructLayout(LayoutKind.Sequential)]
struct SOCKADDR_IN6
{
public Int16 _family;
public Int16 _port;
public Int32 _flowInfo;
public Byte _addr0;
public Byte _addr1;
public Byte _addr2;
public Byte _addr3;
public Byte _addr4;
public Byte _addr5;
public Byte _addr6;
public Byte _addr7;
public Byte _addr8;
public Byte _addr9;
public Byte _addr10;
public Byte _addr11;
public Byte _addr12;
public Byte _addr13;
public Byte _addr14;
public Byte _addr15;
public Int32 _scopeID;
}
static readonly int SIZEOF_SOCKADDR_IN6 = Marshal.SizeOf(typeof(SOCKADDR_IN6));
// Depending on the family type of address represented, either a SOCKADDR_IN
// or a SOCKADDR_IN6 will be referenced by _addr. We'll pin the same object
// to _pinAddr, and finally keep a IntPtr to the alloc.
object _addr;
GCHandle _pinAddr;
IntPtr _pAddr;
public WinsockSockAddr(IPEndPoint source)
: this(source.Address, (short)source.Port)
{
}
public WinsockSockAddr(IPAddress source)
: this(source, 0)
{
}
public WinsockSockAddr(IPAddress source, short port)
{
_pAddr = (IntPtr)0;
if (source.AddressFamily == AddressFamily.InterNetwork)
{
SOCKADDR_IN a;
Byte[] addr = source.GetAddressBytes();
Debug.Assert(addr.Length == 4);
a._family = AF_INET;
a._port = IPAddress.HostToNetworkOrder(port);
a._addr0 = addr[0];
a._addr1 = addr[1];
a._addr2 = addr[2];
a._addr3 = addr[3];
a._nothing = 0;
_addr = a;
}
else if (source.AddressFamily == AddressFamily.InterNetworkV6)
{
SOCKADDR_IN6 a;
Byte[] addr = source.GetAddressBytes();
Debug.Assert(addr.Length == 16);
a._family = AF_INET6;
a._port = IPAddress.HostToNetworkOrder(port);
a._flowInfo = 0;
a._addr0 = addr[0];
a._addr1 = addr[1];
a._addr2 = addr[2];
a._addr3 = addr[3];
a._addr4 = addr[4];
a._addr5 = addr[5];
a._addr6 = addr[6];
a._addr7 = addr[7];
a._addr8 = addr[8];
a._addr9 = addr[9];
a._addr10 = addr[10];
a._addr11 = addr[11];
a._addr12 = addr[12];
a._addr13 = addr[13];
a._addr14 = addr[14];
a._addr15 = addr[15];
a._scopeID = (Int32)source.ScopeId;
_addr = a;
}
else
{
throw new ArgumentException();
}
_pinAddr = GCHandle.Alloc(_addr, GCHandleType.Pinned);
_pAddr = _pinAddr.AddrOfPinnedObject();
}
void Close()
{
if (_pinAddr.IsAllocated)
{
_pinAddr.Free();
}
_addr = null;
_pAddr = (IntPtr)0;
}
~WinsockSockAddr()
{
Close();
}
public IntPtr PinnedSockAddr
{ get { return _pAddr; } }
}
None.