summaryrefslogtreecommitdiff
path: root/src/dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dump.c')
-rw-r--r--src/dump.c211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/dump.c b/src/dump.c
new file mode 100644
index 0000000..9bd3a5f
--- /dev/null
+++ b/src/dump.c
@@ -0,0 +1,211 @@
+/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_DUMPFILE
+
+static u32 packet_count;
+
+/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
+struct pcap_hdr_s {
+ u32 magic_number; /* magic number */
+ u16 version_major; /* major version number */
+ u16 version_minor; /* minor version number */
+ u32 thiszone; /* GMT to local correction */
+ u32 sigfigs; /* accuracy of timestamps */
+ u32 snaplen; /* max length of captured packets, in octets */
+ u32 network; /* data link type */
+};
+
+struct pcaprec_hdr_s {
+ u32 ts_sec; /* timestamp seconds */
+ u32 ts_usec; /* timestamp microseconds */
+ u32 incl_len; /* number of octets of packet saved in file */
+ u32 orig_len; /* actual length of packet */
+};
+
+
+void dump_init(void)
+{
+ struct stat buf;
+ struct pcap_hdr_s header;
+ struct pcaprec_hdr_s pcap_header;
+
+ packet_count = 0;
+
+ if (stat(daemon->dump_file, &buf) == -1)
+ {
+ /* doesn't exist, create and add header */
+ header.magic_number = 0xa1b2c3d4;
+ header.version_major = 2;
+ header.version_minor = 4;
+ header.thiszone = 0;
+ header.sigfigs = 0;
+ header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */
+ header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */
+
+ if (errno != ENOENT ||
+ (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 ||
+ !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0))
+ die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE);
+ }
+ else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 ||
+ !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1))
+ die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE);
+ else if (header.magic_number != 0xa1b2c3d4)
+ die(_("bad header in %s"), daemon->dump_file, EC_FILE);
+ else
+ {
+ /* count existing records */
+ while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1))
+ {
+ lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR);
+ packet_count++;
+ }
+ }
+}
+
+void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
+{
+ struct ip ip;
+ struct ip6_hdr ip6;
+ int family;
+ struct udphdr {
+ u16 uh_sport; /* source port */
+ u16 uh_dport; /* destination port */
+ u16 uh_ulen; /* udp length */
+ u16 uh_sum; /* udp checksum */
+ } udp;
+ struct pcaprec_hdr_s pcap_header;
+ struct timeval time;
+ u32 i, sum;
+ void *iphdr;
+ size_t ipsz;
+ int rc;
+
+ if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask))
+ return;
+
+ /* So wireshark can Id the packet. */
+ udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
+
+ if (src)
+ family = src->sa.sa_family;
+ else
+ family = dst->sa.sa_family;
+
+ if (family == AF_INET6)
+ {
+ iphdr = &ip6;
+ ipsz = sizeof(ip6);
+ memset(&ip6, 0, sizeof(ip6));
+
+ ip6.ip6_vfc = 6 << 4;
+ ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
+ ip6.ip6_nxt = IPPROTO_UDP;
+ ip6.ip6_hops = 64;
+
+ if (src)
+ {
+ memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
+ udp.uh_sport = src->in6.sin6_port;
+ }
+
+ if (dst)
+ {
+ memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ);
+ udp.uh_dport = dst->in6.sin6_port;
+ }
+
+ /* start UDP checksum */
+ for (sum = 0, i = 0; i < IN6ADDRSZ; i+=2)
+ {
+ sum += ip6.ip6_src.s6_addr[i] + (ip6.ip6_src.s6_addr[i+1] << 8) ;
+ sum += ip6.ip6_dst.s6_addr[i] + (ip6.ip6_dst.s6_addr[i+1] << 8) ;
+
+ }
+ }
+ else
+ {
+ iphdr = &ip;
+ ipsz = sizeof(ip);
+ memset(&ip, 0, sizeof(ip));
+
+ ip.ip_v = IPVERSION;
+ ip.ip_hl = sizeof(struct ip) / 4;
+ ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
+ ip.ip_ttl = IPDEFTTL;
+ ip.ip_p = IPPROTO_UDP;
+
+ if (src)
+ {
+ ip.ip_src = src->in.sin_addr;
+ udp.uh_sport = src->in.sin_port;
+ }
+
+ if (dst)
+ {
+ ip.ip_dst = dst->in.sin_addr;
+ udp.uh_dport = dst->in.sin_port;
+ }
+
+ ip.ip_sum = 0;
+ for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
+ sum += ((u16 *)&ip)[i];
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+ ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
+
+ /* start UDP checksum */
+ sum = ip.ip_src.s_addr & 0xffff;
+ sum += (ip.ip_src.s_addr >> 16) & 0xffff;
+ sum += ip.ip_dst.s_addr & 0xffff;
+ sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
+ }
+
+ if (len & 1)
+ ((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
+
+ udp.uh_sum = 0;
+ udp.uh_ulen = htons(sizeof(struct udphdr) + len);
+ sum += htons(IPPROTO_UDP);
+ sum += htons(sizeof(struct udphdr) + len);
+ for (i = 0; i < sizeof(struct udphdr)/2; i++)
+ sum += ((u16 *)&udp)[i];
+ for (i = 0; i < (len + 1) / 2; i++)
+ sum += ((u16 *)packet)[i];
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+ udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
+
+ rc = gettimeofday(&time, NULL);
+ pcap_header.ts_sec = time.tv_sec;
+ pcap_header.ts_usec = time.tv_usec;
+ pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
+
+ if (rc == -1 ||
+ !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
+ !read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
+ !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) ||
+ !read_write(daemon->dumpfd, (void *)packet, len, 0))
+ my_syslog(LOG_ERR, _("failed to write packet dump"));
+ else
+ my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask);
+
+}
+
+#endif