You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
7.8 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
  3. //
  4. // MySQL Connector/NET is licensed under the terms of the GPLv2
  5. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  6. // MySQL Connectors. There are special exceptions to the terms and
  7. // conditions of the GPLv2 as it is applied to this software, see the
  8. // FLOSS License Exception
  9. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  10. //
  11. // This program is free software; you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published
  13. // by the Free Software Foundation; version 2 of the License.
  14. //
  15. // This program is distributed in the hope that it will be useful, but
  16. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. // for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License along
  21. // with this program; if not, write to the Free Software Foundation, Inc.,
  22. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  23. using System;
  24. using System.IO;
  25. using System.Net.Sockets;
  26. using System.Threading.Tasks;
  27. using Externals.MySql.Data.Common;
  28. using Externals.MySql.Data.MySqlClient;
  29. using System.IO.Pipes;
  30. using System.Net;
  31. using System.Linq;
  32. #if !NETSTANDARD1_3
  33. using Externals.MySql.Data.MySqlClient.Common;
  34. using System.IO.MemoryMappedFiles;
  35. #endif
  36. namespace Externals.MySql.Data.Common
  37. {
  38. /// <summary>
  39. /// Summary description for StreamCreator.
  40. /// </summary>
  41. internal class StreamCreator
  42. {
  43. readonly string _hostList;
  44. uint _port;
  45. string pipeName;
  46. uint keepalive;
  47. DBVersion driverVersion;
  48. public StreamCreator(string hosts, uint port, string pipeName, uint keepalive, DBVersion driverVersion)
  49. {
  50. _hostList = hosts;
  51. if (string.IsNullOrEmpty(_hostList))
  52. _hostList = "localhost";
  53. this._port = port;
  54. this.pipeName = pipeName;
  55. this.keepalive = keepalive;
  56. this.driverVersion = driverVersion;
  57. }
  58. public static Stream GetStream(string server, uint port, string pipename, uint keepalive, DBVersion v, uint timeout)
  59. {
  60. MySqlConnectionStringBuilder settings = new MySqlConnectionStringBuilder
  61. {
  62. Server = server,
  63. Port = port,
  64. PipeName = pipename,
  65. Keepalive = keepalive,
  66. ConnectionTimeout = timeout
  67. };
  68. return GetStream(settings);
  69. }
  70. public static Stream GetStream(MySqlConnectionStringBuilder settings)
  71. {
  72. switch (settings.ConnectionProtocol)
  73. {
  74. case MySqlConnectionProtocol.Tcp: return GetTcpStream(settings);
  75. case MySqlConnectionProtocol.UnixSocket: return GetUnixSocketStream(settings);
  76. case MySqlConnectionProtocol.SharedMemory: return GetSharedMemoryStream(settings);
  77. case MySqlConnectionProtocol.NamedPipe: return GetNamedPipeStream(settings);
  78. }
  79. throw new InvalidOperationException(Resources.UnknownConnectionProtocol);
  80. }
  81. private static Stream GetTcpStream(MySqlConnectionStringBuilder settings)
  82. {
  83. Task<IPAddress[]> dnsTask = Dns.GetHostAddressesAsync(settings.Server);
  84. dnsTask.Wait();
  85. if (dnsTask.Result == null || dnsTask.Result.Length == 0)
  86. throw new ArgumentException(Resources.InvalidHostNameOrAddress);
  87. IPAddress addr = dnsTask.Result.SingleOrDefault(c => c.AddressFamily == AddressFamily.InterNetwork);
  88. if (addr == null)
  89. addr = dnsTask.Result[0];
  90. TcpClient client = new TcpClient(addr.AddressFamily);
  91. Task task = client.ConnectAsync(settings.Server, (int)settings.Port);
  92. if (!task.Wait(((int)settings.ConnectionTimeout * 1000)))
  93. throw new MySqlException(Resources.Timeout);
  94. if (settings.Keepalive > 0)
  95. {
  96. SetKeepAlive(client.Client, settings.Keepalive);
  97. }
  98. return client.GetStream();
  99. }
  100. private static Stream GetUnixSocketStream(MySqlConnectionStringBuilder settings)
  101. {
  102. if (Platform.IsWindows())
  103. throw new InvalidOperationException(Resources.NoUnixSocketsOnWindows);
  104. EndPoint endPoint = new UnixEndPoint(settings.Server);
  105. Socket socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
  106. if (settings.Keepalive > 0)
  107. {
  108. SetKeepAlive(socket, settings.Keepalive);
  109. }
  110. try
  111. {
  112. socket.ReceiveTimeout = (int)settings.ConnectionTimeout * 1000;
  113. socket.Connect(endPoint);
  114. return new NetworkStream(socket, true);
  115. }
  116. catch (Exception)
  117. {
  118. socket.Dispose();
  119. throw;
  120. }
  121. }
  122. private static Stream GetSharedMemoryStream(MySqlConnectionStringBuilder settings)
  123. {
  124. #if NETSTANDARD1_3
  125. throw new NotSupportedException("Shared memory streams not currently supported.");
  126. #else
  127. SharedMemoryStream str = new SharedMemoryStream(settings.SharedMemoryName);
  128. str.Open(settings.ConnectionTimeout);
  129. return str;
  130. #endif
  131. }
  132. private static Stream GetNamedPipeStream(MySqlConnectionStringBuilder settings)
  133. {
  134. #if NETSTANDARD1_3
  135. NamedPipeClientStream pipeStream = new NamedPipeClientStream(settings.Server, settings.PipeName, PipeDirection.InOut);
  136. pipeStream.Connect((int)settings.ConnectionTimeout * 1000);
  137. return pipeStream;
  138. #else
  139. Stream stream = NamedPipeStream.Create(settings.PipeName, settings.Server, settings.ConnectionTimeout);
  140. return stream;
  141. #endif
  142. }
  143. /// <summary>
  144. /// Set the keepalive timeout on the socket.
  145. /// </summary>
  146. /// <param name="s">The socket object.</param>
  147. /// <param name="time">The keepalive timeout, in seconds.</param>
  148. private static void SetKeepAlive(Socket s, uint time)
  149. {
  150. uint on = 1;
  151. uint interval = 1000; // default interval = 1 sec
  152. uint timeMilliseconds;
  153. if (time > UInt32.MaxValue / 1000)
  154. timeMilliseconds = UInt32.MaxValue;
  155. else
  156. timeMilliseconds = time * 1000;
  157. // Use Socket.IOControl to implement equivalent of
  158. // WSAIoctl with SOL_KEEPALIVE_VALS
  159. // the native structure passed to WSAIoctl is
  160. //struct tcp_keepalive {
  161. // ULONG onoff;
  162. // ULONG keepalivetime;
  163. // ULONG keepaliveinterval;
  164. //};
  165. // marshal the equivalent of the native structure into a byte array
  166. byte[] inOptionValues = new byte[12];
  167. BitConverter.GetBytes(on).CopyTo(inOptionValues, 0);
  168. BitConverter.GetBytes(timeMilliseconds).CopyTo(inOptionValues, 4);
  169. BitConverter.GetBytes(interval).CopyTo(inOptionValues, 8);
  170. try
  171. {
  172. // call WSAIoctl via IOControl
  173. s.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
  174. return;
  175. }
  176. catch (NotImplementedException)
  177. {
  178. // Mono throws not implemented currently
  179. }
  180. // Fallback if Socket.IOControl is not available ( Compact Framework )
  181. // or not implemented ( Mono ). Keepalive option will still be set, but
  182. // with timeout is kept default.
  183. s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
  184. }
  185. }
  186. }
  187. #endif