diff --git a/source/src/main/java/io/mycat/mycat2/tasks/BackendConCreateTask.java b/source/src/main/java/io/mycat/mycat2/tasks/BackendConCreateTask.java index 3f9edc3..a16edf4 100644 --- a/source/src/main/java/io/mycat/mycat2/tasks/BackendConCreateTask.java +++ b/source/src/main/java/io/mycat/mycat2/tasks/BackendConCreateTask.java @@ -87,9 +87,9 @@ private static long initClientFlags() { flag |= Capabilities.CLIENT_TRANSACTIONS; // flag |= Capabilities.CLIENT_RESERVED; flag |= Capabilities.CLIENT_SECURE_CONNECTION; - // client extension - flag |= Capabilities.CLIENT_MULTI_STATEMENTS; - flag |= Capabilities.CLIENT_MULTI_RESULTS; +// // client extension +// flag |= Capabilities.CLIENT_MULTI_STATEMENTS; +// flag |= Capabilities.CLIENT_MULTI_RESULTS; return flag; } diff --git a/source/src/main/java/io/mycat/mysql/Capabilities.java b/source/src/main/java/io/mycat/mysql/Capabilities.java index 6bfe7bb..509d2e2 100644 --- a/source/src/main/java/io/mycat/mysql/Capabilities.java +++ b/source/src/main/java/io/mycat/mysql/Capabilities.java @@ -26,7 +26,7 @@ /** * 处理能力标识定义 * - * @author mycat + * @author mycat cjw */ public interface Capabilities { @@ -102,11 +102,99 @@ public interface Capabilities { // Enable/disable multi-stmt support // 通知服务器客户端可以发送多条语句(由分号分隔)。如果该标志为没有被设置,多条语句执行。 - public static final int CLIENT_MULTI_STATEMENTS = 65536; + public static final int CLIENT_MULTI_STATEMENTS = 1; // Enable/disable multi-results // 通知服务器客户端可以处理由多语句或者存储过程执行生成的多结果集。 // 当打开CLIENT_MULTI_STATEMENTS时,这个标志自动的被打开。 - public static final int CLIENT_MULTI_RESULTS = 131072; + public static final int CLIENT_MULTI_RESULTS = 1<<1; + /** + ServerCan send multiple resultsets for COM_STMT_EXECUTE. + Client + Can handle multiple resultsets for COM_STMT_EXECUTE. + Value + 0x00040000 + Requires + CLIENT_PROTOCOL_41 + */ + public static final int CLIENT_PS_MULTI_RESULTS = 1<<2; + /** + Server + Sends extra data in Initial Handshake Packet and supports the pluggable authentication protocol. + + Client + Supports authentication plugins. + + Requires + CLIENT_PROTOCOL_41 + */ + public static final int CLIENT_PLUGIN_AUTH = 1<<3; + + /** + Value + 0x00100000 + + Server + Permits connection attributes in Protocol::HandshakeResponse41. + + Client + Sends connection attributes in Protocol::HandshakeResponse41. + */ + + public static final int CLIENT_CONNECT_ATTRS = 1<<4; + + /** + Value + 0x00200000 + + Server + Understands length-encoded integer for auth response data in Protocol::HandshakeResponse41. + + Client + Length of auth response data in Protocol::HandshakeResponse41 is a length-encoded integer. + The flag was introduced in 5.6.6, but had the wrong value. + */ + public static final int CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 1<<5; + + /** + *Value + * 0x00400000 + * + * Server + * Announces support for expired password extension. + * + * Client + * Can handle expired passwords. + */ + public static final int CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = 1<<6; + + /** + * Value + * 0x00800000 + * + * Server + * Can set SERVER_SESSION_STATE_CHANGED in the Status Flags and send session-state change data after a OK packet. + * + * Client + * Expects the server to send sesson-state changes after a OK packet. + */ + public static final int CLIENT_SESSION_TRACK = 1<<7; + + /** + Value + 0x01000000 + + Server + Can send OK after a Text Resultset. + + Client + Expects an OK (instead of EOF) after the resultset rows of a Text Resultset. + + Background + To support CLIENT_SESSION_TRACK, additional information must be sent after all successful commands. Although the OK packet is extensible, the EOF packet is not due to the overlap of its bytes with the content of the Text Resultset Row. + + Therefore, the EOF packet in the Text Resultset is replaced with an OK packet. EOF packets are deprecated as of MySQL 5.7.5. + */ + public static final int CLIENT_DEPRECATE_EOF = 1<<8; } \ No newline at end of file diff --git a/source/src/main/java/io/mycat/mysql/packet/NewHandshakePacket.java b/source/src/main/java/io/mycat/mysql/packet/NewHandshakePacket.java new file mode 100644 index 0000000..10a60e7 --- /dev/null +++ b/source/src/main/java/io/mycat/mysql/packet/NewHandshakePacket.java @@ -0,0 +1,281 @@ +package io.mycat.mysql.packet; + +import io.mycat.mysql.Capabilities; +import io.mycat.proxy.ProxyBuffer; + +import static io.mycat.mysql.Capabilities.CLIENT_PLUGIN_AUTH; + +public class NewHandshakePacket { + public int protocolVersion; + public String serverVersion; + public long connectionId; + public String authPluginDataPartOne;//salt auth plugin data part 1 + public PartOneCapabilityFlags partOnecapabilityFlags; + public boolean hasPartTwo = false; + public int characterSet; + public int statusFlags; + public PartTwoCapabilityFlags partTwoCapabilityFlags; + public int authPluginDataLen; + public String reserved; + public String authPluginDataPartTwo; + public String authPluginName; + + public void read(ProxyBuffer buffer) { + protocolVersion = buffer.readByte(); + if (protocolVersion != 0x0a) { + throw new RuntimeException("unsupport HandshakeV9"); + } + serverVersion = buffer.readNULString(); + connectionId = buffer.readFixInt(4); + authPluginDataPartOne = buffer.readFixString(8); + buffer.skip(1); + partOnecapabilityFlags = new PartOneCapabilityFlags((int) buffer.readFixInt(2)); + if (!buffer.readFinished()) { + hasPartTwo = true; + characterSet = Byte.toUnsignedInt(buffer.readByte()); + statusFlags = (int) buffer.readFixInt(2); + partTwoCapabilityFlags = new PartTwoCapabilityFlags(buffer.readFixInt(2)); + if (partTwoCapabilityFlags.isPluginAuth()) { + byte b = buffer.readByte(); + authPluginDataLen = Byte.toUnsignedInt(b); + } else { + buffer.skip(1); + } + reserved = buffer.readFixString(10); + if (partOnecapabilityFlags.isCanDo41Anthentication()) { + authPluginDataPartTwo = buffer.readFixString(Math.min(13, authPluginDataLen)); + } + if (partTwoCapabilityFlags.isPluginAuth()) { + authPluginName = buffer.readNULString(); + } + } + } + + public void write(ProxyBuffer buffer) { + + } + + /** + * cjw + * 294712221@qq.com + */ + public static class PartOneCapabilityFlags { + public int capabilities = 0; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("PartOneCapabilityFlags{"); + sb.append("capabilities=").append(capabilities); + sb.append('}'); + return sb.toString(); + } + + public PartOneCapabilityFlags(int capabilities) { + this.capabilities = capabilities; + } + + public PartOneCapabilityFlags() { + } + + public boolean isLongPassword() { + return (capabilities & Capabilities.CLIENT_LONG_PASSWORD) != 0; + } + + public void setLongPassword() { + capabilities |= Capabilities.CLIENT_LONG_PASSWORD; + } + + public boolean isFoundRows() { + return (capabilities & Capabilities.CLIENT_FOUND_ROWS) != 0; + } + + public void setFoundRows() { + capabilities |= Capabilities.CLIENT_FOUND_ROWS; + } + + public boolean isLongColumnWithFLags() { + return (capabilities & Capabilities.CLIENT_LONG_FLAG) != 0; + } + + public void setLongColumnWithFLags() { + capabilities |= Capabilities.CLIENT_LONG_FLAG; + } + + public boolean isDoNotAllowDatabaseDotTableDotColumn() { + return (capabilities & Capabilities.CLIENT_CONNECT_WITH_DB) != 0; + } + + public void setDoNotAllowDatabaseDotTableDotColumn() { + capabilities |= Capabilities.CLIENT_CONNECT_WITH_DB; + } + + public boolean isCanUseCompressionProtocol() { + return (capabilities & Capabilities.CLIENT_COMPRESS) != 0; + } + + public void setCanUseCompressionProtocol() { + capabilities |= Capabilities.CLIENT_COMPRESS; + } + + public boolean isODBCClient() { + return (capabilities & Capabilities.CLIENT_ODBC) != 0; + } + + public void setODBCClient() { + capabilities |= Capabilities.CLIENT_ODBC; + } + + public boolean isCanUseLoadDataLocal() { + return (capabilities & Capabilities.CLIENT_LOCAL_FILES) != 0; + } + + public void setCanUseLoadDataLocal() { + capabilities |= Capabilities.CLIENT_LOCAL_FILES; + } + + public boolean isIgnoreSpacesBeforeLeftBracket() { + return (capabilities & Capabilities.CLIENT_IGNORE_SPACE) != 0; + } + + public void setIgnoreSpacesBeforeLeftBracket() { + capabilities |= Capabilities.CLIENT_IGNORE_SPACE; + } + + public boolean isSwitchToSSLAfterHandshake() { + return (capabilities & Capabilities.CLIENT_SSL) != 0; + } + + public void setSwitchToSSLAfterHandshake() { + capabilities |= Capabilities.CLIENT_SSL; + } + + public boolean isIgnoreSigpipes() { + return (capabilities & Capabilities.CLIENT_IGNORE_SIGPIPE) != 0; + } + + public void setIgnoreSigpipes() { + capabilities |= Capabilities.CLIENT_IGNORE_SIGPIPE; + } + + public boolean isKnowsAboutTransactions() { + return (capabilities & Capabilities.CLIENT_TRANSACTIONS) != 0; + } + + public void setKnowsAboutTransactions() { + capabilities |= Capabilities.CLIENT_TRANSACTIONS; + } + + public boolean isSpeak41Protocol() { + return (capabilities & Capabilities.CLIENT_RESERVED) != 0; + } + + public void setSpeak41Protocol() { + capabilities |= Capabilities.CLIENT_RESERVED; + } + + public boolean isCanDo41Anthentication() { + return (capabilities & Capabilities.CLIENT_SECURE_CONNECTION) != 0; + } + + public void setCanDo41Anthentication() { + capabilities |= Capabilities.CLIENT_SECURE_CONNECTION; + } + } + + /** + * cjw + * 294712221@qq.com + */ + public static class PartTwoCapabilityFlags { + public long capabilities = 0; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("PartTwoCapabilityFlags{"); + sb.append("capabilities=").append(capabilities); + sb.append('}'); + return sb.toString(); + } + + public PartTwoCapabilityFlags(long capabilities) { + this.capabilities = capabilities; + } + + public PartTwoCapabilityFlags() { + } + + public boolean isMultipleStatements() { + return (capabilities & Capabilities.CLIENT_MULTI_STATEMENTS) != 0; + } + + public void setMultipleStatements() { + capabilities |= Capabilities.CLIENT_MULTI_STATEMENTS; + } + + public boolean isMultipleResults() { + return (capabilities & Capabilities.CLIENT_MULTI_RESULTS) != 0; + } + + public void setMultipleResults() { + capabilities |= Capabilities.CLIENT_MULTI_RESULTS; + } + + public boolean isPSMultipleResults() { + return (capabilities & Capabilities.CLIENT_PS_MULTI_RESULTS) != 0; + } + + public void setPSMultipleResults() { + capabilities |= Capabilities.CLIENT_PS_MULTI_RESULTS; + } + + public boolean isPluginAuth() { + return (capabilities & CLIENT_PLUGIN_AUTH) != 0; + } + + public void setPluginAuth() { + capabilities |= CLIENT_PLUGIN_AUTH; + } + + public boolean isConnectAttrs() { + return (capabilities & CLIENT_PLUGIN_AUTH) != 0; + } + + public void setConnectAttrs() { + capabilities |= CLIENT_PLUGIN_AUTH; + } + + public boolean isPluginAuthLenencClientData() { + return (capabilities & Capabilities.CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) != 0; + } + + public void setPluginAuthLenencClientData() { + capabilities |= Capabilities.CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA; + } + + public boolean isClientCanHandleExpiredPasswords() { + return (capabilities & Capabilities.CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS) != 0; + } + + public void setClientCanHandleExpiredPasswords() { + capabilities |= Capabilities.CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS; + } + + public boolean isSessionVariableTracking() { + return (capabilities & Capabilities.CLIENT_SESSION_TRACK) != 0; + } + + public void setSessionVariableTracking() { + capabilities |= Capabilities.CLIENT_SESSION_TRACK; + } + + public boolean isDeprecateEOF() { + return (capabilities & Capabilities.CLIENT_DEPRECATE_EOF) != 0; + } + + public void setDeprecateEOF() { + capabilities |= Capabilities.CLIENT_DEPRECATE_EOF; + } + } + + +} diff --git a/source/src/main/java/io/mycat/proxy/ProxyBuffer.java b/source/src/main/java/io/mycat/proxy/ProxyBuffer.java index 0fbf35a..ad341fc 100644 --- a/source/src/main/java/io/mycat/proxy/ProxyBuffer.java +++ b/source/src/main/java/io/mycat/proxy/ProxyBuffer.java @@ -121,7 +121,12 @@ public boolean writeFinished() { //buffer.position(proxyBuffer.readMark); return readIndex == readMark; } + public boolean readFinished() { + //buffer.limit(proxyBuffer.readIndex); + //buffer.position(proxyBuffer.readMark); + return readIndex == writeIndex; + } /** * 需要谨慎使用,调用者需要清除当前Buffer所处的状态!! * diff --git a/source/src/test/java/io/mycat/mycat2/packet/HandshakePacketTest.java b/source/src/test/java/io/mycat/mycat2/packet/HandshakePacketTest.java new file mode 100644 index 0000000..f1a998f --- /dev/null +++ b/source/src/test/java/io/mycat/mycat2/packet/HandshakePacketTest.java @@ -0,0 +1,60 @@ +package io.mycat.mycat2.packet; + +import io.mycat.mysql.packet.NewHandshakePacket; +import io.mycat.proxy.ProxyBuffer; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.ByteBuffer; + +/** + * cjw 294712221@qq.com + */ +public class HandshakePacketTest { + + @Test + public void testReadHandshakePacket() { + NewHandshakePacket handshakePacket = new NewHandshakePacket(); + handshakePacket.read(ofBuffer(pkt17)); + Assert.assertEquals(0x0a,handshakePacket.protocolVersion); + Assert.assertEquals("8.0.12",handshakePacket.serverVersion); + Assert.assertEquals(13,handshakePacket.connectionId); + Assert.assertEquals(new String(of(0x15, 0x5f, 0x3e, 0x56, 0x1d, 0x15, 0x2b, 0x79)),handshakePacket.authPluginDataPartOne); + Assert.assertEquals(0xffff,handshakePacket.partOnecapabilityFlags.capabilities); + Assert.assertEquals(255,handshakePacket.characterSet); + Assert.assertEquals(0x0002,handshakePacket.statusFlags); + Assert.assertEquals(0xc3ff,handshakePacket.partTwoCapabilityFlags.capabilities); + Assert.assertEquals(21,handshakePacket.authPluginDataLen); + Assert.assertEquals(new String(new char[]{0,0,0,0,0,0,0,0,0,0}),handshakePacket.reserved); + Assert.assertEquals("^@y\b\u000F\u001CH~cLI}\u0000",handshakePacket.authPluginDataPartTwo); + Assert.assertEquals("mysql_native_password",handshakePacket.authPluginName); + } + + static int[] pkt17 = { + 0x0a, 0x38, 0x2e, 0x30, /* J....8.0 */ + 0x2e, 0x31, 0x32, 0x00, 0x0d, 0x00, 0x00, 0x00, /* .12..... */ + 0x15, 0x5f, 0x3e, 0x56, 0x1d, 0x15, 0x2b, 0x79, /* ._>V..+y */ + 0x00, 0xff, 0xff, 0xff, 0x02, 0x00, 0xff, 0xc3, /* ........ */ + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */ + 0x00, 0x00, 0x00, 0x5e, 0x40, 0x79, 0x08, 0x0f, /* ...^@y.. */ + 0x1c, 0x48, 0x7e, 0x63, 0x4c, 0x49, 0x7d, 0x00, /* .H~cLI}. */ + 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x5f, 0x6e, 0x61, /* mysql_na */ + 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x73, /* tive_pas */ + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00 /* sword. */ + }; + + public static byte[] of(int... i) { + byte[] bytes = new byte[i.length]; + int j = 0; + for (int i1 : i) { + bytes[j] = (byte) i1; + j++; + } + return bytes; + } + public static ProxyBuffer ofBuffer(int... i) { + ProxyBuffer proxyBuffer = new ProxyBuffer(ByteBuffer.wrap(of(i))); + proxyBuffer.writeIndex = i.length; + return proxyBuffer; + } +}