Last active
June 9, 2016 16:32
-
-
Save brickgao/61710dde777462e511722415c5996a82 to your computer and use it in GitHub Desktop.
A simple sniffer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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