// mysqlsniffer.c Aug 18, 2006 UNSTABLE
/*
mysqlsniffer.c - Watch MySQL traffic on a TCP/IP network
v1.2 Aug 18, 2006 UNSTABLE
Original source and more information at:
http://hackmysql.com/mysqlsniffer
Thanks to Ian Redfern for publishing "MySQL Protocol" at:
http://www.redferni.uklinux.net/mysql/MySQL-Protocol.html.
Thanks to The Tcpdump Group (http://tcpdump.org) for libpcap
and sniffex.c which this program takes strongly from in main().
Thanks to Jay Pipes for publishing the internals.texi in HTML at:
http://www.jpipes.com/mysqldox/
Thanks to Marek for helping me try to get this release stable.
Official documentation of the MySQL protocol is available at:
http://dev.mysql.com/doc/internals/en/client-server-protocol.html
To compile: gcc -O2 -lpcap -lz -o mysqlsniffer mysqlsniffer.c packet_handlers.c misc.c
Modified by power 20100510
*/
#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <getopt.h>
#include <signal.h>
#include <zlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "user_defines.h"
#include "mysql_defines.h"
#include "state_map.h"
#include "mysqlsniffer.h"
#include "sniff_headers.h"
#include "packet_handlers.h"
#include "mysql_misc.h"
#include "ip_sys.h"
/*
Global vars
*/
u_char buff[SNAP_LEN]; // For general MySQL packet processing
u_char *buff_frag = NULL; // Last pkt fragment (tag->frag) + next pkts
pcap_t *handle = NULL;
struct bpf_program fp;
char filter_exp[11] = "port ";
u_int total_mysql_pkts;
u_int total_mysql_bytes;
struct st_options op; // Command line options
//tag_id tags[MAX_TAGS]; // Connections pool to trace
//tag_id *tag; // Information about the current connection
#if defined(__TO_FILE)
FILE* plog = NULL;
#endif
/*
Function protos
*/
// General
static void show_help(void);
static void proc_ops(int argc, char *argv[]);
static void handle_ctrl_c(int signo);
static void handle_sighup(int signo);
static void cleanup(void);
static void print_stats(void);
static void proc_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
static void init_state_map(void);
// MySQL packet processing
static int multi_pkts(const u_char *pkts, u_int total_len);
static int parse_pkt(st_tag_id *tag, u_char *pkt, u_int len);
static void init_logs(void);
// Connection tracking
static void init_tags(void);
static tag_id *get_tag(u_int addr, u_short port);
static void free_tags(void);
/*
Main code and general functions
*/
//int main(int argc, char *argv[])
int test1122(int argc, char *argv[])
{
char *dev = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 mask;
bpf_u_int32 net;
init_logs();
if (argc > 1) {
proc_ops(argc, argv);
dev = argv[optind];
if(!dev) {
ANALYZE_LOG("No network interface name given\n\n");
show_help();
}
if(!op.port) {
strcpy(filter_exp + 5, "3306");
op.port_n = 3306;
}
}
else
{
show_help();
}
if(pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
ANALYZE_LOG("Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = mask = 0;
}
handle = pcap_open_live(dev, SNAP_LEN, 1, 0, errbuf);
if(handle == NULL) {
ANALYZE_LOG("Couldn't open device %s: %s\n", dev, errbuf);
exit(-1);
}
if(pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
ANALYZE_LOG("Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
exit(-1);
}
if (pcap_setfilter(handle, &fp) == -1) {
ANALYZE_LOG("Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
exit(-1);
}
ANALYZE_LOG("mysqlsniffer listening for MySQL on interface %s %s\n", dev, filter_exp);
signal(SIGINT, handle_ctrl_c);
signal(SIGHUP, handle_sighup);
init_tags();
init_state_map();
//抓包循环开始
pcap_loop(handle, -1, proc_packet, NULL);
ANALYZE_LOG("ERROR: pcap_loop returned from an infinite loop\n");
cleanup();
print_stats();
exit(-1);
}
void init_mysqlsniffer()
{
init_tags();
init_state_map();
}
static void show_help(void)
{
ANALYZE_LOG("mysqlsniffer v1.2 - Watch MySQL traffic on a TCP/IP network\n\n");
ANALYZE_LOG("Usage: mysqlsniffer [OPTIONS] INTERFACE\n\n");
ANALYZE_LOG("OPTIONS:\n");
ANALYZE_LOG(" --port N Listen for MySQL on port number N (default 3306)\n");
ANALYZE_LOG(" --verbose Show extra packet information\n");
ANALYZE_LOG(" --tcp-ctrl Show TCP control packets (SYN, FIN, RST, ACK)\n");
ANALYZE_LOG(" --net-hdrs Show major IP and TCP header values\n");
ANALYZE_LOG(" --no-mysql-hdrs Do not show MySQL header (packet ID and length)\n");
ANALYZE_LOG(" --state Show state\n");
ANALYZE_LOG(" --v40 MySQL server is version 4.0\n");
ANALYZE_LOG(" --dump Dump all packets in hex\n");
ANALYZE_LOG(" --help Print this\n");
ANALYZE_LOG("\nOriginal source code and more information at:\n http://hackmysql.com/mysqlsniffer\n");
exit(0);
}
static void proc_ops(int argc, char *argv[])
{
struct option ops[] = {
{"port", 1, NULL, 'p'},
{"verbose", 0, NULL, 'v'},
{"tcp-ctrl", 0, NULL, 't'},
{"net-hdrs", 0, NULL, 'n'},
{"no-mysql-hdrs", 0, NULL, 'm'},
{"state", 0, NULL, 's'},
{"v40", 0, NULL, 'o'},
{"dump", 0, NULL, 'd'},
{"help", 0, NULL, 'h'},
{0, 0, 0, 0}
};
int o;
while((o = getopt_long(argc, argv, "p:qvthsd", ops, NULL)) != -1) {
switch(o) {
case 'p':
op.port = 1;
strncpy(filter_exp + 5, optarg, (strlen(optarg) < 6 ? strlen(optarg) : 5));
filter_exp[10] = '\0';
op.port_n = atoi(optarg);
break;
case 'v':
op.verbose = 1;
break;
case 't':
op.tcp_ctrl = 1;
break;
case 'n':
op.net_hdrs = 1;
break;
case 'm':
op.no_myhdrs = 1;
break;
case 's':
op.state = 1;
break;
case 'o':
op.v40 = 1;
break;
case 'd':
op.dump = 1;
break;
case 'h':
case '?':
show_help();
break;
default:
break;
}
}
}
//捕获 SIGINT 信号
static void handle_ctrl_c(int signo)
{
cleanup();
print_stats();
exit(0);
}
static void handle_sighup(int signo)
{
print_stats();
total_mysql_pkts = total_mysql_bytes = 0;
free_tags();
}
static void cleanup(void)
{
pcap_freecode(&fp);
pcap_close(handle);
if(NULL != buff_frag) {
free(buff_frag);
}
free_tags();
#if defined(__TO_FILE)
if (NULL != plog) {
fflush(plog);
fclose(plog);
}
#endif
}
static void print_stats(void)
{
fprintf(stdout, "%u MySQL packets captured (%u bytes)\n", total_mysql_pkts, total_mysql_bytes);
}
/*
MySQL packet processing
*/
/*
MySQL will send N amount of logical packets in one physical packet. Each
logical packet starts with a MySQL header which says how long that logical
pkt is minus the header itself (m->pkt_length). Along w/ the total length of
captured MySQL data from libpcap (total_len), we can seperate all the logical
pkts even though they all vary in length.
*
*
* 一个物理包可以包含多个逻辑包 [h1][h2][...] ->caplen
* 一个逻辑包也可以跨�