Skip to content

Commit 327f621

Browse files
committed
Send client connection attributes. Fixes #293
1 parent a420190 commit 327f621

File tree

6 files changed

+71
-15
lines changed

6 files changed

+71
-15
lines changed

src/MySqlConnector/MySqlConnector.csproj

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,7 @@
2020
<RepositoryUrl>https://github.com/mysql-net/MySqlConnector.git</RepositoryUrl>
2121
</PropertyGroup>
2222

23-
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
24-
<PackageReference Include="System.Buffers" Version="4.0.0" />
25-
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.0.0" />
26-
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.0.0" />
27-
<Reference Include="System.Data" />
28-
<Reference Include="System.Transactions" />
29-
</ItemGroup>
30-
31-
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
23+
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' OR '$(TargetFramework)' == 'net46' ">
3224
<PackageReference Include="System.Buffers" Version="4.0.0" />
3325
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.0.0" />
3426
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.0.0" />
@@ -39,6 +31,7 @@
3931
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
4032
<PackageReference Include="System.Buffers" Version="4.3.0" />
4133
<PackageReference Include="System.Data.Common" Version="4.3.0" />
34+
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
4235
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
4336
<PackageReference Include="System.Net.Security" Version="4.3.0" />
4437
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.0" />

src/MySqlConnector/Serialization/ChangeUserPayload.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace MySql.Data.Serialization
44
{
55
internal class ChangeUserPayload
66
{
7-
public static PayloadData Create(string user, byte[] authResponse, string schemaName)
7+
public static PayloadData Create(string user, byte[] authResponse, string schemaName, byte[] connectionAttributes)
88
{
99
var writer = new PayloadWriter();
1010

@@ -16,6 +16,8 @@ public static PayloadData Create(string user, byte[] authResponse, string schema
1616
writer.WriteByte((byte) CharacterSet.Utf8Mb4Binary);
1717
writer.WriteByte(0);
1818
writer.WriteNullTerminatedString("mysql_native_password");
19+
if (connectionAttributes != null)
20+
writer.Write(connectionAttributes);
1921

2022
return new PayloadData(new ArraySegment<byte>(writer.ToBytes()));
2123
}

src/MySqlConnector/Serialization/HandshakeResponse41Packet.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ private static PayloadWriter CreateCapabilitiesPayload(ProtocolCapabilities serv
1919
(string.IsNullOrWhiteSpace(cs.Database) ? 0 : ProtocolCapabilities.ConnectWithDatabase) |
2020
(cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
2121
(useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
22+
(serverCapabilities & ProtocolCapabilities.ConnectionAttributes) |
2223
additionalCapabilities));
2324
writer.WriteInt32(0x4000_0000);
2425
writer.WriteByte((byte) CharacterSet.Utf8Mb4Binary);
@@ -32,10 +33,9 @@ public static byte[] InitSsl(ProtocolCapabilities serverCapabilities, Connection
3233
return CreateCapabilitiesPayload(serverCapabilities, cs, useCompression, ProtocolCapabilities.Ssl).ToBytes();
3334
}
3435

35-
public static byte[] Create(InitialHandshakePacket handshake, ConnectionSettings cs, bool useCompression)
36+
public static byte[] Create(InitialHandshakePacket handshake, ConnectionSettings cs, bool useCompression, byte[] connectionAttributes)
3637
{
3738
// TODO: verify server capabilities
38-
3939
var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, useCompression);
4040
writer.WriteNullTerminatedString(cs.UserID);
4141
var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, 0, cs.Password);
@@ -48,6 +48,9 @@ public static byte[] Create(InitialHandshakePacket handshake, ConnectionSettings
4848
if ((handshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
4949
writer.WriteNullTerminatedString("mysql_native_password");
5050

51+
if (connectionAttributes != null)
52+
writer.Write(connectionAttributes);
53+
5154
return writer.ToBytes();
5255
}
5356
}

src/MySqlConnector/Serialization/MySqlSession.cs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
using System;
2+
using System.Diagnostics;
3+
using System.Globalization;
24
using System.IO;
35
using System.Net;
46
using System.Net.Security;
57
using System.Net.Sockets;
8+
using System.Reflection;
9+
using System.Runtime.InteropServices;
610
using System.Security.Authentication;
711
using System.Security.Cryptography;
812
using System.Security.Cryptography.X509Certificates;
@@ -233,7 +237,11 @@ public async Task ConnectAsync(ConnectionSettings cs, IOBehavior ioBehavior, Can
233237
await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
234238
}
235239

236-
var response = HandshakeResponse41Packet.Create(initialHandshake, cs, m_useCompression);
240+
m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
241+
if (m_supportsConnectionAttributes && s_connectionAttributes == null)
242+
s_connectionAttributes = CreateConnectionAttributes();
243+
244+
var response = HandshakeResponse41Packet.Create(initialHandshake, cs, m_useCompression, m_supportsConnectionAttributes ? s_connectionAttributes : null);
237245
payload = new PayloadData(new ArraySegment<byte>(response));
238246
await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
239247
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -270,7 +278,7 @@ public async Task ResetConnectionAsync(ConnectionSettings cs, IOBehavior ioBehav
270278
{
271279
// optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL)
272280
var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, 0, cs.Password);
273-
var payload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database);
281+
var payload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database, m_supportsConnectionAttributes ? s_connectionAttributes : null);
274282
await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
275283
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
276284
if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
@@ -768,6 +776,46 @@ private void VerifyState(State state1, State state2)
768776
throw new InvalidOperationException("Expected state to be ({0}|{1}) but was {2}.".FormatInvariant(state1, state2, m_state));
769777
}
770778

779+
private static byte[] CreateConnectionAttributes()
780+
{
781+
var attributesWriter = new PayloadWriter();
782+
attributesWriter.WriteLengthEncodedString("_client_name");
783+
attributesWriter.WriteLengthEncodedString("MySqlConnector");
784+
attributesWriter.WriteLengthEncodedString("_client_version");
785+
attributesWriter.WriteLengthEncodedString(typeof(MySqlSession).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion);
786+
try
787+
{
788+
var os = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Windows" :
789+
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "Linux" :
790+
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "macOS" : null;
791+
var osDetails = RuntimeInformation.OSDescription;
792+
var platform = RuntimeInformation.ProcessArchitecture.ToString();
793+
if (os != null)
794+
{
795+
attributesWriter.WriteLengthEncodedString("_os");
796+
attributesWriter.WriteLengthEncodedString(os);
797+
}
798+
attributesWriter.WriteLengthEncodedString("_os_details");
799+
attributesWriter.WriteLengthEncodedString(osDetails);
800+
attributesWriter.WriteLengthEncodedString("_platform");
801+
attributesWriter.WriteLengthEncodedString(platform);
802+
}
803+
catch (PlatformNotSupportedException)
804+
{
805+
}
806+
using (var process = Process.GetCurrentProcess())
807+
{
808+
attributesWriter.WriteLengthEncodedString("_pid");
809+
attributesWriter.WriteLengthEncodedString(process.Id.ToString(CultureInfo.InvariantCulture));
810+
}
811+
var connectionAttributes = attributesWriter.ToBytes();
812+
813+
var writer = new PayloadWriter();
814+
writer.WriteLengthEncodedInteger((ulong) connectionAttributes.Length);
815+
writer.Write(connectionAttributes);
816+
return writer.ToBytes();
817+
}
818+
771819
private enum State
772820
{
773821
// The session has been created; no connection has been made.
@@ -798,6 +846,8 @@ private enum State
798846
Failed,
799847
}
800848

849+
static byte[] s_connectionAttributes;
850+
801851
readonly object m_lock;
802852
readonly ArraySegmentHolder<byte> m_payloadCache;
803853
State m_state;
@@ -814,5 +864,6 @@ private enum State
814864
MySqlDataReader m_activeReader;
815865
bool m_useCompression;
816866
bool m_isSecureConnection;
867+
bool m_supportsConnectionAttributes;
817868
}
818869
}

src/MySqlConnector/Serialization/PayloadWriter.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public void WriteLengthEncodedInteger(ulong value)
4343
}
4444
}
4545

46+
public void WriteLengthEncodedString(string value)
47+
{
48+
var bytes = Encoding.UTF8.GetBytes(value);
49+
WriteLengthEncodedInteger((ulong) bytes.Length);
50+
m_writer.Write(bytes);
51+
}
52+
4653
public void WriteNullTerminatedString(string value)
4754
{
4855
var bytes = Encoding.UTF8.GetBytes(value);

src/MySqlConnector/Serialization/ProtocolCapabilities.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ internal enum ProtocolCapabilities
105105
/// <summary>
106106
/// Permits connection attributes in Protocol::HandshakeResponse41.
107107
/// </summary>
108-
ConnectAttributes = 0x10_0000,
108+
ConnectionAttributes = 0x10_0000,
109109

110110
/// <summary>
111111
/// Understands length-encoded integer for auth response data in Protocol::HandshakeResponse41.

0 commit comments

Comments
 (0)