diff options
-rw-r--r-- | sys/conf/NOTES | 4 | ||||
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/conf/options | 2 | ||||
-rw-r--r-- | sys/i386/conf/NOTES | 4 | ||||
-rw-r--r-- | sys/kern/kern_accf.c | 133 | ||||
-rw-r--r-- | sys/kern/uipc_accf.c | 133 | ||||
-rw-r--r-- | sys/kern/uipc_sockbuf.c | 11 | ||||
-rw-r--r-- | sys/kern/uipc_socket.c | 101 | ||||
-rw-r--r-- | sys/kern/uipc_socket2.c | 11 | ||||
-rw-r--r-- | sys/modules/Makefile | 3 | ||||
-rw-r--r-- | sys/modules/accf_data/Makefile | 7 | ||||
-rw-r--r-- | sys/modules/accf_http/Makefile | 7 | ||||
-rw-r--r-- | sys/netinet/accf_data.c | 80 | ||||
-rw-r--r-- | sys/netinet/accf_http.c | 183 | ||||
-rw-r--r-- | sys/sys/socket.h | 6 | ||||
-rw-r--r-- | sys/sys/socketvar.h | 27 |
16 files changed, 714 insertions, 1 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 2244571..75c1783 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -541,6 +541,10 @@ options IPFILTER_LOG #ipfilter logging options IPSTEALTH #support for stealth forwarding options TCPDEBUG +# Statically Link in accept filters +options ACCEPT_FILTER_DATA +options ACCEPT_FILTER_HTTP + # The following options add sysctl variables for controlling how certain # TCP packets are handled. # diff --git a/sys/conf/files b/sys/conf/files index cce585f..3b77465 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -380,6 +380,7 @@ kern/inflate.c optional gzip kern/init_main.c standard kern/init_sysent.c standard kern/kern_acct.c standard +kern/kern_accf.c standard kern/kern_acl.c standard kern/kern_cap.c standard kern/kern_clock.c standard @@ -690,6 +691,8 @@ netgraph/ng_tee.c optional netgraph_tee netgraph/ng_tty.c optional netgraph_tty netgraph/ng_vjc.c optional netgraph_vjc net/slcompress.c optional netgraph_vjc +netinet/accf_data.c optional accept_filter_data +netinet/accf_http.c optional accept_filter_http netinet/fil.c optional ipfilter inet netinet/if_atm.c optional atm netinet/if_ether.c optional ether diff --git a/sys/conf/options b/sys/conf/options index ed463f0..2e577ad 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -216,6 +216,8 @@ DFLDSIZ opt_rlimit.h MAXDSIZ opt_rlimit.h # Net stuff. +ACCEPT_FILTER_DATA +ACCEPT_FILTER_HTTP BOOTP opt_bootp.h BOOTP_COMPAT opt_bootp.h BOOTP_NFSROOT opt_bootp.h diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 2244571..75c1783 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -541,6 +541,10 @@ options IPFILTER_LOG #ipfilter logging options IPSTEALTH #support for stealth forwarding options TCPDEBUG +# Statically Link in accept filters +options ACCEPT_FILTER_DATA +options ACCEPT_FILTER_HTTP + # The following options add sysctl variables for controlling how certain # TCP packets are handled. # diff --git a/sys/kern/kern_accf.c b/sys/kern/kern_accf.c new file mode 100644 index 0000000..67f5152 --- /dev/null +++ b/sys/kern/kern_accf.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 2000 Alfred Perlstein <alfred@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 "opt_param.h" +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/domain.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/queue.h> + +static SLIST_HEAD(, accept_filter) accept_filtlsthd = + SLIST_HEAD_INITIALIZER(&accept_filtlsthd); + +MALLOC_DEFINE(M_ACCF, "accf", "accept filter data"); + +/* + * must be passed a malloc'd structure so we don't explode if the kld + * is unloaded, we leak the struct on deallocation to deal with this, + * but if a filter is loaded with the same name as a leaked one we re-use + * the entry. + */ +int +accept_filt_add(struct accept_filter *filt) +{ + struct accept_filter *p; + + SLIST_FOREACH(p, &accept_filtlsthd, accf_next) + if (strcmp(p->accf_name, filt->accf_name) == 0) { + if (p->accf_callback != NULL) { + return (EEXIST); + } else { + p->accf_callback = filt->accf_callback; + FREE(filt, M_ACCF); + return (0); + } + } + + if (p == NULL) + SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next); + return (0); +} + +int +accept_filt_del(char *name) +{ + struct accept_filter *p; + + p = accept_filt_get(name); + if (p == NULL) + return (ENOENT); + + p->accf_callback = NULL; + return (0); +} + +struct accept_filter * +accept_filt_get(char *name) +{ + struct accept_filter *p; + + SLIST_FOREACH(p, &accept_filtlsthd, accf_next) + if (strcmp(p->accf_name, name) == 0) + return (p); + + return (NULL); +} + +int +accept_filt_generic_mod_event(module_t mod, int event, void *data) +{ + struct accept_filter *p; + struct accept_filter *accfp = (struct accept_filter *) data; + int s, error; + + switch (event) { + case MOD_LOAD: + MALLOC(p, struct accept_filter *, sizeof(*p), M_ACCF, M_WAITOK); + bcopy(accfp, p, sizeof(*p)); + s = splnet(); + error = accept_filt_add(p); + splx(s); + break; + + case MOD_UNLOAD: + s = splnet(); + error = accept_filt_del(accfp->accf_name); + splx(s); + break; + + case MOD_SHUTDOWN: + error = 0; + break; + + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} diff --git a/sys/kern/uipc_accf.c b/sys/kern/uipc_accf.c new file mode 100644 index 0000000..67f5152 --- /dev/null +++ b/sys/kern/uipc_accf.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 2000 Alfred Perlstein <alfred@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 "opt_param.h" +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/domain.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/queue.h> + +static SLIST_HEAD(, accept_filter) accept_filtlsthd = + SLIST_HEAD_INITIALIZER(&accept_filtlsthd); + +MALLOC_DEFINE(M_ACCF, "accf", "accept filter data"); + +/* + * must be passed a malloc'd structure so we don't explode if the kld + * is unloaded, we leak the struct on deallocation to deal with this, + * but if a filter is loaded with the same name as a leaked one we re-use + * the entry. + */ +int +accept_filt_add(struct accept_filter *filt) +{ + struct accept_filter *p; + + SLIST_FOREACH(p, &accept_filtlsthd, accf_next) + if (strcmp(p->accf_name, filt->accf_name) == 0) { + if (p->accf_callback != NULL) { + return (EEXIST); + } else { + p->accf_callback = filt->accf_callback; + FREE(filt, M_ACCF); + return (0); + } + } + + if (p == NULL) + SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next); + return (0); +} + +int +accept_filt_del(char *name) +{ + struct accept_filter *p; + + p = accept_filt_get(name); + if (p == NULL) + return (ENOENT); + + p->accf_callback = NULL; + return (0); +} + +struct accept_filter * +accept_filt_get(char *name) +{ + struct accept_filter *p; + + SLIST_FOREACH(p, &accept_filtlsthd, accf_next) + if (strcmp(p->accf_name, name) == 0) + return (p); + + return (NULL); +} + +int +accept_filt_generic_mod_event(module_t mod, int event, void *data) +{ + struct accept_filter *p; + struct accept_filter *accfp = (struct accept_filter *) data; + int s, error; + + switch (event) { + case MOD_LOAD: + MALLOC(p, struct accept_filter *, sizeof(*p), M_ACCF, M_WAITOK); + bcopy(accfp, p, sizeof(*p)); + s = splnet(); + error = accept_filt_add(p); + splx(s); + break; + + case MOD_UNLOAD: + s = splnet(); + error = accept_filt_del(accfp->accf_name); + splx(s); + break; + + case MOD_SHUTDOWN: + error = 0; + break; + + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index 1026672..42e02a5 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -107,10 +107,21 @@ soisconnected(so) struct socket *so; { struct socket *head = so->so_head; + int s; so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); so->so_state |= SS_ISCONNECTED; if (head && (so->so_state & SS_INCOMP)) { + if ((so->so_options & SO_ACCEPTFILTER) != 0) { + s = splnet(); + so->so_upcall = head->so_accf->so_accept_filter->accf_callback; + so->so_upcallarg = head->so_accf->so_accept_filter_arg; + so->so_rcv.sb_flags |= SB_UPCALL; + so->so_options &= ~SO_ACCEPTFILTER; + splx(s); + so->so_upcall(so, so->so_upcallarg, 0); + return; + } TAILQ_REMOVE(&head->so_incomp, so, so_list); head->so_incqlen--; so->so_state &= ~SS_INCOMP; diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index b172d46..c2d0ad7 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -58,6 +58,8 @@ #include <machine/limits.h> +static int do_setopt_accept_filter(struct socket *so, struct sockopt *sopt); + static int filt_sorattach(struct knote *kn); static void filt_sordetach(struct knote *kn); static int filt_soread(struct knote *kn, long hint); @@ -193,6 +195,15 @@ sodealloc(so) if (so->so_snd.sb_hiwat) (void)chgsbsize(so->so_cred->cr_uid, -(rlim_t)so->so_snd.sb_hiwat); + if (so->so_accf != NULL) { + if (so->so_accf->so_accept_filter != NULL && + so->so_accf->so_accept_filter->accf_destroy != NULL) { + so->so_accf->so_accept_filter->accf_destroy(so); + } + if (so->so_accf->so_accept_filter_str != NULL) + FREE(so->so_accf->so_accept_filter_str, M_ACCF); + FREE(so->so_accf, M_ACCF); + } crfree(so->so_cred); zfreei(so->so_zone, so); } @@ -977,6 +988,77 @@ sorflush(so) sbrelease(&asb, so); } +static int +do_setopt_accept_filter(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + struct accept_filter_arg *afap = NULL; + struct accept_filter *afp; + struct so_accf *af = so->so_accf; + int error = 0; + + /* removing the filter */ + if (sopt == NULL) { + if (af != NULL) { + if (af->so_accept_filter != NULL && + af->so_accept_filter->accf_destroy != NULL) { + af->so_accept_filter->accf_destroy(so); + } + if (af->so_accept_filter_str != NULL) { + FREE(af->so_accept_filter_str, M_ACCF); + } + FREE(af, M_ACCF); + so->so_accf = NULL; + } + so->so_options &= ~SO_ACCEPTFILTER; + return (0); + } + /* adding a filter */ + /* must remove previous filter first */ + if (af != NULL) { + error = EINVAL; + goto out; + } + /* don't put large objects on the kernel stack */ + MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, M_WAITOK); + error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); + afap->af_name[sizeof(afap->af_name)-1] = '\0'; + afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; + if (error) + goto out; + afp = accept_filt_get(afap->af_name); + if (afp == NULL) { + error = ENOENT; + goto out; + } + MALLOC(af, struct so_accf *, sizeof(*af), M_ACCF, M_WAITOK); + bzero(af, sizeof(*af)); + if (afp->accf_create != NULL) { + if (afap->af_name[0] != '\0') { + int len = strlen(afap->af_name) + 1; + + MALLOC(af->so_accept_filter_str, char *, len, M_ACCF, M_WAITOK); + strcpy(af->so_accept_filter_str, afap->af_name); + } + af->so_accept_filter_arg = afp->accf_create(so, afap->af_arg); + if (af->so_accept_filter_arg == NULL) { + FREE(af->so_accept_filter_str, M_ACCF); + FREE(af, M_ACCF); + so->so_accf = NULL; + error = EINVAL; + goto out; + } + } + af->so_accept_filter = afp; + so->so_accf = af; + so->so_options |= SO_ACCEPTFILTER; +out: + if (afap != NULL) + FREE(afap, M_TEMP); + return (error); +} + /* * Perhaps this routine, and sooptcopyout(), below, ought to come in * an additional variant to handle the case where the option value needs @@ -1137,6 +1219,11 @@ sosetopt(so, sopt) } break; + case SO_ACCEPTFILTER: + error = do_setopt_accept_filter(so, sopt); + if (error) + goto bad; + break; default: error = ENOPROTOOPT; break; @@ -1190,6 +1277,7 @@ sogetopt(so, sopt) int error, optval; struct linger l; struct timeval tv; + struct accept_filter_arg *afap; error = 0; if (sopt->sopt_level != SOL_SOCKET) { @@ -1200,6 +1288,19 @@ sogetopt(so, sopt) return (ENOPROTOOPT); } else { switch (sopt->sopt_name) { + case SO_ACCEPTFILTER: + MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), + M_TEMP, M_WAITOK); + bzero(afap, sizeof(*afap)); + if ((so->so_options & SO_ACCEPTFILTER) != 0) { + strcpy(afap->af_name, so->so_accf->so_accept_filter->accf_name); + if (so->so_accf->so_accept_filter_str != NULL) + strcpy(afap->af_arg, so->so_accf->so_accept_filter_str); + } + error = sooptcopyout(sopt, afap, sizeof(*afap)); + FREE(afap, M_TEMP); + break; + case SO_LINGER: l.l_onoff = so->so_options & SO_LINGER; l.l_linger = so->so_linger; diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index 1026672..42e02a5 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -107,10 +107,21 @@ soisconnected(so) struct socket *so; { struct socket *head = so->so_head; + int s; so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); so->so_state |= SS_ISCONNECTED; if (head && (so->so_state & SS_INCOMP)) { + if ((so->so_options & SO_ACCEPTFILTER) != 0) { + s = splnet(); + so->so_upcall = head->so_accf->so_accept_filter->accf_callback; + so->so_upcallarg = head->so_accf->so_accept_filter_arg; + so->so_rcv.sb_flags |= SB_UPCALL; + so->so_options &= ~SO_ACCEPTFILTER; + splx(s); + so->so_upcall(so, so->so_upcallarg, 0); + return; + } TAILQ_REMOVE(&head->so_incomp, so, so_list); head->so_incqlen--; so->so_state &= ~SS_INCOMP; diff --git a/sys/modules/Makefile b/sys/modules/Makefile index b7ec171..198f6d9 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -2,7 +2,8 @@ # XXX present but broken: ip_mroute_mod -SUBDIR= agp aha amr an aue ccd cd9660 coda cue dc fdesc fxp if_disc if_ef \ +SUBDIR= accf_data accf_http agp aha amr an aue \ + ccd cd9660 coda cue dc fdesc fxp if_disc if_ef \ if_ppp if_sl if_tun ipfilter ipfw ispfw joy kernfs kue \ md mfs mii mlx msdos ncp netgraph nfs ntfs nullfs \ nwfs oldcard pccard pcic portal procfs rl rp sf sis sk sn ste syscons \ diff --git a/sys/modules/accf_data/Makefile b/sys/modules/accf_data/Makefile new file mode 100644 index 0000000..9f3a64e --- /dev/null +++ b/sys/modules/accf_data/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../netinet +KMOD = accf_data +SRCS = accf_data.c + +.include <bsd.kmod.mk> diff --git a/sys/modules/accf_http/Makefile b/sys/modules/accf_http/Makefile new file mode 100644 index 0000000..2b76ac6 --- /dev/null +++ b/sys/modules/accf_http/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../netinet +KMOD = accf_http +SRCS = accf_http.c + +.include <bsd.kmod.mk> diff --git a/sys/netinet/accf_data.c b/sys/netinet/accf_data.c new file mode 100644 index 0000000..776047c --- /dev/null +++ b/sys/netinet/accf_data.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2000 Alfred Perlstein <alfred@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/systm.h> +#include <sys/sysproto.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/unistd.h> +#include <sys/file.h> +#include <sys/fcntl.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/stat.h> +#include <sys/mbuf.h> +#include <sys/resource.h> +#include <sys/sysent.h> +#include <sys/resourcevar.h> + +/* accept filter that holds a socket until data arrives */ + +static void sohasdata(struct socket *so, void *arg, int waitflag); + +static struct accept_filter accf_data_filter = { + "dataready", + sohasdata, + NULL, + NULL +}; + +static moduledata_t accf_data_mod = { + "accf_data", + accept_filt_generic_mod_event, + &accf_data_filter +}; + +DECLARE_MODULE(accf_data, accf_data_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); + +static void +sohasdata(struct socket *so, void *arg, int waitflag) +{ + + if (!soreadable(so)) { + return; + } + + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} diff --git a/sys/netinet/accf_http.c b/sys/netinet/accf_http.c new file mode 100644 index 0000000..09a4e9f --- /dev/null +++ b/sys/netinet/accf_http.c @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 2000 Alfred Perlstein <alfred@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/systm.h> +#include <sys/sysproto.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/unistd.h> +#include <sys/file.h> +#include <sys/fcntl.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/stat.h> +#include <sys/mbuf.h> +#include <sys/resource.h> +#include <sys/sysent.h> +#include <sys/resourcevar.h> + +/* + * XXX: doesn't work with 0.9 requests, make a seperate filter + * based on this one if you want to decode those. + */ + +/* check for GET */ +static void sohashttpget(struct socket *so, void *arg, int waitflag); +/* check for end of HTTP request */ +static void soishttpconnected(struct socket *so, void *arg, int waitflag); +static char sbindex(struct mbuf **mp, int *begin, int end); + +static struct accept_filter accf_http_filter = { + "httpready", + sohashttpget, + NULL, + NULL +}; + +static moduledata_t accf_http_mod = { + "accf_http", + accept_filt_generic_mod_event, + &accf_http_filter +}; + +DECLARE_MODULE(accf_http, accf_http_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); + + +static char +sbindex(struct mbuf **mp, int *begin, int end) +{ + struct mbuf *m = *mp; + int diff = end - *begin + 1; + + while (m->m_len < diff) { + *begin += m->m_len; + diff -= m->m_len; + if (m->m_next) { + m = m->m_next; + } else if (m->m_nextpkt) { + m = m->m_nextpkt; + } else { + /* only happens if end > data in socket buffer */ + panic("sbindex: not enough data"); + } + } + *mp = m; + return *(mtod(m, char *) + diff - 1); +} + +static void +sohashttpget(struct socket *so, void *arg, int waitflag) +{ + + if ((so->so_state & SS_CANTRCVMORE) == 0) { + struct mbuf *m; + + if (so->so_rcv.sb_cc < 6) + return; + m = so->so_rcv.sb_mb; + if (bcmp(mtod(m, char *), "GET ", 4) == 0) { + soishttpconnected(so, arg, waitflag); + return; + } + } + + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} + +static void +soishttpconnected(struct socket *so, void *arg, int waitflag) +{ + char a, b, c; + struct mbuf *y, *z; + + if ((so->so_state & SS_CANTRCVMORE) == 0) { + /* seek to end and keep track of next to last mbuf */ + y = so->so_rcv.sb_mb; + while (y->m_nextpkt) + y = y->m_nextpkt; + z = y; + while (y->m_next) { + z = y; + y = y->m_next; + } + + if (z->m_len + y->m_len > 2) { + int index = y->m_len - 1; + + c = *(mtod(y, char *) + index--); + switch (index) { + case -1: + y = z; + index = y->m_len - 1; + b = *(mtod(y, char *) + index--); + break; + case 0: + b = *(mtod(y, char *) + index--); + y = z; + index = y->m_len - 1; + break; + default: + b = *(mtod(y, char *) + index--); + break; + } + a = *(mtod(y, char *) + index--); + } else { + int begin = 0; + int end = so->so_rcv.sb_cc - 3; + + y = so->so_rcv.sb_mb; + a = sbindex(&y, &begin, end++); + b = sbindex(&y, &begin, end++); + c = sbindex(&y, &begin, end++); + } + + if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) { + /* we have all request headers */ + goto done; + } else { + /* still need more data */ + so->so_upcall = soishttpconnected; + so->so_rcv.sb_flags |= SB_UPCALL; + return; + } + } + +done: + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} diff --git a/sys/sys/socket.h b/sys/sys/socket.h index b71e206..7733f32 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -70,6 +70,7 @@ typedef u_int32_t socklen_t; #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ #define SO_REUSEPORT 0x0200 /* allow local address & port reuse */ #define SO_TIMESTAMP 0x0400 /* timestamp received dgram traffic */ +#define SO_ACCEPTFILTER 0x1000 /* there is an accept filter */ /* * Additional options, not kept in so_options. @@ -92,6 +93,11 @@ struct linger { int l_linger; /* linger time */ }; +struct accept_filter_arg { + char af_name[16]; + char af_arg[256-16]; +}; + /* * Level number for (get/set)sockopt() to apply to socket itself. */ diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index add6eb6..f1134b7 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -48,6 +48,8 @@ */ typedef u_quad_t so_gen_t; +struct accept_filter; + struct socket { struct vm_zone *so_zone; /* zone we were allocated from */ short so_type; /* generic type, see socket.h */ @@ -112,6 +114,11 @@ struct socket { /* NB: generation count must not be first; easiest to make it last. */ so_gen_t so_gencnt; /* generation count */ void *so_emuldata; /* private data for emulators */ + struct so_accf { + struct accept_filter *so_accept_filter; + void *so_accept_filter_arg; /* saved filter args */ + char *so_accept_filter_str; /* saved user args */ + } *so_accf; }; /* @@ -270,9 +277,21 @@ struct sf_buf { vm_offset_t kva; /* va of mapping */ }; +struct accept_filter { + char accf_name[16]; + void (*accf_callback) + __P((struct socket *so, void *arg, int waitflag)); + void * (*accf_create) + __P((struct socket *so, char *arg)); + void (*accf_destroy) + __P((struct socket *so)); + SLIST_ENTRY(accept_filter) accf_next; /* next on the list */ +}; + #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_PCB); MALLOC_DECLARE(M_SONAME); +MALLOC_DECLARE(M_ACCF); #endif extern int maxsockets; @@ -380,6 +399,14 @@ int soshutdown __P((struct socket *so, int how)); void sotoxsocket __P((struct socket *so, struct xsocket *xso)); void sowakeup __P((struct socket *so, struct sockbuf *sb)); +/* accept filter functions */ +int accept_filt_add __P((struct accept_filter *filt)); +int accept_filt_del __P((char *name)); +struct accept_filter * accept_filt_get __P((char *name)); +#ifdef ACCEPT_FILTER_MOD +int accept_filt_generic_mod_event __P((module_t mod, int event, void *data)); +#endif /* ACCEPT_FILTER_MOD */ + #endif /* _KERNEL */ #endif /* !_SYS_SOCKETVAR_H_ */ |