/*
* 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.net.mysql;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import java.security.NoSuchAlgorithmException;
import org.opencloudb.config.Capabilities;
import org.opencloudb.mysql.CharsetUtil;
import org.opencloudb.mysql.SecurityUtil;
import org.opencloudb.net.ChannelDataHandler;
import org.opencloudb.net.ConnectionException;
/**
* MySQL 验证处理器
*
* @author mycat
*/
public class MySQLBackendAuthenticator implements ChannelDataHandler {
public static MySQLBackendAuthenticator INSTANCE=new MySQLBackendAuthenticator();
private static final long clientFlags = initClientFlags();
private static 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 byte[] passwd(String pass, HandshakePacket hs)
throws NoSuchAlgorithmException {
if (pass == null || pass.length() == 0) {
return null;
}
byte[] passwd = pass.getBytes();
int sl1 = hs.seed.length;
int sl2 = hs.restOfScrambleBuff.length;
byte[] seed = new byte[sl1 + sl2];
System.arraycopy(hs.seed, 0, seed, 0, sl1);
System.arraycopy(hs.restOfScrambleBuff, 0, seed, sl1, sl2);
return SecurityUtil.scramble411(passwd, seed);
}
private void authenticate(ChannelHandlerContext ctx, BackMysqlConnection con) {
AuthPacket packet = new AuthPacket();
packet.packetId = 1;
packet.clientFlags = clientFlags;
packet.maxPacketSize = 1024 * 1024 * 16;
packet.charsetIndex = 0;
packet.user = con.getUser();
try {
packet.password = passwd(con.getPassword(), con.getHandshake());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage());
}
packet.database = con.getSchema();
ByteBuf buf = ctx.alloc().ioBuffer(128, 1024);
packet.write(buf);
ctx.writeAndFlush(buf);
}
@Override
public void handle(ChannelHandlerContext ctx, byte[] data) {
BackMysqlConnection con =NettyUtil.getBackMysqlConnection(ctx) ;
switch (data[4]) {
case OkPacket.FIELD_COUNT:
HandshakePacket packet = con.getHandshake();
if (packet == null) {
processHandShakePacket(con, data);
// 发送认证数据包
authenticate(ctx, con);
break;
}
// 处理认证结果
con.setAuthenticated(true);
//create back mysql handle to process data flow
NettyUtil.setConnectionHandler(ctx, new BackMySQLHandler(con));
//notify connection required
con.getRespHandler().connectionAcquired(con);
break;
case ErrorPacket.FIELD_COUNT:
ErrorPacket err = new ErrorPacket();
err.read(data);
String errMsg = new String(err.message);
con.close(errMsg);
throw new ConnectionException(err.errno, errMsg);
case EOFPacket.FIELD_COUNT:
auth323(ctx,con,data[3]);
break;
default:
packet = con.getHandshake();
if (packet == null) {
processHandShakePacket(con, data);
// 发送认证数据包
authenticate(ctx, con);
break;
} else {
throw new RuntimeException("Unknown Packet!");
}
}
}
private void processHandShakePacket(BackMysqlConnection con, byte[] data) {
HandshakePacket packet;
// 设置握手数据包
packet = new HandshakePacket();
packet.read(data);
con.setHandshake(packet);
con.setThreadId(packet.threadId);
// 设置字符集编码
int charsetIndex = (packet.serverCharsetIndex & 0xff);
String charset = CharsetUtil.getCharset(charsetIndex);
boolean charsetValid = false;
if (charset != null) {
charsetValid = con.getConInfo().setCharset(charset);
}
if (!charsetValid) {
throw new RuntimeException("Unknown charsetIndex:" + charsetIndex);
}
}
private void auth323(ChannelHandlerContext ctx, BackMysqlConnection con,
byte packetId) {
// 发送323响应认证数据包
Reply323Packet r323 = new Reply323Packet();
r323.packetId = ++packetId;
String pass = con.getPassword();
if (pass != null && pass.length() > 0) {
byte[] seed = con.getHandshake().seed;
r323.seed = SecurityUtil.scramble323(pass, new String(seed))
.getBytes();
}
ByteBuf buffer = ctx.alloc().ioBuffer(128, 512);
r323.write(buffer);
ctx.writeAndFlush(buffer);
}
}