/********************************************************************** * file: pgqueryspy.c * date: Wed Fed 22 20:07:00 PDT 2006 * Author: Scott Sanders (adapted from Martin's disect2.c) * Author: Martin Casado * Last Modified:2006-Feb-22 08:37:22 PM * * Description: * * Spy on all traffic headed to the database server to print out the * queries. * * Large amounts of this code were taken from tcpdump source * namely the following files.. * * print-ether.c * print-ip.c * ip.h * * Compile with: * gcc -Wall -pedantic pgqueryspy.c -lpcap -o pgqueryspy * * Usage: * pgqueryspy (# of packets) "postgres host name" * **********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include /* tcpdump header (ether.h) defines ETHER_HDRLEN) */ #ifndef ETHER_HDRLEN #define ETHER_HDRLEN 14 #endif u_int16_t handle_ethernet (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet); u_char* handle_IP (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet); /* * Structure of an internet header, naked of options. * * Stolen from tcpdump source (thanks tcpdump people) * * We declare ip_len and ip_off to be short, rather than u_short * pragmatically since otherwise unsigned comparisons can result * against negative integers quite easily, and fail in subtle ways. */ struct my_ip { u_int8_t ip_vhl; /* header length, version */ #define IP_V(ip) (((ip)->ip_vhl & 0xf0) >> 4) #define IP_HL(ip) ((ip)->ip_vhl & 0x0f) u_int8_t ip_tos; /* type of service */ u_int16_t ip_len; /* total length */ u_int16_t ip_id; /* identification */ u_int16_t ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_int8_t ip_ttl; /* time to live */ u_int8_t ip_p; /* protocol */ u_int16_t ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; /* looking at ethernet headers */ void my_callback(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet) { u_int16_t type = handle_ethernet(args,pkthdr,packet); if(type == ETHERTYPE_IP) {/* handle IP packet */ handle_IP(args,pkthdr,packet); }else if(type == ETHERTYPE_ARP) {/* handle arp packet */ } else if(type == ETHERTYPE_REVARP) {/* handle reverse arp packet */ } } u_char* handle_IP (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet) { const u_char *payload; /* Packet payload */ int len; /* Postgres packets that I have seen start with 'Q', and then 4 bytes of length. We ignore that, plus the tcp header, plus the ip header, plus the ethernet header */ payload = (u_char *)(packet + sizeof(struct ether_header) + 20 + 32 + 5); len = strlen(payload); /* Ingnore the "END" queries */ if (len > 3) { /* Ignore the BEGIN queries */ if (len > 5) { if (memcmp(payload, "BEGIN", 5)) { printf("%s\n", payload); } } } return NULL; } /* handle ethernet packets, much of this code gleaned from * print-ether.c from tcpdump source */ u_int16_t handle_ethernet (u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet) { u_int caplen = pkthdr->caplen; /* u_int length = pkthdr->len; */ struct ether_header *eptr; /* net/ethernet.h */ u_short ether_type; if (caplen < ETHER_HDRLEN) { fprintf(stdout,"Packet length less than ethernet header length\n"); return -1; } /* lets start with the ether header... */ eptr = (struct ether_header *) packet; ether_type = ntohs(eptr->ether_type); return ether_type; } int main(int argc,char **argv) { char *dev; char errbuf[PCAP_ERRBUF_SIZE]; pcap_t* descr; struct bpf_program fp; /* hold compiled program */ bpf_u_int32 maskp; /* subnet mask */ bpf_u_int32 netp; /* ip */ u_char* args = NULL; /* Options must be passed in as a string because I am lazy */ if(argc < 2){ fprintf(stdout,"Usage: %s numpackets\n",argv[0]); return 0; } /* grab a device to peak into... */ dev = pcap_lookupdev(errbuf); if(dev == NULL) { printf("%s\n",errbuf); exit(1); } /* ask pcap for the network address and mask of the device */ pcap_lookupnet(dev,&netp,&maskp,errbuf); /* open device for reading. NOTE: defaulting to * promiscuous mode*/ descr = pcap_open_live(dev,BUFSIZ,1,-1,errbuf); if(descr == NULL) { printf("pcap_open_live(): %s\n",errbuf); exit(1); } if(argc > 2) { char buff[200]; int len; /* check all packets heading to the database server on postgres port (5432) */ len = sprintf(buff, "dst host %s and dst port postgres", argv[2]); printf("%s\n", buff); /* Lets try and compile the program.. non-optimized */ if(pcap_compile(descr,&fp,buff,0,netp) == -1) { fprintf(stderr,"Error calling pcap_compile\n"); exit(1); } /* set the compiled program as the filter */ if(pcap_setfilter(descr,&fp) == -1) { fprintf(stderr,"Error setting filter\n"); exit(1); } } /* ... and loop */ pcap_loop(descr,atoi(argv[1]),my_callback,args); return 0; }