Skip to content

Commit 7a93eec

Browse files
committed
Fixes for Azure-MySQL connections
Azure has a couple of differences from stock mysql. First, it always sends a "switch auth" packet back from the first auth request. If you google "azure mysql handshake" you'll find a bunch of other libraries that had to deal with this. Second, it's rather persnickitty with the response to this; if we send the response a byte or so at a time azure just closes our connection.
1 parent 777448d commit 7a93eec

File tree

4 files changed

+84
-2
lines changed

4 files changed

+84
-2
lines changed

src/main/java/com/github/shyiko/mysql/binlog/BinaryLogClient.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.github.shyiko.mysql.binlog.network.protocol.PacketChannel;
4747
import com.github.shyiko.mysql.binlog.network.protocol.ResultSetRowPacket;
4848
import com.github.shyiko.mysql.binlog.network.protocol.command.AuthenticateCommand;
49+
import com.github.shyiko.mysql.binlog.network.protocol.command.AuthenticateNativePasswordCommand;
4950
import com.github.shyiko.mysql.binlog.network.protocol.command.Command;
5051
import com.github.shyiko.mysql.binlog.network.protocol.command.DumpBinaryLogCommand;
5152
import com.github.shyiko.mysql.binlog.network.protocol.command.DumpBinaryLogGtidCommand;
@@ -722,8 +723,40 @@ private void authenticate(GreetingPacket greetingPacket) throws IOException {
722723
ErrorPacket errorPacket = new ErrorPacket(bytes);
723724
throw new AuthenticationException(errorPacket.getErrorMessage(), errorPacket.getErrorCode(),
724725
errorPacket.getSqlState());
726+
} else if (authenticationResult[0] == (byte) 0xFE) {
727+
switchAuthentication(authenticationResult);
728+
} else {
729+
throw new AuthenticationException("Unexpected authentication result (" + authenticationResult[0] + ")");
730+
}
731+
}
732+
}
733+
734+
private void switchAuthentication(byte[] authenticationResult) throws IOException {
735+
/*
736+
Azure-MySQL likes to tell us to switch authentication methods, even though
737+
we haven't advertised that we support any. It uses this for some-odd
738+
reason to send the real password scramble.
739+
*/
740+
ByteArrayInputStream buffer = new ByteArrayInputStream(authenticationResult);
741+
buffer.read(1);
742+
743+
String authName = buffer.readZeroTerminatedString();
744+
if ("mysql_native_password".equals(authName)) {
745+
String scramble = buffer.readZeroTerminatedString();
746+
747+
Command switchCommand = new AuthenticateNativePasswordCommand(scramble, password);
748+
channel.writeBuffered(switchCommand, 3);
749+
byte[] authResult = channel.read();
750+
751+
if (authResult[0] != (byte) 0x00) {
752+
byte[] bytes = Arrays.copyOfRange(authResult, 1, authResult.length);
753+
ErrorPacket errorPacket = new ErrorPacket(bytes);
754+
throw new AuthenticationException(errorPacket.getErrorMessage(), errorPacket.getErrorCode(),
755+
errorPacket.getSqlState());
725756
}
726-
throw new AuthenticationException("Unexpected authentication result (" + authenticationResult[0] + ")");
757+
return;
758+
} else {
759+
throw new AuthenticationException("Unsupported authentication type: " + authName);
727760
}
728761
}
729762

src/main/java/com/github/shyiko/mysql/binlog/network/protocol/PacketChannel.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,21 @@ public void write(Command command, int packetNumber) throws IOException {
7171
outputStream.flush();
7272
}
7373

74+
/*
75+
Azure's MySQL has bizarre network properties that force us to write an
76+
auth-response challenge in one shot, lest their hair catch on fire and
77+
forcibly disconnect us.
78+
*/
79+
public void writeBuffered(Command command, int packetNumber) throws IOException {
80+
byte[] body = command.toByteArray();
81+
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
82+
buffer.writeInteger(body.length, 3); // packet length
83+
buffer.writeInteger(packetNumber, 1);
84+
buffer.write(body, 0, body.length);
85+
buffer.flush();
86+
socket.getOutputStream().write(buffer.toByteArray());
87+
}
88+
7489
public void write(Command command) throws IOException {
7590
write(command, 0);
7691
}

src/main/java/com/github/shyiko/mysql/binlog/network/protocol/command/AuthenticateCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public byte[] toByteArray() throws IOException {
7979
/**
8080
* see mysql/sql/password.c scramble(...)
8181
*/
82-
private static byte[] passwordCompatibleWithMySQL411(String password, String salt) {
82+
public static byte[] passwordCompatibleWithMySQL411(String password, String salt) {
8383
MessageDigest sha;
8484
try {
8585
sha = MessageDigest.getInstance("SHA-1");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2013 Stanley Shyiko
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.shyiko.mysql.binlog.network.protocol.command;
17+
18+
import java.io.IOException;
19+
20+
/**
21+
* @author <a href="mailto:[email protected]">Ben Osheroff</a>
22+
*/
23+
public class AuthenticateNativePasswordCommand implements Command {
24+
private final String scramble, password;
25+
26+
public AuthenticateNativePasswordCommand(String scramble, String password) {
27+
this.scramble = scramble;
28+
this.password = password;
29+
}
30+
@Override
31+
public byte[] toByteArray() throws IOException {
32+
return AuthenticateCommand.passwordCompatibleWithMySQL411(password, scramble);
33+
}
34+
}

0 commit comments

Comments
 (0)