6
6
7
7
use MySQLReplication \BinaryDataReader \BinaryDataReader ;
8
8
use MySQLReplication \Config \Config ;
9
- use MySQLReplication \Exception \MySQLReplicationException ;
10
9
use MySQLReplication \Gtid \GtidCollection ;
11
10
use MySQLReplication \Repository \RepositoryInterface ;
12
11
use MySQLReplication \Socket \SocketInterface ;
@@ -17,6 +16,7 @@ class BinLogSocketConnect
17
16
private const COM_BINLOG_DUMP = 0x12 ;
18
17
private const COM_REGISTER_SLAVE = 0x15 ;
19
18
private const COM_BINLOG_DUMP_GTID = 0x1e ;
19
+ private const AUTH_SWITCH_PACKET = 254 ;
20
20
/**
21
21
* https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html 00 FE
22
22
*/
@@ -47,7 +47,8 @@ public function __construct(
47
47
'Server version name: ' . $ this ->binLogServerInfo ->versionName . ', revision: ' . $ this ->binLogServerInfo ->versionRevision
48
48
);
49
49
50
- $ this ->authenticate ();
50
+
51
+ $ this ->authenticate ($ this ->binLogServerInfo ->authPlugin );
51
52
$ this ->getBinlogStream ();
52
53
}
53
54
@@ -110,56 +111,60 @@ private function isWriteSuccessful(string $data): void
110
111
}
111
112
}
112
113
113
- private function authenticate (): void
114
+ private function authenticate (BinLogAuthPluginMode $ authPlugin ): void
114
115
{
115
- if ($ this ->binLogServerInfo ->authPlugin === null ) {
116
- throw new MySQLReplicationException (
117
- MySQLReplicationException::BINLOG_AUTH_NOT_SUPPORTED ,
118
- MySQLReplicationException::BINLOG_AUTH_NOT_SUPPORTED_CODE
119
- );
120
- }
121
-
122
116
$ this ->logger ->info (
123
- 'Trying to authenticate user: ' . $ this ->config ->user . ' using ' . $ this -> binLogServerInfo -> authPlugin ->value . ' plugin '
117
+ 'Trying to authenticate user: ' . $ this ->config ->user . ' using ' . $ authPlugin ->value . ' default plugin '
124
118
);
125
119
126
120
$ data = pack ('L ' , self ::getCapabilities ());
127
121
$ data .= pack ('L ' , $ this ->binaryDataMaxLength );
128
122
$ data .= chr (33 );
129
123
$ data .= str_repeat (chr (0 ), 23 );
130
124
$ data .= $ this ->config ->user . chr (0 );
131
-
132
- $ auth = '' ;
133
- if ($ this ->binLogServerInfo ->authPlugin === BinLogAuthPluginMode::MysqlNativePassword) {
134
- $ auth = $ this ->authenticateMysqlNativePasswordPlugin ();
135
- } elseif ($ this ->binLogServerInfo ->authPlugin === BinLogAuthPluginMode::CachingSha2Password) {
136
- $ auth = $ this ->authenticateCachingSha2PasswordPlugin ();
137
- }
138
-
125
+ $ auth = $ this ->getAuthData ($ authPlugin , $ this ->binLogServerInfo ->salt );
139
126
$ data .= chr (strlen ($ auth )) . $ auth ;
140
- $ data .= $ this -> binLogServerInfo -> authPlugin ->value . chr (0 );
127
+ $ data .= $ authPlugin ->value . chr (0 );
141
128
$ str = pack ('L ' , strlen ($ data ));
142
129
$ s = $ str [0 ] . $ str [1 ] . $ str [2 ];
143
130
$ data = $ s . chr (1 ) . $ data ;
144
131
145
132
$ this ->socket ->writeToSocket ($ data );
146
- $ this ->getResponse ();
133
+ $ response = $ this ->getResponse ();
134
+
135
+ // Check for AUTH_SWITCH_PACKET
136
+ if (isset ($ response [0 ]) && ord ($ response [0 ]) === self ::AUTH_SWITCH_PACKET ) {
137
+ $ this ->switchAuth ($ response );
138
+ }
147
139
148
140
$ this ->logger ->info ('User authenticated ' );
149
141
}
150
142
151
- private function authenticateCachingSha2PasswordPlugin (): string
143
+ private function getAuthData (?BinLogAuthPluginMode $ authPlugin , string $ salt ): string
144
+ {
145
+ if ($ authPlugin === BinLogAuthPluginMode::MysqlNativePassword) {
146
+ return $ this ->authenticateMysqlNativePasswordPlugin ($ salt );
147
+ }
148
+
149
+ if ($ authPlugin === BinLogAuthPluginMode::CachingSha2Password) {
150
+ return $ this ->authenticateCachingSha2PasswordPlugin ($ salt );
151
+ }
152
+
153
+ return '' ;
154
+ }
155
+
156
+ private function authenticateCachingSha2PasswordPlugin (string $ salt ): string
152
157
{
153
158
$ hash1 = hash ('sha256 ' , $ this ->config ->password , true );
154
159
$ hash2 = hash ('sha256 ' , $ hash1 , true );
155
- $ hash3 = hash ('sha256 ' , $ hash2 . $ this -> binLogServerInfo -> salt , true );
160
+ $ hash3 = hash ('sha256 ' , $ hash2 . $ salt , true );
156
161
return $ hash1 ^ $ hash3 ;
157
162
}
158
163
159
- private function authenticateMysqlNativePasswordPlugin (): string
164
+ private function authenticateMysqlNativePasswordPlugin (string $ salt ): string
160
165
{
161
166
$ hash1 = sha1 ($ this ->config ->password , true );
162
- $ hash2 = sha1 ($ this -> binLogServerInfo -> salt . sha1 (sha1 ($ this ->config ->password , true ), true ), true );
167
+ $ hash2 = sha1 ($ salt . sha1 (sha1 ($ this ->config ->password , true ), true ), true );
163
168
return $ hash1 ^ $ hash2 ;
164
169
}
165
170
@@ -316,4 +321,17 @@ private function setBinLogDump(): void
316
321
317
322
$ this ->logger ->info ('Set binlog to start from: ' . $ binFileName . ': ' . $ binFilePos );
318
323
}
324
+
325
+ private function switchAuth (string $ response ): void
326
+ {
327
+ // skip AUTH_SWITCH_PACKET byte
328
+ $ offset = 1 ;
329
+ $ authPluginSwitched = BinLogAuthPluginMode::make (BinaryDataReader::decodeNullLength ($ response , $ offset ));
330
+ $ salt = BinaryDataReader::decodeNullLength ($ response , $ offset );
331
+ $ auth = $ this ->getAuthData ($ authPluginSwitched , $ salt );
332
+
333
+ $ this ->logger ->info ('Auth switch packet received, switching to ' . $ authPluginSwitched ->value );
334
+
335
+ $ this ->socket ->writeToSocket (pack ('L ' , (strlen ($ auth )) | (3 << 24 )) . $ auth );
336
+ }
319
337
}
0 commit comments