PeerUnsafeNativeMethods.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Channels
  5. {
  6. using System.Collections.Generic;
  7. using System.Collections.ObjectModel;
  8. using System.Net;
  9. using System.Net.Sockets;
  10. using System.Runtime;
  11. using System.Runtime.CompilerServices;
  12. using System.Runtime.ConstrainedExecution;
  13. using System.Runtime.InteropServices;
  14. using System.Security;
  15. using System.Security.Permissions;
  16. using Microsoft.Win32.SafeHandles;
  17. static class PeerWinsock
  18. {
  19. [DllImport("ws2_32.dll", SetLastError = true, EntryPoint = "WSAIoctl")]
  20. internal static extern int WSAIoctl(
  21. [In] IntPtr socketHandle,
  22. [In] int ioControlCode,
  23. [In] IntPtr inBuffer,
  24. [In] int inBufferSize,
  25. [Out] IntPtr outBuffer,
  26. [In] int outBufferSize,
  27. [Out] out int bytesTransferred,
  28. [In] IntPtr overlapped,
  29. [In] IntPtr completionRoutine);
  30. }
  31. [Serializable, StructLayout(LayoutKind.Sequential)]
  32. struct SocketAddress
  33. {
  34. IntPtr sockAddr;
  35. int sockAddrLength;
  36. public IntPtr SockAddr { get { return sockAddr; } }
  37. public int SockAddrLength { get { return sockAddrLength; } }
  38. public void InitializeFromCriticalAllocHandleSocketAddress(CriticalAllocHandleSocketAddress sockAddr)
  39. {
  40. this.sockAddr = (IntPtr)sockAddr;
  41. this.sockAddrLength = sockAddr.Size;
  42. }
  43. }
  44. [StructLayout(LayoutKind.Sequential)]
  45. struct SocketAddressList
  46. {
  47. int count;
  48. internal const int maxAddresses = 50;
  49. [MarshalAs(UnmanagedType.ByValArray, SizeConst = maxAddresses)]
  50. SocketAddress[] addresses;
  51. public SocketAddress[] Addresses { get { return addresses; } }
  52. public int Count { get { return count; } }
  53. public SocketAddressList(SocketAddress[] addresses, int count)
  54. {
  55. this.addresses = addresses;
  56. this.count = count;
  57. }
  58. public static ReadOnlyCollection<IPAddress> SortAddresses(Socket socket, IPAddress listenAddress, ReadOnlyCollection<IPAddress> addresses)
  59. {
  60. ReadOnlyCollection<IPAddress> sortedAddresses = null;
  61. // Skip sort if ipv6 isn't installed or if address array has a single address
  62. if (socket == null || addresses.Count <= 1)
  63. {
  64. sortedAddresses = addresses;
  65. }
  66. else
  67. {
  68. CriticalAllocHandleSocketAddressList inputBuffer = null;
  69. CriticalAllocHandleSocketAddressList outputBuffer = null;
  70. try
  71. {
  72. inputBuffer = CriticalAllocHandleSocketAddressList.FromAddressList(addresses);
  73. outputBuffer = CriticalAllocHandleSocketAddressList.FromAddressCount(0);
  74. // Invoke ioctl to sort the addresses
  75. int realOutputBufferSize;
  76. int error = UnsafeNativeMethods.ERROR_SUCCESS;
  77. int errorCode = PeerWinsock.WSAIoctl(socket.Handle,
  78. unchecked((int)IOControlCode.AddressListSort),
  79. (IntPtr)inputBuffer,
  80. inputBuffer.Size,
  81. (IntPtr)outputBuffer,
  82. outputBuffer.Size,
  83. out realOutputBufferSize,
  84. IntPtr.Zero,
  85. IntPtr.Zero);
  86. if (errorCode == -1)
  87. {
  88. // Get the Win32 error code before doing anything else
  89. error = Marshal.GetLastWin32Error();
  90. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SocketException(error));
  91. }
  92. // Marshal the sorted SOCKET_ADDRESS_LIST into IPAddresses
  93. sortedAddresses = outputBuffer.ToAddresses();
  94. }
  95. finally
  96. {
  97. if (inputBuffer != null) inputBuffer.Dispose();
  98. if (outputBuffer != null) outputBuffer.Dispose();
  99. }
  100. }
  101. return sortedAddresses;
  102. }
  103. }
  104. // Type that converts from sockaddr_in6 to IPAddress and vice-versa
  105. [Serializable, StructLayout(LayoutKind.Sequential)]
  106. struct sockaddr_in6
  107. {
  108. short sin6_family;
  109. ushort sin6_port;
  110. uint sin6_flowinfo;
  111. [MarshalAs(UnmanagedType.ByValArray, SizeConst = addrByteCount)]
  112. byte[] sin6_addr;
  113. uint sin6_scope_id;
  114. const int addrByteCount = 16;
  115. // if the addr is v4-mapped-v6, 10th and 11th byte contain 0xFF. the last 4 bytes contain the ipv4 address
  116. const int v4MapIndex = 10;
  117. const int v4Index = v4MapIndex + 2;
  118. public sockaddr_in6(IPAddress address)
  119. {
  120. if (address.AddressFamily == AddressFamily.InterNetworkV6)
  121. {
  122. this.sin6_addr = address.GetAddressBytes();
  123. this.sin6_scope_id = (uint)address.ScopeId;
  124. }
  125. else
  126. {
  127. // Map v4 address to v4-mapped v6 addr (i.e., ::FFFF:XX.XX.XX.XX)
  128. byte[] v4AddressBytes = address.GetAddressBytes();
  129. this.sin6_addr = new byte[addrByteCount];
  130. for (int i = 0; i < v4MapIndex; i++)
  131. this.sin6_addr[i] = 0;
  132. this.sin6_addr[v4MapIndex] = 0xff;
  133. this.sin6_addr[v4MapIndex + 1] = 0xff;
  134. for (int i = v4Index; i < addrByteCount; i++)
  135. this.sin6_addr[i] = v4AddressBytes[i - v4Index];
  136. this.sin6_scope_id = 0; // V4 address doesn't have a scope ID
  137. }
  138. this.sin6_family = (short)AddressFamily.InterNetworkV6;
  139. this.sin6_port = 0;
  140. this.sin6_flowinfo = 0;
  141. }
  142. public short Family { get { return this.sin6_family; } }
  143. public uint FlowInfo { get { return this.sin6_flowinfo; } }
  144. // Returns true if the address is a v4-mapped v6 address
  145. // Adapted from ws2ipdef.w's IN6_IS_ADDR_V4MAPPED macro
  146. private bool IsV4Mapped
  147. {
  148. get
  149. {
  150. // A v4-mapped v6 address will have the last 4 bytes contain the IPv4 address.
  151. // The preceding 2 bytes contain 0xFFFF. All others are 0.
  152. if (sin6_addr[v4MapIndex] != 0xff || sin6_addr[v4MapIndex + 1] != 0xff)
  153. return false;
  154. for (int i = 0; i < v4MapIndex; i++)
  155. if (sin6_addr[i] != 0)
  156. return false;
  157. return true;
  158. }
  159. }
  160. public ushort Port { get { return this.sin6_port; } }
  161. // Converts a sockaddr_in6 to IPAddress
  162. // A v4 mapped v6 address is converted to a v4 address
  163. public IPAddress ToIPAddress()
  164. {
  165. if (!(this.sin6_family == (short)AddressFamily.InterNetworkV6))
  166. {
  167. throw Fx.AssertAndThrow("AddressFamily expected to be InterNetworkV6");
  168. }
  169. if (IsV4Mapped)
  170. {
  171. byte[] addr =
  172. {
  173. this.sin6_addr[v4Index],
  174. this.sin6_addr[v4Index + 1],
  175. this.sin6_addr[v4Index + 2],
  176. this.sin6_addr[v4Index + 3]
  177. };
  178. return new IPAddress(addr);
  179. }
  180. else
  181. {
  182. return new IPAddress(this.sin6_addr, this.sin6_scope_id);
  183. }
  184. }
  185. }
  186. class CriticalAllocHandleSocketAddressList : CriticalAllocHandle
  187. {
  188. int count;
  189. int size;
  190. CriticalAllocHandleSocketAddress[] socketHandles;
  191. public int Count { get { return count; } }
  192. public int Size { get { return size; } }
  193. public static CriticalAllocHandleSocketAddressList FromAddressList(ICollection<IPAddress> addresses)
  194. {
  195. if (addresses == null)
  196. {
  197. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("addresses");
  198. }
  199. int count = addresses.Count;
  200. CriticalAllocHandleSocketAddress[] socketHandles = new CriticalAllocHandleSocketAddress[SocketAddressList.maxAddresses];
  201. SocketAddressList socketAddressList = new SocketAddressList(new SocketAddress[SocketAddressList.maxAddresses], count);
  202. int i = 0;
  203. foreach (IPAddress address in addresses)
  204. {
  205. if (i == SocketAddressList.maxAddresses) break; // due to Marshalling fixed sized array of SocketAddresses.
  206. socketHandles[i] = CriticalAllocHandleSocketAddress.FromIPAddress(address);
  207. socketAddressList.Addresses[i].InitializeFromCriticalAllocHandleSocketAddress(socketHandles[i]);
  208. ++i;
  209. }
  210. int size = Marshal.SizeOf(socketAddressList);
  211. CriticalAllocHandleSocketAddressList result = CriticalAllocHandleSocketAddressList.FromSize(size);
  212. result.count = count;
  213. result.socketHandles = socketHandles;
  214. Marshal.StructureToPtr(socketAddressList, result, false);
  215. return result;
  216. }
  217. public static CriticalAllocHandleSocketAddressList FromAddressCount(int count)
  218. {
  219. SocketAddressList socketAddressList = new SocketAddressList(new SocketAddress[SocketAddressList.maxAddresses], 0);
  220. int size = Marshal.SizeOf(socketAddressList);
  221. CriticalAllocHandleSocketAddressList result = CriticalAllocHandleSocketAddressList.FromSize(size);
  222. result.count = count;
  223. Marshal.StructureToPtr(socketAddressList, result, false);
  224. return result;
  225. }
  226. static new CriticalAllocHandleSocketAddressList FromSize(int size)
  227. {
  228. CriticalAllocHandleSocketAddressList result = new CriticalAllocHandleSocketAddressList();
  229. RuntimeHelpers.PrepareConstrainedRegions();
  230. try { }
  231. finally
  232. {
  233. result.SetHandle(Marshal.AllocHGlobal(size));
  234. result.size = size;
  235. }
  236. return result;
  237. }
  238. public ReadOnlyCollection<IPAddress> ToAddresses()
  239. {
  240. SocketAddressList socketAddressList = (SocketAddressList)Marshal.PtrToStructure(this, typeof(SocketAddressList));
  241. IPAddress[] addresses = new IPAddress[socketAddressList.Count];
  242. for (int i = 0; i < addresses.Length; i++)
  243. {
  244. if (!(socketAddressList.Addresses[i].SockAddrLength == Marshal.SizeOf(typeof(sockaddr_in6))))
  245. {
  246. throw Fx.AssertAndThrow("sockAddressLength in SOCKET_ADDRESS expected to be valid");
  247. }
  248. sockaddr_in6 sockAddr = (sockaddr_in6)Marshal.PtrToStructure(socketAddressList.Addresses[i].SockAddr, typeof(sockaddr_in6));
  249. addresses[i] = sockAddr.ToIPAddress();
  250. }
  251. return Array.AsReadOnly<IPAddress>(addresses);
  252. }
  253. }
  254. class CriticalAllocHandleSocketAddress : CriticalAllocHandle
  255. {
  256. int size;
  257. public int Size { get { return size; } }
  258. public static CriticalAllocHandleSocketAddress FromIPAddress(IPAddress input)
  259. {
  260. if (input == null)
  261. {
  262. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("input");
  263. }
  264. CriticalAllocHandleSocketAddress result = null;
  265. int size = Marshal.SizeOf(typeof(sockaddr_in6));
  266. result = CriticalAllocHandleSocketAddress.FromSize(size);
  267. sockaddr_in6 sa = new sockaddr_in6(input);
  268. Marshal.StructureToPtr(sa, (IntPtr)result, false);
  269. return result;
  270. }
  271. public static new CriticalAllocHandleSocketAddress FromSize(int size)
  272. {
  273. CriticalAllocHandleSocketAddress result = new CriticalAllocHandleSocketAddress();
  274. RuntimeHelpers.PrepareConstrainedRegions();
  275. try { }
  276. finally
  277. {
  278. result.SetHandle(Marshal.AllocHGlobal(size));
  279. result.size = size;
  280. }
  281. return result;
  282. }
  283. }
  284. class CriticalAllocHandle : CriticalHandleZeroOrMinusOneIsInvalid
  285. {
  286. [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
  287. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  288. public static implicit operator IntPtr(CriticalAllocHandle safeHandle)
  289. {
  290. return (safeHandle != null) ? safeHandle.handle : (IntPtr)null;
  291. }
  292. protected override bool ReleaseHandle()
  293. {
  294. Marshal.FreeHGlobal(handle);
  295. return true;
  296. }
  297. public static CriticalAllocHandle FromSize(int size)
  298. {
  299. CriticalAllocHandle result = new CriticalAllocHandle();
  300. RuntimeHelpers.PrepareConstrainedRegions();
  301. try { }
  302. finally
  303. {
  304. result.SetHandle(Marshal.AllocHGlobal(size));
  305. }
  306. return result;
  307. }
  308. }
  309. class CriticalAllocHandleBlob : CriticalAllocHandle
  310. {
  311. [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
  312. public static CriticalAllocHandle FromBlob<T>(T id)
  313. {
  314. int size = Marshal.SizeOf(typeof(T));
  315. CriticalAllocHandle result = CriticalAllocHandle.FromSize(size);
  316. Marshal.StructureToPtr(id, (IntPtr)result, false);
  317. return result;
  318. }
  319. }
  320. class CriticalAllocHandleGuid : CriticalAllocHandle
  321. {
  322. public static CriticalAllocHandle FromGuid(Guid input)
  323. {
  324. int guidSize = Marshal.SizeOf(typeof(Guid));
  325. CriticalAllocHandle result = CriticalAllocHandle.FromSize(guidSize);
  326. Marshal.Copy(input.ToByteArray(), 0, (IntPtr)result, guidSize);
  327. return result;
  328. }
  329. }
  330. }