dnsniff

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

commit 30d8a9904de544ba14e5cc92b41244867e4c848e
Author: Louis Burda <quent.burda@gmail.com>
Date:   Wed,  4 Jan 2023 14:51:30 +0100

Initial version

Diffstat:
A.gitignore | 1+
AMakefile | 9+++++++++
Adnsniff.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 274 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +dnsniff diff --git a/Makefile b/Makefile @@ -0,0 +1,9 @@ + +all: dnsniff + +clean: + rm -f dnsniff + +dnsniff: dnsniff.c + +.PHONY: all clean diff --git a/dnsniff.c b/dnsniff.c @@ -0,0 +1,264 @@ +#include <asm-generic/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/udp.h> +#include <netinet/ip.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/if_ether.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <err.h> +#include <errno.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> + +#define HDRLEN (sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr)) + +struct dnshdr { + uint16_t id; + struct { + uint16_t qr : 1; + uint16_t op : 4; + uint16_t aa : 1; + uint16_t tc : 1; + uint16_t rd : 1; + uint16_t ra : 1; + uint16_t zr : 3; + uint16_t rc : 4; + } flags; + uint16_t questions; + uint16_t answers; + uint16_t authorities; + uint16_t additionals; +} __attribute__((packed)); + +struct dnsdec { + struct dnshdr *hdr; + size_t pos; + + char *qname; + uint16_t type; + uint16_t class; +}; + +struct packet { + struct ethhdr *eth; + struct iphdr *ip; + struct udphdr *udp; + uint8_t *data; + size_t len; +}; + +struct filter { + uint32_t src_ip, dst_ip; + uint16_t dst_port; +}; + +static struct sock_filter udp_filter[] = { + { BPF_LD + BPF_H + BPF_ABS, 0, 0, 12 }, + { BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0x800 }, + { BPF_LD + BPF_B + BPF_ABS, 0, 0, 23 }, + { BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0x11 }, + { BPF_RET + BPF_K, 0, 0, 0xffff }, + { BPF_RET + BPF_K, 0, 0, 0x0000 }, +}; + +static int sock, ifid; + +void +usage(void) +{ + printf("Usage: dnsniff INTERFACE\n"); + exit(0); +} + +void +hexdump(const uint8_t *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + printf("%02X ", data[i]); + if ((i + 1) % 16 == 0) + printf("\n"); + } + if (len % 16 != 0) + printf("\n"); +} + +void +sniff_init(const char *ifname) +{ + struct ifreq ifreq; + struct packet_mreq mreq; + struct sock_fprog prog; + struct sockaddr_ll sa; + int ret; + + sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (sock < 0) err(1, "failed to open socket"); + + /* get interface index */ + strncpy(ifreq.ifr_name, ifname, IFNAMSIZ); + ret = ioctl(sock, SIOCGIFINDEX, &ifreq); + if (ret) err(1, "failed to lookup interface index"); + ifid = ifreq.ifr_ifindex; + + /* enable promiscuous mode on interface */ + mreq.mr_ifindex = ifid; + mreq.mr_type = PACKET_MR_PROMISC; + ret = setsockopt(sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (ret) err(1, "failed to enable promiscuous mode"); + + /* filter for udp */ + prog.len = sizeof(udp_filter) / sizeof(struct sock_filter); + prog.filter = udp_filter; + ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, + &prog, sizeof(prog)); + if (ret) err(1, "failed to attach udp filter"); + + /* bind socket to interface */ + sa.sll_family = AF_PACKET; + sa.sll_ifindex = ifid; + sa.sll_protocol = htons(ETH_P_ALL); + ret = bind(sock, (struct sockaddr *)&sa, sizeof(sa)); + if (ret) err(1, "failed to bind interface"); +} + +bool +sniff(struct packet *pkt, struct filter *flt, uint8_t *buf, size_t len) +{ + char src_tmp[INET_ADDRSTRLEN]; + ssize_t res; + + while (true) { + res = recvfrom(sock, buf, len, 0, NULL, 0); + if (res < 0 && errno == EINTR) + return false; + + if (res < 0) err(1, "failed to read packet"); + + if (res < HDRLEN) continue; + + pkt->eth = (struct ethhdr *) buf; + pkt->ip = (struct iphdr *) (&pkt->eth[1]); + pkt->udp = (struct udphdr *) (&pkt->ip[1]); + pkt->data = (uint8_t *) (&pkt->udp[1]); + pkt->len = htons(pkt->udp->len) - sizeof(struct udphdr); + + if (flt->src_ip && flt->src_ip != pkt->ip->saddr) + continue; + + if (flt->dst_ip && flt->dst_ip != pkt->ip->daddr) + continue; + + if (flt->dst_port && flt->dst_port != pkt->udp->dest) + continue; + + break; + } + + return true; +} + +void +sniff_deinit(void) +{ + struct packet_mreq mreq; + int ret; + + /* disable promiscuous mode on interface */ + mreq.mr_ifindex = ifid; + mreq.mr_type = PACKET_MR_PROMISC; + ret = setsockopt(sock, SOL_PACKET, PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (ret) err(1, "failed to enable promiscuous mode"); +} + +void +decode_init(struct dnsdec *dec) +{ + memset(dec, 0, sizeof(struct dnsdec)); +} + +bool +decode_query(struct dnsdec *dec, const uint8_t *data, size_t len) +{ + const uint8_t *end; + char *c; + + if (!dec->pos) { + dec->hdr = (struct dnshdr *) data; + dec->hdr->questions = ntohs(dec->hdr->questions); + dec->pos = sizeof(struct dnshdr); + } + + end = memchr(data + dec->pos, '\0', len - dec->pos); + if (!end || end + 5 > data + len) return false; + dec->qname = strndup((const char *) data + dec->pos, + end - data - dec->pos); + if (!dec->qname) err(1, "strndup"); + for (c = dec->qname; *c; c++) { + if (*c == 0x0a) *c = '.'; + if (*c == 0x03) *c = '.'; + if (*c == 0x09) *c = '.'; + } + + dec->type = ntohs(*(uint16_t *)(end + 1)); + dec->class = ntohs(*(uint16_t *)(end + 3)); + dec->pos = end + 5 - data; + + return true; +} + +void +decode_deinit(struct dnsdec *dec) +{ + free(dec->qname); +} + +int +main(int argc, const char **argv) +{ + const char *ifname; + char src_ip[INET_ADDRSTRLEN]; + char dst_ip[INET_ADDRSTRLEN]; + struct dnsdec dec; + struct filter flt; + struct packet pkt; + uint8_t *buf; + + if (argc != 2) usage(); + ifname = argv[1]; + + buf = malloc(1024 * 1024); + if (!buf) err(1, "malloc"); + + sniff_init(ifname); + + flt.dst_ip = 0; + flt.src_ip = 0; + flt.dst_port = htons(53); + while (sniff(&pkt, &flt, buf, 1024 * 1024)) { + inet_ntop(AF_INET, (struct in_addr *) &pkt.ip->saddr, + src_ip, INET_ADDRSTRLEN); + inet_ntop(AF_INET, (struct in_addr *) &pkt.ip->daddr, + dst_ip, INET_ADDRSTRLEN); + printf("%s -> %s\n", src_ip, dst_ip); + decode_init(&dec); + while (decode_query(&dec, pkt.data, pkt.len)) + printf(" %s\n", dec.qname); + decode_deinit(&dec); + printf("\n"); + } + + free(buf); + sniff_deinit(); +}