freebsd-dev/sys/netinet/accf_dns.c
David Malone 744eaff7e6 Add an accept filter for TCP based DNS requests. It waits until the
whole first request is present before returning from accept.
2008-07-18 14:44:51 +00:00

136 lines
3.6 KiB
C

/*
* Copyright (C) 2007 David Malone <dwmalone@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#define ACCEPT_FILTER_MOD
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/signalvar.h>
#include <sys/sysctl.h>
#include <sys/socketvar.h>
/* check for full DNS request */
static void sohasdns(struct socket *so, void *arg, int waitflag);
struct packet {
struct mbuf *m; /* Current mbuf. */
struct mbuf *n; /* nextpkt mbuf. */
unsigned long moff; /* Offset of the beginning of m. */
unsigned long offset; /* Which offset we are working at. */
unsigned long len; /* The number of bytes we have to play with. */
};
#define DNS_OK 0
#define DNS_WAIT -1
#define DNS_RUN -2
/* check we can skip over various parts of DNS request */
static int skippacket(struct sockbuf *sb);
static struct accept_filter accf_dns_filter = {
"dnsready",
sohasdns,
NULL,
NULL
};
static moduledata_t accf_dns_mod = {
"accf_dns",
accept_filt_generic_mod_event,
&accf_dns_filter
};
DECLARE_MODULE(accf_dns, accf_dns_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
static void
sohasdns(struct socket *so, void *arg, int waitflag)
{
struct sockbuf *sb = &so->so_rcv;
/* If the socket is full, we're ready. */
if (sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax)
goto ready;
/* Check and see if we have a request. */
if (skippacket(sb) == DNS_WAIT)
return;
ready:
so->so_upcall = NULL;
so->so_rcv.sb_flags &= ~SB_UPCALL;
soisconnected(so);
return;
}
#define GET8(p, val) do { \
if (p->offset < p->moff) \
return DNS_RUN; \
while (p->offset >= p->moff + p->m->m_len) { \
p->moff += p->m->m_len; \
p->m = p->m->m_next; \
if (p->m == NULL) { \
p->m = p->n; \
p->n = p->m->m_nextpkt; \
} \
if (p->m == NULL) \
return DNS_WAIT; \
} \
val = *(mtod(p->m, unsigned char *) + (p->offset - p->moff)); \
p->offset++; \
} while (0)
#define GET16(p, val) do { \
unsigned int v0, v1; \
GET8(p, v0); \
GET8(p, v1); \
val = v0 * 0x100 + v1; \
} while (0)
static int
skippacket(struct sockbuf *sb) {
unsigned long packlen;
struct packet q, *p = &q;
if (sb->sb_cc < 2)
return DNS_WAIT;
q.m = sb->sb_mb;
q.n = q.m->m_nextpkt;
q.moff = 0;
q.offset = 0;
q.len = sb->sb_cc;
GET16(p, packlen);
if (packlen + 2 < q.len)
return DNS_WAIT;
return DNS_OK;
}