dnsniff

DNS sniffer
git clone https://git.sinitax.com/sinitax/dnsniff
Log | Files | Refs | LICENSE | sfeed.txt

dnsniff.c (5680B)


      1#include <asm-generic/socket.h>
      2#include <net/if.h>
      3#include <netinet/in.h>
      4#include <netinet/udp.h>
      5#include <netinet/ip.h>
      6#include <netpacket/packet.h>
      7#include <linux/filter.h>
      8#include <linux/if_ether.h>
      9#include <arpa/inet.h>
     10#include <sys/socket.h>
     11#include <sys/ioctl.h>
     12#include <err.h>
     13#include <errno.h>
     14
     15#include <stdlib.h>
     16#include <stdio.h>
     17#include <string.h>
     18#include <stdint.h>
     19#include <stdbool.h>
     20
     21#define PACKETBUFLEN (1024 * 1024)
     22#define HDRLEN (sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr))
     23
     24struct dnshdr {
     25	uint16_t id;
     26	struct {
     27		uint16_t qr : 1;
     28		uint16_t op : 4;
     29		uint16_t aa : 1;
     30		uint16_t tc : 1;
     31		uint16_t rd : 1;
     32		uint16_t ra : 1;
     33		uint16_t zr : 3;
     34		uint16_t rc : 4;
     35	} flags;
     36	uint16_t questions;
     37	uint16_t answers;
     38	uint16_t authorities;
     39	uint16_t additionals;
     40} __attribute__((packed));
     41
     42struct dnsdec {
     43	struct dnshdr *hdr;
     44	size_t pos;
     45
     46	char *qname;
     47	uint16_t type;
     48	uint16_t class;
     49};
     50
     51struct packet {
     52	struct ethhdr *eth;
     53	struct iphdr *ip;
     54	struct udphdr *udp;
     55	uint8_t *data;
     56	size_t len;
     57};
     58
     59struct filter {
     60	uint32_t src_ip, dst_ip;
     61	uint16_t dst_port;
     62};
     63
     64static struct sock_filter udp_filter[] = {
     65	{ BPF_LD + BPF_H + BPF_ABS,  0, 0,     12 },
     66	{ BPF_JMP + BPF_JEQ + BPF_K, 0, 1,  0x800 },
     67	{ BPF_LD + BPF_B + BPF_ABS,  0, 0,     23 },
     68	{ BPF_JMP + BPF_JEQ + BPF_K, 0, 1,   0x11 },
     69	{ BPF_RET + BPF_K,           0, 0, 0xffff },
     70	{ BPF_RET + BPF_K,           0, 0, 0x0000 },
     71};
     72
     73static int sock, ifid;
     74
     75void
     76usage(void)
     77{
     78	printf("Usage: dnsniff IFACE\n");
     79	exit(0);
     80}
     81
     82void
     83hexdump(const uint8_t *data, size_t len)
     84{
     85	size_t i;
     86
     87	for (i = 0; i < len; i++) {
     88		printf("%02X ", data[i]);
     89		if ((i + 1) % 16 == 0)
     90			printf("\n");
     91	}
     92	if (len % 16 != 0)
     93		printf("\n");
     94}
     95
     96void
     97sniff_init(const char *ifname)
     98{
     99	struct ifreq ifreq;
    100	struct packet_mreq mreq;
    101	struct sock_fprog prog;
    102	struct sockaddr_ll sa;
    103	int ret;
    104
    105	sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    106	if (sock < 0) err(1, "failed to open socket");
    107
    108	/* get interface index */
    109	strncpy(ifreq.ifr_name, ifname, IFNAMSIZ);
    110	ret = ioctl(sock, SIOCGIFINDEX, &ifreq);
    111	if (ret) err(1, "failed to lookup interface index");
    112	ifid = ifreq.ifr_ifindex;
    113
    114	/* enable promiscuous mode on interface */
    115	mreq.mr_ifindex = ifid;
    116	mreq.mr_type = PACKET_MR_PROMISC;
    117	ret = setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
    118		&mreq, sizeof(mreq));
    119	if (ret) err(1, "failed to enable promiscuous mode");
    120
    121	/* filter for udp */
    122	prog.len = sizeof(udp_filter) / sizeof(struct sock_filter);
    123	prog.filter = udp_filter;
    124	ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
    125		&prog, sizeof(prog));
    126	if (ret) err(1, "failed to attach udp filter");
    127
    128	/* bind socket to interface */
    129	sa.sll_family = AF_PACKET;
    130	sa.sll_ifindex = ifid;
    131	sa.sll_protocol = htons(ETH_P_ALL);
    132	ret = bind(sock, (struct sockaddr *)&sa, sizeof(sa));
    133	if (ret) err(1, "failed to bind interface");
    134}
    135
    136bool
    137sniff(struct packet *pkt, struct filter *flt, uint8_t *buf, size_t len)
    138{
    139	char src_tmp[INET_ADDRSTRLEN];
    140	ssize_t res;
    141
    142	while (true) {
    143		res = recvfrom(sock, buf, len, 0, NULL, 0);
    144		if (res < 0 && errno == EINTR)
    145			return false;
    146
    147		if (res < 0) err(1, "failed to read packet");
    148
    149		if (res < HDRLEN) continue;
    150
    151		pkt->eth = (struct ethhdr *) buf;
    152		pkt->ip = (struct iphdr *) (&pkt->eth[1]);
    153		pkt->udp = (struct udphdr *) (&pkt->ip[1]);
    154		pkt->data = (uint8_t *) (&pkt->udp[1]);
    155		pkt->len = htons(pkt->udp->len) - sizeof(struct udphdr);
    156
    157		if (flt->src_ip && flt->src_ip != pkt->ip->saddr)
    158			continue;
    159
    160		if (flt->dst_ip && flt->dst_ip != pkt->ip->daddr)
    161			continue;
    162
    163		if (flt->dst_port && flt->dst_port != pkt->udp->dest)
    164			continue;
    165
    166		break;
    167	}
    168
    169	return true;
    170}
    171
    172void
    173sniff_deinit(void)
    174{
    175	struct packet_mreq mreq;
    176	int ret;
    177
    178	/* disable promiscuous mode on interface */
    179	mreq.mr_ifindex = ifid;
    180	mreq.mr_type = PACKET_MR_PROMISC;
    181	ret = setsockopt(sock, SOL_PACKET, PACKET_DROP_MEMBERSHIP,
    182		&mreq, sizeof(mreq));
    183	if (ret) err(1, "failed to enable promiscuous mode");
    184}
    185
    186void
    187decode_init(struct dnsdec *dec)
    188{
    189	memset(dec, 0, sizeof(struct dnsdec));
    190}
    191
    192bool
    193decode_query(struct dnsdec *dec, const uint8_t *data, size_t len)
    194{
    195	const uint8_t *end;
    196	char *c;
    197
    198	if (!dec->pos) {
    199		dec->hdr = (struct dnshdr *) data;
    200		dec->hdr->questions = ntohs(dec->hdr->questions);
    201		dec->pos = sizeof(struct dnshdr);
    202	}
    203
    204	end = memchr(data + dec->pos, '\0', len - dec->pos);
    205	if (!end || end + 5 > data + len) return false;
    206	dec->qname = strndup((const char *) data + dec->pos,
    207		end - data - dec->pos);
    208	if (!dec->qname) err(1, "strndup");
    209	for (c = dec->qname; *c; c++) {
    210		if (*c == 0x0a) *c = '.';
    211		if (*c == 0x03) *c = '.';
    212		if (*c == 0x09) *c = '.';
    213	}
    214
    215	dec->type = ntohs(*(uint16_t *)(end + 1));
    216	dec->class = ntohs(*(uint16_t *)(end + 3));
    217	dec->pos = end + 5 - data;
    218
    219	return true;
    220}
    221
    222void
    223decode_deinit(struct dnsdec *dec)
    224{
    225	free(dec->qname);
    226}
    227
    228int
    229main(int argc, const char **argv)
    230{
    231	const char *ifname;
    232	char src_ip[INET_ADDRSTRLEN];
    233	char dst_ip[INET_ADDRSTRLEN];
    234	struct dnsdec dec;
    235	struct filter flt;
    236	struct packet pkt;
    237	uint8_t *buf;
    238
    239	if (argc != 2) usage();
    240	ifname = argv[1];
    241
    242	buf = malloc(PACKETBUFLEN);
    243	if (!buf) err(1, "malloc");
    244
    245	sniff_init(ifname);
    246
    247	flt.dst_ip = 0;
    248	flt.src_ip = 0;
    249	flt.dst_port = htons(53);
    250	while (sniff(&pkt, &flt, buf, PACKETBUFLEN)) {
    251		inet_ntop(AF_INET, (struct in_addr *) &pkt.ip->saddr,
    252			src_ip, INET_ADDRSTRLEN);
    253		inet_ntop(AF_INET, (struct in_addr *) &pkt.ip->daddr,
    254			dst_ip, INET_ADDRSTRLEN);
    255		printf("%s -> %s\n", src_ip, dst_ip);
    256		decode_init(&dec);
    257		while (decode_query(&dec, pkt.data, pkt.len))
    258			printf("  %s\n", dec.qname);
    259		decode_deinit(&dec);
    260		printf("\n");
    261	}
    262
    263	free(buf);
    264	sniff_deinit();
    265}