完整代码git: https://git.oschina.net/kfeicat/protocolAnalyse.git
参考资料:
http://dev.mysql.com/doc/internals/en/client-server-protocol.html
http://www.haodaima.net/art/2574673
http://hutaow.com/blog/2013/11/06/mysql-protocol-analysis/#11
http://blog.csdn.net/gapaul/article/details/12046843
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<String,Object> properties = new HashMap<String,Object>(); // 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"); } /** * <b>通过客户端向服务端回应的握手包,获得一个MysqlAnlayse实例。</b> <br/> * 建立连接时,server先向client发送握手包,此包包括server支持的特性和编码等信息,<br/> * client回应包中需要说明本地支持什么特性,比如是否支持(zlib)压缩。 * @param buff * @param off * @param len * @return * @see <a href="http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse"> * http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse<a/> */ 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; } /** * <b> 解析数据包 </b> <br/> 未压缩:指客户端已开启压缩功能,但数据包本身不值得压缩,所以未进行压缩。<br/> 未开启压缩:指客户端未开启压缩功能,比如:./mysql 没有加-C, --compress参数。<br/> 客户端开启压缩功能后,向服务器发送数据包时,将会在包头(3+1字节)后,额外多3个字节,<br/> 表示压缩包大小,如果此值为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语句<br/> * 注:这不一定是一个完整的sql语句<br/> * @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语句 <br/> * 只有当requestType==ClientRequestType.COM_QUERY时可用 * @return 类型不符合时,返回"" */ public String getSql(){ if(ClientRequestType.COM_QUERY!=this.requestType){ return ""; } // return sql.toString(); return toSql(_packet,_off,_len); } /** * 返回sql语句 <br/> * 只有当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<String,Object> properties){ this.properties.putAll(properties);; } /** * 取特性 * @param name * @return */ public Object getProperty(String name){ return this.properties.get(name); } /** * 返回所有特性 * @return */ public Map<String,Object> getProperties(){ return this.properties; } // /** // * 设置传输包大小<br/> // * 默认值 PACKET_MAXIMUM_LENGTH // * @param len // * @see #PACKET_MAXIMUM_LENGTH // */ // public void setTranPacketLength(int len){ // this.packetLen = len; // } }
package mysql; /** * 特性 * @author shanl * @see <a href="http://dev.mysql.com/doc/internals/en/capability-flags.html#flag-CLIENT_PROTOCOL_41">http://dev.mysql.com/doc/internals/en/capability-flags.html#flag-CLIENT_PROTOCOL_41</a> */ 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 <a href="http://dev.mysql.com/doc/internals/en/text-protocol.html">http://dev.mysql.com/doc/internals/en/text-protocol.html</a> */ 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])<<(8*c); } 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])<<(8*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 <a href="http://dev.mysql.com/doc/internals/en/character-set.html#packet-Protocol::CharacterSet"> * http://dev.mysql.com/doc/internals/en/character-set.html#packet-Protocol::CharacterSet</a> */ 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])<<(8*c); } 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])<<(8*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])<<(8*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代理 <br/> * 主要用于分析和截获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 目标服务器地址,格式 <ip>[: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){} } } } }