/**
* <pre>
* This program is free software; you can redistribute it and/or modify it under the terms of
* the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program 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 AFFERO GENERAL PUBLIC LICENSE for more details.
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* </pre>
*/
package com.meidusa.amoeba.mysql.net.packet;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import com.meidusa.amoeba.mysql.util.Security;
import com.meidusa.amoeba.mysql.util.Util;
import com.meidusa.amoeba.net.packet.AbstractPacketBuffer;
/**
* From client to server during initial handshake.
*
* <pre>
*
* <b>VERSION 4.0</b>
* Bytes Name
* ----- ----
* 2 client_flags
* 3 max_packet_size
* n (Null-Terminated String) user
* 8 scramble_buff
* 1 (filler) always 0x00
*
* <b>VERSION 4.1</b>
* Bytes Name
* ----- ----
* 4 client_flags
* 4 max_packet_size
* 1 charset_number
* 23 (filler) always 0x00...
* n (Null-Terminated String) user
* n (Length Coded Binary) scramble_buff (1 + x bytes)
* 1 (filler) always 0x00
* n (Null-Terminated String) databasename
*
* client_flags: CLIENT_xxx options. The list of possible flag
* values is in the description of the Handshake
* Initialisation Packet, for server_capabilities.
* For some of the bits, the server passed "what
* it's capable of". The client leaves some of the
* bits on, adds others, and passes back to the server.
* One important flag is: whether compression is desired.
*
* max_packet_size: the maximum number of bytes in a packet for the client
*
* charset_number: in the same domain as the server_language field that
* the server passes in the Handshake Initialization packet.
*
* user: identification
*
* scramble_buff: the password, after encrypting using the scramble_buff
* contents passed by the server (see "Password functions"
* section elsewhere in this document)
* if length is zero, no password was given
*
* databasename: name of schema to use initially
*
* </pre>
*
* �����ݰ�ֻ֧��mysql 4.1�汾�Ժ�,mysqlЭ��汾10�������Ƿǰ�ȫ����
*
* @author <a href=mailto:piratebase@sina.com>Struct chen</a>
*/
public class AuthenticationPacket extends AbstractPacket {
public long clientParam;
public long maxThreeBytes;
// charset, JDBC will connect as 'latin1',
// and use 'SET NAMES' to change to the desired
// charset after the connection is established.
public byte charsetNumber = 8;
public String user;
public String seed;
public String password;
// 16�ֽ�
public byte[] encryptedPassword;
public String database;
public void init(AbstractPacketBuffer mybuffer) {
super.init(mybuffer);
MysqlPacketBuffer buffer = (MysqlPacketBuffer) mybuffer;
int position = buffer.getPosition();
clientParam = buffer.readInt();
if((clientParam & CLIENT_PROTOCOL_41) == 0){
//protocol version 4.0
//we will reject version 4.0 ,so ignore decode this packet
}else{
//protocol version 4.1
buffer.setPosition(position);
clientParam = buffer.readLong();
maxThreeBytes = buffer.readLong();
charsetNumber = buffer.readByte();
// ����23������ֽ�
buffer.setPosition(buffer.getPosition() + 23);
user = buffer.readString();
// buffer.read
long passwordLength = buffer.readFieldLength();
encryptedPassword = buffer.getBytes(buffer.getPosition(), (int) passwordLength);
// encryptedPassword = buffer.readString(CODE_PAGE_1252);
buffer.setPosition(buffer.getPosition() + (int) passwordLength);
if ((clientParam & CLIENT_CONNECT_WITH_DB) != 0) {
if (buffer.getPosition() < buffer.getBufLength()) {
database = buffer.readString();
}
}
}
}
public void write2Buffer(AbstractPacketBuffer mybuffer) throws UnsupportedEncodingException {
super.write2Buffer(mybuffer);
MysqlPacketBuffer buffer = (MysqlPacketBuffer) mybuffer;
if ((this.clientParam & CLIENT_SECURE_CONNECTION) != 0) {
buffer.writeLong(clientParam);
buffer.writeLong(maxThreeBytes);
buffer.writeByte(charsetNumber);
buffer.writeBytesNoNull(new byte[23]);
if (user == null) {
user = "";
}
buffer.writeString(user);
if (this.password != null && password.length() != 0) {
try {
encryptedPassword = Security.scramble411(password, this.seed);
} catch (NoSuchAlgorithmException e) {
logger.error("encrypt Password error", e);
}
if (encryptedPassword != null && encryptedPassword.length != 0) {
buffer.writeFieldLength(encryptedPassword.length);
buffer.writeBytesNoNull(encryptedPassword);
} else {
buffer.writeByte((byte) 0);
}
} else {
buffer.writeByte((byte) 0);
}
if ((clientParam & CLIENT_CONNECT_WITH_DB) != 0) {
if (database != null) {
buffer.writeString(database);
}
}
} else {
buffer.writeLong(this.clientParam);
buffer.writeLong(this.maxThreeBytes);
// charset, JDBC will connect as 'latin1',
// and use 'SET NAMES' to change to the desired
// charset after the connection is established.
buffer.writeByte(charsetNumber);
// Set of bytes reserved for future use.
buffer.writeBytesNoNull(new byte[23]);
buffer.writeString(user, CODE_PAGE_1252);
buffer.writeString(Util.newCrypt(password, this.seed), CODE_PAGE_1252);
if ((clientParam & CLIENT_CONNECT_WITH_DB) != 0) {
if (database != null) {
buffer.writeString(database, CODE_PAGE_1252);
}
}
}
}
protected int calculatePacketSize() {
int packLength = super.calculatePacketSize();
int passwordLength = 16;
int userLength = (user != null) ? user.length() : 0;
int databaseLength = (database != null) ? database.length() : 0;
packLength += ((userLength + passwordLength + databaseLength) * 2) + 7 + HEADER_SIZE + AUTH_411_OVERHEAD;
return packLength;
}
public static void main(String[] args) {
AuthenticationPacket auth = new AuthenticationPacket();
auth.charsetNumber = 8;
auth.password = "hello12323";
auth.seed = "12345678912345678901";
auth.packetId = 1;
auth.database = "test";
auth.clientParam = 63487;
auth.user = "testamoeba";
AuthenticationPacket other = new AuthenticationPacket();
other.init(auth.toByteBuffer(null).array(), null);
System.out.println(other);
System.out.println(41516 & CLIENT_SECURE_CONNECTION);
}
}