Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/uipc_accf.c
Show First 20 Lines • Show All 167 Lines • ▼ Show 20 Lines | accept_filt_getopt(struct socket *so, struct sockopt *sopt) | ||||
error = 0; | error = 0; | ||||
afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK | M_ZERO); | afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK | M_ZERO); | ||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
if ((so->so_options & SO_ACCEPTCONN) == 0) { | if ((so->so_options & SO_ACCEPTCONN) == 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
if ((so->so_options & SO_ACCEPTFILTER) == 0) { | if (so->sol_accept_filter == NULL) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
strcpy(afap->af_name, so->so_accf->so_accept_filter->accf_name); | strcpy(afap->af_name, so->sol_accept_filter->accf_name); | ||||
if (so->so_accf->so_accept_filter_str != NULL) | if (so->sol_accept_filter_str != NULL) | ||||
strcpy(afap->af_arg, so->so_accf->so_accept_filter_str); | strcpy(afap->af_arg, so->sol_accept_filter_str); | ||||
out: | out: | ||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
if (error == 0) | if (error == 0) | ||||
error = sooptcopyout(sopt, afap, sizeof(*afap)); | error = sooptcopyout(sopt, afap, sizeof(*afap)); | ||||
free(afap, M_TEMP); | free(afap, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
accept_filt_setopt(struct socket *so, struct sockopt *sopt) | accept_filt_setopt(struct socket *so, struct sockopt *sopt) | ||||
{ | { | ||||
struct accept_filter_arg *afap; | struct accept_filter_arg *afap; | ||||
struct accept_filter *afp; | struct accept_filter *afp; | ||||
struct so_accf *newaf; | char *accept_filter_str = NULL; | ||||
int error = 0; | void *accept_filter_arg = NULL; | ||||
int error; | |||||
/* | /* | ||||
* Handle the simple delete case first. | * Handle the simple delete case first. | ||||
*/ | */ | ||||
if (sopt == NULL || sopt->sopt_val == NULL) { | if (sopt == NULL || sopt->sopt_val == NULL) { | ||||
struct socket *sp, *sp1; | |||||
int wakeup; | |||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
if ((so->so_options & SO_ACCEPTCONN) == 0) { | if ((so->so_options & SO_ACCEPTCONN) == 0) { | ||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
if (so->so_accf != NULL) { | if (so->sol_accept_filter == NULL) { | ||||
struct so_accf *af = so->so_accf; | SOCK_UNLOCK(so); | ||||
if (af->so_accept_filter != NULL && | return (0); | ||||
af->so_accept_filter->accf_destroy != NULL) { | |||||
af->so_accept_filter->accf_destroy(so); | |||||
} | } | ||||
if (af->so_accept_filter_str != NULL) | if (so->sol_accept_filter->accf_destroy != NULL) | ||||
free(af->so_accept_filter_str, M_ACCF); | so->sol_accept_filter->accf_destroy(so); | ||||
free(af, M_ACCF); | if (so->sol_accept_filter_str != NULL) | ||||
so->so_accf = NULL; | free(so->sol_accept_filter_str, M_ACCF); | ||||
} | so->sol_accept_filter = NULL; | ||||
so->sol_accept_filter_arg = NULL; | |||||
so->sol_accept_filter_str = NULL; | |||||
so->so_options &= ~SO_ACCEPTFILTER; | so->so_options &= ~SO_ACCEPTFILTER; | ||||
SOCK_UNLOCK(so); | |||||
/* | |||||
* Move from incomplete queue to complete only those | |||||
* connections, that are blocked by us. | |||||
*/ | |||||
wakeup = 0; | |||||
TAILQ_FOREACH_SAFE(sp, &so->sol_incomp, so_list, sp1) { | |||||
SOCK_LOCK(sp); | |||||
if (sp->so_options & SO_ACCEPTFILTER) { | |||||
TAILQ_REMOVE(&so->sol_incomp, sp, so_list); | |||||
TAILQ_INSERT_TAIL(&so->sol_comp, sp, so_list); | |||||
sp->so_qstate = SQ_COMP; | |||||
sp->so_options &= ~SO_ACCEPTFILTER; | |||||
so->sol_incqlen--; | |||||
so->sol_qlen++; | |||||
wakeup = 1; | |||||
} | |||||
SOCK_UNLOCK(sp); | |||||
} | |||||
if (wakeup) | |||||
solisten_wakeup(so); /* unlocks */ | |||||
else | |||||
SOLISTEN_UNLOCK(so); | |||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Pre-allocate any memory we may need later to avoid blocking at | * Pre-allocate any memory we may need later to avoid blocking at | ||||
* untimely moments. This does not optimize for invalid arguments. | * untimely moments. This does not optimize for invalid arguments. | ||||
*/ | */ | ||||
afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK); | afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK); | ||||
error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); | error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); | ||||
afap->af_name[sizeof(afap->af_name)-1] = '\0'; | afap->af_name[sizeof(afap->af_name)-1] = '\0'; | ||||
afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; | afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; | ||||
if (error) { | if (error) { | ||||
free(afap, M_TEMP); | free(afap, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
afp = accept_filt_get(afap->af_name); | afp = accept_filt_get(afap->af_name); | ||||
if (afp == NULL) { | if (afp == NULL) { | ||||
free(afap, M_TEMP); | free(afap, M_TEMP); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
/* | |||||
* Allocate the new accept filter instance storage. We may | |||||
* have to free it again later if we fail to attach it. If | |||||
* attached properly, 'newaf' is NULLed to avoid a free() | |||||
* while in use. | |||||
*/ | |||||
newaf = malloc(sizeof(*newaf), M_ACCF, M_WAITOK | M_ZERO); | |||||
if (afp->accf_create != NULL && afap->af_name[0] != '\0') { | if (afp->accf_create != NULL && afap->af_name[0] != '\0') { | ||||
size_t len = strlen(afap->af_name) + 1; | size_t len = strlen(afap->af_name) + 1; | ||||
newaf->so_accept_filter_str = malloc(len, M_ACCF, M_WAITOK); | accept_filter_str = malloc(len, M_ACCF, M_WAITOK); | ||||
strcpy(newaf->so_accept_filter_str, afap->af_name); | strcpy(accept_filter_str, afap->af_name); | ||||
} | } | ||||
/* | /* | ||||
* Require a listen socket; don't try to replace an existing filter | * Require a listen socket; don't try to replace an existing filter | ||||
* without first removing it. | * without first removing it. | ||||
*/ | */ | ||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
if (((so->so_options & SO_ACCEPTCONN) == 0) || | if ((so->so_options & SO_ACCEPTCONN) == 0 || | ||||
(so->so_accf != NULL)) { | so->sol_accept_filter != NULL) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
/* | /* | ||||
* Invoke the accf_create() method of the filter if required. The | * Invoke the accf_create() method of the filter if required. The | ||||
* socket mutex is held over this call, so create methods for filters | * socket mutex is held over this call, so create methods for filters | ||||
* can't block. | * can't block. | ||||
*/ | */ | ||||
if (afp->accf_create != NULL) { | if (afp->accf_create != NULL) { | ||||
newaf->so_accept_filter_arg = | accept_filter_arg = afp->accf_create(so, afap->af_arg); | ||||
afp->accf_create(so, afap->af_arg); | if (accept_filter_arg == NULL) { | ||||
if (newaf->so_accept_filter_arg == NULL) { | |||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
} | } | ||||
newaf->so_accept_filter = afp; | so->sol_accept_filter = afp; | ||||
so->so_accf = newaf; | so->sol_accept_filter_arg = accept_filter_arg; | ||||
so->sol_accept_filter_str = accept_filter_str; | |||||
so->so_options |= SO_ACCEPTFILTER; | so->so_options |= SO_ACCEPTFILTER; | ||||
newaf = NULL; | |||||
out: | out: | ||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
if (newaf != NULL) { | if (accept_filter_str != NULL) | ||||
if (newaf->so_accept_filter_str != NULL) | free(accept_filter_str, M_ACCF); | ||||
free(newaf->so_accept_filter_str, M_ACCF); | |||||
free(newaf, M_ACCF); | |||||
} | |||||
if (afap != NULL) | |||||
free(afap, M_TEMP); | free(afap, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } |