完整代码git: https://git.oschina.net/kfeicat/protocolAnalyse.git
参考资料:
mysql起始握手,先由server发起,client分析并回应自已同意的特性,然后双方依照这些特性处理数据包。
通信时是否采用压缩会改变数据包的字节变化。
客户端的特性在首个回应(既握手包)服务器中体现,如:是否开启压缩、字符集、用户登录信息等。
1.未采用压缩时,客户端向服务器发送的包格式:
格式:3*byte,1*byte,1*byte,n*byte
表示:消息长度,包序号,请求类型,请求内容
2.采用压缩后,客户端向服务器发送的包格式:
格式:3*byte,1*byte,3*byte,n*byte
表示:消息长度,包序号,压缩包大小,压缩包内容
当压缩包大小为0x00时,表示当前包未采用压缩,则n*byte内容为1*byte,n*byte,既请求类型,请求内容
当压缩包大小大于0x00时,表示当前包已采用zlib压缩,则n*byte内容,先解压缩,解压后内容为1*byte,n*byte,既请求类型,请求内容。
package mysql;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
/**
* 支持mysql 4.1+,不支持ssl
* @author shanl
*
*/
public class MysqlAnalyse {
/**传输包默认最大长度16M*/
public static final int PACKET_MAXIMUM_LENGTH = 1024*1024*16;
Map properties = new HashMap();
//StringBuilder sql = new StringBuilder();
long requestType = -1;
boolean endPacket = true;
//int packetLen = PACKET_MAXIMUM_LENGTH;
byte[] _packet = null;
int _off = 0;
int _len = 0;
/**
* 默认开启压缩、utf-8编码
*/
public MysqlAnalyse(){
this.setProperty("compress", true);
this.setProperty("encoding", "utf-8");
}
/**
* 通过客户端向服务端回应的握手包,获得一个MysqlAnlayse实例。
* 建立连接时,server先向client发送握手包,此包包括server支持的特性和编码等信息,
* client回应包中需要说明本地支持什么特性,比如是否支持(zlib)压缩。
* @param buff
* @param off
* @param len
* @return
* @see
* http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
*/
public static MysqlAnalyse handshake(byte[] buff,int off,int len){
MysqlAnalyse anly = new MysqlAnalyse();
//2 byte,capability_flags
long capability_flags = Util.littleBys2Uint32(new byte[]{
buff[off],buff[off+1]
}, 0, 2);
//4byte,前2byte表示mysql特性(如下:是否开启压缩),后2byte客户端扩展特性
boolean compress = CapabilityFlag.CLIENT_COMPRESS==(CapabilityFlag.CLIENT_COMPRESS & capability_flags);
anly.setProperty("compress", compress);
//4byte,最大发送包字节数
long max_packet_size = Util.littleBys2Uint32(new byte[]{
buff[off+4],buff[off+5],
buff[off+6],buff[off+7]
}, 0, 4);
anly.setProperty("max.packet.size", max_packet_size);
//1byte,字符编码
short character_set_id = buff[8];
String character_set = "utf-8"; //需要分析
//SET NAMES utf8; 显示客户端发送的SQL语句中使用什么字符集
anly.setProperty("encoding", character_set);
return anly;
}
/**
* 解析数据包
未压缩:指客户端已开启压缩功能,但数据包本身不值得压缩,所以未进行压缩。
未开启压缩:指客户端未开启压缩功能,比如:./mysql 没有加-C, --compress参数。
客户端开启压缩功能后,向服务器发送数据包时,将会在包头(3+1字节)后,额外多3个字节,
表示压缩包大小,如果此值为0x000000(小端字节顺),则表示数据未压缩。
* @param buff
* @param off
* @param len
* @return 如果是最后一个包返回true
*/
public boolean parse(byte[] packet,int off,int len){
//sql = endPacket? new StringBuilder():sql;
//_buff = (null==_buff? new byte[packetLen]: _buff);
//_buff = packet;
//_off = off;
//_len = len;
if((Boolean)properties.get("compress")){
endPacket=compress(packet,off,len);
}else{
endPacket=noncompress(packet,off,len);
}
return endPacket;
}
/**
* (客户端)支持压缩
* @param packet
* @param off
* @param len
*/
boolean compress(byte[] packet,int off,int len){
long tranPackLen = Util.littleBys2Uint32(packet,off+0,3); //3 byte,传输包长度,此长度已经-7
short tranPackNo = packet[off+3];//1 byte,传输包序号
long compressPackLen = Util.littleBys2Uint32(packet,off+4,3);//3 byte,压缩后包长度
//未压缩
if(0x000000==compressPackLen){
long srcLen = Util.littleBys2Uint32(packet,off+3+1+3,4)-1;//4byte,(包含类型信息的)源数据长度
this.requestType = packet[off+3+1+3+4];//1byte,类型信息
_packet = packet;
_off = off+3+1+3;
_len = (int)srcLen;
//sql.append(toSql(packet, off+3+1+3, (int)srcLen));
}else{ //已压缩
byte[] src = Util.decompress(packet,off+3+1+3,
(int)tranPackLen,(int)compressPackLen);//n byte,src为zlib解压后的源数据
long srcLen = Util.littleBys2Uint32(src,0,4)-1;//4byte,(包含类型信息的)源数据长度
this.requestType = src[4];//1byte,类型信息
_packet = src;
_off = 0;
_len = (int) srcLen;
//sql.append(toSql(src,0,(int)srcLen));
}
return 0x00==tranPackNo;
}
/**
* (客户端)不支持压缩
* @param packet
* @param off
* @param len
*/
boolean noncompress(byte[] packet,int off,int len){
long tranPackLen = Util.littleBys2Uint32(packet,off+0,3); //3 byte
short tranPackNo = packet[off+3];//1 byte
this.requestType = packet[off+4];//1 byte
//sql.append(toSql(packet,off,(int)tranPackLen));
_packet = packet;
_off = off;
_len = (int)tranPackLen;
return 0x00==tranPackNo;
}
/**
* 当前包中的sql语句
* 注:这不一定是一个完整的sql语句
* @param packet
* @param off
* @param len
* @return 当requestType!=ClientRequestType.COM_QUERY时,返回""
*/
String toSql(byte[] packet,int off,int len){
try {
return new String(packet,off+5,len, (String)getProperty("encoding"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* 返回sql语句
* 只有当requestType==ClientRequestType.COM_QUERY时可用
* @return 类型不符合时,返回""
*/
public String getSql(){
if(ClientRequestType.COM_QUERY!=this.requestType){
return "";
}
//return sql.toString();
return toSql(_packet,_off,_len);
}
/**
* 返回sql语句
* 只有当requestType==ClientRequestType.COM_QUERY时可用
* @param data
* @param off
* @return
*/
public int getSql(byte[] data, int off){
System.arraycopy(_packet, _off, data, off, _len);
return _len;
}
public void writeSql(OutputStream out) throws IOException{
out.write(_packet, _off, _len);
out.flush();
}
/**
* 返回当前sql请求类型,参见ClientRequestType
* @return 未知请求返回-1
*/
public long getRequestType(){
return requestType;
}
/**
* 设置特性
* @param name
* @param value
*/
public void setProperty(String name,Object value){
this.properties.put(name, value);
}
/**
* 设置特性
* @param properties
*/
public void putProperty(Map properties){
this.properties.putAll(properties);;
}
/**
* 取特性
* @param name
* @return
*/
public Object getProperty(String name){
return this.properties.get(name);
}
/**
* 返回所有特性
* @return
*/
public Map getProperties(){
return this.properties;
}
///**
// * 设置传输包大小
// * 默认值 PACKET_MAXIMUM_LENGTH
// * @param len
// * @see #PACKET_MAXIMUM_LENGTH
// */
//public void setTranPacketLength(int len){
//this.packetLen = len;
//}
}
package mysql;
/**
* 特性
* @author shanl
* @see http://dev.mysql.com/doc/internals/en/capability-flags.html#flag-CLIENT_PROTOCOL_41
*/
public interface CapabilityFlag {
long CLIENT_LONG_PASSWORD = 0x00000001;
long CLIENT_FOUND_ROWS= 0x00000002;
long CLIENT_LONG_FLAG= 0x00000004;
long CLIENT_CONNECT_WITH_DB = 0x00000008;
long CLIENT_NO_SCHEMA= 0x00000010;
long CLIENT_COMPRESS =0x00000020;
long CLIENT_ODBC = 0x00000040;
long CLIENT_LOCAL_FILES = 0x00000080;
long CLIENT_IGNORE_SPACE= 0x00000100;
long CLIENT_PROTOCOL_41= 0x00000200;
long CLIENT_INTERACTIVE = 0x00000400;
long CLIENT_SSL= 0x00000800;
long CLIENT_IGNORE_SIGPIPE= 0x00001000;
long CLIENT_TRANSACTIONS= 0x00002000;
long CLIENT_RESERVED = 0x00004000;
long CLIENT_SECURE_CONNECTION=0x00008000;
long CLIENT_MULTI_STATEMENTS= 0x00010000;
long CLIENT_MULTI_RESULTS= 0x00020000;
long CLIENT_PS_MULTI_RESULTS= 0x00040000;
long CLIENT_PLUGIN_AUTH= 0x00080000;
long CLIENT_CONNECT_ATTRS= 0x00100000;
long CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA=0x00200000;
long CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= 0x00400000;
long CLIENT_SESSION_TRACK= 0x00800000;
long CLIENT_DEPRECATE_EOF=0x01000000;
}
package mysql;
/**
* 客户端请求类型
* @author shanl
* @see http://dev.mysql.com/doc/internals/en/text-protocol.html
*/
public interface ClientRequestType {
/**(内部线程状态)*/
int COM_SLEEP= 0x00;
/**关闭连接*/
int COM_QUIT = 0x01;
/**切换数据库*/
int COM_INIT_DB = 0x02;
/**SQL查询请求*/
int COM_QUERY = 0x03;
/**获取数据表字段信息*/
int COM_FIELD_LIST = 0x04;
/**创建数据库*/
int COM_CREATE_DB = 0x05;
/**删除数据库*/
int COM_DROP_DB = 0x06;
/**清除缓存*/
int COM_REFRESH = 0x07;
/**停止服务器*/
int COM_SHUTDOWN = 0x08;
/**获取服务器统计信息*/
int COM_STATISTICS = 0x09;
/**获取当前连接的列表*/
int COM_PROCESS_INFO = 0x0A;
/**(内部线程状态)*/
int COM_CONNECT = 0x0B;
/**中断某个连接*/
int COM_PROCESS_KILL = 0x0C;
/**保存服务器调试信息*/
int COM_DEBUG = 0x0D;
/**测试连通性*/
int COM_PING = 0x0E;
/**(内部线程状态)*/
int COM_TIME= 0x0F;
/**(内部线程状态)*/
int COM_DELAYED_INSERT = 0x10;
/**重新登陆(不断连接*/
int COM_CHANGE_USER = 0x11;
/**获取二进制日志信息*/
int COM_BINLOG_DUMP= 0x12;
/**获取数据表结构信息*/
int COM_TABLE_DUMP= 0x13;
/**(内部线程状态)*/
int COM_CONNECT_OUT = 0x14;
/**从服务器向主服务器进行注册*/
int COM_REGISTER_SLAVE= 0x15;
/**预处理SQL语句*/
int COM_STMT_PREPARE = 0x16;
/**执行预处理语句*/
int COM_STMT_EXECUTE = 0x17;
/**发送BLOB类型的数据*/
int COM_STMT_SEND_LONG_DATA=0x18;
/**销毁预处理语句*/
int COM_STMT_CLOSE= 0x19;
/**清除预处理语句参数缓存*/
int COM_STMT_RESET = 0x1A;
/**设置语句选项*/
int COM_SET_OPTION = 0x1B;
/**获取预处理语句的执行结果*/
int COM_STMT_FETCH= 0x1C;
}
package mysql;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.zip.Inflater;
/**
* 为mysql分析专用工具
* @author shanl
*
*/
public class Util {
/**
* Big-Endian字节数组转uint32
* @param bys
* @param off
* @param len
* @return
*/
public static long bigBys2Uint32(byte[] bys,int off,int len){
long uint32 = 0;
for(int i=0,end=len-1,c=end; i<=end; i++,c--){
uint32 |= (0xff&bys[off+i])<
}
return uint32;
}
/**
* Little-Endian字节数组转uint32
* @param bys
* @param off
* @param len
* @return
*/
public static long littleBys2Uint32(byte[] bys,int off,int len){
long uint32 = 0;
for(int i=len-1; i>=0; i--){
uint32 |= (0xff&bys[off+i])<
}
return uint32;
}
/**
* 适用于mysql与客户端交互时zlib解压
* @param data
* @param off
* @param len
* @param out
* @throws Exception
*/
public static void decompress(byte[] data, int off, int len,OutputStream out){
Inflater decompresser = new Inflater();
decompresser.reset();
decompresser.setInput(data, off, len);
byte[] buf = new byte[1024];
try{
while(!decompresser.finished()){
int i = decompresser.inflate(buf);
out.write(buf, 0, i);
out.flush();
}
}catch(Exception ex){
throw new RuntimeException(ex);
}
}
/**
* 适用于mysql与客户端交互时zlib解压
* @param data 数据
* @param off 偏移量
* @param len 长度
* @return
*/
public static byte[] decompress(byte[] data, int off, int len) {
byte[] output = null;
Inflater decompresser = new Inflater();
decompresser.reset();
//decompresser.setInput(data);
decompresser.setInput(data, off, len);
ByteArrayOutputStream o = new ByteArrayOutputStream(data.length);
try {
byte[] buf = new byte[1024];
while (!decompresser.finished()) {
int i = decompresser.inflate(buf);
o.write(buf, 0, i);
}
output = o.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
o.close();
} catch (Exception e) {
}
}
decompresser.end();
return output;
}
/**
* 适用于mysql与客户端交互时zlib解压
* @param data 数据
* @param off 偏移量
* @param len 长度
* @param srcLen 源数据长度
* @return
*/
public static byte[] decompress(byte[] data, int off, int len, int srcLen) {
byte[] output = null;
Inflater decompresser = new Inflater();
decompresser.reset();
//decompresser.setInput(data);
decompresser.setInput(data, off, len);
ByteArrayOutputStream o = new ByteArrayOutputStream(srcLen);
try {
o.reset();
byte[] buf = new byte[1024];
while (!decompresser.finished()) {
int i = decompresser.inflate(buf);
o.write(buf, 0, i);
}
output = o.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
o.close();
} catch (Exception e) {
}
}
decompresser.end();
return output;
}
}
package mysql;
/**
* 支持的字符编码
* @author shanl
* @see
* http://dev.mysql.com/doc/internals/en/character-set.html#packet-Protocol::CharacterSet
*/
public class CharacterSet {
/***
int big5_chinese_ci =1;
int latin2_czech_cs =2;
int dec8_swedish_ci =3;
int cp850_general_ci =4;
int latin1_german1_ci =5;
int hp8_english_ci =6;
int koi8r_general_ci =7;
int latin1_swedish_ci =8;
int latin2_general_ci =9;
int swe7_swedish_ci =10;
int ascii_general_ci =11;
int ujis_japanese_ci =12;
int sjis_japanese_ci =13;
int cp1251_bulgarian_ci =14;
int latin1_danish_ci =15;
int hebrew_general_ci =16;
int tis620_thai_ci =18;
int euckr_korean_ci =19;
int latin7_estonian_cs =20;
int latin2_hungarian_ci =21;
int koi8u_general_ci =22;
int cp1251_ukrainian_ci =23;
int gb2312_chinese_ci =24;
int greek_general_ci =25;
int cp1250_general_ci =26;
int latin2_croatian_ci =27;
int gbk_chinese_ci =28;
int cp1257_lithuanian_ci =29;
int latin5_turkish_ci =30;
int latin1_german2_ci =31;
int armscii8_general_ci =32;
int utf8_general_ci =33;
int cp1250_czech_cs =34;
int ucs2_general_ci =35;
int cp866_general_ci =36;
int keybcs2_general_ci =37;
int macce_general_ci =38;
int macroman_general_ci =39;
int cp852_general_ci =40;
int latin7_general_ci =41;
int latin7_general_cs =42;
int macce_bin =43;
int cp1250_croatian_ci =44;
int utf8mb4_general_ci =45;
int utf8mb4_bin =46;
int latin1_bin =47;
int latin1_general_ci =48;
int latin1_general_cs =49;
int cp1251_bin =50;
int cp1251_general_ci =51;
int cp1251_general_cs =52;
int macroman_bin 53
int utf16_general_ci 54
int utf16_bin 55
int cp1256_general_ci 57
int cp1257_bin 58
int cp1257_general_ci 59
int utf32_general_ci 60
int utf32_bin 61
int binary 63
int armscii8_bin 64
int ascii_bin 65
int cp1250_bin 66
int cp1256_bin 67
int cp866_bin 68
int dec8_bin 69
int greek_bin 70
int hebrew_bin 71
int hp8_bin 72
int keybcs2_bin 73
int koi8r_bin 74
int koi8u_bin 75
int latin2_bin 77
int latin5_bin 78
int latin7_bin 79
int cp850_bin 80
int cp852_bin 81
int swe7_bin 82
int utf8_bin 83
int big5_bin 84
int euckr_bin 85
int gb2312_bin 86
int gbk_bin 87
int sjis_bin 88
int tis620_bin 89
int ucs2_bin 90
int ujis_bin 91
int geostd8_general_ci 92
int geostd8_bin 93
latin1_spanish_ci 94
cp932_japanese_ci 95
cp932_bin 96
eucjpms_japanese_ci 97
eucjpms_bin 98
cp1250_polish_ci 99
utf16_unicode_ci 101
utf16_icelandic_ci 102
utf16_latvian_ci 103
utf16_romanian_ci 104
utf16_slovenian_ci 105
utf16_polish_ci 106
utf16_estonian_ci 107
utf16_spanish_ci 108
utf16_swedish_ci 109
utf16_turkish_ci 110
utf16_czech_ci 111
utf16_danish_ci 112
utf16_lithuanian_ci 113
utf16_slovak_ci 114
utf16_spanish2_ci 115
utf16_roman_ci 116
utf16_persian_ci 117
utf16_esperanto_ci 118
utf16_hungarian_ci 119
utf16_sinhala_ci 120
ucs2_unicode_ci 128
ucs2_icelandic_ci 129
ucs2_latvian_ci 130
ucs2_romanian_ci 131
ucs2_slovenian_ci 132
ucs2_polish_ci 133
ucs2_estonian_ci 134
ucs2_spanish_ci 135
ucs2_swedish_ci 136
ucs2_turkish_ci 137
ucs2_czech_ci 138
ucs2_danish_ci 139
ucs2_lithuanian_ci 140
ucs2_slovak_ci 141
ucs2_spanish2_ci 142
ucs2_roman_ci 143
ucs2_persian_ci 144
ucs2_esperanto_ci 145
ucs2_hungarian_ci 146
ucs2_sinhala_ci 147
ucs2_general_mysql500_ci 159
utf32_unicode_ci 160
utf32_icelandic_ci 161
utf32_latvian_ci 162
utf32_romanian_ci 163
utf32_slovenian_ci 164
utf32_polish_ci 165
utf32_estonian_ci 166
utf32_spanish_ci 167
utf32_swedish_ci 168
utf32_turkish_ci 169
utf32_czech_ci 170
utf32_danish_ci 171
utf32_lithuanian_ci 172
utf32_slovak_ci 173
utf32_spanish2_ci 174
utf32_roman_ci 175
utf32_persian_ci 176
utf32_esperanto_ci 177
utf32_hungarian_ci 178
utf32_sinhala_ci 179
utf8_unicode_ci 192
utf8_icelandic_ci 193
utf8_latvian_ci 194
utf8_romanian_ci 195
utf8_slovenian_ci 196
utf8_polish_ci 197
utf8_estonian_ci 198
utf8_spanish_ci 199
utf8_swedish_ci 200
utf8_turkish_ci 201
utf8_czech_ci 202
utf8_danish_ci 203
utf8_lithuanian_ci 204
utf8_slovak_ci 205
utf8_spanish2_ci 206
utf8_roman_ci 207
utf8_persian_ci 208
utf8_esperanto_ci 209
utf8_hungarian_ci 210
utf8_sinhala_ci 211
utf8_general_mysql500_ci 223
utf8mb4_unicode_ci 224
utf8mb4_icelandic_ci 225
utf8mb4_latvian_ci 226
utf8mb4_romanian_ci 227
utf8mb4_slovenian_ci 228
utf8mb4_polish_ci 229
utf8mb4_estonian_ci 230
utf8mb4_spanish_ci 231
utf8mb4_swedish_ci 232
utf8mb4_turkish_ci 233
utf8mb4_czech_ci 234
utf8mb4_danish_ci 235
utf8mb4_lithuanian_ci 236
utf8mb4_slovak_ci 237
utf8mb4_spanish2_ci 238
utf8mb4_roman_ci 239
utf8mb4_persian_ci 240
utf8mb4_esperanto_ci 241
utf8mb4_hungarian_ci 242
utf8mb4_sinhala_ci 243
***/
}
package test2;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import mysql.ClientRequestType;
import mysql.MysqlAnalyse;
import mysql.MysqlSimpleProxy;
import mysql.Util;
/**
* 这是测试类
* @author shanl
*
*/
public class Test4 {
public static void main(String[] args){
//t1();
//t2();
//t3();
//t4();
//t5();
//t6();
//t7();
//t8();
t9();
}
static void t9(){
//InetSocketAddress address = new InetSocketAddress(1024);
//System.out.println(address.getHostName());
MysqlSimpleProxy proxy = new MysqlSimpleProxy("192.168.2.182:3306");
proxy.setListenPort(3306);
proxy.setRecordDir("e:/a/2");
//proxy.start();
proxy.service(null);
}
/**
* 调用MysqlAnlyse进行分析
*/
static void t8(){
long packLen = 0; //3byte
long number = 0;//1byte
long compress = 0;//3byte
File srcDir = new File("e://a//2");
File compressFile = new File(srcDir,"fc_103533_0.out.compress.3"); //uncompress
//File compressFile = new File(srcDir,"fc_103533_0.out.compress.2"); //uncompress
//File compressFile = new File(srcDir,"fc_103533_0.out.compress.1"); //compress
//File compressFile = new File(srcDir,"fc_103533_0.out.decompress"); //non-compress
int readLen = 0;
byte[] buff = new byte[1024*4];
try {
FileInputStream fin = new FileInputStream(compressFile);
readLen = fin.read(buff);
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
try{
MysqlAnalyse anly = new MysqlAnalyse();
anly.setProperty("compress", true);
anly.setProperty("encoding", "utf-8");
anly.parse(buff, 0, readLen);
System.out.println(anly.getRequestType());
System.out.println(anly.getSql());
}catch(Exception e){
e.printStackTrace();
}
}
static void t7(){
byte[] capability_flags_lower = {
(byte) 0xff,(byte) 0xf7
};
byte[] capability_flags_upper = {
(byte) 7f,(byte) 0x80
};
//byte[] bys = {
//capability_flags_upper[1],capability_flags_upper[0],
//capability_flags_lower[1],capability_flags_lower[0]
//};
//byte[] bys = {
//capability_flags_lower[0],capability_flags_lower[1],
//capability_flags_upper[0],capability_flags_upper[1],
//};
byte[] bys = {
capability_flags_upper[0],capability_flags_upper[1],
capability_flags_lower[0],capability_flags_lower[1],
};
//byte[] bys = {
(byte) 0x85,(byte) 0xa6
//(byte) 0xa5,(byte) 0xa6
//};
long capability_flags = bigBys2Uint32(bys,0,bys.length);
//System.out.println(capability_flags);
long compress_flag = 0x00000020;
long odbc_flag = 0x00000040;
long l = capability_flags & compress_flag;
System.out.println(l);
System.out.println(compress_flag==l);
}
static void t6(){
byte[] bys = "中".getBytes();
long i32 = bigBys2Uint32(bys,0,bys.length);
System.out.println(i32);
}
static void t5(){
long packLen = 0; //3byte
long number = 0;//1byte
long compress = 0;//3byte
File srcDir = new File("e://a//2");
File compressFile = new File(srcDir,"fc_103533_0.out.compress.1"); //uncompress
//File compressFile = new File(srcDir,"fc_103533_0.out.compress.2"); //compress
int readLen = 0;
byte[] buff = new byte[1024*4];
try {
FileInputStream fin = new FileInputStream(compressFile);
readLen = fin.read(buff);
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
try{
packLen = littleBys2Uint32(buff,0,3);
number = littleBys2Uint32(buff,3,1);
compress = littleBys2Uint32(buff,4,3);
System.out.println("readLen: "+readLen);
System.out.println("packLen: "+packLen);
System.out.println("number: " +number);
System.out.println("compress: "+compress);
}catch(Exception e){
e.printStackTrace();
}
//decompress
long srcLen = 0;
long srcReqType = 0;
byte[] srcData = null;
try{
if(compress>0){
byte[] decompressData = Util.decompress(buff,3+1+3,readLen-7);
srcLen = Util.littleBys2Uint32(decompressData,0,4)-1;//-1去掉clientRequestType
srcReqType = Util.littleBys2Uint32(decompressData, 4, 1);
srcData = new byte[(int) srcLen];
System.arraycopy(decompressData, 5, srcData, 0, (int)srcLen);//
}else{
srcLen = Util.littleBys2Uint32(buff,3+1+3,4)-1; //-1去掉clientRequestType
srcReqType = Util.littleBys2Uint32(buff, 3+1+3+4, 1);
srcData = new byte[(int) srcLen];
System.arraycopy(buff, 3+1+3+4+1, srcData, 0, (int)srcLen);
}
System.out.println("srcLen: "+srcLen);
System.out.println("srcReqType: "+srcReqType);
if((int)srcReqType==ClientRequestType.COM_QUERY){
System.out.println("sql: "+new String(srcData));
}else{
System.out.println("non-sql request.");
}
}catch(Exception e){
e.printStackTrace();
}
}
static void t4(){
byte[] bys = {
(byte) 0xda,0x00,0x00,
};
byte[] bys1 = {
0x00,0x00,(byte) 0xda,
};
System.out.println(littleBys2Uint32(bys,0,bys.length));
System.out.println(bigBys2Uint32(bys1,0,bys1.length));
}
/**
* Big-Endian字节数组转uint32
* @param bys
* @param off
* @param len
* @return
*/
static long bigBys2Uint32(byte[] bys,int off,int len){
long uint32 = 0;
for(int i=0,end=len-1,c=end; i<=end; i++,c--){
uint32 |= (0xff&bys[off+i])<
}
return uint32;
}
/**
* Little-Endian字节数组转uint32
* @param bys
* @param off
* @param len
* @return
*/
static long littleBys2Uint32(byte[] bys,int off,int len){
long uint32 = 0;
for(int i=len-1; i>=0; i--){
uint32 |= (0xff&bys[off+i])<
}
return uint32;
}
static void t3(){
short[] bys = {
0xda,0x00,0x00,0x00
};
long uint32 = 0;
for(int i=bys.length-1; i>=0; i--){
uint32 |= (0xff&bys[i])<
}
System.out.println(uint32);
//System.out.println(0xff&0xda);
}
public static byte[] decompress(byte[] data, int off, int len) {
byte[] output = new byte[0];
Inflater decompresser = new Inflater();
decompresser.reset();
//decompresser.setInput(data);
decompresser.setInput(data, off, len);
ByteArrayOutputStream o = new ByteArrayOutputStream(data.length);
try {
byte[] buf = new byte[1024];
while (!decompresser.finished()) {
int i = decompresser.inflate(buf);
o.write(buf, 0, i);
}
output = o.toByteArray();
} catch (Exception e) {
output = data;
e.printStackTrace();
} finally {
try {
o.close();
} catch (IOException e) {
e.printStackTrace();
}
}
decompresser.end();
return output;
}
public static void decompress(byte[] data, int off, int len,OutputStream out) throws Exception{
Inflater decompresser = new Inflater();
decompresser.reset();
decompresser.setInput(data, off, len);
byte[] buf = new byte[1024];
while(!decompresser.finished()){
int i = decompresser.inflate(buf);
out.write(buf, 0, i);
out.flush();
}
}
static void t2(){
File recordDir = new File("e://a//2");
String srcFile = "fc_103533_0.out";
File cf = new File(recordDir,srcFile+".compress");
//File cf1 = new File(recordDir,"fc_103533_0.out.compress.1");
File df = new File(recordDir,srcFile+".decompress");
int readLen = 0;
byte[] buff = new byte[1024*4];
try {
FileInputStream fin = new FileInputStream(cf);
readLen = fin.read(buff);
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
//try{
//FileOutputStream fout = new FileOutputStream(cf1);
//fout.write(buff,7,readLen-7);
//fout.close();
//}catch(Exception ex){
//ex.printStackTrace();
//}
try{
FileOutputStream fout = new FileOutputStream(df);
decompress(buff,7,readLen-7,fout);
fout.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
static final SimpleDateFormat sdf = new SimpleDateFormat("[HH:mm:ss]");
static final SimpleDateFormat fsdf = new SimpleDateFormat("HHmmss");
/**
* 简单的代理,主要用于本地存储通信字节
*/
static void t1(){
File recordDir = new File("e://a//2");
String tsip = "192.168.2.182";
int tsport = 3306;
int listenPort = 3306;
ServerSocket ss = null;
Socket fcsocket = null;
Socket tssocket = null;
int readTimeout = 1000*60*5;
try{
System.out.println("listen port:"+listenPort);
ss = new ServerSocket(listenPort);
for(int i=0;;i++){
fcsocket = ss.accept();
fcsocket.setSoTimeout(readTimeout);
System.out.println("new connected "+i);
tssocket = new Socket(tsip,tsport);
tssocket.setSoTimeout(readTimeout);
String fname_padding = fsdf.format(new Date())+"_"+i+".out";
new Recording(fcsocket,tssocket,new File(recordDir,"fc_"+fname_padding)).start();
new Recording(tssocket,fcsocket,new File(recordDir,"ts_"+fname_padding)).start();
}
}catch(Exception ex){
}
}
static class Recording extends Thread{
Socket fcsocket = null;
Socket tssocket = null;
File recordFile = null;
public Recording(Socket from,Socket to,File file){
this.fcsocket = from;
this.tssocket = to;
this.recordFile = file;
}
public void run(){
InputStream fcin = null;
OutputStream tsout = null;
int readLen = 0;
byte[] buff = new byte[1024*4];
FileOutputStream fileOut = null;
try{
recordFile.createNewFile();
fcin = fcsocket.getInputStream();
tsout = tssocket.getOutputStream();
fileOut = new FileOutputStream(recordFile);
}catch(Exception ex){
try { fileOut.close(); } catch (IOException e) { }
try { fcsocket.close(); } catch (IOException e) { }
try { tssocket.close(); } catch (IOException e) { }
return;
}
for(;;){
try{
readLen = fcin.read(buff);
tsout.write(buff,0,readLen);
tsout.flush();
fileOut.write(sdf.format(new Date()).getBytes());
fileOut.write(buff,0,readLen);
fileOut.flush();
}catch(SocketTimeoutException ste){
continue;
}catch(Exception ex){
break;
}
}
try{fileOut.close();}catch(Exception ex){}
}
}
}
package mysql;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import org.apache.log4j.Logger;
/**
* 一个简单的mysql代理
* 主要用于分析和截获sql语句
* @author shanl
*
*/
public class MysqlSimpleProxy {
ExecutorService threadPool = null;
InetSocketAddress targetAddress = null;
int listenPort = 3306;
static final Logger logger = Logger.getLogger(MysqlSimpleProxy.class);
String recordDir = ".";
/**
*
* @param targetAddress 目标服务器地址,格式 [:port] port默认3306
*/
public MysqlSimpleProxy(String targetAddress){
String[] ss = targetAddress.split(":");
this.targetAddress = new InetSocketAddress(ss[0],
1==ss.length? 3306: Integer.parseInt(ss[1]));
}
public void start(){
ServerSocket ss = null;
try {
ss = new ServerSocket(listenPort);
} catch (IOException e) {
logger.error(e.getMessage(), e);
return;
}
logger.info("MysqlPort listen port:" + listenPort);
System.err.println("MysqlPort listen port:" + listenPort);
Socket fromClient = null;
for(int i=0;;i++){
try{
fromClient = ss.accept();
connectHandler(fromClient);
logger.info("MysqlPort new connect:"+i);
}catch(Exception e){
logger.error(e.getMessage(), e);
break;
}
}
try{ss.close();}catch(Exception ex){}
}
/**
* 以服务方式启动
* @param threadPool 可以为null
*/
public void service(ExecutorService threadPool){
Thread t = new Thread(
new Runnable(){
@Override
public void run() {
start();
}},
"MysqlProxy");
if(null==threadPool) t.start();
else threadPool.execute(t);
}
static final SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss");
static final SimpleDateFormat head = new SimpleDateFormat("[dd HH:mm:ss]");
void connectHandler(Socket fromClient){
Socket toServer = null;
try{
toServer = new Socket();
toServer.connect(targetAddress, 1000*10);
FileOutputStream out = new FileOutputStream(new File(recordDir,
"c_"+sdf.format(new Date())+"_"+fromClient.getInetAddress().getHostAddress()
+".record"));
InetAddress fcAddress = fromClient.getInetAddress();
out.write(head.format(new Date()).getBytes());
out.write(("client:"+fcAddress.getHostAddress()).getBytes());
out.write((",server:"+targetAddress.getHostString()+":"+targetAddress.getPort()).getBytes());
out.flush();
Thread c2s = new Thread(new Worker(fromClient, toServer, out));
Thread s2c = new Thread(new Worker(toServer, fromClient, null));
if(null==threadPool){
c2s.start();
s2c.start();
}else{
threadPool.execute(c2s);
threadPool.execute(s2c);
}
}catch(Exception e){
logger.error(e.getMessage(), e);
try{if(null!=toServer)toServer.close();}catch(Exception ex){}
}
}
/**
* 设置本地监听地址,默认3306
* @param port
*/
public void setListenPort(int port){
this.listenPort = port;
}
public void setThreadPool(ExecutorService threadPool){
this.threadPool = threadPool;
}
/**
* 设置截获数据的存储目录
* @param dir 默认'./'
*/
public void setRecordDir(String dir){
this.recordDir = dir;
}
static class Worker extends Thread{
Socket from = null;
Socket to = null;
OutputStream record = null;
public Worker(Socket from,Socket to,OutputStream record){
this.from = from;
this.to = to;
this.record = record;
}
public void run(){
int readLen = 0;
byte[] buff = new byte[1024*1024*16];
MysqlAnalyse analy = null;
StringBuilder sb = new StringBuilder();
try{
InputStream in = from.getInputStream();
OutputStream out = to.getOutputStream();
readLen = in.read(buff);
out.write(buff,0,readLen);
out.flush();
if(null!=record)analy = MysqlAnalyse.handshake(buff, 0, readLen);
while(-1!=(readLen=in.read(buff)) ){
if(null!=record){
boolean endPack = analy.parse(buff, 0, readLen);
if(ClientRequestType.COM_QUERY==analy.getRequestType()){
sb.append(analy.getSql());
if(endPack){
record.write((MysqlSimpleProxy.head.format(new Date())).getBytes());
record.write(sb.toString().getBytes());
record.flush();
sb = new StringBuilder();
}
}
//logger.info("requestType:"+analy.getRequestType()+",sql:"+analy.getSql());
}
out.write(buff,0,readLen);
out.flush();
}
}catch(Exception e){
logger.error(e.getMessage(), e);
}finally{
try{if(null!=from)from.close();}catch(Exception e){}
try{if(null!=to)to.close();}catch(Exception e){}
}
}
}
}