Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge identical outgoing requests.

This patch fixes dnscache to combine *same* client queries into one
single outgoing request, thus securing the server from possible cache
poisoning attacks. This fixes one of the cache poisoning vulnerability
reported by Mr Mark Johnson
 -> https://bugzilla.redhat.com/show_bug.cgi?id=838965.

Nonetheless the original patch for this issue was created by
Mr Jeff king -> http://www.your.org/dnscache/

Sincere thanks to Mr Mark for reporting this issue and Mr Jeff for
creating the patch and releasing it under public domain.
  • Loading branch information...
commit 847523271f3966cf4618c5689b905703c41dec1c 1 parent ef18759
@pjps authored
View
4 Makefile.am
@@ -70,9 +70,9 @@ djbdns.1: djbdns.ms
cp djbdns.ms djbdns.1
dnscache_SOURCES = dnscache.c droproot.c okclient.c log.c cache.c query.c \
- response.c dd.c roots.c iopause.c prot.c common.c \
+ response.c dd.c roots.c iopause.c prot.c common.c qmerge.c \
response.h select.h prot.h roots.h query.h cache.h log.h okclient.h \
- dd.h direntry.h hasshsgr.h version.h common.h
+ dd.h direntry.h hasshsgr.h version.h common.h qmerge.h
dnscache_LDADD = libdns.a libenv.a liballoc.a libbuffer.a libtai.a \
libunix.a libbyte.a
View
6 dnscache.c
@@ -69,6 +69,7 @@ enum op_mode { DAEMON = 1, DEBUG = 2 };
#include "response.h"
#include "okclient.h"
#include "droproot.h"
+#include "maxclient.h"
static int
packetquery (char *buf, unsigned int len, char **q,
@@ -116,8 +117,6 @@ static char myipoutgoing[4];
static char myipincoming[4];
static int udp53 = 0;
-
-#define MAXUDP 200
static struct udpclient
{
struct query q;
@@ -217,10 +216,7 @@ u_new (void)
}
}
-
static int tcp53 = 0;
-
-#define MAXTCP 20
struct tcpclient
{
struct query q;
View
12 log.c
@@ -250,6 +250,18 @@ log_tx (const char *q, const char qtype[2], const char *control,
}
void
+log_tx_piggyback (const char *q, const char qtype[2], const char *control)
+{
+ string("txpb ");
+ logtype (qtype);
+ space ();
+ name (q);
+ space ();
+ name (control);
+ line ();
+}
+
+void
log_cachedanswer (const char *q, const char type[2])
{
string ("cached ");
View
1  log.h
@@ -18,6 +18,7 @@ extern void log_cachednxdomain(const char *);
extern void log_cachedns(const char *,const char *);
extern void log_tx(const char *,const char *,const char *,const char *,unsigned int);
+extern void log_tx_piggyback(const char *, const char [2], const char *);
extern void log_nxdomain(const char *,const char *,unsigned int);
extern void log_nodata(const char *,const char *,const char *,unsigned int);
View
7 maxclient.h
@@ -0,0 +1,7 @@
+#ifndef MAXCLIENT_H
+#define MAXCLIENT_H
+
+#define MAXTCP 20
+#define MAXUDP 200
+
+#endif /* MAXCLIENT_H */
View
172 qmerge.c
@@ -0,0 +1,172 @@
+/*
+ * qmerge.c: This file is part of the `ndjbdns' project. Originally written
+ * by Mr Jeff King <peff@peff.net> as part of a patch to merge outgoing
+ * queries and released under public-domain -> http://www.your.org/dnscache/
+ *
+ * Copyright (C) 2012 Prasad J Pandit
+ *
+ * This program is a free software; you can redistribute it and/or modify
+ * it under the terms of GNU General Public License as published by Free
+ * Software Foundation; either version 2 of the license or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * of 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, write to Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "log.h"
+#include "byte.h"
+#include "qmerge.h"
+#include "maxclient.h"
+
+#define QMERGE_MAX (MAXUDP+MAXTCP)
+struct qmerge inprogress[QMERGE_MAX];
+
+static int
+qmerge_key_init (struct qmerge_key *qmk, const char *q,
+ const char qtype[2], const char *control)
+{
+ if (!dns_domain_copy (&qmk->q, q))
+ return 0;
+
+ byte_copy (qmk->qtype, 2, qtype);
+ if (!dns_domain_copy (&qmk->control, control))
+ return 0;
+
+ return 1;
+}
+
+static int
+qmerge_key_equal (struct qmerge_key *a, struct qmerge_key *b)
+{
+ return byte_equal (a->qtype, 2, b->qtype)
+ && dns_domain_equal (a->q, b->q)
+ && dns_domain_equal (a->control, b->control);
+}
+
+static void
+qmerge_key_free (struct qmerge_key *qmk)
+{
+ dns_domain_free (&qmk->q);
+ dns_domain_free (&qmk->control);
+}
+
+void
+qmerge_free (struct qmerge **x)
+{
+ struct qmerge *qm = *x;
+
+ *x = 0;
+ if (!qm || !qm->active)
+ return;
+
+ qm->active--;
+ if (!qm->active)
+ {
+ qmerge_key_free (&qm->key);
+ dns_transmit_free (&qm->dt);
+ }
+}
+
+int
+qmerge_start (struct qmerge **qm, const char servers[64],
+ int flagrecursive, const char *q,
+ const char qtype[2], const char localip[4], const char *control)
+{
+ int i = 0, r = 0;
+ struct qmerge_key k;
+
+ qmerge_free (qm);
+
+ byte_zero (&k, sizeof (k));
+ if (!qmerge_key_init (&k, q, qtype, control))
+ return -1;
+
+ for (i = 0; i < QMERGE_MAX; i++)
+ {
+ if (!inprogress[i].active)
+ continue;
+ if (!qmerge_key_equal (&k, &inprogress[i].key))
+ continue;
+
+ log_tx_piggyback (q, qtype, control);
+ inprogress[i].active++;
+ *qm = &inprogress[i];
+ qmerge_key_free (&k);
+
+ return 0;
+ }
+
+ for (i = 0; i < QMERGE_MAX; i++)
+ if (!inprogress[i].active)
+ break;
+ if (i == QMERGE_MAX)
+ return -1;
+
+ log_tx (q, qtype, control, servers, 0);
+ r = dns_transmit_start (&inprogress[i].dt, servers,
+ flagrecursive, q, qtype, localip);
+ if (r == -1)
+ {
+ qmerge_key_free (&k);
+ return -1;
+ }
+
+ inprogress[i].active++;
+ inprogress[i].state = 0;
+ qmerge_key_free (&inprogress[i].key);
+ byte_copy (&inprogress[i].key, sizeof (k), &k);
+ *qm = &inprogress[i];
+
+ return 0;
+}
+
+void
+qmerge_io (struct qmerge *qm, iopause_fd *io, struct taia *deadline)
+{
+ if (qm->state == 0)
+ {
+ dns_transmit_io (&qm->dt, io, deadline);
+ qm->state = 1;
+ }
+ else
+ {
+ io->fd = -1;
+ io->events = 0;
+ }
+}
+
+int
+qmerge_get (struct qmerge **x, const iopause_fd *io, const struct taia *when)
+{
+ int r = 0;
+ struct qmerge *qm = *x;
+
+ if (qm->state == -1)
+ return -1; /* previous error */
+ if (qm->state == 0)
+ return 0; /* no packet */
+ if (qm->state == 2)
+ return 1; /* already got packet */
+
+ r = dns_transmit_get (&qm->dt, io, when);
+ switch (r)
+ {
+ case -1: /* error */
+ case 0: /* must wait for i/o */
+ qm->state = r;
+ return r;
+
+ case 1: /* got packet */
+ qm->state = 2;
+ return r;
+ }
+
+ return -1; /* bug */
+}
View
53 qmerge.h
@@ -0,0 +1,53 @@
+/*
+ * qmerge.h: This file is part of the `ndjbdns' project. Originally written
+ * by Mr Jeff King <peff@peff.net> as part of a patch to merge outgoing
+ * queries and released under public-domain -> http://www.your.org/dnscache/
+ *
+ * Copyright (C) 2012 Prasad J Pandit
+ *
+ * This program is a free software; you can redistribute it and/or modify
+ * it under the terms of GNU General Public License as published by Free
+ * Software Foundation; either version 2 of the license or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * of 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, write to Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QMERGE_H
+#define QMERGE_H
+
+#include "dns.h"
+
+struct qmerge_key
+{
+ char *q;
+ char qtype[2];
+ char *control;
+};
+
+struct qmerge
+{
+ int active;
+ struct qmerge_key key;
+ struct dns_transmit dt;
+ int state; /* -1 = error, 0 = need io, 1 = need get, 2 = got packet */
+};
+
+extern int qmerge_start(struct qmerge **, const char *, int, const char *,
+ const char *, const char *, const char *);
+
+extern void qmerge_io(struct qmerge *, iopause_fd *, struct taia *);
+
+extern int qmerge_get(struct qmerge **, const iopause_fd *,
+ const struct taia *);
+
+extern void qmerge_free(struct qmerge **);
+
+#endif /* QMERGE_H */
View
53 query.c
@@ -20,20 +20,20 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "error.h"
-#include "roots.h"
+#include "dd.h"
#include "log.h"
+#include "dns.h"
+#include "byte.h"
#include "case.h"
+#include "alloc.h"
#include "cache.h"
-#include "byte.h"
-#include "dns.h"
+#include "error.h"
+#include "query.h"
+#include "roots.h"
#include "uint64.h"
#include "uint32.h"
#include "uint16.h"
-#include "dd.h"
-#include "alloc.h"
#include "response.h"
-#include "query.h"
extern short debug_level;
static int flagforwardonly = 0;
@@ -122,7 +122,7 @@ cleanup (struct query *z)
{
int j = 0, k = 0;
- dns_transmit_free (&z->dt);
+ qmerge_free (&z->qm);
for (j = 0; j < QUERY_MAXALIAS; ++j)
dns_domain_free (&z->alias[j]);
@@ -241,7 +241,6 @@ doit (struct query *z, int state)
unsigned int posauthority = 0;
uint16 numglue = 0;
- unsigned int posglue = 0;
unsigned int pos = 0, pos2 = 0;
uint16 datalen = 0;
@@ -461,7 +460,7 @@ doit (struct query *z, int state)
goto DIE;
pos = 0;
- while (pos = dns_packet_copy(cached, cachedlen, pos, misc, 20))
+ while((pos = dns_packet_copy(cached, cachedlen,pos, misc, 20)))
{
pos = dns_packet_getname (cached, cachedlen, pos, &t2);
if (!pos)
@@ -651,25 +650,12 @@ doit (struct query *z, int state)
goto SERVFAIL;
dns_sortip (z->servers[z->level], 64);
- if (z->level)
- {
- if (debug_level > 2)
- log_tx (z->name[z->level], DNS_T_A,
- z->control[z->level], z->servers[z->level],z->level);
-
- if (dns_transmit_start (&z->dt, z->servers[z->level], flagforwardonly,
- z->name[z->level], DNS_T_A,z->localip) == -1)
- goto DIE;
- }
- else
- {
- if (debug_level > 2)
- log_tx (z->name[0], z->type, z->control[0], z->servers[0], 0);
+ dtype = z->level ? DNS_T_A : z->type;
+ if (qmerge_start (&z->qm, z->servers[z->level],
+ flagforwardonly, z->name[z->level], dtype,
+ z->localip, z->control[z->level]) == -1)
+ goto DIE;
- if (dns_transmit_start (&z->dt, z->servers[0], flagforwardonly,
- z->name[0], z->type, z->localip) == -1)
- goto DIE;
- }
return 0;
@@ -684,10 +670,10 @@ doit (struct query *z, int state)
HAVEPACKET:
if (++z->loop == 100)
goto DIE;
- buf = z->dt.packet;
- len = z->dt.packetlen;
+ buf = z->qm->dt.packet;
+ len = z->qm->dt.packetlen;
- whichserver = z->dt.servers + 4 * z->dt.curserver;
+ whichserver = z->qm->dt.servers + 4 * z->qm->dt.curserver;
control = z->control[z->level];
d = z->name[z->level];
dtype = z->level ? DNS_T_A : z->type;
@@ -765,7 +751,6 @@ doit (struct query *z, int state)
uint16_unpack_big (header + 8, &datalen);
pos += datalen;
}
- posglue = pos;
if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa)
{
@@ -1302,7 +1287,7 @@ query_start (struct query *z, char *dn, char type[2],
int
query_get (struct query *z, iopause_fd *x, struct taia *stamp)
{
- switch (dns_transmit_get (&z->dt, x, stamp))
+ switch (qmerge_get (&z->qm, x, stamp))
{
case 1:
return doit (z, 1);
@@ -1316,5 +1301,5 @@ query_get (struct query *z, iopause_fd *x, struct taia *stamp)
void
query_io (struct query *z, iopause_fd *x, struct taia *deadline)
{
- dns_transmit_io (&z->dt, x, deadline);
+ qmerge_io (z->qm, x, deadline);
}
View
6 query.h
@@ -1,12 +1,12 @@
#ifndef QUERY_H
#define QUERY_H
-#include "dns.h"
+#include "qmerge.h"
#include "uint32.h"
+#define QUERY_MAXNS 16
#define QUERY_MAXLEVEL 5
#define QUERY_MAXALIAS 16
-#define QUERY_MAXNS 16
struct query {
unsigned int loop;
@@ -20,7 +20,7 @@ struct query {
char localip[4];
char type[2];
char class[2];
- struct dns_transmit dt;
+ struct qmerge *qm;
};
extern int query_start(struct query *,char *,char *,char *,char *);
Please sign in to comment.
Something went wrong with that request. Please try again.