/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package org.opencloudb.heartbeat; import java.nio.channels.AsynchronousSocketChannel; import java.security.NoSuchAlgorithmException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.log4j.Logger; import org.opencloudb.config.Capabilities; import org.opencloudb.config.ErrorCode; import org.opencloudb.mysql.SecurityUtil; import org.opencloudb.net.BackendConnection; import org.opencloudb.net.mysql.AuthPacket; import org.opencloudb.net.mysql.HandshakePacket; import org.opencloudb.net.mysql.HeartbeatPacket; import org.opencloudb.net.mysql.MySQLPacket; import org.opencloudb.util.TimeUtil; /** * @author mycat */ public class MyCATDetector extends BackendConnection { private static final Logger LOGGER = Logger.getLogger(MyCATDetector.class); private static final long CLIENT_FLAGS = initClientFlags(); private static final Logger HEARTBEAT = Logger.getLogger("heartbeat"); private MyCATHeartbeat heartbeat; private final long clientFlags; private HandshakePacket handshake; private int charsetIndex; private boolean isAuthenticated; private String user; private String password; private long heartbeatTimeout; private final AtomicBoolean isQuit; public MyCATDetector(AsynchronousSocketChannel channel) { super(channel); this.clientFlags = CLIENT_FLAGS; this.handler = new MyCATDetectorAuthenticator(this); this.isQuit = new AtomicBoolean(false); } public MyCATHeartbeat getHeartbeat() { return heartbeat; } public void setHeartbeat(MyCATHeartbeat heartbeat) { this.heartbeat = heartbeat; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public long getHeartbeatTimeout() { return heartbeatTimeout; } public void setHeartbeatTimeout(long heartbeatTimeout) { this.heartbeatTimeout = heartbeatTimeout; } public boolean isHeartbeatTimeout() { return TimeUtil.currentTimeMillis() > Math.max(lastWriteTime, lastReadTime) + heartbeatTimeout; } public long lastReadTime() { return lastReadTime; } public long lastWriteTime() { return lastWriteTime; } public boolean isAuthenticated() { return isAuthenticated; } public void setAuthenticated(boolean isAuthenticated) { this.isAuthenticated = isAuthenticated; } public HandshakePacket getHandshake() { return handshake; } public void setHandshake(HandshakePacket handshake) { this.handshake = handshake; } public void setCharsetIndex(int charsetIndex) { this.charsetIndex = charsetIndex; } public void authenticate() { AuthPacket packet = new AuthPacket(); packet.packetId = 1; packet.clientFlags = clientFlags; packet.maxPacketSize = maxPacketSize; packet.charsetIndex = charsetIndex; packet.user = user; try { packet.password = getPass(password, handshake); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e.getMessage()); } packet.write(this); } public void heartbeat() { if (isAuthenticated) { HeartbeatPacket hp = new HeartbeatPacket(); hp.packetId = 0; hp.command = MySQLPacket.COM_HEARTBEAT; hp.id = heartbeat.detectCount.incrementAndGet(); hp.write(this); if (HEARTBEAT.isInfoEnabled()) { HEARTBEAT.info(heartbeat.requestMessage(MyCATHeartbeat.SEND, String.valueOf(hp.id).getBytes())); } } else { authenticate(); } } public void quit() { if (isQuit.compareAndSet(false, true)) { close("heartbeat quit"); } } public boolean isQuit() { return isQuit.get(); } @Override public void error(int errCode, Throwable t) { LOGGER.warn(toString(), t); switch (errCode) { case ErrorCode.ERR_HANDLE_DATA: heartbeat.setResult(MyCATHeartbeat.ERROR_STATUS, this, false, null); break; default: heartbeat.setResult(MyCATHeartbeat.ERROR_STATUS, this, true, null); } } @Override protected void idleCheck() { if (isIdleTimeout()) { LOGGER.warn(toString() + " heartbeat idle timeout"); quit(); } } private static final long initClientFlags() { int flag = 0; flag |= Capabilities.CLIENT_LONG_PASSWORD; flag |= Capabilities.CLIENT_FOUND_ROWS; flag |= Capabilities.CLIENT_LONG_FLAG; // flag |= Capabilities.CLIENT_CONNECT_WITH_DB; // flag |= Capabilities.CLIENT_NO_SCHEMA; // flag |= Capabilities.CLIENT_COMPRESS; flag |= Capabilities.CLIENT_ODBC; // flag |= Capabilities.CLIENT_LOCAL_FILES; flag |= Capabilities.CLIENT_IGNORE_SPACE; flag |= Capabilities.CLIENT_PROTOCOL_41; flag |= Capabilities.CLIENT_INTERACTIVE; // flag |= Capabilities.CLIENT_SSL; flag |= Capabilities.CLIENT_IGNORE_SIGPIPE; 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; return flag; } private static final byte[] getPass(String src, HandshakePacket hsp) throws NoSuchAlgorithmException { if (src == null || src.length() == 0) { return null; } byte[] passwd = src.getBytes(); int sl1 = hsp.seed.length; int sl2 = hsp.restOfScrambleBuff.length; byte[] seed = new byte[sl1 + sl2]; System.arraycopy(hsp.seed, 0, seed, 0, sl1); System.arraycopy(hsp.restOfScrambleBuff, 0, seed, sl1, sl2); return SecurityUtil.scramble411(passwd, seed); } }