Currently rebound ignores packets with bad IDs. we should log them.

But while we're at it, we should do something a little sneakier. Leave the
socket open and listen for additional replies. Ordinarily that shouldn't
happen, but it can mean that somebody is trying to mess with us. A local
attacker can see our queries and try to beat the dns server sending replies.

To prevent dos, log messages are ratelimited.

There are some more sophisticated antipoison techniques, but this is a good
starting point.


Index: rebound.c
===================================================================
RCS file: /cvs/src/usr.sbin/rebound/rebound.c,v
retrieving revision 1.74
diff -u -p -r1.74 rebound.c
--- rebound.c   8 Oct 2016 06:33:59 -0000       1.74
+++ rebound.c   13 Oct 2016 21:40:28 -0000
@@ -94,16 +94,18 @@ static uint64_t cachehits;
 struct request {
        int s;
        int client;
-       int tcp;
+       uint16_t tcp;
+       uint16_t zombie;
+       uint16_t clientid;
+       uint16_t reqid;
        union sockun from;
        socklen_t fromlen;
        struct timespec ts;
        TAILQ_ENTRY(request) fifo;
-       uint16_t clientid;
-       uint16_t reqid;
        struct dnscache *cacheent;
 };
 static TAILQ_HEAD(, request) reqfifo;
+static TAILQ_HEAD(, request) zombielist;
 
 static int conncount;
 static int connmax;
@@ -181,14 +183,26 @@ freerequest(struct request *req)
 {
        struct dnscache *ent;
 
+       if (req->s != -1) {
+               /* zombify live UDP sockets; free everything else */
+               if (req->tcp == 0) {
+                       if (req->zombie == 0) {
+                               req->zombie = 1;
+                               TAILQ_REMOVE(&reqfifo, req, fifo);
+                               TAILQ_INSERT_TAIL(&zombielist, req, fifo);
+                               return;
+                       } else {
+                               TAILQ_REMOVE(&zombielist, req, fifo);
+                       }
+               } else {
+                       TAILQ_REMOVE(&reqfifo, req, fifo);
+               }
+               close(req->s);
+       }
        if (req->tcp)
                conncount -= 2;
        else
                conncount -= 1;
-       if (req->s != -1) {
-               TAILQ_REMOVE(&reqfifo, req, fifo);
-               close(req->s);
-       }
        if (req->client != -1)
                close(req->client);
        if ((ent = req->cacheent) && !ent->resp) {
@@ -343,6 +357,44 @@ minttl(struct dnspacket *resp, size_t rl
 }
 
 static void
+secondreply(void)
+{
+       static uint64_t count;
+       static struct timespec ts;
+
+       if (ts.tv_sec < now.tv_sec + 10) {
+               if (count) {
+                       logmsg(LOG_ALERT, "another %llu duplicate replies", 
count);
+                       count = 0;
+               }
+               ts = now;
+       }
+       if (count == 0)
+               logmsg(LOG_ALERT, "received a second reply!");
+       else
+               count++;
+}
+
+static void
+badid(void)
+{
+       static uint64_t count;
+       static struct timespec ts;
+
+       if (ts.tv_sec < now.tv_sec + 10) {
+               if (count) {
+                       logmsg(LOG_ALERT, "another %llu bad ID replies", count);
+                       count = 0;
+               }
+               ts = now;
+       }
+       if (count == 0)
+               logmsg(LOG_ALERT, "received reply with a bad ID!");
+       else
+               count++;
+}
+
+static void
 sendreply(int ud, struct request *req)
 {
        uint8_t buf[65536];
@@ -356,8 +408,14 @@ sendreply(int ud, struct request *req)
        r = recv(req->s, buf, sizeof(buf), 0);
        if (r == 0 || r == -1 || r < sizeof(struct dnspacket))
                return;
-       if (resp->id != req->reqid)
+       if (resp->id != req->reqid) {
+               badid();
+               return;
+       }
+       if (req->zombie) {
+               secondreply();
                return;
+       }
        resp->id = req->clientid;
        sendto(ud, buf, r, 0, &req->from.a, req->fromlen);
        if ((ent = req->cacheent)) {
@@ -624,6 +682,8 @@ launch(int conffd, int ud, int ld)
                        timeout = &ts;
                }
 
+               while (conncount > connmax - 20 && !TAILQ_EMPTY(&zombielist))
+                       freerequest(TAILQ_FIRST(&zombielist));
                while (conncount > connmax)
                        freerequest(TAILQ_FIRST(&reqfifo));
                while (cachecount > cachemax)
@@ -648,6 +708,13 @@ launch(int conffd, int ud, int ld)
                        else
                                break;
                }
+               /* bury the zombies */
+               while ((req = TAILQ_FIRST(&zombielist))) {
+                       if (timespeccmp(&req->ts, &now, <=))
+                               freerequest(req);
+                       else
+                               break;
+               }
                if (req && (!ent || timespeccmp(&req->ts, &ent->ts, <=))) {
                        timespecsub(&req->ts, &now, &ts);
                        timeout = &ts;
@@ -742,6 +809,7 @@ main(int argc, char **argv)
        openlog("rebound", LOG_PID | LOG_NDELAY, LOG_DAEMON);
 
        TAILQ_INIT(&reqfifo);
+       TAILQ_INIT(&zombielist);
        TAILQ_INIT(&cachefifo);
        RB_INIT(&cachetree);
 

Reply via email to