Skip to content

Instantly share code, notes, and snippets.

@brickgao
Last active June 9, 2016 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brickgao/61710dde777462e511722415c5996a82 to your computer and use it in GitHub Desktop.
Save brickgao/61710dde777462e511722415c5996a82 to your computer and use it in GitHub Desktop.
A simple sniffer
/*
* The MIT License (MIT)
* Copyright (c) <2016> <Brickgao>
*
* Permission is hereby granted, free of charge, to any person obtaininga
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <map>
#include <pcap.h>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <exception>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <boost/program_options.hpp>
const std::string COLOR_RED = "\033[0;31m";
const std::string COLOR_OFF = "\033[0m";
const std::string COLOR_GREEN = "\033[0;32m";
const std::string COLOR_YELLOW = "\033[0;33m";
void log_debug(std::string msg) {
std::cout << "[DEBUG] " << msg << std::endl;
}
void log_info(std::string msg) {
std::cout << COLOR_GREEN << "[INFO] " << msg << COLOR_OFF << std::endl;
}
void log_error(std::string msg) {
std::cout << COLOR_RED << "[ERROR] " << msg << COLOR_OFF << std::endl;
}
void log_warning(std::string msg) {
std::cout << COLOR_YELLOW << "[WARNNING] " << msg << COLOR_OFF << std::endl;
}
void unsignedchar2str(std::ostringstream &stringStream, const unsigned char &ch) {
stringStream << std::hex << std::setfill('0') << std::setw(2) << static_cast <unsigned int> (ch) << std::dec;
}
std::string unsignedchar2ip(const unsigned char *data) {
struct in_addr ip_addr;
unsigned int ip_data = 0;
for (int i = 0; i < 4; ++ i) {
ip_data <<= 8;
ip_data |= static_cast <unsigned int> (data[i]);
}
ip_addr.s_addr = htonl(ip_data);
return inet_ntoa(ip_addr);
}
void deal_with_arp_packet(const u_char *packet) {
std::ostringstream stringStream;
const unsigned char *payload = static_cast <const unsigned char *> (packet + 14);
log_info("Ethernet II Type: 0x0806(ARP)");
for (int i = 0; i < 2; ++ i)
unsignedchar2str(stringStream, payload[i]);
log_info("Hardware type: 0x" + stringStream.str() + (stringStream.str() == "0001" ? "(Ethernet)" : ""));
stringStream.str("");
for (int i = 0; i < 2; ++ i)
unsignedchar2str(stringStream, payload[i + 2]);
log_info("Portocol type: 0x" + stringStream.str() + (stringStream.str() == "0800" ? "(IPv4)" : ""));
stringStream.str("");
unsignedchar2str(stringStream, payload[4]);
log_info("Hardware size: 0x" + stringStream.str());
stringStream.str("");
unsignedchar2str(stringStream, payload[5]);
log_info("Protocol size: 0x" + stringStream.str());
stringStream.str("");
for (int i = 0; i < 2; ++ i)
unsignedchar2str(stringStream, payload[6 + i]);
log_info("Operation: 0x" + stringStream.str() + (stringStream.str() == "0001" ? "(Request)" : "(Reply)"));
stringStream.str("");
for (int i = 0; i < 6; ++ i) {
unsignedchar2str(stringStream, payload[8 + i]);
stringStream << (i != 5 ? ":" : "");
}
log_info("Sender physical address: " + stringStream.str());
stringStream.str("");
log_info("Sender IP address: " + unsignedchar2ip(payload + 14));
for (int i = 0; i < 6; ++ i) {
unsignedchar2str(stringStream, packet[i + 18]);
stringStream << (i != 5 ? ":" : "");
}
log_info("Target physical address: " + stringStream.str());
stringStream.str("");
log_info("Target IP address: " + unsignedchar2ip(payload + 24));
}
void deal_with_tcp_packet(const u_char *packet, int ip_payload_length) {
std::ostringstream stringStream;
const struct tcphdr *tcp_struct = reinterpret_cast <const struct tcphdr *> (packet);
stringStream << "Source port: " << ntohs(tcp_struct->source);
log_info(stringStream.str());
stringStream.str("");
stringStream << "Destination port: " << ntohs(tcp_struct->dest);
log_info(stringStream.str());
stringStream.str("");
stringStream << "Sequence number: " << tcp_struct->seq;
log_info(stringStream.str());
stringStream.str("");
stringStream << "Acknowledgement number: " << tcp_struct->ack_seq;
log_info(stringStream.str());
stringStream.str("");
unsigned int tcp_header_size = tcp_struct->doff * 4;
stringStream << "TCP header size: " << tcp_header_size;
log_info(stringStream.str());
stringStream.str("");
stringStream << "Reserve bits: " << ((tcp_struct->res1 << 2) | tcp_struct->res2);
log_info(stringStream.str());
stringStream.str("");
stringStream << "Urgent: " << (tcp_struct->urg ? "Set" : "Not Set");
log_info(stringStream.str());
stringStream.str("");
stringStream << "Acknowledgement: " << (tcp_struct->ack ? "Set" : "Not Set");
log_info(stringStream.str());
stringStream.str("");
stringStream << "Push: " << (tcp_struct->psh ? "Set" : "Not Set");
log_info(stringStream.str());
stringStream.str("");
stringStream << "Reset: " << (tcp_struct->rst ? "Set" : "Not Set");
log_info(stringStream.str());
stringStream.str("");
stringStream << "Syn: " << (tcp_struct->syn ? "Set" : "Not Set");
log_info(stringStream.str());
stringStream.str("");
stringStream << "Fin: " << (tcp_struct->fin ? "Set" : "Not Set");
log_info(stringStream.str());
stringStream.str("");
stringStream << "Windows size value: " << tcp_struct->window;
log_info(stringStream.str());
stringStream.str("");
stringStream << "TCP header checksum: 0x" << std::hex << static_cast <unsigned int> (tcp_struct->check) << std::dec;
log_info(stringStream.str());
stringStream.str("");
const u_char *payload = packet + tcp_header_size;
const int payload_length = ip_payload_length - tcp_header_size;
stringStream << "TCP payload: " << std::hex;
if (payload_length == 0) stringStream << "None";
else {
for (int i = 0; i < payload_length; ++ i) stringStream << std::setw(2) << std::setfill('0') << static_cast <unsigned int> (payload[i]) << " ";
stringStream << std::dec;
}
log_info(stringStream.str());
stringStream.str("");
}
void deal_with_udp_packet(const u_char *packet) {
std::ostringstream stringStream;
const struct udphdr *udp_struct = reinterpret_cast <const struct udphdr *> (packet);
stringStream << "Source port: " << ntohs(udp_struct->source);
log_info(stringStream.str());
stringStream.str("");
stringStream << "Destination port: " << ntohs(udp_struct->dest);
log_info(stringStream.str());
stringStream.str("");
const int udp_len = ntohs(udp_struct->len);
stringStream << "UDP length: " << udp_len;
log_info(stringStream.str());
stringStream.str("");
stringStream << "TCP header checksum: 0x" << std::hex << static_cast <unsigned int> (udp_struct->check) << std::dec;
log_info(stringStream.str());
stringStream.str("");
const int udp_header_size = 16;
const int payload_length = udp_len - udp_header_size;
const u_char *payload = packet + udp_header_size;
stringStream << "UDP payload: " << std::hex;
if (payload_length == 0) stringStream << "None";
else {
for (int i = 0; i < payload_length; ++ i) stringStream << std::setw(2) << std::setfill('0') << static_cast <unsigned int> (payload[i]) << " ";
stringStream << std::dec;
}
log_info(stringStream.str());
stringStream.str("");
}
void deal_with_icmp_packet(const u_char *packet) {
std::ostringstream stringStream;
const struct icmphdr *icmp_struct = reinterpret_cast <const struct icmphdr *> (packet);
std::map <int, std::string> icmp_type2str = {{0, "Echo Reply"}, {3, "Destination Unreachable"}, {5, "Redirect"}, {8, "Echo Request"}};
stringStream << "ICMP type: " << static_cast <unsigned int> (icmp_struct->type);
if (icmp_type2str.find(icmp_struct->type) != icmp_type2str.end()) stringStream << "(" + icmp_type2str[icmp_struct->type] + ")";
log_info(stringStream.str());
stringStream.str("");
stringStream << "ICMP code: " << static_cast <unsigned int> (icmp_struct->code);
log_info(stringStream.str());
stringStream.str("");
stringStream << "ICMP header checksum: 0x" << std::hex << static_cast <unsigned int> (icmp_struct->checksum) << std::dec;
log_info(stringStream.str());
stringStream.str("");
switch (static_cast <unsigned int> (icmp_struct->code)) {
case 0:
case 8: {
int id = static_cast <unsigned int> (packet[4]) << 8;
id |= static_cast <unsigned int> (packet[5]);
stringStream << "ICMP identification: " << std::hex << id << std::dec;
log_info(stringStream.str());
stringStream.str("");
int seq = static_cast <unsigned int> (packet[6]) << 8;
seq |= static_cast <unsigned int> (packet[7]);
stringStream << "ICMP sequence: " << seq;
log_info(stringStream.str());
stringStream.str("");
stringStream << "ICMP payload: " << std::hex;
for (int i = 8; i < 40; ++ i) stringStream << std::setw(2) << std::setfill('0') << static_cast <unsigned int> (packet[i]) << " ";
stringStream << std::dec;
log_info(stringStream.str());
stringStream.str("");
break;
}
default:
log_warning("Unsupported ICMP Protocol");
break;
}
}
void deal_with_ip_packet(const u_char *packet) {
std::ostringstream stringStream;
log_info("Ethernet II Type: 0x0800(IPv4)");
struct ip *ip_struct = (struct ip *)(packet + 14);
stringStream << "IP Version: " << static_cast <unsigned int> (ip_struct->ip_v);
log_info(stringStream.str());
stringStream.str("");
int ip_hl = ((static_cast <unsigned int> (ip_struct->ip_hl)) << 2);
stringStream << "IP header length: " << ip_hl;
log_info(stringStream.str());
stringStream.str("");
stringStream << "Differentiated services codepoint: " << ((static_cast <unsigned int> (packet[16])) >> 2);
log_info(stringStream.str());
stringStream.str("");
stringStream << "Explicit congestion notification: " << ((static_cast <unsigned int> (packet[16])) & 0x03);
log_info(stringStream.str());
stringStream.str("");
const int ip_total_length = ntohs(ip_struct->ip_len);
stringStream << "Total length: " << ip_total_length;
log_info(stringStream.str());
stringStream.str("");
stringStream << "Identification: 0x" << std::hex << static_cast <unsigned int> (ip_struct->ip_id) << std::dec;
log_info(stringStream.str());
stringStream.str("");
int flags = static_cast <int> (packet[20]) >> 5;
stringStream << "Flags: 0x" << std::hex << flags << std::dec;
log_info(stringStream.str());
stringStream.str("");
log_info(std::string("Reservered bit: ") + (flags & 0x4 ? "set" : "not set"));
log_info(std::string("Don't fragment: ") + (flags & 0x2 ? "set" : "not set"));
log_info(std::string("More fragement: ") + (flags & 0x1 ? "set" : "not set"));
unsigned int ip_off = (static_cast <unsigned int>(packet[20]) & 31) << 8;
ip_off |= static_cast <unsigned int>(packet[21]);
stringStream << "Fragment offset: " << ip_off;
log_info(stringStream.str());
stringStream.str("");
stringStream << "Time to live: " << static_cast <unsigned int> (ip_struct->ip_ttl);
log_info(stringStream.str());
stringStream.str("");
stringStream << "Header checksum: 0x" << std::hex << static_cast <unsigned int> (ip_struct->ip_sum) << std::dec;
log_info(stringStream.str());
stringStream.str("");
stringStream << "Source: " << inet_ntoa(ip_struct->ip_src);
log_info(stringStream.str());
stringStream.str("");
stringStream << "Destination: " << inet_ntoa(ip_struct->ip_dst);
log_info(stringStream.str());
stringStream.str("");
switch (ip_struct->ip_p) {
case IPPROTO_TCP:
log_info("Protocol: TCP(6)");
if (ip_off == 0) deal_with_tcp_packet(packet + 14 + ip_hl, ip_total_length - ip_hl);
else log_warning("This packet isn't first packet, ignore it");
break;
case IPPROTO_UDP:
log_info("Protocol UDP(17)");
if (ip_off == 0) deal_with_udp_packet(packet + 14 + ip_hl);
else log_warning("This packet isn't first packet, ignore it");
break;
case IPPROTO_ICMP:
log_info("Protocol ICMP(1)");
if (ip_off == 0) deal_with_icmp_packet(packet + 14 + ip_hl);
else log_warning("This packet isn't first packet, ignore it");
break;
default:
log_warning("Unsupported IP Protocol");
break;
}
}
void get_packet(u_char*args, const struct pcap_pkthdr *header, const u_char *packet) {
static int count = 1;
std::ostringstream stringStream;
stringStream << "Package No." << count ++;
log_debug(stringStream.str());
stringStream.str("");
log_info("Get a Ethernet II packet");
for (int i = 0; i < 6; ++ i) {
unsignedchar2str(stringStream, packet[i]);
stringStream << (i != 5 ? ":" : "");
}
log_info("Destination physical address: " + stringStream.str());
stringStream.str("");
for (int i = 0; i < 6; ++ i) {
unsignedchar2str(stringStream, packet[i + 6]);
stringStream << (i != 5 ? ":" : "");
}
log_info("Source physical address: " + stringStream.str());
stringStream.str("");
if (static_cast <unsigned int> (packet[12]) == 0x08 && static_cast <unsigned int> (packet[13]) == 0x06)
deal_with_arp_packet(packet);
else if (static_cast <unsigned int> (packet[12]) == 0x08 && static_cast <unsigned int> (packet[13]) == 0x00)
deal_with_ip_packet(packet);
else
log_warning("Unsupported Ethernet II type");
}
int main(int argc, char *argv[]) {
std::ostringstream stringStream;
char *dev = NULL, errbuf[PCAP_ERRBUF_SIZE], dev_buffer[255];
struct bpf_program fp;
std::string filter_exp, dev_string;
int total_packets = 10;
bpf_u_int32 mask, net;
// prase the arguments
namespace po = boost::program_options;
po::options_description options("Allowed options");
options.add_options()
("help,h", "produce help message")
("interface,i", po::value<std::string>(&dev_string), "specific interface")
("filter,f", po::value<std::string>(&filter_exp), "filter of sniffer")
("number,n", po::value<int>(&total_packets)->default_value(10), "numbers of packets to deal with")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, options), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << "A simple sniffer by Brickgao" << std::endl;
std::cout << options;
std::cout << "Example:" << std::endl;
std::cout << " sniffer" << std::endl;
std::cout << " sniffer -i eth0" << std::endl;
std::cout << " sniffer -f \"port 80\"" << std::endl;
std::cout << " sniffer -i eth0 -f \"port 80\"" << std::endl;
std::cout << " sniffer -i eth0 -f \"port 80\" -n 20" << std::endl;
return(0);
}
if (vm.count("interface")) {
strcpy(dev_buffer, dev_string.c_str());
dev = dev_buffer;
}
if (dev == NULL) {
// get the first device it can find(if any) except the loopback device
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
log_error(errbuf);
return(2);
}
}
stringStream << "The sniff interface is: " << dev;
std::string get_interface_info_msg= stringStream.str();
stringStream.str("");
log_info(get_interface_info_msg);
// get subnet mask and subnet address of current device
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
log_error(errbuf);
mask = net = 0;
}
else {
struct in_addr net_addr, subnet_mask;
net_addr.s_addr = net;
stringStream << "Current subnet address is: " << inet_ntoa(net_addr);
std::string net_str = stringStream.str();
stringStream.str("");
log_info(net_str);
subnet_mask.s_addr = mask;
stringStream << "Current subnet mask is: " << inet_ntoa(subnet_mask);
std::string mask_str = stringStream.str();
stringStream.str("");
log_info(mask_str);
}
// get the handler that could use to get packets
pcap_t * handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
if (handle == NULL) {
log_error(errbuf);
return(2);
}
// compiler the filter for packets
if (pcap_compile(handle, &fp, filter_exp.c_str(), 0, net) == -1) {
stringStream << "Can't parse filter " << filter_exp << ":" << pcap_geterr(handle);
log_error(stringStream.str());
stringStream.str("");
return(2);
}
else {
if (filter_exp != "") {
stringStream << "Current filter for packets is: " << filter_exp;
log_info(stringStream.str());
stringStream.str("");
}
}
// set filter
if (pcap_setfilter(handle,&fp) == -1){
stringStream << "Can't install filter " << filter_exp << ":" << pcap_geterr(handle);
log_error(stringStream.str());
stringStream.str("");
return(2);
}
try {
pcap_loop(handle, total_packets, get_packet, NULL);
}
catch (std::exception& e) {
log_error(e.what());
}
pcap_freecode(&fp);
pcap_close(handle);
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment