On Wed, 2009-12-09 at 09:18 +0000, David Woodhouse wrote:
> Next trick is to stop it from fucking up the responses... 

--- plugins/dnsproxy.c~ 2009-12-09 08:04:13.000000000 +0000
+++ plugins/dnsproxy.c  2009-12-09 11:24:09.000000000 +0000
@@ -150,7 +150,7 @@ static gboolean server_event(GIOChannel 
 {
        struct server_data *data = user_data;
        struct request_data *req;
-       unsigned char buf[768];
+       unsigned char buf[4096];
        struct domain_hdr *hdr = (void *) &buf;
        int sk, err, len;
 

However, that doesn't seem to be sufficient. When I run pidgin with
Kerberos authentication, for example, something is sending EDNS0
requests with the buffer size set to 2KiB, which isn't large enough for
the SRV response (which in the case of our internal Kerberos setup is
2297 bytes).

I found a dirty hack which makes it work -- we artificially bump the
buffer size up to 4KiB when we see a request which is smaller. You
wouldn't want to do this in a DNS proxy which serves arbitrary clients
that might crash when they get a larger response than they asked for.
But since we're only serving the local host and we know that glibc
_does_ cope, we can probably get away with it for now.

I think we really will want to do TCP though. Or at least find a way to
ensure that glibc always sets the EDNS0 buffer size high enough,
consistently.

--- plugins/dnsproxy.c~ 2009-12-09 08:04:13.000000000 +0000
+++ plugins/dnsproxy.c  2009-12-09 11:24:09.000000000 +0000
@@ -338,6 +338,7 @@ static int parse_request(unsigned char *
 {
        struct domain_hdr *hdr = (void *) buf;
        uint16_t qdcount = ntohs(hdr->qdcount);
+       uint16_t arcount = ntohs(hdr->arcount);
        unsigned char *ptr;
        char *last_label = NULL;
        int label_count = 0;
@@ -346,8 +347,8 @@ static int parse_request(unsigned char *
        if (len < 12)
                return -EINVAL;
 
-       DBG("id 0x%04x qr %d opcode %d qdcount %d",
-                               hdr->id, hdr->qr, hdr->opcode, qdcount);
+       DBG("id 0x%04x qr %d opcode %d qdcount %d arcount %d",
+           hdr->id, hdr->qr, hdr->opcode, qdcount, arcount);
 
        if (hdr->qr != 0 || qdcount != 1)
                return -EINVAL;
@@ -380,8 +381,29 @@ static int parse_request(unsigned char *
                remain -= len + 1;
        }
 
-       DBG("query %s (%d labels)", name, label_count);
-
+       if (arcount && remain >= 9 && last_label[4] == 0 /* root */ &&
+           last_label[5] == 0 && last_label[6] == 0x29 /* OPT */) {
+               uint16_t edns0_bufsize;
+
+               edns0_bufsize = last_label[7] << 8;
+               edns0_bufsize += last_label[8];
+
+               DBG("query %s (%d labels), EDNS0 bufsize 0x%x", name,
+                   label_count, edns0_bufsize);
+
+               /* Evil hack. Since we don't support TCP we really don't want
+                  people trying to fall back to it. And sometimes they send
+                  EDNS0 requests with a too-small buffer size. Since glibc
+                  doesn't seem to crash when it gets a response bigger than 
+                  it requested -- it actually seems to _work_ -- just bump
+                  the buffer size up to 4KiB. */
+               if (edns0_bufsize < 0x1000) {
+                       last_label[7] = 0x10;
+                       last_label[8] = 0x00;
+               }
+       } else {
+               DBG("query %s (%d labels)", name, label_count);
+       }
        return 0;
 }
 


-- 
dwmw2

_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman

Reply via email to