/*
* Written by Pejman Moghadam / 1404-10-30
* Public domain.
*
*/
/* gcc analyze.c -lpcap -o analyze */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <pcap/pcap.h>
#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <stdint.h>
// DNS header as specified by RFC 1035, November 1987.
struct dnshdr {
uint16_t id; // Identification number
// Flags fields (bit fields)
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t rd :1; // Recursion desired
uint8_t tc :1; // Truncated
uint8_t aa :1; // Authoritative answer
uint8_t opcode :4; // Operation code
uint8_t qr :1; // Query/response flag
uint8_t rcode :4; // Response code
uint8_t z :3; // Reserved (zero)
uint8_t ra :1; // Recursion available
#else
uint8_t qr :1; // Query/response flag
uint8_t opcode :4; // Operation code
uint8_t aa :1; // Authoritative answer
uint8_t tc :1; // Truncated
uint8_t rd :1; // Recursion desired
uint8_t ra :1; // Recursion available
uint8_t z :3; // Reserved (zero)
uint8_t rcode :4; // Response code
#endif
uint16_t qdcount; // Question count
uint16_t ancount; // Answer count
uint16_t nscount; // Authority count
uint16_t arcount; // Additional count
} __attribute__((packed));
struct {
int datalink_type;
int datalink_header_len;
} pktinfo;
char *ether_ntoa_padded(const struct ether_addr *addr, char *buf)
{
sprintf (buf, "%02x:%02x:%02x:%02x:%02x:%02x",
addr->ether_addr_octet[0], addr->ether_addr_octet[1],
addr->ether_addr_octet[2], addr->ether_addr_octet[3],
addr->ether_addr_octet[4], addr->ether_addr_octet[5]);
return buf;
}
void show_ethernet_header(const u_char *packet, int len)
{
/* man 3 ether_aton */
/* /usr/include/linux/if_ether.h */
struct ethhdr *h;
h = (struct ethhdr *)packet;
struct ether_addr *dst;
dst = (struct ether_addr *)(h->h_dest);
struct ether_addr *src;
src = (struct ether_addr *)(h->h_source);
char addr_str[INET6_ADDRSTRLEN];
printf("Ethernet header info:\n");
printf("\tdst: %s (%s)\n", ether_ntoa(dst),
ether_ntoa_padded(dst, addr_str));
printf("\tsrc: %s (%s)\n", ether_ntoa(src),
ether_ntoa_padded(src, addr_str));
printf("\ttype: 0x%04X\n", ntohs(h->h_proto));
printf("\n");
}
struct sll_header {
uint16_t sll_pkttype; /* packet type */
uint16_t sll_hatype; /* link-layer address type */
uint16_t sll_halen; /* link-layer address length */
char sll_addr[8]; /* link-layer address */
uint16_t sll_protocol; /* protocol */
};
void show_linuxsll_header(const u_char *packet, int len)
{
/* man 3 ether_aton */
/* /usr/include/linux/if_ether.h */
struct sll_header *h;
h = (struct sll_header *)packet;
printf("SLL header info:\n");
printf("\tPacket type : %d\n", ntohs(h->sll_pkttype));
printf("\tlink-layer address type : %d\n", ntohs(h->sll_hatype));
printf("\tlink-layer address length : %d\n", ntohs(h->sll_halen));
printf("\tprotocol : %d\n", ntohs(h->sll_protocol));
printf("\n");
}
void show_endianness()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
printf("Endianness : Little Endian\n");
#elif __BYTE_ORDER == __BIG_ENDIAN
printf("Endianness : Big Endian\n");
#else
printf("Endianness : Unknown\n");
#endif
}
void show_ip_header(const u_char *packet, int len)
{
/* /usr/include/netinet/ip.h */
int length = sizeof(struct ip);
int offset = pktinfo.datalink_header_len;
struct ip *h;
h = (struct ip *)(packet + offset);
printf("IP Header (Offset:%d, Length:%d)\n", offset, length);
printf("\tver : %X\n",h->ip_v);
printf("\tihl : %X\n", h->ip_hl);
printf("\ttos : 0x%X\n", h->ip_tos);
printf("\tlen : %u\n", ntohs(h->ip_len));
printf("\tid : %u\n", ntohs(h->ip_id));
printf("\tfrag : %X ", ntohs(h->ip_off));
if(ntohs(h->ip_off) == 0x4000)
printf("(DF)");
if(ntohs(h->ip_off) == 0x2000)
printf("(MF)");
printf("\n");
printf("\tttl : %u\n", h->ip_ttl);
printf("\tproto : %u\n", h->ip_p);
printf("\tsum : 0x%X\n", ntohs(h->ip_sum));
char ipv4str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &h->ip_src, ipv4str, INET_ADDRSTRLEN);
printf("\tsaddr : %X (%s)\n", ntohl(*(uint32_t *)(&h->ip_src)),
ipv4str);
inet_ntop(AF_INET, &h->ip_dst, ipv4str, INET_ADDRSTRLEN);
printf("\tdaddr : %X (%s)\n", ntohl(*(uint32_t *)(&h->ip_dst)),
ipv4str);
printf("\n");
}
void show_udp_header(const u_char *packet, int len)
{
// UDP header as specified by RFC 768, August 1980.
// /usr/include/netinet/udp.h
int length = sizeof(struct udphdr);
int offset = pktinfo.datalink_header_len + sizeof(struct ip);
struct udphdr *h;
h = (struct udphdr *)(packet + offset);
printf("UDP Header (Offset:%d, Length:%d)\n", offset, length);
printf("\tSource Port : 0x%x (%u)\n",
ntohs(h->source), ntohs(h->source));
printf("\tDestination Port : 0x%x (%u)\n",
ntohs(h->dest), ntohs(h->dest));
printf("\tLength : %u\n", ntohs(h->len));
printf("\tChecksum : 0x%x\n", ntohs(h->check));
printf("\n");
}
void show_packet(const u_char *packet, int len)
{
int j = 0;
char s[17] = {0};
for(int i=0; i<len; i++) {
if(j==0)
printf("0x%04x: ", i);
printf("%02x", *(packet+i));
if(*(packet+i) >= 32 && *(packet+i) <=127)
s[j] = *(packet+i);
else
s[j] = '.';
if((j%2)!=0)
printf(" ");
if(j==15) {
s[16] = '\0';
printf(" %s\n", s);
j = 0;
}
else
j++;
}
s[j] = '\0';
int space = 39-(j*2)+(j/2)-1;
printf(" %*s\n", space+2, s);
printf("\n");
}
void show_dns_header(const u_char *packet, int len)
{
int length = sizeof(struct dnshdr);
int offset = pktinfo.datalink_header_len +
sizeof(struct ip) +
sizeof(struct udphdr);
struct dnshdr *h;
h = (struct dnshdr *)(packet + offset);
printf("DNS Header (Offset:%d, Length:%d)\n", offset, length);
printf("\tID : ");
printf("0x%x (%d)\n", htons(h->id), htons(h->id));
printf("\tMessage type : ");
if(h->qr == 0) puts("Query");
if(h->qr == 1) puts("Response");
printf("\tQuery type : ");
if(h->opcode == 0) puts("Standard query (QUERY)");
if(h->opcode == 1) puts("Inverse query (IQUERY)");
if(h->opcode == 2) puts("Server status request (STATUS)");
printf("\tAuthorization : ");
if(h->qr == 1) {
// Response
if(h->aa == 1)
puts("Authoritative answer");
if(h->aa == 0)
puts("Non-authoritative answer");
} else {
// Query
if(h->aa == 1)
puts("ANOMALY (AA bit set in query message)");
if(h->aa == 0)
puts("Not set");
}
printf("\tTrunCation : ");
if(h->tc == 1) puts("Truncated");
if(h->tc == 0) puts("Not truncated");
printf("\tRecursion Desired : ");
if(h->rd == 1) puts("Yes");
if(h->rd == 0) puts("No");
printf("\tRecursion Available : ");
if(h->qr == 1) {
// Response
if(h->ra == 1)
puts("Yes");
if(h->ra == 0)
puts("No");
} else {
// Query
if(h->aa == 1)
puts("ANOMALY (RA bit set in query message)");
if(h->aa == 0)
puts("Not set");
}
printf("\tReserved : ");
if(h->z == 0) puts("Not set");
if(h->z != 0) puts("ANOMALY (Z bit not zero)");
printf("\tResponse code : ");
if(h->qr == 1) {
// Response
if(h->rcode == 0) puts("No error");
if(h->rcode == 1) puts("Format error");
if(h->rcode == 2) puts("Server failure");
if(h->rcode == 3) {
if(h->aa == 1) // Authoritative answer
puts("Name error");
if(h->aa == 0)
puts("ANOMALY"
" (RCODE bit set in Non-authoritative answer)");
}
if(h->rcode == 4) puts("Not Implemented");
if(h->rcode == 5) puts("Refused");
if((h->rcode >= 6) && (h->rcode <= 15)) puts("Reserved");
} else {
// Query
if(h->rcode == 0)
puts("Not set");
if(h->rcode != 0)
puts("ANOMALY (RCODE bit set in query message)");
}
printf("\tQuestion entries : %d\n", ntohs(h->qdcount));
printf("\tAnswer RRs : %d\n", ntohs(h->ancount));
printf("\tAuthority RRs : %d\n", ntohs(h->nscount));
printf("\tAdditional RRs : %d\n", ntohs(h->arcount));
printf("\n");
}
void show_datalink_type_name(pcap_t *p)
{
printf("datalink type: %d\n", pktinfo.datalink_type);
printf("datalink name: %s\n",
pcap_datalink_val_to_name(pktinfo.datalink_type));
printf("datalink description: %s\n",
pcap_datalink_val_to_description_or_dlt(pktinfo.datalink_type));
}
int get_datalink_header_len(pcap_t *p)
{
switch(pktinfo.datalink_type) {
case DLT_EN10MB:
// Ethernet
pktinfo.datalink_header_len = 14;
break;
case DLT_LINUX_SLL:
// Linux cooked-mode capture sockaddr_ll
pktinfo.datalink_header_len = 16;
break;
case DLT_LINUX_SLL2:
// Linux cooked-mode capture sockaddr_ll Ver2
pktinfo.datalink_header_len = 20;
break;
case DLT_NULL:
// Loopback
pktinfo.datalink_header_len = 4;
break;
default:
printf("Unknown header size for DLT %d\n",
pktinfo.datalink_type);
printf("Check pcap-linktype(7) man page"
"and https://www.tcpdump.org/linktypes.html"
"then update get_datalink_header_len() fuinction\n");
exit(EXIT_FAILURE);
break;
}
}
void show_datalink_header(const u_char *packet, int len)
{
// Ethernet
if(pktinfo.datalink_type == DLT_EN10MB)
show_ethernet_header(packet, len);
// Linux SLL
if(pktinfo.datalink_type == DLT_LINUX_SLL)
show_linuxsll_header(packet, len);
}
void packet_handler(u_char *user, const struct pcap_pkthdr *h,
const u_char *bytes)
{
static int packet_count = 0;
printf("--------------------------------------"
"--------------------------------------\n");
printf("# Packet: %d, Size: %d\n\n", ++packet_count, h->len);
show_packet(bytes, h->len);
show_datalink_header(bytes, h->len);
show_ip_header(bytes, h->len);
show_udp_header(bytes, h->len);
show_dns_header(bytes, h->len);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "\nUsage: %s <pcap_file>\n", argv[0]);
return 1;
}
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *p = pcap_open_offline(argv[1], errbuf);
if (p == NULL) {
fprintf(stderr, "\npcap_open_offline() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
show_endianness();
pktinfo.datalink_type = pcap_datalink(p);
show_datalink_type_name(p);
get_datalink_header_len(p);
if (pcap_loop(p, 0, packet_handler, NULL) < 0) {
fprintf(stderr, "\npcap_loop() failed: %s\n", pcap_geterr(p));
exit(EXIT_FAILURE);
}
return 0;
}
|