org.opencloudb.net.mysql.MySQLBackendAuthenticator.java Source code

Java tutorial

Introduction

Here is the source code for org.opencloudb.net.mysql.MySQLBackendAuthenticator.java

Source

/*
 * 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);
    }

}