11using System ;
22using System . Net ;
33using System . Net . Sockets ;
4+ using System . Security . Cryptography ;
5+ using System . Text ;
46using System . Threading ;
57using System . Threading . Tasks ;
68using SuperSocket . Client ;
@@ -19,8 +21,14 @@ public class MySQLConnection : EasyClient<MySQLPacket>
1921
2022 private static readonly MySQLPacketEncoder PacketEncoder = new MySQLPacketEncoder ( ) ;
2123
24+ public bool IsAuthenticated { get ; private set ; }
25+
2226 public MySQLConnection ( string host , int port , string userName , string password )
23- : this ( new MySQLPacketFactory ( ) . RegisterPacketType < HandshakeResponsePacket > ( 0x00 ) )
27+ : this ( new MySQLPacketFactory ( )
28+ . RegisterPacketType < HandshakePacket > ( 0x0A )
29+ . RegisterPacketType < HandshakeResponsePacket > ( 0x00 )
30+ . RegisterPacketType < OKPacket > ( 0x00 )
31+ . RegisterPacketType < ErrorPacket > ( 0xFF ) )
2432 {
2533 _host = host ?? throw new ArgumentNullException ( nameof ( host ) ) ;
2634 _port = port > 0 ? port : DefaultPort ;
@@ -50,11 +58,127 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
5058
5159 await ConnectAsync ( endPoint , cancellationToken ) . ConfigureAwait ( false ) ;
5260
53- // Send initial handshake packet
54- var handshakePacket = new HandshakePacket ( ) ;
55- await SendAsync ( PacketEncoder , handshakePacket ) . ConfigureAwait ( false ) ;
61+ // Wait for server's handshake packet
62+ var packet = await ReceiveAsync ( ) . ConfigureAwait ( false ) ;
63+ if ( ! ( packet is HandshakePacket handshakePacket ) )
64+ throw new InvalidOperationException ( "Expected handshake packet from server." ) ;
65+
66+ // Prepare handshake response
67+ var handshakeResponse = new HandshakeResponsePacket
68+ {
69+ CapabilityFlags = ( uint ) ( ClientCapabilities . CLIENT_PROTOCOL_41 |
70+ ClientCapabilities . CLIENT_SECURE_CONNECTION |
71+ ClientCapabilities . CLIENT_PLUGIN_AUTH |
72+ ClientCapabilities . CLIENT_CONNECT_WITH_DB ) ,
73+ MaxPacketSize = 16777216 , // 16MB
74+ CharacterSet = 0x21 , // utf8_general_ci
75+ Username = _userName ,
76+ Database = string . Empty , // Can be set later if needed
77+ AuthPluginName = "mysql_native_password"
78+ } ;
79+
80+ // Generate authentication response
81+ handshakeResponse . AuthResponse = GenerateAuthResponse ( handshakePacket ) ;
82+
83+ // Send handshake response
84+ await SendAsync ( PacketEncoder , handshakeResponse ) . ConfigureAwait ( false ) ;
85+
86+ // Wait for authentication result (OK packet or Error packet)
87+ var authResult = await ReceiveAsync ( ) . ConfigureAwait ( false ) ;
88+
89+ switch ( authResult )
90+ {
91+ case OKPacket okPacket :
92+ // Authentication successful
93+ IsAuthenticated = true ;
94+ break ;
95+ case ErrorPacket errorPacket :
96+ // Authentication failed
97+ var errorMsg = ! string . IsNullOrEmpty ( errorPacket . ErrorMessage )
98+ ? errorPacket . ErrorMessage
99+ : "Authentication failed" ;
100+ throw new InvalidOperationException ( $ "MySQL authentication failed: { errorMsg } (Error { errorPacket . ErrorCode } )") ;
101+ default :
102+ throw new InvalidOperationException ( $ "Unexpected packet received during authentication: { authResult ? . GetType ( ) . Name ?? "null" } ") ;
103+ }
104+ }
105+
106+ private byte [ ] GenerateAuthResponse ( HandshakePacket handshakePacket )
107+ {
108+ if ( string . IsNullOrEmpty ( _password ) )
109+ return Array . Empty < byte > ( ) ;
110+
111+ // Combine auth plugin data parts to form the complete salt
112+ var salt = new byte [ 20 ] ;
113+ handshakePacket . AuthPluginDataPart1 ? . CopyTo ( salt , 0 ) ;
114+ if ( handshakePacket . AuthPluginDataPart2 != null )
115+ {
116+ var part2Length = Math . Min ( handshakePacket . AuthPluginDataPart2 . Length , 12 ) ;
117+ Array . Copy ( handshakePacket . AuthPluginDataPart2 , 0 , salt , 8 , part2Length ) ;
118+ }
119+
120+ // MySQL native password authentication algorithm:
121+ // SHA1(password) XOR SHA1(salt + SHA1(SHA1(password)))
122+ using ( var sha1 = SHA1 . Create ( ) )
123+ {
124+ var passwordBytes = Encoding . UTF8 . GetBytes ( _password ) ;
125+ var sha1Password = sha1 . ComputeHash ( passwordBytes ) ;
126+ var sha1Sha1Password = sha1 . ComputeHash ( sha1Password ) ;
127+
128+ var combined = new byte [ salt . Length + sha1Sha1Password . Length ] ;
129+ salt . CopyTo ( combined , 0 ) ;
130+ sha1Sha1Password . CopyTo ( combined , salt . Length ) ;
56131
57- // Handle authentication to be implemented here
132+ var sha1Combined = sha1 . ComputeHash ( combined ) ;
133+
134+ var result = new byte [ sha1Password . Length ] ;
135+ for ( int i = 0 ; i < sha1Password . Length ; i ++ )
136+ {
137+ result [ i ] = ( byte ) ( sha1Password [ i ] ^ sha1Combined [ i ] ) ;
138+ }
139+
140+ return result ;
141+ }
142+ }
143+
144+ /// <summary>
145+ /// Executes a simple query (placeholder implementation)
146+ /// </summary>
147+ /// <param name="query">The SQL query to execute</param>
148+ /// <param name="cancellationToken">Cancellation token</param>
149+ /// <returns>Task representing the async operation</returns>
150+ public async Task < string > ExecuteQueryAsync ( string query , CancellationToken cancellationToken = default )
151+ {
152+ if ( ! IsAuthenticated )
153+ throw new InvalidOperationException ( "Connection is not authenticated. Call ConnectAsync first." ) ;
154+
155+ if ( string . IsNullOrEmpty ( query ) )
156+ throw new ArgumentException ( "Query cannot be null or empty." , nameof ( query ) ) ;
157+
158+ // This is a placeholder implementation
159+ // In a complete implementation, you would:
160+ // 1. Create a COM_QUERY packet with the SQL query
161+ // 2. Send the packet to the server
162+ // 3. Receive and parse the result set
163+ // 4. Return the results
164+
165+ await Task . Delay ( 10 , cancellationToken ) ; // Simulate async operation
166+ return "Query execution not fully implemented yet" ;
167+ }
168+
169+ /// <summary>
170+ /// Disconnects from the MySQL server and resets authentication state
171+ /// </summary>
172+ public async Task DisconnectAsync ( )
173+ {
174+ try
175+ {
176+ await CloseAsync ( ) ;
177+ }
178+ finally
179+ {
180+ IsAuthenticated = false ;
181+ }
58182 }
59183 }
60184}
0 commit comments