properly adjust the ttl of replies instead of freezing them in time
authortedu <tedu@openbsd.org>
Tue, 4 Jul 2017 00:30:45 +0000 (00:30 +0000)
committertedu <tedu@openbsd.org>
Tue, 4 Jul 2017 00:30:45 +0000 (00:30 +0000)
usr.sbin/rebound/rebound.c

index 03c9333..40b4e4a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: rebound.c,v 1.86 2017/07/03 16:36:48 tedu Exp $ */
+/* $OpenBSD: rebound.c,v 1.87 2017/07/04 00:30:45 tedu Exp $ */
 /*
  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
  *
@@ -82,6 +82,7 @@ struct dnscache {
        struct dnspacket *resp;
        size_t resplen;
        struct timespec ts;
+       struct timespec basetime;
 };
 static TAILQ_HEAD(, dnscache) cachefifo;
 static RB_HEAD(cachetree, dnscache) cachetree;
@@ -201,6 +202,61 @@ freecacheent(struct dnscache *ent)
        free(ent);
 }
 
+static int
+adjustttl(struct dnscache *ent)
+{
+       struct dnspacket *resp = ent->resp;
+       char *p = (char *)resp;
+       u_int rlen = ent->resplen;
+       u_int used = 0;
+       uint32_t ttl, cnt, i;
+       uint16_t len;
+       time_t diff;
+
+       diff = now.tv_sec - ent->basetime.tv_sec;
+       if (diff <= 0)
+               return 0;
+
+       /* checks are redundant; checked when cacheent is created */
+       /* skip past packet header */
+       used += sizeof(struct dnspacket);
+       if (used >= rlen)
+               return -1;
+       if (ntohs(resp->qdcount) != 1)
+               return -1;
+       /* skip past query name, type, and class */
+       used += strnlen(p + used, rlen - used);
+       used += 2;
+       used += 2;
+       cnt = ntohs(resp->ancount);
+       for (i = 0; i < cnt; i++) {
+               if (used >= rlen)
+                       return -1;
+               /* skip past answer name, type, and class */
+               used += strnlen(p + used, rlen - used);
+               used += 2;
+               used += 2;
+               if (used + 4 >= rlen)
+                       return -1;
+               memcpy(&ttl, p + used, 4);
+               ttl = ntohl(ttl);
+               /* expired */
+               if (diff >= ttl)
+                       return -1;
+               ttl -= diff;
+               ttl = ntohl(ttl);
+               memcpy(p + used, &ttl, 4);
+               used += 4;
+               if (used + 2 >= rlen)
+                       return -1;
+               memcpy(&len, p + used, 2);
+               used += 2;
+               used += ntohs(len);
+       }
+       ent->basetime.tv_sec += diff;
+       return 0;
+}
+
 static struct dnscache *
 cachelookup(struct dnspacket *dnsreq, size_t reqlen)
 {
@@ -224,7 +280,7 @@ cachelookup(struct dnspacket *dnsreq, size_t reqlen)
        key.req = dnsreq;
        hit = RB_FIND(cachetree, &cachetree, &key);
        if (hit) {
-               if (timespeccmp(&hit->ts, &now, <=)) {
+               if (adjustttl(hit) != 0) {
                        freecacheent(hit);
                        hit = NULL;
                } else
@@ -446,6 +502,7 @@ sendreply(struct request *req)
                        return;
                ent->ts = now;
                ent->ts.tv_sec += MINIMUM(ttl, 300);
+               ent->basetime = now;
                ent->resp = malloc(r);
                if (!ent->resp) {
                        RB_REMOVE(cachetree, &cachetree, ent);