| // SPDX-License-Identifier: LGPL-2.1-or-later |
| // Copyright (c) 2012-2014 Monty Program Ab |
| // Copyright (c) 2015-2021 MariaDB Corporation Ab |
| |
| package org.mariadb.jdbc.message.server; |
| |
| import org.mariadb.jdbc.client.ReadableByteBuf; |
| import org.mariadb.jdbc.client.ServerVersion; |
| import org.mariadb.jdbc.message.ServerMessage; |
| import org.mariadb.jdbc.message.server.util.ServerVersionUtility; |
| import org.mariadb.jdbc.util.constants.Capabilities; |
| |
| /** |
| * Server initial handshake parser. see |
| * https://mariadb.com/kb/en/connection/#initial-handshake-packet |
| */ |
| public final class InitialHandshakePacket implements ServerMessage { |
| |
| private static final String MARIADB_RPL_HACK_PREFIX = "5.5.5-"; |
| |
| private final long threadId; |
| private final byte[] seed; |
| private final long capabilities; |
| private final short defaultCollation; |
| private final short serverStatus; |
| private final String authenticationPluginType; |
| private final ServerVersion version; |
| |
| /** |
| * parse result |
| * |
| * @param serverVersion server version |
| * @param threadId server thread id |
| * @param seed seed |
| * @param capabilities server capabilities |
| * @param defaultCollation default server collation |
| * @param serverStatus server status flags |
| * @param mariaDBServer is a mariadb server |
| * @param authenticationPluginType default authentication plugin type |
| */ |
| private InitialHandshakePacket( |
| String serverVersion, |
| long threadId, |
| byte[] seed, |
| long capabilities, |
| short defaultCollation, |
| short serverStatus, |
| boolean mariaDBServer, |
| String authenticationPluginType) { |
| |
| this.threadId = threadId; |
| this.seed = seed; |
| this.capabilities = capabilities; |
| this.defaultCollation = defaultCollation; |
| this.serverStatus = serverStatus; |
| this.authenticationPluginType = authenticationPluginType; |
| this.version = new ServerVersionUtility(serverVersion, mariaDBServer); |
| } |
| |
| /** |
| * parsing packet |
| * |
| * @param reader packet reader |
| * @return Parsed packet |
| */ |
| public static InitialHandshakePacket decode(ReadableByteBuf reader) { |
| byte protocolVersion = reader.readByte(); |
| if (protocolVersion != 0x0a) { |
| throw new IllegalArgumentException( |
| String.format("Unexpected initial handshake protocol value [%s]", protocolVersion)); |
| } |
| |
| String serverVersion = reader.readStringNullEnd(); |
| long threadId = reader.readInt(); |
| final byte[] seed1 = new byte[8]; |
| reader.readBytes(seed1); |
| reader.skip(); |
| int serverCapabilities2FirstBytes = reader.readUnsignedShort(); |
| short defaultCollation = reader.readUnsignedByte(); |
| short serverStatus = reader.readShort(); |
| int serverCapabilities4FirstBytes = serverCapabilities2FirstBytes + (reader.readShort() << 16); |
| int saltLength = 0; |
| |
| if ((serverCapabilities4FirstBytes & Capabilities.PLUGIN_AUTH) != 0) { |
| saltLength = Math.max(12, reader.readByte() - 9); |
| } else { |
| reader.skip(); |
| } |
| reader.skip(6); |
| |
| // MariaDB additional capabilities. |
| // Filled only if MariaDB server 10.2+ |
| long mariaDbAdditionalCapacities = reader.readInt(); |
| byte[] seed; |
| if ((serverCapabilities4FirstBytes & Capabilities.SECURE_CONNECTION) != 0) { |
| final byte[] seed2; |
| if (saltLength > 0) { |
| seed2 = new byte[saltLength]; |
| reader.readBytes(seed2); |
| } else { |
| seed2 = reader.readBytesNullEnd(); |
| } |
| seed = new byte[seed1.length + seed2.length]; |
| System.arraycopy(seed1, 0, seed, 0, seed1.length); |
| System.arraycopy(seed2, 0, seed, seed1.length, seed2.length); |
| } else { |
| seed = seed1; |
| } |
| reader.skip(); |
| |
| /* |
| * check for MariaDB 10.x replication hack , remove fake prefix if needed |
| * (see comments about MARIADB_RPL_HACK_PREFIX) |
| */ |
| boolean serverMariaDb; |
| if (serverVersion.startsWith(MARIADB_RPL_HACK_PREFIX)) { |
| serverMariaDb = true; |
| serverVersion = serverVersion.substring(MARIADB_RPL_HACK_PREFIX.length()); |
| } else { |
| serverMariaDb = serverVersion.contains("MariaDB"); |
| } |
| |
| // since MariaDB 10.2 |
| long serverCapabilities; |
| if ((serverCapabilities4FirstBytes & Capabilities.CLIENT_MYSQL) == 0) { |
| serverCapabilities = |
| (serverCapabilities4FirstBytes & 0xffffffffL) + (mariaDbAdditionalCapacities << 32); |
| serverMariaDb = true; |
| } else { |
| serverCapabilities = serverCapabilities4FirstBytes & 0xffffffffL; |
| } |
| |
| String authenticationPluginType = null; |
| if ((serverCapabilities4FirstBytes & Capabilities.PLUGIN_AUTH) != 0) { |
| authenticationPluginType = reader.readStringNullEnd(); |
| } |
| |
| return new InitialHandshakePacket( |
| serverVersion, |
| threadId, |
| seed, |
| serverCapabilities, |
| defaultCollation, |
| serverStatus, |
| serverMariaDb, |
| authenticationPluginType); |
| } |
| |
| /** |
| * Server Version object |
| * |
| * @return server version |
| */ |
| public ServerVersion getVersion() { |
| return version; |
| } |
| |
| /** |
| * Server thread id |
| * |
| * @return thread id |
| */ |
| public long getThreadId() { |
| return threadId; |
| } |
| |
| /** |
| * Seed for authentication plugin encryption |
| * |
| * @return seed |
| */ |
| public byte[] getSeed() { |
| return seed; |
| } |
| |
| /** |
| * Server capabilities |
| * |
| * @return server capabilities |
| */ |
| public long getCapabilities() { |
| return capabilities; |
| } |
| |
| /** |
| * Server default collation |
| * |
| * @return server default collation |
| */ |
| public short getDefaultCollation() { |
| return defaultCollation; |
| } |
| |
| /** |
| * Server status flags |
| * |
| * @return server status |
| */ |
| public short getServerStatus() { |
| return serverStatus; |
| } |
| |
| /** |
| * return authentication plugin type |
| * |
| * @return authentication plugin type |
| */ |
| public String getAuthenticationPluginType() { |
| return authenticationPluginType; |
| } |
| } |