This commit was generated by cvs2svn to compensate for changes in r171169,
which included commits to RCS files with non-trunk default branches.
This commit is contained in:
commit
fc515400ab
387
contrib/pf/ftp-proxy/filter.c
Normal file
387
contrib/pf/ftp-proxy/filter.c
Normal file
@ -0,0 +1,387 @@
|
||||
/* $OpenBSD: filter.c,v 1.5 2006/12/01 07:31:21 camield Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/pfvar.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "filter.h"
|
||||
|
||||
/* From netinet/in.h, but only _KERNEL_ gets them. */
|
||||
#define satosin(sa) ((struct sockaddr_in *)(sa))
|
||||
#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
|
||||
|
||||
enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
|
||||
|
||||
int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
|
||||
u_int16_t);
|
||||
int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
|
||||
struct sockaddr_in *);
|
||||
int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
|
||||
struct sockaddr_in6 *);
|
||||
|
||||
static struct pfioc_pooladdr pfp;
|
||||
static struct pfioc_rule pfr;
|
||||
static struct pfioc_trans pft;
|
||||
static struct pfioc_trans_e pfte[TRANS_SIZE];
|
||||
static int dev, rule_log;
|
||||
static char *qname;
|
||||
|
||||
int
|
||||
add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
|
||||
struct sockaddr *dst, u_int16_t d_port)
|
||||
{
|
||||
if (!src || !dst || !d_port) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1)
|
||||
return (-1);
|
||||
|
||||
pfr.rule.direction = dir;
|
||||
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
|
||||
u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
|
||||
u_int16_t nat_range_high)
|
||||
{
|
||||
if (!src || !dst || !d_port || !nat || !nat_range_low ||
|
||||
(src->sa_family != nat->sa_family)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1)
|
||||
return (-1);
|
||||
|
||||
if (nat->sa_family == AF_INET) {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v4,
|
||||
&satosin(nat)->sin_addr.s_addr, 4);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
|
||||
} else {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v6,
|
||||
&satosin6(nat)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
|
||||
}
|
||||
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
|
||||
return (-1);
|
||||
|
||||
pfr.rule.rpool.proxy_port[0] = nat_range_low;
|
||||
pfr.rule.rpool.proxy_port[1] = nat_range_high;
|
||||
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
|
||||
u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port)
|
||||
{
|
||||
if (!src || !dst || !d_port || !rdr || !rdr_port ||
|
||||
(src->sa_family != rdr->sa_family)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1)
|
||||
return (-1);
|
||||
|
||||
if (rdr->sa_family == AF_INET) {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v4,
|
||||
&satosin(rdr)->sin_addr.s_addr, 4);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
|
||||
} else {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v6,
|
||||
&satosin6(rdr)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
|
||||
}
|
||||
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
|
||||
return (-1);
|
||||
|
||||
pfr.rule.rpool.proxy_port[0] = rdr_port;
|
||||
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_commit(void)
|
||||
{
|
||||
if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_rollback(void)
|
||||
{
|
||||
if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
init_filter(char *opt_qname, int opt_verbose)
|
||||
{
|
||||
struct pf_status status;
|
||||
|
||||
qname = opt_qname;
|
||||
|
||||
if (opt_verbose == 1)
|
||||
rule_log = PF_LOG;
|
||||
else if (opt_verbose == 2)
|
||||
rule_log = PF_LOG_ALL;
|
||||
|
||||
dev = open("/dev/pf", O_RDWR);
|
||||
if (dev == -1)
|
||||
err(1, "/dev/pf");
|
||||
if (ioctl(dev, DIOCGETSTATUS, &status) == -1)
|
||||
err(1, "DIOCGETSTATUS");
|
||||
if (!status.running)
|
||||
errx(1, "pf is disabled");
|
||||
}
|
||||
|
||||
int
|
||||
prepare_commit(u_int32_t id)
|
||||
{
|
||||
char an[PF_ANCHOR_NAME_SIZE];
|
||||
int i;
|
||||
|
||||
memset(&pft, 0, sizeof pft);
|
||||
pft.size = TRANS_SIZE;
|
||||
pft.esize = sizeof pfte[0];
|
||||
pft.array = pfte;
|
||||
|
||||
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
|
||||
getpid(), id);
|
||||
for (i = 0; i < TRANS_SIZE; i++) {
|
||||
memset(&pfte[i], 0, sizeof pfte[0]);
|
||||
strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
|
||||
switch (i) {
|
||||
case TRANS_FILTER:
|
||||
pfte[i].rs_num = PF_RULESET_FILTER;
|
||||
break;
|
||||
case TRANS_NAT:
|
||||
pfte[i].rs_num = PF_RULESET_NAT;
|
||||
break;
|
||||
case TRANS_RDR:
|
||||
pfte[i].rs_num = PF_RULESET_RDR;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
|
||||
struct sockaddr *dst, u_int16_t d_port)
|
||||
{
|
||||
char an[PF_ANCHOR_NAME_SIZE];
|
||||
|
||||
if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
|
||||
(src->sa_family != dst->sa_family)) {
|
||||
errno = EPROTONOSUPPORT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memset(&pfp, 0, sizeof pfp);
|
||||
memset(&pfr, 0, sizeof pfr);
|
||||
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
|
||||
getpid(), id);
|
||||
strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
|
||||
strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
|
||||
|
||||
switch (rs_num) {
|
||||
case PF_RULESET_FILTER:
|
||||
pfr.ticket = pfte[TRANS_FILTER].ticket;
|
||||
break;
|
||||
case PF_RULESET_NAT:
|
||||
pfr.ticket = pfte[TRANS_NAT].ticket;
|
||||
break;
|
||||
case PF_RULESET_RDR:
|
||||
pfr.ticket = pfte[TRANS_RDR].ticket;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
|
||||
return (-1);
|
||||
pfr.pool_ticket = pfp.ticket;
|
||||
|
||||
/* Generic for all rule types. */
|
||||
pfr.rule.af = src->sa_family;
|
||||
pfr.rule.proto = IPPROTO_TCP;
|
||||
pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
|
||||
pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
|
||||
if (src->sa_family == AF_INET) {
|
||||
memcpy(&pfr.rule.src.addr.v.a.addr.v4,
|
||||
&satosin(src)->sin_addr.s_addr, 4);
|
||||
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
|
||||
memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
|
||||
&satosin(dst)->sin_addr.s_addr, 4);
|
||||
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
|
||||
} else {
|
||||
memcpy(&pfr.rule.src.addr.v.a.addr.v6,
|
||||
&satosin6(src)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
|
||||
memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
|
||||
&satosin6(dst)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
|
||||
}
|
||||
pfr.rule.dst.port_op = PF_OP_EQ;
|
||||
pfr.rule.dst.port[0] = htons(d_port);
|
||||
|
||||
switch (rs_num) {
|
||||
case PF_RULESET_FILTER:
|
||||
/*
|
||||
* pass quick [log] inet[6] proto tcp \
|
||||
* from $src to $dst port = $d_port flags S/SA keep state
|
||||
* (max 1) [queue qname]
|
||||
*/
|
||||
pfr.rule.action = PF_PASS;
|
||||
pfr.rule.quick = 1;
|
||||
pfr.rule.log = rule_log;
|
||||
pfr.rule.keep_state = 1;
|
||||
pfr.rule.flags = TH_SYN;
|
||||
pfr.rule.flagset = (TH_SYN|TH_ACK);
|
||||
pfr.rule.max_states = 1;
|
||||
if (qname != NULL)
|
||||
strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
|
||||
break;
|
||||
case PF_RULESET_NAT:
|
||||
/*
|
||||
* nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
|
||||
*/
|
||||
pfr.rule.action = PF_NAT;
|
||||
break;
|
||||
case PF_RULESET_RDR:
|
||||
/*
|
||||
* rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
|
||||
*/
|
||||
pfr.rule.action = PF_RDR;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
server_lookup(struct sockaddr *client, struct sockaddr *proxy,
|
||||
struct sockaddr *server)
|
||||
{
|
||||
if (client->sa_family == AF_INET)
|
||||
return (server_lookup4(satosin(client), satosin(proxy),
|
||||
satosin(server)));
|
||||
|
||||
if (client->sa_family == AF_INET6)
|
||||
return (server_lookup6(satosin6(client), satosin6(proxy),
|
||||
satosin6(server)));
|
||||
|
||||
errno = EPROTONOSUPPORT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
|
||||
struct sockaddr_in *server)
|
||||
{
|
||||
struct pfioc_natlook pnl;
|
||||
|
||||
memset(&pnl, 0, sizeof pnl);
|
||||
pnl.direction = PF_OUT;
|
||||
pnl.af = AF_INET;
|
||||
pnl.proto = IPPROTO_TCP;
|
||||
memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
|
||||
memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
|
||||
pnl.sport = client->sin_port;
|
||||
pnl.dport = proxy->sin_port;
|
||||
|
||||
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
|
||||
return (-1);
|
||||
|
||||
memset(server, 0, sizeof(struct sockaddr_in));
|
||||
server->sin_len = sizeof(struct sockaddr_in);
|
||||
server->sin_family = AF_INET;
|
||||
memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
|
||||
sizeof server->sin_addr.s_addr);
|
||||
server->sin_port = pnl.rdport;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
|
||||
struct sockaddr_in6 *server)
|
||||
{
|
||||
struct pfioc_natlook pnl;
|
||||
|
||||
memset(&pnl, 0, sizeof pnl);
|
||||
pnl.direction = PF_OUT;
|
||||
pnl.af = AF_INET6;
|
||||
pnl.proto = IPPROTO_TCP;
|
||||
memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
|
||||
memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
|
||||
pnl.sport = client->sin6_port;
|
||||
pnl.dport = proxy->sin6_port;
|
||||
|
||||
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
|
||||
return (-1);
|
||||
|
||||
memset(server, 0, sizeof(struct sockaddr_in6));
|
||||
server->sin6_len = sizeof(struct sockaddr_in6);
|
||||
server->sin6_family = AF_INET6;
|
||||
memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
|
||||
sizeof server->sin6_addr);
|
||||
server->sin6_port = pnl.rdport;
|
||||
|
||||
return (0);
|
||||
}
|
31
contrib/pf/ftp-proxy/filter.h
Normal file
31
contrib/pf/ftp-proxy/filter.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* $OpenBSD: filter.h,v 1.3 2005/06/07 14:12:07 camield Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define FTP_PROXY_ANCHOR "ftp-proxy"
|
||||
|
||||
int add_filter(u_int32_t, u_int8_t, struct sockaddr *, struct sockaddr *,
|
||||
u_int16_t);
|
||||
int add_nat(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
|
||||
struct sockaddr *, u_int16_t, u_int16_t);
|
||||
int add_rdr(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
|
||||
struct sockaddr *, u_int16_t);
|
||||
int do_commit(void);
|
||||
int do_rollback(void);
|
||||
void init_filter(char *, int);
|
||||
int prepare_commit(u_int32_t);
|
||||
int server_lookup(struct sockaddr *, struct sockaddr *, struct sockaddr *);
|
456
contrib/pf/libevent/buffer.c
Normal file
456
contrib/pf/libevent/buffer.c
Normal file
@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VASPRINTF
|
||||
/* If we have vasprintf, we need to define this before we include stdio.h. */
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
|
||||
struct evbuffer *
|
||||
evbuffer_new(void)
|
||||
{
|
||||
struct evbuffer *buffer;
|
||||
|
||||
buffer = calloc(1, sizeof(struct evbuffer));
|
||||
|
||||
return (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
evbuffer_free(struct evbuffer *buffer)
|
||||
{
|
||||
if (buffer->orig_buffer != NULL)
|
||||
free(buffer->orig_buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a destructive add. The data from one buffer moves into
|
||||
* the other buffer.
|
||||
*/
|
||||
|
||||
#define SWAP(x,y) do { \
|
||||
(x)->buffer = (y)->buffer; \
|
||||
(x)->orig_buffer = (y)->orig_buffer; \
|
||||
(x)->misalign = (y)->misalign; \
|
||||
(x)->totallen = (y)->totallen; \
|
||||
(x)->off = (y)->off; \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Short cut for better performance */
|
||||
if (outbuf->off == 0) {
|
||||
struct evbuffer tmp;
|
||||
size_t oldoff = inbuf->off;
|
||||
|
||||
/* Swap them directly */
|
||||
SWAP(&tmp, outbuf);
|
||||
SWAP(outbuf, inbuf);
|
||||
SWAP(inbuf, &tmp);
|
||||
|
||||
/*
|
||||
* Optimization comes with a price; we need to notify the
|
||||
* buffer if necessary of the changes. oldoff is the amount
|
||||
* of data that we tranfered from inbuf to outbuf
|
||||
*/
|
||||
if (inbuf->off != oldoff && inbuf->cb != NULL)
|
||||
(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
|
||||
if (oldoff && outbuf->cb != NULL)
|
||||
(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
|
||||
if (res == 0) {
|
||||
/* We drain the input buffer on success */
|
||||
evbuffer_drain(inbuf, inbuf->off);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buffer;
|
||||
size_t space;
|
||||
size_t oldoff = buf->off;
|
||||
int sz;
|
||||
va_list aq;
|
||||
|
||||
for (;;) {
|
||||
buffer = (char *)buf->buffer + buf->off;
|
||||
space = buf->totallen - buf->misalign - buf->off;
|
||||
|
||||
#ifndef va_copy
|
||||
#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
|
||||
#endif
|
||||
va_copy(aq, ap);
|
||||
|
||||
#ifdef WIN32
|
||||
sz = vsnprintf(buffer, space - 1, fmt, aq);
|
||||
buffer[space - 1] = '\0';
|
||||
#else
|
||||
sz = vsnprintf(buffer, space, fmt, aq);
|
||||
#endif
|
||||
|
||||
va_end(aq);
|
||||
|
||||
if (sz == -1)
|
||||
return (-1);
|
||||
if (sz < space) {
|
||||
buf->off += sz;
|
||||
if (buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
return (sz);
|
||||
}
|
||||
if (evbuffer_expand(buf, sz + 1) == -1)
|
||||
return (-1);
|
||||
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
|
||||
{
|
||||
int res = -1;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
res = evbuffer_add_vprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/* Reads data from an event buffer and drains the bytes read */
|
||||
|
||||
int
|
||||
evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
|
||||
{
|
||||
size_t nread = datlen;
|
||||
if (nread >= buf->off)
|
||||
nread = buf->off;
|
||||
|
||||
memcpy(data, buf->buffer, nread);
|
||||
evbuffer_drain(buf, nread);
|
||||
|
||||
return (nread);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
|
||||
* The returned buffer needs to be freed by the called.
|
||||
*/
|
||||
|
||||
char *
|
||||
evbuffer_readline(struct evbuffer *buffer)
|
||||
{
|
||||
u_char *data = EVBUFFER_DATA(buffer);
|
||||
size_t len = EVBUFFER_LENGTH(buffer);
|
||||
char *line;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] == '\r' || data[i] == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == len)
|
||||
return (NULL);
|
||||
|
||||
if ((line = malloc(i + 1)) == NULL) {
|
||||
fprintf(stderr, "%s: out of memory\n", __func__);
|
||||
evbuffer_drain(buffer, i);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
memcpy(line, data, i);
|
||||
line[i] = '\0';
|
||||
|
||||
/*
|
||||
* Some protocols terminate a line with '\r\n', so check for
|
||||
* that, too.
|
||||
*/
|
||||
if ( i < len - 1 ) {
|
||||
char fch = data[i], sch = data[i+1];
|
||||
|
||||
/* Drain one more character if needed */
|
||||
if ( (sch == '\r' || sch == '\n') && sch != fch )
|
||||
i += 1;
|
||||
}
|
||||
|
||||
evbuffer_drain(buffer, i + 1);
|
||||
|
||||
return (line);
|
||||
}
|
||||
|
||||
/* Adds data to an event buffer */
|
||||
|
||||
static inline void
|
||||
evbuffer_align(struct evbuffer *buf)
|
||||
{
|
||||
memmove(buf->orig_buffer, buf->buffer, buf->off);
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
}
|
||||
|
||||
/* Expands the available space in the event buffer to at least datlen */
|
||||
|
||||
int
|
||||
evbuffer_expand(struct evbuffer *buf, size_t datlen)
|
||||
{
|
||||
size_t need = buf->misalign + buf->off + datlen;
|
||||
|
||||
/* If we can fit all the data, then we don't have to do anything */
|
||||
if (buf->totallen >= need)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* If the misalignment fulfills our data needs, we just force an
|
||||
* alignment to happen. Afterwards, we have enough space.
|
||||
*/
|
||||
if (buf->misalign >= datlen) {
|
||||
evbuffer_align(buf);
|
||||
} else {
|
||||
void *newbuf;
|
||||
size_t length = buf->totallen;
|
||||
|
||||
if (length < 256)
|
||||
length = 256;
|
||||
while (length < need)
|
||||
length <<= 1;
|
||||
|
||||
if (buf->orig_buffer != buf->buffer)
|
||||
evbuffer_align(buf);
|
||||
if ((newbuf = realloc(buf->buffer, length)) == NULL)
|
||||
return (-1);
|
||||
|
||||
buf->orig_buffer = buf->buffer = newbuf;
|
||||
buf->totallen = length;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
|
||||
{
|
||||
size_t need = buf->misalign + buf->off + datlen;
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (buf->totallen < need) {
|
||||
if (evbuffer_expand(buf, datlen) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(buf->buffer + buf->off, data, datlen);
|
||||
buf->off += datlen;
|
||||
|
||||
if (datlen && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evbuffer_drain(struct evbuffer *buf, size_t len)
|
||||
{
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (len >= buf->off) {
|
||||
buf->off = 0;
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
buf->buffer += len;
|
||||
buf->misalign += len;
|
||||
|
||||
buf->off -= len;
|
||||
|
||||
done:
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads data from a file descriptor into a buffer.
|
||||
*/
|
||||
|
||||
#define EVBUFFER_MAX_READ 4096
|
||||
|
||||
int
|
||||
evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
|
||||
{
|
||||
u_char *p;
|
||||
size_t oldoff = buf->off;
|
||||
int n = EVBUFFER_MAX_READ;
|
||||
#ifdef WIN32
|
||||
DWORD dwBytesRead;
|
||||
#endif
|
||||
|
||||
#ifdef FIONREAD
|
||||
if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
|
||||
n = EVBUFFER_MAX_READ;
|
||||
} else if (n > EVBUFFER_MAX_READ && n > howmuch) {
|
||||
/*
|
||||
* It's possible that a lot of data is available for
|
||||
* reading. We do not want to exhaust resources
|
||||
* before the reader has a chance to do something
|
||||
* about it. If the reader does not tell us how much
|
||||
* data we should read, we artifically limit it.
|
||||
*/
|
||||
if (n > buf->totallen << 2)
|
||||
n = buf->totallen << 2;
|
||||
if (n < EVBUFFER_MAX_READ)
|
||||
n = EVBUFFER_MAX_READ;
|
||||
}
|
||||
#endif
|
||||
if (howmuch < 0 || howmuch > n)
|
||||
howmuch = n;
|
||||
|
||||
/* If we don't have FIONREAD, we might waste some space here */
|
||||
if (evbuffer_expand(buf, howmuch) == -1)
|
||||
return (-1);
|
||||
|
||||
/* We can append new data at this point */
|
||||
p = buf->buffer + buf->off;
|
||||
|
||||
#ifndef WIN32
|
||||
n = read(fd, p, howmuch);
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
#else
|
||||
n = ReadFile((HANDLE)fd, p, howmuch, &dwBytesRead, NULL);
|
||||
if (n == 0)
|
||||
return (-1);
|
||||
if (dwBytesRead == 0)
|
||||
return (0);
|
||||
n = dwBytesRead;
|
||||
#endif
|
||||
|
||||
buf->off += n;
|
||||
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_write(struct evbuffer *buffer, int fd)
|
||||
{
|
||||
int n;
|
||||
#ifdef WIN32
|
||||
DWORD dwBytesWritten;
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
n = write(fd, buffer->buffer, buffer->off);
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
#else
|
||||
n = WriteFile((HANDLE)fd, buffer->buffer, buffer->off, &dwBytesWritten, NULL);
|
||||
if (n == 0)
|
||||
return (-1);
|
||||
if (dwBytesWritten == 0)
|
||||
return (0);
|
||||
n = dwBytesWritten;
|
||||
#endif
|
||||
evbuffer_drain(buffer, n);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
u_char *
|
||||
evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
|
||||
{
|
||||
size_t remain = buffer->off;
|
||||
u_char *search = buffer->buffer;
|
||||
u_char *p;
|
||||
|
||||
while ((p = memchr(search, *what, remain)) != NULL) {
|
||||
remain = buffer->off - (size_t)(search - buffer->buffer);
|
||||
if (remain < len)
|
||||
break;
|
||||
if (memcmp(p, what, len) == 0)
|
||||
return (p);
|
||||
search = p + 1;
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void evbuffer_setcb(struct evbuffer *buffer,
|
||||
void (*cb)(struct evbuffer *, size_t, size_t, void *),
|
||||
void *cbarg)
|
||||
{
|
||||
buffer->cb = cb;
|
||||
buffer->cbarg = cbarg;
|
||||
}
|
413
contrib/pf/libevent/evbuffer.c
Normal file
413
contrib/pf/libevent/evbuffer.c
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
|
||||
/* prototypes */
|
||||
|
||||
void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t);
|
||||
void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
|
||||
|
||||
static int
|
||||
bufferevent_add(struct event *ev, int timeout)
|
||||
{
|
||||
struct timeval tv, *ptv = NULL;
|
||||
|
||||
if (timeout) {
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = timeout;
|
||||
ptv = &tv;
|
||||
}
|
||||
|
||||
return (event_add(ev, ptv));
|
||||
}
|
||||
|
||||
/*
|
||||
* This callback is executed when the size of the input buffer changes.
|
||||
* We use it to apply back pressure on the reading side.
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
|
||||
void *arg) {
|
||||
struct bufferevent *bufev = arg;
|
||||
/*
|
||||
* If we are below the watermark then reschedule reading if it's
|
||||
* still enabled.
|
||||
*/
|
||||
if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
|
||||
evbuffer_setcb(buf, NULL, NULL);
|
||||
|
||||
if (bufev->enabled & EV_READ)
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bufferevent_readcb(int fd, short event, void *arg)
|
||||
{
|
||||
struct bufferevent *bufev = arg;
|
||||
int res = 0;
|
||||
short what = EVBUFFER_READ;
|
||||
size_t len;
|
||||
int howmuch = -1;
|
||||
|
||||
if (event == EV_TIMEOUT) {
|
||||
what |= EVBUFFER_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a high watermark configured then we don't want to
|
||||
* read more data than would make us reach the watermark.
|
||||
*/
|
||||
if (bufev->wm_read.high != 0)
|
||||
howmuch = bufev->wm_read.high;
|
||||
|
||||
res = evbuffer_read(bufev->input, fd, howmuch);
|
||||
if (res == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
goto reschedule;
|
||||
/* error case */
|
||||
what |= EVBUFFER_ERROR;
|
||||
} else if (res == 0) {
|
||||
/* eof case */
|
||||
what |= EVBUFFER_EOF;
|
||||
}
|
||||
|
||||
if (res <= 0)
|
||||
goto error;
|
||||
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
|
||||
/* See if this callbacks meets the water marks */
|
||||
len = EVBUFFER_LENGTH(bufev->input);
|
||||
if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
|
||||
return;
|
||||
if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
|
||||
struct evbuffer *buf = bufev->input;
|
||||
event_del(&bufev->ev_read);
|
||||
|
||||
/* Now schedule a callback for us */
|
||||
evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Invoke the user callback - must always be called last */
|
||||
if (bufev->readcb != NULL)
|
||||
(*bufev->readcb)(bufev, bufev->cbarg);
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
return;
|
||||
|
||||
error:
|
||||
(*bufev->errorcb)(bufev, what, bufev->cbarg);
|
||||
}
|
||||
|
||||
static void
|
||||
bufferevent_writecb(int fd, short event, void *arg)
|
||||
{
|
||||
struct bufferevent *bufev = arg;
|
||||
int res = 0;
|
||||
short what = EVBUFFER_WRITE;
|
||||
|
||||
if (event == EV_TIMEOUT) {
|
||||
what |= EVBUFFER_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(bufev->output)) {
|
||||
res = evbuffer_write(bufev->output, fd);
|
||||
if (res == -1) {
|
||||
#ifndef WIN32
|
||||
/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
|
||||
*set errno. thus this error checking is not portable*/
|
||||
if (errno == EAGAIN ||
|
||||
errno == EINTR ||
|
||||
errno == EINPROGRESS)
|
||||
goto reschedule;
|
||||
/* error case */
|
||||
what |= EVBUFFER_ERROR;
|
||||
|
||||
#else
|
||||
goto reschedule;
|
||||
#endif
|
||||
|
||||
} else if (res == 0) {
|
||||
/* eof case */
|
||||
what |= EVBUFFER_EOF;
|
||||
}
|
||||
if (res <= 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(bufev->output) != 0)
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
|
||||
/*
|
||||
* Invoke the user callback if our buffer is drained or below the
|
||||
* low watermark.
|
||||
*/
|
||||
if (bufev->writecb != NULL &&
|
||||
EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
|
||||
(*bufev->writecb)(bufev, bufev->cbarg);
|
||||
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
if (EVBUFFER_LENGTH(bufev->output) != 0)
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
return;
|
||||
|
||||
error:
|
||||
(*bufev->errorcb)(bufev, what, bufev->cbarg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new buffered event object.
|
||||
*
|
||||
* The read callback is invoked whenever we read new data.
|
||||
* The write callback is invoked whenever the output buffer is drained.
|
||||
* The error callback is invoked on a write/read error or on EOF.
|
||||
*
|
||||
* Both read and write callbacks maybe NULL. The error callback is not
|
||||
* allowed to be NULL and have to be provided always.
|
||||
*/
|
||||
|
||||
struct bufferevent *
|
||||
bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
|
||||
everrorcb errorcb, void *cbarg)
|
||||
{
|
||||
struct bufferevent *bufev;
|
||||
|
||||
if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
if ((bufev->input = evbuffer_new()) == NULL) {
|
||||
free(bufev);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((bufev->output = evbuffer_new()) == NULL) {
|
||||
evbuffer_free(bufev->input);
|
||||
free(bufev);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
|
||||
event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
|
||||
|
||||
bufev->readcb = readcb;
|
||||
bufev->writecb = writecb;
|
||||
bufev->errorcb = errorcb;
|
||||
|
||||
bufev->cbarg = cbarg;
|
||||
|
||||
/*
|
||||
* Set to EV_WRITE so that using bufferevent_write is going to
|
||||
* trigger a callback. Reading needs to be explicitly enabled
|
||||
* because otherwise no data will be available.
|
||||
*/
|
||||
bufev->enabled = EV_WRITE;
|
||||
|
||||
return (bufev);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_priority_set(struct bufferevent *bufev, int priority)
|
||||
{
|
||||
if (event_priority_set(&bufev->ev_read, priority) == -1)
|
||||
return (-1);
|
||||
if (event_priority_set(&bufev->ev_write, priority) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Closing the file descriptor is the responsibility of the caller */
|
||||
|
||||
void
|
||||
bufferevent_free(struct bufferevent *bufev)
|
||||
{
|
||||
event_del(&bufev->ev_read);
|
||||
event_del(&bufev->ev_write);
|
||||
|
||||
evbuffer_free(bufev->input);
|
||||
evbuffer_free(bufev->output);
|
||||
|
||||
free(bufev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 on success;
|
||||
* -1 on failure.
|
||||
*/
|
||||
|
||||
int
|
||||
bufferevent_write(struct bufferevent *bufev, void *data, size_t size)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = evbuffer_add(bufev->output, data, size);
|
||||
|
||||
if (res == -1)
|
||||
return (res);
|
||||
|
||||
/* If everything is okay, we need to schedule a write */
|
||||
if (size > 0 && (bufev->enabled & EV_WRITE))
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = bufferevent_write(bufev, buf->buffer, buf->off);
|
||||
if (res != -1)
|
||||
evbuffer_drain(buf, buf->off);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
size_t
|
||||
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
|
||||
{
|
||||
struct evbuffer *buf = bufev->input;
|
||||
|
||||
if (buf->off < size)
|
||||
size = buf->off;
|
||||
|
||||
/* Copy the available data to the user buffer */
|
||||
memcpy(data, buf->buffer, size);
|
||||
|
||||
if (size)
|
||||
evbuffer_drain(buf, size);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_enable(struct bufferevent *bufev, short event)
|
||||
{
|
||||
if (event & EV_READ) {
|
||||
if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
|
||||
return (-1);
|
||||
}
|
||||
if (event & EV_WRITE) {
|
||||
if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
bufev->enabled |= event;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_disable(struct bufferevent *bufev, short event)
|
||||
{
|
||||
if (event & EV_READ) {
|
||||
if (event_del(&bufev->ev_read) == -1)
|
||||
return (-1);
|
||||
}
|
||||
if (event & EV_WRITE) {
|
||||
if (event_del(&bufev->ev_write) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
bufev->enabled &= ~event;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the read and write timeout for a buffered event.
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_settimeout(struct bufferevent *bufev,
|
||||
int timeout_read, int timeout_write) {
|
||||
bufev->timeout_read = timeout_read;
|
||||
bufev->timeout_write = timeout_write;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the water marks
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_setwatermark(struct bufferevent *bufev, short events,
|
||||
size_t lowmark, size_t highmark)
|
||||
{
|
||||
if (events & EV_READ) {
|
||||
bufev->wm_read.low = lowmark;
|
||||
bufev->wm_read.high = highmark;
|
||||
}
|
||||
|
||||
if (events & EV_WRITE) {
|
||||
bufev->wm_write.low = lowmark;
|
||||
bufev->wm_write.high = highmark;
|
||||
}
|
||||
|
||||
/* If the watermarks changed then see if we should call read again */
|
||||
bufferevent_read_pressure_cb(bufev->input,
|
||||
0, EVBUFFER_LENGTH(bufev->input), bufev);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = event_base_set(base, &bufev->ev_read);
|
||||
if (res == -1)
|
||||
return (res);
|
||||
|
||||
res = event_base_set(base, &bufev->ev_write);
|
||||
return (res);
|
||||
}
|
56
contrib/pf/libevent/event-internal.h
Normal file
56
contrib/pf/libevent/event-internal.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifndef _EVENT_INTERNAL_H_
|
||||
#define _EVENT_INTERNAL_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct event_base {
|
||||
const struct eventop *evsel;
|
||||
void *evbase;
|
||||
int event_count; /* counts number of total events */
|
||||
int event_count_active; /* counts number of active events */
|
||||
|
||||
int event_gotterm; /* Set to terminate loop */
|
||||
|
||||
/* active event management */
|
||||
struct event_list **activequeues;
|
||||
int nactivequeues;
|
||||
|
||||
struct event_list eventqueue;
|
||||
struct timeval event_tv;
|
||||
|
||||
RB_HEAD(event_tree, event) timetree;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _EVENT_INTERNAL_H_ */
|
878
contrib/pf/libevent/event.c
Normal file
878
contrib/pf/libevent/event.c
Normal file
@ -0,0 +1,878 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#include "misc.h"
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/tree.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "event-internal.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef HAVE_EVENT_PORTS
|
||||
extern const struct eventop evportops;
|
||||
#endif
|
||||
#ifdef HAVE_SELECT
|
||||
extern const struct eventop selectops;
|
||||
#endif
|
||||
#ifdef HAVE_POLL
|
||||
extern const struct eventop pollops;
|
||||
#endif
|
||||
#ifdef HAVE_RTSIG
|
||||
extern const struct eventop rtsigops;
|
||||
#endif
|
||||
#ifdef HAVE_EPOLL
|
||||
extern const struct eventop epollops;
|
||||
#endif
|
||||
#ifdef HAVE_WORKING_KQUEUE
|
||||
extern const struct eventop kqops;
|
||||
#endif
|
||||
#ifdef HAVE_DEVPOLL
|
||||
extern const struct eventop devpollops;
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
extern const struct eventop win32ops;
|
||||
#endif
|
||||
|
||||
/* In order of preference */
|
||||
const struct eventop *eventops[] = {
|
||||
#ifdef HAVE_EVENT_PORTS
|
||||
&evportops,
|
||||
#endif
|
||||
#ifdef HAVE_WORKING_KQUEUE
|
||||
&kqops,
|
||||
#endif
|
||||
#ifdef HAVE_EPOLL
|
||||
&epollops,
|
||||
#endif
|
||||
#ifdef HAVE_DEVPOLL
|
||||
&devpollops,
|
||||
#endif
|
||||
#ifdef HAVE_RTSIG
|
||||
&rtsigops,
|
||||
#endif
|
||||
#ifdef HAVE_POLL
|
||||
&pollops,
|
||||
#endif
|
||||
#ifdef HAVE_SELECT
|
||||
&selectops,
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
&win32ops,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Global state */
|
||||
struct event_list signalqueue;
|
||||
|
||||
struct event_base *current_base = NULL;
|
||||
|
||||
/* Handle signals - This is a deprecated interface */
|
||||
int (*event_sigcb)(void); /* Signal callback when gotsig is set */
|
||||
volatile sig_atomic_t event_gotsig; /* Set in signal handler */
|
||||
|
||||
/* Prototypes */
|
||||
static void event_queue_insert(struct event_base *, struct event *, int);
|
||||
static void event_queue_remove(struct event_base *, struct event *, int);
|
||||
static int event_haveevents(struct event_base *);
|
||||
|
||||
static void event_process_active(struct event_base *);
|
||||
|
||||
static int timeout_next(struct event_base *, struct timeval *);
|
||||
static void timeout_process(struct event_base *);
|
||||
static void timeout_correct(struct event_base *, struct timeval *);
|
||||
|
||||
static int
|
||||
compare(struct event *a, struct event *b)
|
||||
{
|
||||
if (timercmp(&a->ev_timeout, &b->ev_timeout, <))
|
||||
return (-1);
|
||||
else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
|
||||
return (1);
|
||||
if (a < b)
|
||||
return (-1);
|
||||
else if (a > b)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
gettime(struct timeval *tp)
|
||||
{
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
struct timespec ts;
|
||||
|
||||
#ifdef HAVE_CLOCK_MONOTONIC
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
|
||||
#else
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
|
||||
#endif
|
||||
return (-1);
|
||||
tp->tv_sec = ts.tv_sec;
|
||||
tp->tv_usec = ts.tv_nsec / 1000;
|
||||
#else
|
||||
gettimeofday(tp, NULL);
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
|
||||
|
||||
RB_GENERATE(event_tree, event, ev_timeout_node, compare);
|
||||
|
||||
|
||||
void *
|
||||
event_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((current_base = calloc(1, sizeof(struct event_base))) == NULL)
|
||||
event_err(1, "%s: calloc");
|
||||
|
||||
event_sigcb = NULL;
|
||||
event_gotsig = 0;
|
||||
gettime(¤t_base->event_tv);
|
||||
|
||||
RB_INIT(¤t_base->timetree);
|
||||
TAILQ_INIT(¤t_base->eventqueue);
|
||||
TAILQ_INIT(&signalqueue);
|
||||
|
||||
current_base->evbase = NULL;
|
||||
for (i = 0; eventops[i] && !current_base->evbase; i++) {
|
||||
current_base->evsel = eventops[i];
|
||||
|
||||
current_base->evbase = current_base->evsel->init();
|
||||
}
|
||||
|
||||
if (current_base->evbase == NULL)
|
||||
event_errx(1, "%s: no event mechanism available", __func__);
|
||||
|
||||
if (getenv("EVENT_SHOW_METHOD"))
|
||||
event_msgx("libevent using: %s\n",
|
||||
current_base->evsel->name);
|
||||
|
||||
/* allocate a single active event queue */
|
||||
event_base_priority_init(current_base, 1);
|
||||
|
||||
return (current_base);
|
||||
}
|
||||
|
||||
void
|
||||
event_base_free(struct event_base *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (base == NULL && current_base)
|
||||
base = current_base;
|
||||
if (base == current_base)
|
||||
current_base = NULL;
|
||||
|
||||
assert(base);
|
||||
assert(TAILQ_EMPTY(&base->eventqueue));
|
||||
for (i=0; i < base->nactivequeues; ++i)
|
||||
assert(TAILQ_EMPTY(base->activequeues[i]));
|
||||
|
||||
assert(RB_EMPTY(&base->timetree));
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i)
|
||||
free(base->activequeues[i]);
|
||||
free(base->activequeues);
|
||||
|
||||
if (base->evsel->dealloc != NULL)
|
||||
base->evsel->dealloc(base->evbase);
|
||||
|
||||
free(base);
|
||||
}
|
||||
|
||||
int
|
||||
event_priority_init(int npriorities)
|
||||
{
|
||||
return event_base_priority_init(current_base, npriorities);
|
||||
}
|
||||
|
||||
int
|
||||
event_base_priority_init(struct event_base *base, int npriorities)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (base->event_count_active)
|
||||
return (-1);
|
||||
|
||||
if (base->nactivequeues && npriorities != base->nactivequeues) {
|
||||
for (i = 0; i < base->nactivequeues; ++i) {
|
||||
free(base->activequeues[i]);
|
||||
}
|
||||
free(base->activequeues);
|
||||
}
|
||||
|
||||
/* Allocate our priority queues */
|
||||
base->nactivequeues = npriorities;
|
||||
base->activequeues = (struct event_list **)calloc(base->nactivequeues,
|
||||
npriorities * sizeof(struct event_list *));
|
||||
if (base->activequeues == NULL)
|
||||
event_err(1, "%s: calloc", __func__);
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i) {
|
||||
base->activequeues[i] = malloc(sizeof(struct event_list));
|
||||
if (base->activequeues[i] == NULL)
|
||||
event_err(1, "%s: malloc", __func__);
|
||||
TAILQ_INIT(base->activequeues[i]);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
event_haveevents(struct event_base *base)
|
||||
{
|
||||
return (base->event_count > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Active events are stored in priority queues. Lower priorities are always
|
||||
* process before higher priorities. Low priority events can starve high
|
||||
* priority ones.
|
||||
*/
|
||||
|
||||
static void
|
||||
event_process_active(struct event_base *base)
|
||||
{
|
||||
struct event *ev;
|
||||
struct event_list *activeq = NULL;
|
||||
int i;
|
||||
short ncalls;
|
||||
|
||||
if (!base->event_count_active)
|
||||
return;
|
||||
|
||||
for (i = 0; i < base->nactivequeues; ++i) {
|
||||
if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
|
||||
activeq = base->activequeues[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(activeq != NULL);
|
||||
|
||||
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
|
||||
event_queue_remove(base, ev, EVLIST_ACTIVE);
|
||||
|
||||
/* Allows deletes to work */
|
||||
ncalls = ev->ev_ncalls;
|
||||
ev->ev_pncalls = &ncalls;
|
||||
while (ncalls) {
|
||||
ncalls--;
|
||||
ev->ev_ncalls = ncalls;
|
||||
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
|
||||
if (event_gotsig)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait continously for events. We exit only if no events are left.
|
||||
*/
|
||||
|
||||
int
|
||||
event_dispatch(void)
|
||||
{
|
||||
return (event_loop(0));
|
||||
}
|
||||
|
||||
int
|
||||
event_base_dispatch(struct event_base *event_base)
|
||||
{
|
||||
return (event_base_loop(event_base, 0));
|
||||
}
|
||||
|
||||
static void
|
||||
event_loopexit_cb(int fd, short what, void *arg)
|
||||
{
|
||||
struct event_base *base = arg;
|
||||
base->event_gotterm = 1;
|
||||
}
|
||||
|
||||
/* not thread safe */
|
||||
|
||||
int
|
||||
event_loopexit(struct timeval *tv)
|
||||
{
|
||||
return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
|
||||
current_base, tv));
|
||||
}
|
||||
|
||||
int
|
||||
event_base_loopexit(struct event_base *event_base, struct timeval *tv)
|
||||
{
|
||||
return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
|
||||
event_base, tv));
|
||||
}
|
||||
|
||||
/* not thread safe */
|
||||
|
||||
int
|
||||
event_loop(int flags)
|
||||
{
|
||||
return event_base_loop(current_base, flags);
|
||||
}
|
||||
|
||||
int
|
||||
event_base_loop(struct event_base *base, int flags)
|
||||
{
|
||||
const struct eventop *evsel = base->evsel;
|
||||
void *evbase = base->evbase;
|
||||
struct timeval tv;
|
||||
int res, done;
|
||||
|
||||
done = 0;
|
||||
while (!done) {
|
||||
/* Calculate the initial events that we are waiting for */
|
||||
if (evsel->recalc(base, evbase, 0) == -1)
|
||||
return (-1);
|
||||
|
||||
/* Terminate the loop if we have been asked to */
|
||||
if (base->event_gotterm) {
|
||||
base->event_gotterm = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* You cannot use this interface for multi-threaded apps */
|
||||
while (event_gotsig) {
|
||||
event_gotsig = 0;
|
||||
if (event_sigcb) {
|
||||
res = (*event_sigcb)();
|
||||
if (res == -1) {
|
||||
errno = EINTR;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if time is running backwards */
|
||||
gettime(&tv);
|
||||
if (timercmp(&tv, &base->event_tv, <)) {
|
||||
struct timeval off;
|
||||
event_debug(("%s: time is running backwards, corrected",
|
||||
__func__));
|
||||
timersub(&base->event_tv, &tv, &off);
|
||||
timeout_correct(base, &off);
|
||||
}
|
||||
base->event_tv = tv;
|
||||
|
||||
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK))
|
||||
timeout_next(base, &tv);
|
||||
else
|
||||
timerclear(&tv);
|
||||
|
||||
/* If we have no events, we just exit */
|
||||
if (!event_haveevents(base)) {
|
||||
event_debug(("%s: no events registered.", __func__));
|
||||
return (1);
|
||||
}
|
||||
|
||||
res = evsel->dispatch(base, evbase, &tv);
|
||||
|
||||
if (res == -1)
|
||||
return (-1);
|
||||
|
||||
timeout_process(base);
|
||||
|
||||
if (base->event_count_active) {
|
||||
event_process_active(base);
|
||||
if (!base->event_count_active && (flags & EVLOOP_ONCE))
|
||||
done = 1;
|
||||
} else if (flags & EVLOOP_NONBLOCK)
|
||||
done = 1;
|
||||
}
|
||||
|
||||
event_debug(("%s: asked to terminate loop.", __func__));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Sets up an event for processing once */
|
||||
|
||||
struct event_once {
|
||||
struct event ev;
|
||||
|
||||
void (*cb)(int, short, void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
/* One-time callback, it deletes itself */
|
||||
|
||||
static void
|
||||
event_once_cb(int fd, short events, void *arg)
|
||||
{
|
||||
struct event_once *eonce = arg;
|
||||
|
||||
(*eonce->cb)(fd, events, eonce->arg);
|
||||
free(eonce);
|
||||
}
|
||||
|
||||
/* Schedules an event once */
|
||||
|
||||
int
|
||||
event_once(int fd, short events,
|
||||
void (*callback)(int, short, void *), void *arg, struct timeval *tv)
|
||||
{
|
||||
struct event_once *eonce;
|
||||
struct timeval etv;
|
||||
int res;
|
||||
|
||||
/* We cannot support signals that just fire once */
|
||||
if (events & EV_SIGNAL)
|
||||
return (-1);
|
||||
|
||||
if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
|
||||
return (-1);
|
||||
|
||||
eonce->cb = callback;
|
||||
eonce->arg = arg;
|
||||
|
||||
if (events == EV_TIMEOUT) {
|
||||
if (tv == NULL) {
|
||||
timerclear(&etv);
|
||||
tv = &etv;
|
||||
}
|
||||
|
||||
evtimer_set(&eonce->ev, event_once_cb, eonce);
|
||||
} else if (events & (EV_READ|EV_WRITE)) {
|
||||
events &= EV_READ|EV_WRITE;
|
||||
|
||||
event_set(&eonce->ev, fd, events, event_once_cb, eonce);
|
||||
} else {
|
||||
/* Bad event combination */
|
||||
free(eonce);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
res = event_add(&eonce->ev, tv);
|
||||
if (res != 0) {
|
||||
free(eonce);
|
||||
return (res);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
event_set(struct event *ev, int fd, short events,
|
||||
void (*callback)(int, short, void *), void *arg)
|
||||
{
|
||||
/* Take the current base - caller needs to set the real base later */
|
||||
ev->ev_base = current_base;
|
||||
|
||||
ev->ev_callback = callback;
|
||||
ev->ev_arg = arg;
|
||||
ev->ev_fd = fd;
|
||||
ev->ev_events = events;
|
||||
ev->ev_flags = EVLIST_INIT;
|
||||
ev->ev_ncalls = 0;
|
||||
ev->ev_pncalls = NULL;
|
||||
|
||||
/* by default, we put new events into the middle priority */
|
||||
ev->ev_pri = current_base->nactivequeues/2;
|
||||
}
|
||||
|
||||
int
|
||||
event_base_set(struct event_base *base, struct event *ev)
|
||||
{
|
||||
/* Only innocent events may be assigned to a different base */
|
||||
if (ev->ev_flags != EVLIST_INIT)
|
||||
return (-1);
|
||||
|
||||
ev->ev_base = base;
|
||||
ev->ev_pri = base->nactivequeues/2;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set's the priority of an event - if an event is already scheduled
|
||||
* changing the priority is going to fail.
|
||||
*/
|
||||
|
||||
int
|
||||
event_priority_set(struct event *ev, int pri)
|
||||
{
|
||||
if (ev->ev_flags & EVLIST_ACTIVE)
|
||||
return (-1);
|
||||
if (pri < 0 || pri >= ev->ev_base->nactivequeues)
|
||||
return (-1);
|
||||
|
||||
ev->ev_pri = pri;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if a specific event is pending or scheduled.
|
||||
*/
|
||||
|
||||
int
|
||||
event_pending(struct event *ev, short event, struct timeval *tv)
|
||||
{
|
||||
struct timeval now, res;
|
||||
int flags = 0;
|
||||
|
||||
if (ev->ev_flags & EVLIST_INSERTED)
|
||||
flags |= (ev->ev_events & (EV_READ|EV_WRITE));
|
||||
if (ev->ev_flags & EVLIST_ACTIVE)
|
||||
flags |= ev->ev_res;
|
||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||
flags |= EV_TIMEOUT;
|
||||
if (ev->ev_flags & EVLIST_SIGNAL)
|
||||
flags |= EV_SIGNAL;
|
||||
|
||||
event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);
|
||||
|
||||
/* See if there is a timeout that we should report */
|
||||
if (tv != NULL && (flags & event & EV_TIMEOUT)) {
|
||||
gettime(&now);
|
||||
timersub(&ev->ev_timeout, &now, &res);
|
||||
/* correctly remap to real time */
|
||||
gettimeofday(&now, NULL);
|
||||
timeradd(&now, &res, tv);
|
||||
}
|
||||
|
||||
return (flags & event);
|
||||
}
|
||||
|
||||
int
|
||||
event_add(struct event *ev, struct timeval *tv)
|
||||
{
|
||||
struct event_base *base = ev->ev_base;
|
||||
const struct eventop *evsel = base->evsel;
|
||||
void *evbase = base->evbase;
|
||||
|
||||
event_debug((
|
||||
"event_add: event: %p, %s%s%scall %p",
|
||||
ev,
|
||||
ev->ev_events & EV_READ ? "EV_READ " : " ",
|
||||
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
|
||||
tv ? "EV_TIMEOUT " : " ",
|
||||
ev->ev_callback));
|
||||
|
||||
assert(!(ev->ev_flags & ~EVLIST_ALL));
|
||||
|
||||
if (tv != NULL) {
|
||||
struct timeval now;
|
||||
|
||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||
event_queue_remove(base, ev, EVLIST_TIMEOUT);
|
||||
|
||||
/* Check if it is active due to a timeout. Rescheduling
|
||||
* this timeout before the callback can be executed
|
||||
* removes it from the active list. */
|
||||
if ((ev->ev_flags & EVLIST_ACTIVE) &&
|
||||
(ev->ev_res & EV_TIMEOUT)) {
|
||||
/* See if we are just active executing this
|
||||
* event in a loop
|
||||
*/
|
||||
if (ev->ev_ncalls && ev->ev_pncalls) {
|
||||
/* Abort loop */
|
||||
*ev->ev_pncalls = 0;
|
||||
}
|
||||
|
||||
event_queue_remove(base, ev, EVLIST_ACTIVE);
|
||||
}
|
||||
|
||||
gettime(&now);
|
||||
timeradd(&now, tv, &ev->ev_timeout);
|
||||
|
||||
event_debug((
|
||||
"event_add: timeout in %d seconds, call %p",
|
||||
tv->tv_sec, ev->ev_callback));
|
||||
|
||||
event_queue_insert(base, ev, EVLIST_TIMEOUT);
|
||||
}
|
||||
|
||||
if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
|
||||
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
|
||||
event_queue_insert(base, ev, EVLIST_INSERTED);
|
||||
|
||||
return (evsel->add(evbase, ev));
|
||||
} else if ((ev->ev_events & EV_SIGNAL) &&
|
||||
!(ev->ev_flags & EVLIST_SIGNAL)) {
|
||||
event_queue_insert(base, ev, EVLIST_SIGNAL);
|
||||
|
||||
return (evsel->add(evbase, ev));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
event_del(struct event *ev)
|
||||
{
|
||||
struct event_base *base;
|
||||
const struct eventop *evsel;
|
||||
void *evbase;
|
||||
|
||||
event_debug(("event_del: %p, callback %p",
|
||||
ev, ev->ev_callback));
|
||||
|
||||
/* An event without a base has not been added */
|
||||
if (ev->ev_base == NULL)
|
||||
return (-1);
|
||||
|
||||
base = ev->ev_base;
|
||||
evsel = base->evsel;
|
||||
evbase = base->evbase;
|
||||
|
||||
assert(!(ev->ev_flags & ~EVLIST_ALL));
|
||||
|
||||
/* See if we are just active executing this event in a loop */
|
||||
if (ev->ev_ncalls && ev->ev_pncalls) {
|
||||
/* Abort loop */
|
||||
*ev->ev_pncalls = 0;
|
||||
}
|
||||
|
||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||
event_queue_remove(base, ev, EVLIST_TIMEOUT);
|
||||
|
||||
if (ev->ev_flags & EVLIST_ACTIVE)
|
||||
event_queue_remove(base, ev, EVLIST_ACTIVE);
|
||||
|
||||
if (ev->ev_flags & EVLIST_INSERTED) {
|
||||
event_queue_remove(base, ev, EVLIST_INSERTED);
|
||||
return (evsel->del(evbase, ev));
|
||||
} else if (ev->ev_flags & EVLIST_SIGNAL) {
|
||||
event_queue_remove(base, ev, EVLIST_SIGNAL);
|
||||
return (evsel->del(evbase, ev));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
event_active(struct event *ev, int res, short ncalls)
|
||||
{
|
||||
/* We get different kinds of events, add them together */
|
||||
if (ev->ev_flags & EVLIST_ACTIVE) {
|
||||
ev->ev_res |= res;
|
||||
return;
|
||||
}
|
||||
|
||||
ev->ev_res = res;
|
||||
ev->ev_ncalls = ncalls;
|
||||
ev->ev_pncalls = NULL;
|
||||
event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
|
||||
}
|
||||
|
||||
int
|
||||
timeout_next(struct event_base *base, struct timeval *tv)
|
||||
{
|
||||
struct timeval dflt = TIMEOUT_DEFAULT;
|
||||
|
||||
struct timeval now;
|
||||
struct event *ev;
|
||||
|
||||
if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) {
|
||||
*tv = dflt;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (gettime(&now) == -1)
|
||||
return (-1);
|
||||
|
||||
if (timercmp(&ev->ev_timeout, &now, <=)) {
|
||||
timerclear(tv);
|
||||
return (0);
|
||||
}
|
||||
|
||||
timersub(&ev->ev_timeout, &now, tv);
|
||||
|
||||
assert(tv->tv_sec >= 0);
|
||||
assert(tv->tv_usec >= 0);
|
||||
|
||||
event_debug(("timeout_next: in %d seconds", tv->tv_sec));
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
timeout_correct(struct event_base *base, struct timeval *off)
|
||||
{
|
||||
struct event *ev;
|
||||
|
||||
/*
|
||||
* We can modify the key element of the node without destroying
|
||||
* the key, beause we apply it to all in the right order.
|
||||
*/
|
||||
RB_FOREACH(ev, event_tree, &base->timetree)
|
||||
timersub(&ev->ev_timeout, off, &ev->ev_timeout);
|
||||
}
|
||||
|
||||
void
|
||||
timeout_process(struct event_base *base)
|
||||
{
|
||||
struct timeval now;
|
||||
struct event *ev, *next;
|
||||
|
||||
gettime(&now);
|
||||
|
||||
for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) {
|
||||
if (timercmp(&ev->ev_timeout, &now, >))
|
||||
break;
|
||||
next = RB_NEXT(event_tree, &base->timetree, ev);
|
||||
|
||||
event_queue_remove(base, ev, EVLIST_TIMEOUT);
|
||||
|
||||
/* delete this event from the I/O queues */
|
||||
event_del(ev);
|
||||
|
||||
event_debug(("timeout_process: call %p",
|
||||
ev->ev_callback));
|
||||
event_active(ev, EV_TIMEOUT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
event_queue_remove(struct event_base *base, struct event *ev, int queue)
|
||||
{
|
||||
int docount = 1;
|
||||
|
||||
if (!(ev->ev_flags & queue))
|
||||
event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
|
||||
ev, ev->ev_fd, queue);
|
||||
|
||||
if (ev->ev_flags & EVLIST_INTERNAL)
|
||||
docount = 0;
|
||||
|
||||
if (docount)
|
||||
base->event_count--;
|
||||
|
||||
ev->ev_flags &= ~queue;
|
||||
switch (queue) {
|
||||
case EVLIST_ACTIVE:
|
||||
if (docount)
|
||||
base->event_count_active--;
|
||||
TAILQ_REMOVE(base->activequeues[ev->ev_pri],
|
||||
ev, ev_active_next);
|
||||
break;
|
||||
case EVLIST_SIGNAL:
|
||||
TAILQ_REMOVE(&signalqueue, ev, ev_signal_next);
|
||||
break;
|
||||
case EVLIST_TIMEOUT:
|
||||
RB_REMOVE(event_tree, &base->timetree, ev);
|
||||
break;
|
||||
case EVLIST_INSERTED:
|
||||
TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
|
||||
break;
|
||||
default:
|
||||
event_errx(1, "%s: unknown queue %x", __func__, queue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
event_queue_insert(struct event_base *base, struct event *ev, int queue)
|
||||
{
|
||||
int docount = 1;
|
||||
|
||||
if (ev->ev_flags & queue) {
|
||||
/* Double insertion is possible for active events */
|
||||
if (queue & EVLIST_ACTIVE)
|
||||
return;
|
||||
|
||||
event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
|
||||
ev, ev->ev_fd, queue);
|
||||
}
|
||||
|
||||
if (ev->ev_flags & EVLIST_INTERNAL)
|
||||
docount = 0;
|
||||
|
||||
if (docount)
|
||||
base->event_count++;
|
||||
|
||||
ev->ev_flags |= queue;
|
||||
switch (queue) {
|
||||
case EVLIST_ACTIVE:
|
||||
if (docount)
|
||||
base->event_count_active++;
|
||||
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
|
||||
ev,ev_active_next);
|
||||
break;
|
||||
case EVLIST_SIGNAL:
|
||||
TAILQ_INSERT_TAIL(&signalqueue, ev, ev_signal_next);
|
||||
break;
|
||||
case EVLIST_TIMEOUT: {
|
||||
struct event *tmp = RB_INSERT(event_tree, &base->timetree, ev);
|
||||
assert(tmp == NULL);
|
||||
break;
|
||||
}
|
||||
case EVLIST_INSERTED:
|
||||
TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
|
||||
break;
|
||||
default:
|
||||
event_errx(1, "%s: unknown queue %x", __func__, queue);
|
||||
}
|
||||
}
|
||||
|
||||
/* Functions for debugging */
|
||||
|
||||
const char *
|
||||
event_get_version(void)
|
||||
{
|
||||
return (VERSION);
|
||||
}
|
||||
|
||||
/*
|
||||
* No thread-safe interface needed - the information should be the same
|
||||
* for all threads.
|
||||
*/
|
||||
|
||||
const char *
|
||||
event_get_method(void)
|
||||
{
|
||||
return (current_base->evsel->name);
|
||||
}
|
341
contrib/pf/libevent/event.h
Normal file
341
contrib/pf/libevent/event.h
Normal file
@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifndef _EVENT_H_
|
||||
#define _EVENT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
typedef unsigned char u_char;
|
||||
typedef unsigned short u_short;
|
||||
#endif
|
||||
|
||||
#define EVLIST_TIMEOUT 0x01
|
||||
#define EVLIST_INSERTED 0x02
|
||||
#define EVLIST_SIGNAL 0x04
|
||||
#define EVLIST_ACTIVE 0x08
|
||||
#define EVLIST_INTERNAL 0x10
|
||||
#define EVLIST_INIT 0x80
|
||||
|
||||
/* EVLIST_X_ Private space: 0x1000-0xf000 */
|
||||
#define EVLIST_ALL (0xf000 | 0x9f)
|
||||
|
||||
#define EV_TIMEOUT 0x01
|
||||
#define EV_READ 0x02
|
||||
#define EV_WRITE 0x04
|
||||
#define EV_SIGNAL 0x08
|
||||
#define EV_PERSIST 0x10 /* Persistant event */
|
||||
|
||||
/* Fix so that ppl dont have to run with <sys/queue.h> */
|
||||
#ifndef TAILQ_ENTRY
|
||||
#define _EVENT_DEFINED_TQENTRY
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
#endif /* !TAILQ_ENTRY */
|
||||
#ifndef RB_ENTRY
|
||||
#define _EVENT_DEFINED_RBENTRY
|
||||
#define RB_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
#endif /* !RB_ENTRY */
|
||||
|
||||
struct event_base;
|
||||
struct event {
|
||||
TAILQ_ENTRY (event) ev_next;
|
||||
TAILQ_ENTRY (event) ev_active_next;
|
||||
TAILQ_ENTRY (event) ev_signal_next;
|
||||
RB_ENTRY (event) ev_timeout_node;
|
||||
|
||||
struct event_base *ev_base;
|
||||
int ev_fd;
|
||||
short ev_events;
|
||||
short ev_ncalls;
|
||||
short *ev_pncalls; /* Allows deletes in callback */
|
||||
|
||||
struct timeval ev_timeout;
|
||||
|
||||
int ev_pri; /* smaller numbers are higher priority */
|
||||
|
||||
void (*ev_callback)(int, short, void *arg);
|
||||
void *ev_arg;
|
||||
|
||||
int ev_res; /* result passed to event callback */
|
||||
int ev_flags;
|
||||
};
|
||||
|
||||
#define EVENT_SIGNAL(ev) (int)(ev)->ev_fd
|
||||
#define EVENT_FD(ev) (int)(ev)->ev_fd
|
||||
|
||||
/*
|
||||
* Key-Value pairs. Can be used for HTTP headers but also for
|
||||
* query argument parsing.
|
||||
*/
|
||||
struct evkeyval {
|
||||
TAILQ_ENTRY(evkeyval) next;
|
||||
|
||||
char *key;
|
||||
char *value;
|
||||
};
|
||||
|
||||
#ifdef _EVENT_DEFINED_TQENTRY
|
||||
#undef TAILQ_ENTRY
|
||||
struct event_list;
|
||||
struct evkeyvalq;
|
||||
#undef _EVENT_DEFINED_TQENTRY
|
||||
#else
|
||||
TAILQ_HEAD (event_list, event);
|
||||
TAILQ_HEAD (evkeyvalq, evkeyval);
|
||||
#endif /* _EVENT_DEFINED_TQENTRY */
|
||||
#ifdef _EVENT_DEFINED_RBENTRY
|
||||
#undef RB_ENTRY
|
||||
#undef _EVENT_DEFINED_RBENTRY
|
||||
#endif /* _EVENT_DEFINED_RBENTRY */
|
||||
|
||||
struct eventop {
|
||||
char *name;
|
||||
void *(*init)(void);
|
||||
int (*add)(void *, struct event *);
|
||||
int (*del)(void *, struct event *);
|
||||
int (*recalc)(struct event_base *, void *, int);
|
||||
int (*dispatch)(struct event_base *, void *, struct timeval *);
|
||||
void (*dealloc)(void *);
|
||||
};
|
||||
|
||||
#define TIMEOUT_DEFAULT {5, 0}
|
||||
|
||||
void *event_init(void);
|
||||
int event_dispatch(void);
|
||||
int event_base_dispatch(struct event_base *);
|
||||
void event_base_free(struct event_base *);
|
||||
|
||||
#define _EVENT_LOG_DEBUG 0
|
||||
#define _EVENT_LOG_MSG 1
|
||||
#define _EVENT_LOG_WARN 2
|
||||
#define _EVENT_LOG_ERR 3
|
||||
typedef void (*event_log_cb)(int severity, const char *msg);
|
||||
void event_set_log_callback(event_log_cb cb);
|
||||
|
||||
/* Associate a different event base with an event */
|
||||
int event_base_set(struct event_base *, struct event *);
|
||||
|
||||
#define EVLOOP_ONCE 0x01
|
||||
#define EVLOOP_NONBLOCK 0x02
|
||||
int event_loop(int);
|
||||
int event_base_loop(struct event_base *, int);
|
||||
int event_loopexit(struct timeval *); /* Causes the loop to exit */
|
||||
int event_base_loopexit(struct event_base *, struct timeval *);
|
||||
|
||||
#define evtimer_add(ev, tv) event_add(ev, tv)
|
||||
#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
|
||||
#define evtimer_del(ev) event_del(ev)
|
||||
#define evtimer_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
|
||||
#define evtimer_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
|
||||
|
||||
#define timeout_add(ev, tv) event_add(ev, tv)
|
||||
#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
|
||||
#define timeout_del(ev) event_del(ev)
|
||||
#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
|
||||
#define timeout_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
|
||||
|
||||
#define signal_add(ev, tv) event_add(ev, tv)
|
||||
#define signal_set(ev, x, cb, arg) \
|
||||
event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
|
||||
#define signal_del(ev) event_del(ev)
|
||||
#define signal_pending(ev, tv) event_pending(ev, EV_SIGNAL, tv)
|
||||
#define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
|
||||
|
||||
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
|
||||
int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
|
||||
|
||||
int event_add(struct event *, struct timeval *);
|
||||
int event_del(struct event *);
|
||||
void event_active(struct event *, int, short);
|
||||
|
||||
int event_pending(struct event *, short, struct timeval *);
|
||||
|
||||
#ifdef WIN32
|
||||
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != (int)INVALID_HANDLE_VALUE)
|
||||
#else
|
||||
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
|
||||
#endif
|
||||
|
||||
/* Some simple debugging functions */
|
||||
const char *event_get_version(void);
|
||||
const char *event_get_method(void);
|
||||
|
||||
/* These functions deal with event priorities */
|
||||
|
||||
int event_priority_init(int);
|
||||
int event_base_priority_init(struct event_base *, int);
|
||||
int event_priority_set(struct event *, int);
|
||||
|
||||
/* These functions deal with buffering input and output */
|
||||
|
||||
struct evbuffer {
|
||||
u_char *buffer;
|
||||
u_char *orig_buffer;
|
||||
|
||||
size_t misalign;
|
||||
size_t totallen;
|
||||
size_t off;
|
||||
|
||||
void (*cb)(struct evbuffer *, size_t, size_t, void *);
|
||||
void *cbarg;
|
||||
};
|
||||
|
||||
/* Just for error reporting - use other constants otherwise */
|
||||
#define EVBUFFER_READ 0x01
|
||||
#define EVBUFFER_WRITE 0x02
|
||||
#define EVBUFFER_EOF 0x10
|
||||
#define EVBUFFER_ERROR 0x20
|
||||
#define EVBUFFER_TIMEOUT 0x40
|
||||
|
||||
struct bufferevent;
|
||||
typedef void (*evbuffercb)(struct bufferevent *, void *);
|
||||
typedef void (*everrorcb)(struct bufferevent *, short what, void *);
|
||||
|
||||
struct event_watermark {
|
||||
size_t low;
|
||||
size_t high;
|
||||
};
|
||||
|
||||
struct bufferevent {
|
||||
struct event ev_read;
|
||||
struct event ev_write;
|
||||
|
||||
struct evbuffer *input;
|
||||
struct evbuffer *output;
|
||||
|
||||
struct event_watermark wm_read;
|
||||
struct event_watermark wm_write;
|
||||
|
||||
evbuffercb readcb;
|
||||
evbuffercb writecb;
|
||||
everrorcb errorcb;
|
||||
void *cbarg;
|
||||
|
||||
int timeout_read; /* in seconds */
|
||||
int timeout_write; /* in seconds */
|
||||
|
||||
short enabled; /* events that are currently enabled */
|
||||
};
|
||||
|
||||
struct bufferevent *bufferevent_new(int fd,
|
||||
evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
|
||||
int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev);
|
||||
int bufferevent_priority_set(struct bufferevent *bufev, int pri);
|
||||
void bufferevent_free(struct bufferevent *bufev);
|
||||
int bufferevent_write(struct bufferevent *bufev, void *data, size_t size);
|
||||
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
|
||||
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
|
||||
int bufferevent_enable(struct bufferevent *bufev, short event);
|
||||
int bufferevent_disable(struct bufferevent *bufev, short event);
|
||||
void bufferevent_settimeout(struct bufferevent *bufev,
|
||||
int timeout_read, int timeout_write);
|
||||
|
||||
#define EVBUFFER_LENGTH(x) (x)->off
|
||||
#define EVBUFFER_DATA(x) (x)->buffer
|
||||
#define EVBUFFER_INPUT(x) (x)->input
|
||||
#define EVBUFFER_OUTPUT(x) (x)->output
|
||||
|
||||
struct evbuffer *evbuffer_new(void);
|
||||
void evbuffer_free(struct evbuffer *);
|
||||
int evbuffer_expand(struct evbuffer *, size_t);
|
||||
int evbuffer_add(struct evbuffer *, const void *, size_t);
|
||||
int evbuffer_remove(struct evbuffer *, void *, size_t);
|
||||
char *evbuffer_readline(struct evbuffer *);
|
||||
int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
|
||||
int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...);
|
||||
int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
|
||||
void evbuffer_drain(struct evbuffer *, size_t);
|
||||
int evbuffer_write(struct evbuffer *, int);
|
||||
int evbuffer_read(struct evbuffer *, int, int);
|
||||
u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
|
||||
void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
|
||||
|
||||
/*
|
||||
* Marshaling tagged data - We assume that all tags are inserted in their
|
||||
* numeric order - so that unknown tags will always be higher than the
|
||||
* known ones - and we can just ignore the end of an event buffer.
|
||||
*/
|
||||
|
||||
void evtag_init(void);
|
||||
|
||||
void evtag_marshal(struct evbuffer *evbuf, u_int8_t tag, const void *data,
|
||||
u_int32_t len);
|
||||
|
||||
void encode_int(struct evbuffer *evbuf, u_int32_t number);
|
||||
|
||||
void evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag,
|
||||
u_int32_t integer);
|
||||
|
||||
void evtag_marshal_string(struct evbuffer *buf, u_int8_t tag,
|
||||
const char *string);
|
||||
|
||||
void evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag,
|
||||
struct timeval *tv);
|
||||
|
||||
void evtag_test(void);
|
||||
|
||||
int evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag,
|
||||
struct evbuffer *dst);
|
||||
int evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag);
|
||||
int evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength);
|
||||
int evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength);
|
||||
int evtag_consume(struct evbuffer *evbuf);
|
||||
|
||||
int evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
|
||||
u_int32_t *pinteger);
|
||||
|
||||
int evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
|
||||
size_t len);
|
||||
|
||||
int evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag,
|
||||
char **pstring);
|
||||
|
||||
int evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag,
|
||||
struct timeval *ptv);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _EVENT_H_ */
|
35
contrib/pf/libevent/evsignal.h
Normal file
35
contrib/pf/libevent/evsignal.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifndef _EVSIGNAL_H_
|
||||
#define _EVSIGNAL_H_
|
||||
|
||||
void evsignal_init(void);
|
||||
void evsignal_process(void);
|
||||
int evsignal_add(struct event *);
|
||||
int evsignal_del(struct event *);
|
||||
|
||||
#endif /* _EVSIGNAL_H_ */
|
413
contrib/pf/libevent/kqueue.c
Normal file
413
contrib/pf/libevent/kqueue.c
Normal file
@ -0,0 +1,413 @@
|
||||
/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <sys/event.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
|
||||
#define INTPTR(x) (intptr_t)x
|
||||
#else
|
||||
#define INTPTR(x) x
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "log.h"
|
||||
|
||||
#define EVLIST_X_KQINKERNEL 0x1000
|
||||
|
||||
#define NEVENT 64
|
||||
|
||||
struct kqop {
|
||||
struct kevent *changes;
|
||||
int nchanges;
|
||||
struct kevent *events;
|
||||
int nevents;
|
||||
int kq;
|
||||
};
|
||||
|
||||
void *kq_init (void);
|
||||
int kq_add (void *, struct event *);
|
||||
int kq_del (void *, struct event *);
|
||||
int kq_recalc (struct event_base *, void *, int);
|
||||
int kq_dispatch (struct event_base *, void *, struct timeval *);
|
||||
int kq_insert (struct kqop *, struct kevent *);
|
||||
void kq_dealloc (void *);
|
||||
|
||||
const struct eventop kqops = {
|
||||
"kqueue",
|
||||
kq_init,
|
||||
kq_add,
|
||||
kq_del,
|
||||
kq_recalc,
|
||||
kq_dispatch,
|
||||
kq_dealloc
|
||||
};
|
||||
|
||||
void *
|
||||
kq_init(void)
|
||||
{
|
||||
int kq;
|
||||
struct kqop *kqueueop;
|
||||
|
||||
/* Disable kqueue when this environment variable is set */
|
||||
if (getenv("EVENT_NOKQUEUE"))
|
||||
return (NULL);
|
||||
|
||||
if (!(kqueueop = calloc(1, sizeof(struct kqop))))
|
||||
return (NULL);
|
||||
|
||||
/* Initalize the kernel queue */
|
||||
|
||||
if ((kq = kqueue()) == -1) {
|
||||
event_warn("kqueue");
|
||||
free (kqueueop);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
kqueueop->kq = kq;
|
||||
|
||||
/* Initalize fields */
|
||||
kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
|
||||
if (kqueueop->changes == NULL) {
|
||||
free (kqueueop);
|
||||
return (NULL);
|
||||
}
|
||||
kqueueop->events = malloc(NEVENT * sizeof(struct kevent));
|
||||
if (kqueueop->events == NULL) {
|
||||
free (kqueueop->changes);
|
||||
free (kqueueop);
|
||||
return (NULL);
|
||||
}
|
||||
kqueueop->nevents = NEVENT;
|
||||
|
||||
/* Check for Mac OS X kqueue bug. */
|
||||
kqueueop->changes[0].ident = -1;
|
||||
kqueueop->changes[0].filter = EVFILT_READ;
|
||||
kqueueop->changes[0].flags = EV_ADD;
|
||||
/*
|
||||
* If kqueue works, then kevent will succeed, and it will
|
||||
* stick an error in events[0]. If kqueue is broken, then
|
||||
* kevent will fail.
|
||||
*/
|
||||
if (kevent(kq,
|
||||
kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
|
||||
kqueueop->events[0].ident != -1 ||
|
||||
kqueueop->events[0].flags != EV_ERROR) {
|
||||
event_warn("%s: detected broken kqueue; not using.", __func__);
|
||||
free(kqueueop->changes);
|
||||
free(kqueueop->events);
|
||||
free(kqueueop);
|
||||
close(kq);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (kqueueop);
|
||||
}
|
||||
|
||||
int
|
||||
kq_recalc(struct event_base *base, void *arg, int max)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kq_insert(struct kqop *kqop, struct kevent *kev)
|
||||
{
|
||||
int nevents = kqop->nevents;
|
||||
|
||||
if (kqop->nchanges == nevents) {
|
||||
struct kevent *newchange;
|
||||
struct kevent *newresult;
|
||||
|
||||
nevents *= 2;
|
||||
|
||||
newchange = realloc(kqop->changes,
|
||||
nevents * sizeof(struct kevent));
|
||||
if (newchange == NULL) {
|
||||
event_warn("%s: malloc", __func__);
|
||||
return (-1);
|
||||
}
|
||||
kqop->changes = newchange;
|
||||
|
||||
newresult = realloc(kqop->events,
|
||||
nevents * sizeof(struct kevent));
|
||||
|
||||
/*
|
||||
* If we fail, we don't have to worry about freeing,
|
||||
* the next realloc will pick it up.
|
||||
*/
|
||||
if (newresult == NULL) {
|
||||
event_warn("%s: malloc", __func__);
|
||||
return (-1);
|
||||
}
|
||||
kqop->events = newresult;
|
||||
|
||||
kqop->nevents = nevents;
|
||||
}
|
||||
|
||||
memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
|
||||
|
||||
event_debug(("%s: fd %d %s%s",
|
||||
__func__, kev->ident,
|
||||
kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
|
||||
kev->flags == EV_DELETE ? " (del)" : ""));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
kq_sighandler(int sig)
|
||||
{
|
||||
/* Do nothing here */
|
||||
}
|
||||
|
||||
int
|
||||
kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
|
||||
{
|
||||
struct kqop *kqop = arg;
|
||||
struct kevent *changes = kqop->changes;
|
||||
struct kevent *events = kqop->events;
|
||||
struct event *ev;
|
||||
struct timespec ts;
|
||||
int i, res;
|
||||
|
||||
TIMEVAL_TO_TIMESPEC(tv, &ts);
|
||||
|
||||
res = kevent(kqop->kq, changes, kqop->nchanges,
|
||||
events, kqop->nevents, &ts);
|
||||
kqop->nchanges = 0;
|
||||
if (res == -1) {
|
||||
if (errno != EINTR) {
|
||||
event_warn("kevent");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
event_debug(("%s: kevent reports %d", __func__, res));
|
||||
|
||||
for (i = 0; i < res; i++) {
|
||||
int which = 0;
|
||||
|
||||
if (events[i].flags & EV_ERROR) {
|
||||
/*
|
||||
* Error messages that can happen, when a delete fails.
|
||||
* EBADF happens when the file discriptor has been
|
||||
* closed,
|
||||
* ENOENT when the file discriptor was closed and
|
||||
* then reopened.
|
||||
* EINVAL for some reasons not understood; EINVAL
|
||||
* should not be returned ever; but FreeBSD does :-\
|
||||
* An error is also indicated when a callback deletes
|
||||
* an event we are still processing. In that case
|
||||
* the data field is set to ENOENT.
|
||||
*/
|
||||
if (events[i].data == EBADF ||
|
||||
events[i].data == EINVAL ||
|
||||
events[i].data == ENOENT)
|
||||
continue;
|
||||
errno = events[i].data;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ev = (struct event *)events[i].udata;
|
||||
|
||||
if (events[i].filter == EVFILT_READ) {
|
||||
which |= EV_READ;
|
||||
} else if (events[i].filter == EVFILT_WRITE) {
|
||||
which |= EV_WRITE;
|
||||
} else if (events[i].filter == EVFILT_SIGNAL) {
|
||||
which |= EV_SIGNAL;
|
||||
}
|
||||
|
||||
if (!which)
|
||||
continue;
|
||||
|
||||
if (!(ev->ev_events & EV_PERSIST))
|
||||
event_del(ev);
|
||||
|
||||
event_active(ev, which,
|
||||
ev->ev_events & EV_SIGNAL ? events[i].data : 1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
kq_add(void *arg, struct event *ev)
|
||||
{
|
||||
struct kqop *kqop = arg;
|
||||
struct kevent kev;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL) {
|
||||
int nsignal = EVENT_SIGNAL(ev);
|
||||
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
kev.ident = nsignal;
|
||||
kev.filter = EVFILT_SIGNAL;
|
||||
kev.flags = EV_ADD;
|
||||
if (!(ev->ev_events & EV_PERSIST))
|
||||
kev.flags |= EV_ONESHOT;
|
||||
kev.udata = INTPTR(ev);
|
||||
|
||||
if (kq_insert(kqop, &kev) == -1)
|
||||
return (-1);
|
||||
|
||||
if (signal(nsignal, kq_sighandler) == SIG_ERR)
|
||||
return (-1);
|
||||
|
||||
ev->ev_flags |= EVLIST_X_KQINKERNEL;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_READ) {
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
kev.ident = ev->ev_fd;
|
||||
kev.filter = EVFILT_READ;
|
||||
#ifdef NOTE_EOF
|
||||
/* Make it behave like select() and poll() */
|
||||
kev.fflags = NOTE_EOF;
|
||||
#endif
|
||||
kev.flags = EV_ADD;
|
||||
if (!(ev->ev_events & EV_PERSIST))
|
||||
kev.flags |= EV_ONESHOT;
|
||||
kev.udata = INTPTR(ev);
|
||||
|
||||
if (kq_insert(kqop, &kev) == -1)
|
||||
return (-1);
|
||||
|
||||
ev->ev_flags |= EVLIST_X_KQINKERNEL;
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_WRITE) {
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
kev.ident = ev->ev_fd;
|
||||
kev.filter = EVFILT_WRITE;
|
||||
kev.flags = EV_ADD;
|
||||
if (!(ev->ev_events & EV_PERSIST))
|
||||
kev.flags |= EV_ONESHOT;
|
||||
kev.udata = INTPTR(ev);
|
||||
|
||||
if (kq_insert(kqop, &kev) == -1)
|
||||
return (-1);
|
||||
|
||||
ev->ev_flags |= EVLIST_X_KQINKERNEL;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kq_del(void *arg, struct event *ev)
|
||||
{
|
||||
struct kqop *kqop = arg;
|
||||
struct kevent kev;
|
||||
|
||||
if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
|
||||
return (0);
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL) {
|
||||
int nsignal = EVENT_SIGNAL(ev);
|
||||
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
kev.ident = nsignal;
|
||||
kev.filter = EVFILT_SIGNAL;
|
||||
kev.flags = EV_DELETE;
|
||||
|
||||
if (kq_insert(kqop, &kev) == -1)
|
||||
return (-1);
|
||||
|
||||
if (signal(nsignal, SIG_DFL) == SIG_ERR)
|
||||
return (-1);
|
||||
|
||||
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_READ) {
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
kev.ident = ev->ev_fd;
|
||||
kev.filter = EVFILT_READ;
|
||||
kev.flags = EV_DELETE;
|
||||
|
||||
if (kq_insert(kqop, &kev) == -1)
|
||||
return (-1);
|
||||
|
||||
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_WRITE) {
|
||||
memset(&kev, 0, sizeof(kev));
|
||||
kev.ident = ev->ev_fd;
|
||||
kev.filter = EVFILT_WRITE;
|
||||
kev.flags = EV_DELETE;
|
||||
|
||||
if (kq_insert(kqop, &kev) == -1)
|
||||
return (-1);
|
||||
|
||||
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
kq_dealloc(void *arg)
|
||||
{
|
||||
struct kqop *kqop = arg;
|
||||
|
||||
if (kqop->changes)
|
||||
free(kqop->changes);
|
||||
if (kqop->events)
|
||||
free(kqop->events);
|
||||
if (kqop->kq)
|
||||
close(kqop->kq);
|
||||
memset(kqop, 0, sizeof(struct kqop));
|
||||
free(kqop);
|
||||
}
|
219
contrib/pf/libevent/log.c
Normal file
219
contrib/pf/libevent/log.c
Normal file
@ -0,0 +1,219 @@
|
||||
/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
|
||||
|
||||
/*
|
||||
* log.c
|
||||
*
|
||||
* Based on err.c, which was adapted from OpenBSD libc *err* *warn* code.
|
||||
*
|
||||
* Copyright (c) 2005 Nick Mathewson <nickm@freehaven.net>
|
||||
*
|
||||
* Copyright (c) 2000 Dug Song <dugsong@monkey.org>
|
||||
*
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. 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.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#include "misc.h"
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/tree.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_time.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "event.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static void _warn_helper(int severity, int log_errno, const char *fmt,
|
||||
va_list ap);
|
||||
static void event_log(int severity, const char *msg);
|
||||
|
||||
static int
|
||||
event_vsnprintf(char *str, size_t size, const char *format, va_list args)
|
||||
{
|
||||
int r;
|
||||
if (size == 0)
|
||||
return -1;
|
||||
#ifdef WIN32
|
||||
r = _vsnprintf(str, size, format, args);
|
||||
#else
|
||||
r = vsnprintf(str, size, format, args);
|
||||
#endif
|
||||
str[size-1] = '\0';
|
||||
if (r < 0 || ((size_t)r) >= size) {
|
||||
/* different platforms behave differently on overflow;
|
||||
* handle both kinds. */
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
event_snprintf(char *str, size_t size, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int r;
|
||||
va_start(ap, format);
|
||||
r = event_vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
event_err(int eval, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_warn_helper(_EVENT_LOG_ERR, errno, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(eval);
|
||||
}
|
||||
|
||||
void
|
||||
event_warn(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_warn_helper(_EVENT_LOG_WARN, errno, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
event_errx(int eval, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_warn_helper(_EVENT_LOG_ERR, -1, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(eval);
|
||||
}
|
||||
|
||||
void
|
||||
event_warnx(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_warn_helper(_EVENT_LOG_WARN, -1, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
event_msgx(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_warn_helper(_EVENT_LOG_MSG, -1, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
_event_debugx(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_warn_helper(_EVENT_LOG_DEBUG, -1, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void
|
||||
_warn_helper(int severity, int log_errno, const char *fmt, va_list ap)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t len;
|
||||
|
||||
if (fmt != NULL)
|
||||
event_vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
else
|
||||
buf[0] = '\0';
|
||||
|
||||
if (log_errno >= 0) {
|
||||
len = strlen(buf);
|
||||
if (len < sizeof(buf) - 3) {
|
||||
event_snprintf(buf + len, sizeof(buf) - len, ": %s",
|
||||
strerror(log_errno));
|
||||
}
|
||||
}
|
||||
|
||||
event_log(severity, buf);
|
||||
}
|
||||
|
||||
static event_log_cb log_fn = NULL;
|
||||
|
||||
void
|
||||
event_set_log_callback(event_log_cb cb)
|
||||
{
|
||||
log_fn = cb;
|
||||
}
|
||||
|
||||
static void
|
||||
event_log(int severity, const char *msg)
|
||||
{
|
||||
if (log_fn)
|
||||
log_fn(severity, msg);
|
||||
else {
|
||||
const char *severity_str;
|
||||
switch (severity) {
|
||||
case _EVENT_LOG_DEBUG:
|
||||
severity_str = "debug";
|
||||
break;
|
||||
case _EVENT_LOG_MSG:
|
||||
severity_str = "msg";
|
||||
break;
|
||||
case _EVENT_LOG_WARN:
|
||||
severity_str = "warn";
|
||||
break;
|
||||
case _EVENT_LOG_ERR:
|
||||
severity_str = "err";
|
||||
break;
|
||||
default:
|
||||
severity_str = "???";
|
||||
break;
|
||||
}
|
||||
(void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
|
||||
}
|
||||
}
|
43
contrib/pf/libevent/log.h
Normal file
43
contrib/pf/libevent/log.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifndef _LOG_H_
|
||||
#define _LOG_H_
|
||||
|
||||
void event_err(int eval, const char *fmt, ...);
|
||||
void event_warn(const char *fmt, ...);
|
||||
void event_errx(int eval, const char *fmt, ...);
|
||||
void event_warnx(const char *fmt, ...);
|
||||
void event_msgx(const char *fmt, ...);
|
||||
void _event_debugx(const char *fmt, ...);
|
||||
|
||||
#ifdef USE_DEBUG
|
||||
#define event_debug(x) _event_debugx x
|
||||
#else
|
||||
#define event_debug(x) do {;} while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
388
contrib/pf/libevent/poll.c
Normal file
388
contrib/pf/libevent/poll.c
Normal file
@ -0,0 +1,388 @@
|
||||
/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <sys/tree.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#ifdef CHECK_INVARIANTS
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "event-internal.h"
|
||||
#include "evsignal.h"
|
||||
#include "log.h"
|
||||
|
||||
extern volatile sig_atomic_t evsignal_caught;
|
||||
|
||||
struct pollop {
|
||||
int event_count; /* Highest number alloc */
|
||||
int nfds; /* Size of event_* */
|
||||
int fd_count; /* Size of idxplus1_by_fd */
|
||||
struct pollfd *event_set;
|
||||
struct event **event_r_back;
|
||||
struct event **event_w_back;
|
||||
int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so
|
||||
* that 0 (which is easy to memset) can mean
|
||||
* "no entry." */
|
||||
};
|
||||
|
||||
void *poll_init (void);
|
||||
int poll_add (void *, struct event *);
|
||||
int poll_del (void *, struct event *);
|
||||
int poll_recalc (struct event_base *, void *, int);
|
||||
int poll_dispatch (struct event_base *, void *, struct timeval *);
|
||||
void poll_dealloc (void *);
|
||||
|
||||
const struct eventop pollops = {
|
||||
"poll",
|
||||
poll_init,
|
||||
poll_add,
|
||||
poll_del,
|
||||
poll_recalc,
|
||||
poll_dispatch,
|
||||
poll_dealloc
|
||||
};
|
||||
|
||||
void *
|
||||
poll_init(void)
|
||||
{
|
||||
struct pollop *pollop;
|
||||
|
||||
/* Disable poll when this environment variable is set */
|
||||
if (getenv("EVENT_NOPOLL"))
|
||||
return (NULL);
|
||||
|
||||
if (!(pollop = calloc(1, sizeof(struct pollop))))
|
||||
return (NULL);
|
||||
|
||||
evsignal_init();
|
||||
|
||||
return (pollop);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with the highest fd that we know about. If it is 0, completely
|
||||
* recalculate everything.
|
||||
*/
|
||||
|
||||
int
|
||||
poll_recalc(struct event_base *base, void *arg, int max)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef CHECK_INVARIANTS
|
||||
static void
|
||||
poll_check_ok(struct pollop *pop)
|
||||
{
|
||||
int i, idx;
|
||||
struct event *ev;
|
||||
|
||||
for (i = 0; i < pop->fd_count; ++i) {
|
||||
idx = pop->idxplus1_by_fd[i]-1;
|
||||
if (idx < 0)
|
||||
continue;
|
||||
assert(pop->event_set[idx].fd == i);
|
||||
if (pop->event_set[idx].events & POLLIN) {
|
||||
ev = pop->event_r_back[idx];
|
||||
assert(ev);
|
||||
assert(ev->ev_events & EV_READ);
|
||||
assert(ev->ev_fd == i);
|
||||
}
|
||||
if (pop->event_set[idx].events & POLLOUT) {
|
||||
ev = pop->event_w_back[idx];
|
||||
assert(ev);
|
||||
assert(ev->ev_events & EV_WRITE);
|
||||
assert(ev->ev_fd == i);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < pop->nfds; ++i) {
|
||||
struct pollfd *pfd = &pop->event_set[i];
|
||||
assert(pop->idxplus1_by_fd[pfd->fd] == i+1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define poll_check_ok(pop)
|
||||
#endif
|
||||
|
||||
int
|
||||
poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
|
||||
{
|
||||
int res, i, sec, nfds;
|
||||
struct pollop *pop = arg;
|
||||
|
||||
poll_check_ok(pop);
|
||||
sec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
|
||||
nfds = pop->nfds;
|
||||
res = poll(pop->event_set, nfds, sec);
|
||||
|
||||
if (res == -1) {
|
||||
if (errno != EINTR) {
|
||||
event_warn("poll");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
evsignal_process();
|
||||
return (0);
|
||||
} else if (evsignal_caught)
|
||||
evsignal_process();
|
||||
|
||||
event_debug(("%s: poll reports %d", __func__, res));
|
||||
|
||||
if (res == 0)
|
||||
return (0);
|
||||
|
||||
for (i = 0; i < nfds; i++) {
|
||||
int what = pop->event_set[i].revents;
|
||||
struct event *r_ev = NULL, *w_ev = NULL;
|
||||
if (!what)
|
||||
continue;
|
||||
|
||||
res = 0;
|
||||
|
||||
/* If the file gets closed notify */
|
||||
if (what & (POLLHUP|POLLERR))
|
||||
what |= POLLIN|POLLOUT;
|
||||
if (what & POLLIN) {
|
||||
res |= EV_READ;
|
||||
r_ev = pop->event_r_back[i];
|
||||
}
|
||||
if (what & POLLOUT) {
|
||||
res |= EV_WRITE;
|
||||
w_ev = pop->event_w_back[i];
|
||||
}
|
||||
if (res == 0)
|
||||
continue;
|
||||
|
||||
if (r_ev && (res & r_ev->ev_events)) {
|
||||
if (!(r_ev->ev_events & EV_PERSIST))
|
||||
event_del(r_ev);
|
||||
event_active(r_ev, res & r_ev->ev_events, 1);
|
||||
}
|
||||
if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
|
||||
if (!(w_ev->ev_events & EV_PERSIST))
|
||||
event_del(w_ev);
|
||||
event_active(w_ev, res & w_ev->ev_events, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
poll_add(void *arg, struct event *ev)
|
||||
{
|
||||
struct pollop *pop = arg;
|
||||
struct pollfd *pfd = NULL;
|
||||
int i;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_add(ev));
|
||||
if (!(ev->ev_events & (EV_READ|EV_WRITE)))
|
||||
return (0);
|
||||
|
||||
poll_check_ok(pop);
|
||||
if (pop->nfds + 1 >= pop->event_count) {
|
||||
struct pollfd *tmp_event_set;
|
||||
struct event **tmp_event_r_back;
|
||||
struct event **tmp_event_w_back;
|
||||
int tmp_event_count;
|
||||
|
||||
if (pop->event_count < 32)
|
||||
tmp_event_count = 32;
|
||||
else
|
||||
tmp_event_count = pop->event_count * 2;
|
||||
|
||||
/* We need more file descriptors */
|
||||
tmp_event_set = realloc(pop->event_set,
|
||||
tmp_event_count * sizeof(struct pollfd));
|
||||
if (tmp_event_set == NULL) {
|
||||
event_warn("realloc");
|
||||
return (-1);
|
||||
}
|
||||
pop->event_set = tmp_event_set;
|
||||
|
||||
tmp_event_r_back = realloc(pop->event_r_back,
|
||||
tmp_event_count * sizeof(struct event *));
|
||||
if (tmp_event_r_back == NULL) {
|
||||
/* event_set overallocated; that's okay. */
|
||||
event_warn("realloc");
|
||||
return (-1);
|
||||
}
|
||||
pop->event_r_back = tmp_event_r_back;
|
||||
|
||||
tmp_event_w_back = realloc(pop->event_w_back,
|
||||
tmp_event_count * sizeof(struct event *));
|
||||
if (tmp_event_w_back == NULL) {
|
||||
/* event_set and event_r_back overallocated; that's
|
||||
* okay. */
|
||||
event_warn("realloc");
|
||||
return (-1);
|
||||
}
|
||||
pop->event_w_back = tmp_event_w_back;
|
||||
|
||||
pop->event_count = tmp_event_count;
|
||||
}
|
||||
if (ev->ev_fd >= pop->fd_count) {
|
||||
int *tmp_idxplus1_by_fd;
|
||||
int new_count;
|
||||
if (pop->fd_count < 32)
|
||||
new_count = 32;
|
||||
else
|
||||
new_count = pop->fd_count * 2;
|
||||
while (new_count <= ev->ev_fd)
|
||||
new_count *= 2;
|
||||
tmp_idxplus1_by_fd =
|
||||
realloc(pop->idxplus1_by_fd, new_count * sizeof(int));
|
||||
if (tmp_idxplus1_by_fd == NULL) {
|
||||
event_warn("realloc");
|
||||
return (-1);
|
||||
}
|
||||
pop->idxplus1_by_fd = tmp_idxplus1_by_fd;
|
||||
memset(pop->idxplus1_by_fd + pop->fd_count,
|
||||
0, sizeof(int)*(new_count - pop->fd_count));
|
||||
pop->fd_count = new_count;
|
||||
}
|
||||
|
||||
i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
|
||||
if (i >= 0) {
|
||||
pfd = &pop->event_set[i];
|
||||
} else {
|
||||
i = pop->nfds++;
|
||||
pfd = &pop->event_set[i];
|
||||
pfd->events = 0;
|
||||
pfd->fd = ev->ev_fd;
|
||||
pop->event_w_back[i] = pop->event_r_back[i] = NULL;
|
||||
pop->idxplus1_by_fd[ev->ev_fd] = i + 1;
|
||||
}
|
||||
|
||||
pfd->revents = 0;
|
||||
if (ev->ev_events & EV_WRITE) {
|
||||
pfd->events |= POLLOUT;
|
||||
pop->event_w_back[i] = ev;
|
||||
}
|
||||
if (ev->ev_events & EV_READ) {
|
||||
pfd->events |= POLLIN;
|
||||
pop->event_r_back[i] = ev;
|
||||
}
|
||||
poll_check_ok(pop);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Nothing to be done here.
|
||||
*/
|
||||
|
||||
int
|
||||
poll_del(void *arg, struct event *ev)
|
||||
{
|
||||
struct pollop *pop = arg;
|
||||
struct pollfd *pfd = NULL;
|
||||
int i;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_del(ev));
|
||||
|
||||
if (!(ev->ev_events & (EV_READ|EV_WRITE)))
|
||||
return (0);
|
||||
|
||||
poll_check_ok(pop);
|
||||
i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
|
||||
if (i < 0)
|
||||
return (-1);
|
||||
|
||||
/* Do we still want to read or write? */
|
||||
pfd = &pop->event_set[i];
|
||||
if (ev->ev_events & EV_READ) {
|
||||
pfd->events &= ~POLLIN;
|
||||
pop->event_r_back[i] = NULL;
|
||||
}
|
||||
if (ev->ev_events & EV_WRITE) {
|
||||
pfd->events &= ~POLLOUT;
|
||||
pop->event_w_back[i] = NULL;
|
||||
}
|
||||
poll_check_ok(pop);
|
||||
if (pfd->events)
|
||||
/* Another event cares about that fd. */
|
||||
return (0);
|
||||
|
||||
/* Okay, so we aren't interested in that fd anymore. */
|
||||
pop->idxplus1_by_fd[ev->ev_fd] = 0;
|
||||
|
||||
--pop->nfds;
|
||||
if (i != pop->nfds) {
|
||||
/*
|
||||
* Shift the last pollfd down into the now-unoccupied
|
||||
* position.
|
||||
*/
|
||||
memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
|
||||
sizeof(struct pollfd));
|
||||
pop->event_r_back[i] = pop->event_r_back[pop->nfds];
|
||||
pop->event_w_back[i] = pop->event_w_back[pop->nfds];
|
||||
pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1;
|
||||
}
|
||||
|
||||
poll_check_ok(pop);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
poll_dealloc(void *arg)
|
||||
{
|
||||
struct pollop *pop = arg;
|
||||
|
||||
if (pop->event_set)
|
||||
free(pop->event_set);
|
||||
if (pop->event_r_back)
|
||||
free(pop->event_r_back);
|
||||
if (pop->event_w_back)
|
||||
free(pop->event_w_back);
|
||||
if (pop->idxplus1_by_fd)
|
||||
free(pop->idxplus1_by_fd);
|
||||
|
||||
memset(pop, 0, sizeof(struct pollop));
|
||||
free(pop);
|
||||
}
|
370
contrib/pf/libevent/select.c
Normal file
370
contrib/pf/libevent/select.c
Normal file
@ -0,0 +1,370 @@
|
||||
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <sys/tree.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#ifdef CHECK_INVARIANTS
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "event-internal.h"
|
||||
#include "evsignal.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifndef howmany
|
||||
#define howmany(x, y) (((x)+((y)-1))/(y))
|
||||
#endif
|
||||
|
||||
extern volatile sig_atomic_t evsignal_caught;
|
||||
|
||||
struct selectop {
|
||||
int event_fds; /* Highest fd in fd set */
|
||||
int event_fdsz;
|
||||
fd_set *event_readset_in;
|
||||
fd_set *event_writeset_in;
|
||||
fd_set *event_readset_out;
|
||||
fd_set *event_writeset_out;
|
||||
struct event **event_r_by_fd;
|
||||
struct event **event_w_by_fd;
|
||||
};
|
||||
|
||||
void *select_init (void);
|
||||
int select_add (void *, struct event *);
|
||||
int select_del (void *, struct event *);
|
||||
int select_recalc (struct event_base *, void *, int);
|
||||
int select_dispatch (struct event_base *, void *, struct timeval *);
|
||||
void select_dealloc (void *);
|
||||
|
||||
const struct eventop selectops = {
|
||||
"select",
|
||||
select_init,
|
||||
select_add,
|
||||
select_del,
|
||||
select_recalc,
|
||||
select_dispatch,
|
||||
select_dealloc
|
||||
};
|
||||
|
||||
static int select_resize(struct selectop *sop, int fdsz);
|
||||
|
||||
void *
|
||||
select_init(void)
|
||||
{
|
||||
struct selectop *sop;
|
||||
|
||||
/* Disable select when this environment variable is set */
|
||||
if (getenv("EVENT_NOSELECT"))
|
||||
return (NULL);
|
||||
|
||||
if (!(sop = calloc(1, sizeof(struct selectop))))
|
||||
return (NULL);
|
||||
|
||||
select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask));
|
||||
|
||||
evsignal_init();
|
||||
|
||||
return (sop);
|
||||
}
|
||||
|
||||
#ifdef CHECK_INVARIANTS
|
||||
static void
|
||||
check_selectop(struct selectop *sop)
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<=sop->event_fds;++i) {
|
||||
if (FD_ISSET(i, sop->event_readset_in)) {
|
||||
assert(sop->event_r_by_fd[i]);
|
||||
assert(sop->event_r_by_fd[i]->ev_events & EV_READ);
|
||||
assert(sop->event_r_by_fd[i]->ev_fd == i);
|
||||
} else {
|
||||
assert(! sop->event_r_by_fd[i]);
|
||||
}
|
||||
if (FD_ISSET(i, sop->event_writeset_in)) {
|
||||
assert(sop->event_w_by_fd[i]);
|
||||
assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE);
|
||||
assert(sop->event_w_by_fd[i]->ev_fd == i);
|
||||
} else {
|
||||
assert(! sop->event_w_by_fd[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
#define check_selectop(sop) do { (void) sop; } while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Called with the highest fd that we know about. If it is 0, completely
|
||||
* recalculate everything.
|
||||
*/
|
||||
|
||||
int
|
||||
select_recalc(struct event_base *base, void *arg, int max)
|
||||
{
|
||||
struct selectop *sop = arg;
|
||||
|
||||
check_selectop(sop);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
|
||||
{
|
||||
int res, i;
|
||||
struct selectop *sop = arg;
|
||||
|
||||
check_selectop(sop);
|
||||
|
||||
memcpy(sop->event_readset_out, sop->event_readset_in,
|
||||
sop->event_fdsz);
|
||||
memcpy(sop->event_writeset_out, sop->event_writeset_in,
|
||||
sop->event_fdsz);
|
||||
|
||||
res = select(sop->event_fds + 1, sop->event_readset_out,
|
||||
sop->event_writeset_out, NULL, tv);
|
||||
|
||||
check_selectop(sop);
|
||||
|
||||
if (res == -1) {
|
||||
if (errno != EINTR) {
|
||||
event_warn("select");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
evsignal_process();
|
||||
return (0);
|
||||
} else if (evsignal_caught)
|
||||
evsignal_process();
|
||||
|
||||
event_debug(("%s: select reports %d", __func__, res));
|
||||
|
||||
check_selectop(sop);
|
||||
for (i = 0; i <= sop->event_fds; ++i) {
|
||||
struct event *r_ev = NULL, *w_ev = NULL;
|
||||
res = 0;
|
||||
if (FD_ISSET(i, sop->event_readset_out)) {
|
||||
r_ev = sop->event_r_by_fd[i];
|
||||
res |= EV_READ;
|
||||
}
|
||||
if (FD_ISSET(i, sop->event_writeset_out)) {
|
||||
w_ev = sop->event_w_by_fd[i];
|
||||
res |= EV_WRITE;
|
||||
}
|
||||
if (r_ev && (res & r_ev->ev_events)) {
|
||||
if (!(r_ev->ev_events & EV_PERSIST))
|
||||
event_del(r_ev);
|
||||
event_active(r_ev, res & r_ev->ev_events, 1);
|
||||
}
|
||||
if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
|
||||
if (!(w_ev->ev_events & EV_PERSIST))
|
||||
event_del(w_ev);
|
||||
event_active(w_ev, res & w_ev->ev_events, 1);
|
||||
}
|
||||
}
|
||||
check_selectop(sop);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
select_resize(struct selectop *sop, int fdsz)
|
||||
{
|
||||
int n_events, n_events_old;
|
||||
|
||||
fd_set *readset_in = NULL;
|
||||
fd_set *writeset_in = NULL;
|
||||
fd_set *readset_out = NULL;
|
||||
fd_set *writeset_out = NULL;
|
||||
struct event **r_by_fd = NULL;
|
||||
struct event **w_by_fd = NULL;
|
||||
|
||||
n_events = (fdsz/sizeof(fd_mask)) * NFDBITS;
|
||||
n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS;
|
||||
|
||||
if (sop->event_readset_in)
|
||||
check_selectop(sop);
|
||||
|
||||
if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL)
|
||||
goto error;
|
||||
sop->event_readset_in = readset_in;
|
||||
if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL)
|
||||
goto error;
|
||||
sop->event_readset_out = readset_out;
|
||||
if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL)
|
||||
goto error;
|
||||
sop->event_writeset_in = writeset_in;
|
||||
if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL)
|
||||
goto error;
|
||||
sop->event_writeset_out = writeset_out;
|
||||
if ((r_by_fd = realloc(sop->event_r_by_fd,
|
||||
n_events*sizeof(struct event*))) == NULL)
|
||||
goto error;
|
||||
sop->event_r_by_fd = r_by_fd;
|
||||
if ((w_by_fd = realloc(sop->event_w_by_fd,
|
||||
n_events * sizeof(struct event*))) == NULL)
|
||||
goto error;
|
||||
sop->event_w_by_fd = w_by_fd;
|
||||
|
||||
memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
|
||||
fdsz - sop->event_fdsz);
|
||||
memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
|
||||
fdsz - sop->event_fdsz);
|
||||
memset(sop->event_r_by_fd + n_events_old, 0,
|
||||
(n_events-n_events_old) * sizeof(struct event*));
|
||||
memset(sop->event_w_by_fd + n_events_old, 0,
|
||||
(n_events-n_events_old) * sizeof(struct event*));
|
||||
|
||||
sop->event_fdsz = fdsz;
|
||||
check_selectop(sop);
|
||||
|
||||
return (0);
|
||||
|
||||
error:
|
||||
event_warn("malloc");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
select_add(void *arg, struct event *ev)
|
||||
{
|
||||
struct selectop *sop = arg;
|
||||
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_add(ev));
|
||||
|
||||
check_selectop(sop);
|
||||
/*
|
||||
* Keep track of the highest fd, so that we can calculate the size
|
||||
* of the fd_sets for select(2)
|
||||
*/
|
||||
if (sop->event_fds < ev->ev_fd) {
|
||||
int fdsz = sop->event_fdsz;
|
||||
|
||||
if (fdsz < sizeof(fd_mask))
|
||||
fdsz = sizeof(fd_mask);
|
||||
|
||||
while (fdsz <
|
||||
(howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask)))
|
||||
fdsz *= 2;
|
||||
|
||||
if (fdsz != sop->event_fdsz) {
|
||||
if (select_resize(sop, fdsz)) {
|
||||
check_selectop(sop);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
sop->event_fds = ev->ev_fd;
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_READ) {
|
||||
FD_SET(ev->ev_fd, sop->event_readset_in);
|
||||
sop->event_r_by_fd[ev->ev_fd] = ev;
|
||||
}
|
||||
if (ev->ev_events & EV_WRITE) {
|
||||
FD_SET(ev->ev_fd, sop->event_writeset_in);
|
||||
sop->event_w_by_fd[ev->ev_fd] = ev;
|
||||
}
|
||||
check_selectop(sop);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Nothing to be done here.
|
||||
*/
|
||||
|
||||
int
|
||||
select_del(void *arg, struct event *ev)
|
||||
{
|
||||
struct selectop *sop = arg;
|
||||
|
||||
check_selectop(sop);
|
||||
if (ev->ev_events & EV_SIGNAL)
|
||||
return (evsignal_del(ev));
|
||||
|
||||
if (sop->event_fds < ev->ev_fd) {
|
||||
check_selectop(sop);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_READ) {
|
||||
FD_CLR(ev->ev_fd, sop->event_readset_in);
|
||||
sop->event_r_by_fd[ev->ev_fd] = NULL;
|
||||
}
|
||||
|
||||
if (ev->ev_events & EV_WRITE) {
|
||||
FD_CLR(ev->ev_fd, sop->event_writeset_in);
|
||||
sop->event_w_by_fd[ev->ev_fd] = NULL;
|
||||
}
|
||||
|
||||
check_selectop(sop);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
select_dealloc(void *arg)
|
||||
{
|
||||
struct selectop *sop = arg;
|
||||
|
||||
if (sop->event_readset_in)
|
||||
free(sop->event_readset_in);
|
||||
if (sop->event_writeset_in)
|
||||
free(sop->event_writeset_in);
|
||||
if (sop->event_readset_out)
|
||||
free(sop->event_readset_out);
|
||||
if (sop->event_writeset_out)
|
||||
free(sop->event_writeset_out);
|
||||
if (sop->event_r_by_fd)
|
||||
free(sop->event_r_by_fd);
|
||||
if (sop->event_w_by_fd)
|
||||
free(sop->event_w_by_fd);
|
||||
|
||||
memset(sop, 0, sizeof(struct selectop));
|
||||
free(sop);
|
||||
}
|
180
contrib/pf/libevent/signal.c
Normal file
180
contrib/pf/libevent/signal.c
Normal file
@ -0,0 +1,180 @@
|
||||
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "event.h"
|
||||
#include "evsignal.h"
|
||||
#include "log.h"
|
||||
|
||||
extern struct event_list signalqueue;
|
||||
|
||||
static sig_atomic_t evsigcaught[NSIG];
|
||||
volatile sig_atomic_t evsignal_caught = 0;
|
||||
|
||||
static struct event ev_signal;
|
||||
static int ev_signal_pair[2];
|
||||
static int ev_signal_added;
|
||||
|
||||
static void evsignal_handler(int sig);
|
||||
|
||||
/* Callback for when the signal handler write a byte to our signaling socket */
|
||||
static void
|
||||
evsignal_cb(int fd, short what, void *arg)
|
||||
{
|
||||
static char signals[100];
|
||||
struct event *ev = arg;
|
||||
ssize_t n;
|
||||
|
||||
n = read(fd, signals, sizeof(signals));
|
||||
if (n == -1)
|
||||
event_err(1, "%s: read", __func__);
|
||||
event_add(ev, NULL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETFD
|
||||
#define FD_CLOSEONEXEC(x) do { \
|
||||
if (fcntl(x, F_SETFD, 1) == -1) \
|
||||
event_warn("fcntl(%d, F_SETFD)", x); \
|
||||
} while (0)
|
||||
#else
|
||||
#define FD_CLOSEONEXEC(x)
|
||||
#endif
|
||||
|
||||
void
|
||||
evsignal_init(void)
|
||||
{
|
||||
/*
|
||||
* Our signal handler is going to write to one end of the socket
|
||||
* pair to wake up our event loop. The event loop then scans for
|
||||
* signals that got delivered.
|
||||
*/
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ev_signal_pair) == -1)
|
||||
event_err(1, "%s: socketpair", __func__);
|
||||
|
||||
FD_CLOSEONEXEC(ev_signal_pair[0]);
|
||||
FD_CLOSEONEXEC(ev_signal_pair[1]);
|
||||
|
||||
fcntl(ev_signal_pair[0], F_SETFL, O_NONBLOCK);
|
||||
|
||||
event_set(&ev_signal, ev_signal_pair[1], EV_READ,
|
||||
evsignal_cb, &ev_signal);
|
||||
ev_signal.ev_flags |= EVLIST_INTERNAL;
|
||||
}
|
||||
|
||||
int
|
||||
evsignal_add(struct event *ev)
|
||||
{
|
||||
int evsignal;
|
||||
struct sigaction sa;
|
||||
|
||||
if (ev->ev_events & (EV_READ|EV_WRITE))
|
||||
event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
|
||||
evsignal = EVENT_SIGNAL(ev);
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = evsignal_handler;
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_flags |= SA_RESTART;
|
||||
|
||||
if (sigaction(evsignal, &sa, NULL) == -1)
|
||||
return (-1);
|
||||
|
||||
if (!ev_signal_added) {
|
||||
ev_signal_added = 1;
|
||||
event_add(&ev_signal, NULL);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Nothing to be done here.
|
||||
*/
|
||||
|
||||
int
|
||||
evsignal_del(struct event *ev)
|
||||
{
|
||||
int evsignal;
|
||||
|
||||
evsignal = EVENT_SIGNAL(ev);
|
||||
|
||||
return (sigaction(EVENT_SIGNAL(ev),(struct sigaction *)SIG_DFL, NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
evsignal_handler(int sig)
|
||||
{
|
||||
int save_errno = errno;
|
||||
|
||||
evsigcaught[sig]++;
|
||||
evsignal_caught = 1;
|
||||
|
||||
/* Wake up our notification mechanism */
|
||||
write(ev_signal_pair[0], "a", 1);
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
void
|
||||
evsignal_process(void)
|
||||
{
|
||||
struct event *ev;
|
||||
sig_atomic_t ncalls;
|
||||
|
||||
evsignal_caught = 0;
|
||||
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
|
||||
ncalls = evsigcaught[EVENT_SIGNAL(ev)];
|
||||
if (ncalls) {
|
||||
if (!(ev->ev_events & EV_PERSIST))
|
||||
event_del(ev);
|
||||
event_active(ev, EV_SIGNAL, ncalls);
|
||||
evsigcaught[EVENT_SIGNAL(ev)] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: pfctl_osfp.c,v 1.12 2005/02/17 13:18:00 aaron Exp $ */
|
||||
/* $OpenBSD: pfctl_osfp.c,v 1.15 2006/12/13 05:10:15 itojun Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.org>
|
||||
@ -23,6 +23,10 @@
|
||||
#include <net/if.h>
|
||||
#include <net/pfvar.h>
|
||||
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
@ -240,6 +244,10 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
|
||||
sizeof(fp.fp_os.fp_subtype_nm));
|
||||
|
||||
add_fingerprint(dev, opts, &fp);
|
||||
|
||||
fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6);
|
||||
fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip);
|
||||
add_fingerprint(dev, opts, &fp);
|
||||
}
|
||||
|
||||
if (class)
|
||||
@ -250,6 +258,8 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
|
||||
free(subtype);
|
||||
if (desc)
|
||||
free(desc);
|
||||
if (tcpopts)
|
||||
free(tcpopts);
|
||||
|
||||
fclose(in);
|
||||
|
||||
@ -762,7 +772,6 @@ sort_name_list(int opts, struct name_list *nml)
|
||||
LIST_INSERT_AFTER(nmlast, nm, nm_entry);
|
||||
nmlast = nm;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse the next integer in a formatted config file line */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: pfctl_radix.c,v 1.26 2004/06/14 20:44:22 cedric Exp $ */
|
||||
/* $OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Cedric Berger
|
||||
@ -421,7 +421,7 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
|
||||
/* interface management code */
|
||||
|
||||
int
|
||||
pfi_get_ifaces(const char *filter, struct pfi_if *buf, int *size, int flags)
|
||||
pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
|
||||
{
|
||||
struct pfioc_iface io;
|
||||
|
||||
@ -430,7 +430,6 @@ pfi_get_ifaces(const char *filter, struct pfi_if *buf, int *size, int flags)
|
||||
return (-1);
|
||||
}
|
||||
bzero(&io, sizeof io);
|
||||
io.pfiio_flags = flags;
|
||||
if (filter != NULL)
|
||||
if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
|
||||
sizeof(io.pfiio_name)) {
|
||||
@ -451,7 +450,7 @@ pfi_get_ifaces(const char *filter, struct pfi_if *buf, int *size, int flags)
|
||||
size_t buf_esize[PFRB_MAX] = { 0,
|
||||
sizeof(struct pfr_table), sizeof(struct pfr_tstats),
|
||||
sizeof(struct pfr_addr), sizeof(struct pfr_astats),
|
||||
sizeof(struct pfi_if), sizeof(struct pfioc_trans_e)
|
||||
sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: pflogd.h,v 1.2 2004/01/15 20:15:14 canacar Exp $ */
|
||||
/* $OpenBSD: pflogd.h,v 1.3 2006/01/15 16:38:04 canacar Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003 Can Erkin Acar
|
||||
@ -37,6 +37,7 @@ void logmsg(int priority, const char *message, ...);
|
||||
int priv_init(void);
|
||||
int priv_set_snaplen(int snaplen);
|
||||
int priv_open_log(void);
|
||||
int priv_move_log(void);
|
||||
pcap_t *pcap_open_live_fd(int fd, int snaplen, char *ebuf);
|
||||
|
||||
void set_pcap_filter(void);
|
||||
|
397
contrib/pf/tftp-proxy/filter.c
Normal file
397
contrib/pf/tftp-proxy/filter.c
Normal file
@ -0,0 +1,397 @@
|
||||
/* $OpenBSD: filter.c,v 1.1 2005/12/28 19:07:07 jcs Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/pfvar.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "filter.h"
|
||||
|
||||
/* From netinet/in.h, but only _KERNEL_ gets them. */
|
||||
#define satosin(sa) ((struct sockaddr_in *)(sa))
|
||||
#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
|
||||
|
||||
enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
|
||||
|
||||
int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
|
||||
u_int16_t, u_int8_t);
|
||||
int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
|
||||
struct sockaddr_in *, u_int8_t);
|
||||
int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
|
||||
struct sockaddr_in6 *, u_int8_t);
|
||||
|
||||
static struct pfioc_pooladdr pfp;
|
||||
static struct pfioc_rule pfr;
|
||||
static struct pfioc_trans pft;
|
||||
static struct pfioc_trans_e pfte[TRANS_SIZE];
|
||||
static int dev, rule_log;
|
||||
static char *qname;
|
||||
|
||||
int
|
||||
add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
|
||||
struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
|
||||
{
|
||||
if (!src || !dst || !d_port || !proto) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port, proto) == -1)
|
||||
return (-1);
|
||||
|
||||
pfr.rule.direction = dir;
|
||||
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
|
||||
u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
|
||||
u_int16_t nat_range_high, u_int8_t proto)
|
||||
{
|
||||
if (!src || !dst || !d_port || !nat || !nat_range_low || !proto ||
|
||||
(src->sa_family != nat->sa_family)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port, proto) == -1)
|
||||
return (-1);
|
||||
|
||||
if (nat->sa_family == AF_INET) {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v4,
|
||||
&satosin(nat)->sin_addr.s_addr, 4);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
|
||||
} else {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v6,
|
||||
&satosin6(nat)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
|
||||
}
|
||||
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
|
||||
return (-1);
|
||||
|
||||
pfr.rule.rpool.proxy_port[0] = nat_range_low;
|
||||
pfr.rule.rpool.proxy_port[1] = nat_range_high;
|
||||
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
|
||||
u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
|
||||
{
|
||||
if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
|
||||
(src->sa_family != rdr->sa_family)) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port, proto) == -1)
|
||||
return (-1);
|
||||
|
||||
if (rdr->sa_family == AF_INET) {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v4,
|
||||
&satosin(rdr)->sin_addr.s_addr, 4);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
|
||||
} else {
|
||||
memcpy(&pfp.addr.addr.v.a.addr.v6,
|
||||
&satosin6(rdr)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
|
||||
}
|
||||
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
|
||||
return (-1);
|
||||
|
||||
pfr.rule.rpool.proxy_port[0] = rdr_port;
|
||||
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_commit(void)
|
||||
{
|
||||
if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_rollback(void)
|
||||
{
|
||||
if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
init_filter(char *opt_qname, int opt_verbose)
|
||||
{
|
||||
struct pf_status status;
|
||||
|
||||
qname = opt_qname;
|
||||
|
||||
if (opt_verbose == 1)
|
||||
rule_log = PF_LOG;
|
||||
else if (opt_verbose == 2)
|
||||
rule_log = PF_LOG_ALL;
|
||||
|
||||
dev = open("/dev/pf", O_RDWR);
|
||||
if (dev == -1) {
|
||||
syslog(LOG_ERR, "can't open /dev/pf");
|
||||
exit(1);
|
||||
}
|
||||
if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
|
||||
syslog(LOG_ERR, "DIOCGETSTATUS");
|
||||
exit(1);
|
||||
}
|
||||
if (!status.running) {
|
||||
syslog(LOG_ERR, "pf is disabled");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
prepare_commit(u_int32_t id)
|
||||
{
|
||||
char an[PF_ANCHOR_NAME_SIZE];
|
||||
int i;
|
||||
|
||||
memset(&pft, 0, sizeof pft);
|
||||
pft.size = TRANS_SIZE;
|
||||
pft.esize = sizeof pfte[0];
|
||||
pft.array = pfte;
|
||||
|
||||
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
|
||||
getpid(), id);
|
||||
for (i = 0; i < TRANS_SIZE; i++) {
|
||||
memset(&pfte[i], 0, sizeof pfte[0]);
|
||||
strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
|
||||
switch (i) {
|
||||
case TRANS_FILTER:
|
||||
pfte[i].rs_num = PF_RULESET_FILTER;
|
||||
break;
|
||||
case TRANS_NAT:
|
||||
pfte[i].rs_num = PF_RULESET_NAT;
|
||||
break;
|
||||
case TRANS_RDR:
|
||||
pfte[i].rs_num = PF_RULESET_RDR;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
|
||||
struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
|
||||
{
|
||||
char an[PF_ANCHOR_NAME_SIZE];
|
||||
|
||||
if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
|
||||
(src->sa_family != dst->sa_family)) {
|
||||
errno = EPROTONOSUPPORT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memset(&pfp, 0, sizeof pfp);
|
||||
memset(&pfr, 0, sizeof pfr);
|
||||
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
|
||||
getpid(), id);
|
||||
strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
|
||||
strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
|
||||
|
||||
switch (rs_num) {
|
||||
case PF_RULESET_FILTER:
|
||||
pfr.ticket = pfte[TRANS_FILTER].ticket;
|
||||
break;
|
||||
case PF_RULESET_NAT:
|
||||
pfr.ticket = pfte[TRANS_NAT].ticket;
|
||||
break;
|
||||
case PF_RULESET_RDR:
|
||||
pfr.ticket = pfte[TRANS_RDR].ticket;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
|
||||
return (-1);
|
||||
pfr.pool_ticket = pfp.ticket;
|
||||
|
||||
/* Generic for all rule types. */
|
||||
pfr.rule.af = src->sa_family;
|
||||
pfr.rule.proto = proto;
|
||||
pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
|
||||
pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
|
||||
if (src->sa_family == AF_INET) {
|
||||
memcpy(&pfr.rule.src.addr.v.a.addr.v4,
|
||||
&satosin(src)->sin_addr.s_addr, 4);
|
||||
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
|
||||
memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
|
||||
&satosin(dst)->sin_addr.s_addr, 4);
|
||||
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
|
||||
} else {
|
||||
memcpy(&pfr.rule.src.addr.v.a.addr.v6,
|
||||
&satosin6(src)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
|
||||
memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
|
||||
&satosin6(dst)->sin6_addr.s6_addr, 16);
|
||||
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
|
||||
}
|
||||
pfr.rule.dst.port_op = PF_OP_EQ;
|
||||
pfr.rule.dst.port[0] = htons(d_port);
|
||||
|
||||
switch (rs_num) {
|
||||
case PF_RULESET_FILTER:
|
||||
/*
|
||||
* pass quick [log] inet[6] proto tcp \
|
||||
* from $src to $dst port = $d_port flags S/SAFR keep state
|
||||
* (max 1) [queue qname]
|
||||
*/
|
||||
pfr.rule.action = PF_PASS;
|
||||
pfr.rule.quick = 1;
|
||||
pfr.rule.log = rule_log;
|
||||
pfr.rule.keep_state = 1;
|
||||
pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : NULL);
|
||||
pfr.rule.flagset = (proto == IPPROTO_TCP ?
|
||||
(TH_SYN|TH_ACK|TH_FIN|TH_RST) : NULL);
|
||||
pfr.rule.max_states = 1;
|
||||
if (qname != NULL)
|
||||
strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
|
||||
break;
|
||||
case PF_RULESET_NAT:
|
||||
/*
|
||||
* nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
|
||||
*/
|
||||
pfr.rule.action = PF_NAT;
|
||||
break;
|
||||
case PF_RULESET_RDR:
|
||||
/*
|
||||
* rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
|
||||
*/
|
||||
pfr.rule.action = PF_RDR;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
server_lookup(struct sockaddr *client, struct sockaddr *proxy,
|
||||
struct sockaddr *server, u_int8_t proto)
|
||||
{
|
||||
if (client->sa_family == AF_INET)
|
||||
return (server_lookup4(satosin(client), satosin(proxy),
|
||||
satosin(server), proto));
|
||||
|
||||
if (client->sa_family == AF_INET6)
|
||||
return (server_lookup6(satosin6(client), satosin6(proxy),
|
||||
satosin6(server), proto));
|
||||
|
||||
errno = EPROTONOSUPPORT;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
|
||||
struct sockaddr_in *server, u_int8_t proto)
|
||||
{
|
||||
struct pfioc_natlook pnl;
|
||||
|
||||
memset(&pnl, 0, sizeof pnl);
|
||||
pnl.direction = PF_OUT;
|
||||
pnl.af = AF_INET;
|
||||
pnl.proto = proto;
|
||||
memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
|
||||
memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
|
||||
pnl.sport = client->sin_port;
|
||||
pnl.dport = proxy->sin_port;
|
||||
|
||||
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
|
||||
return (-1);
|
||||
|
||||
memset(server, 0, sizeof(struct sockaddr_in));
|
||||
server->sin_len = sizeof(struct sockaddr_in);
|
||||
server->sin_family = AF_INET;
|
||||
memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
|
||||
sizeof server->sin_addr.s_addr);
|
||||
server->sin_port = pnl.rdport;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
|
||||
struct sockaddr_in6 *server, u_int8_t proto)
|
||||
{
|
||||
struct pfioc_natlook pnl;
|
||||
|
||||
memset(&pnl, 0, sizeof pnl);
|
||||
pnl.direction = PF_OUT;
|
||||
pnl.af = AF_INET6;
|
||||
pnl.proto = proto;
|
||||
memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
|
||||
memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
|
||||
pnl.sport = client->sin6_port;
|
||||
pnl.dport = proxy->sin6_port;
|
||||
|
||||
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
|
||||
return (-1);
|
||||
|
||||
memset(server, 0, sizeof(struct sockaddr_in6));
|
||||
server->sin6_len = sizeof(struct sockaddr_in6);
|
||||
server->sin6_family = AF_INET6;
|
||||
memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
|
||||
sizeof server->sin6_addr);
|
||||
server->sin6_port = pnl.rdport;
|
||||
|
||||
return (0);
|
||||
}
|
32
contrib/pf/tftp-proxy/filter.h
Normal file
32
contrib/pf/tftp-proxy/filter.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* $OpenBSD: filter.h,v 1.1 2005/12/28 19:07:07 jcs Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define FTP_PROXY_ANCHOR "tftp-proxy"
|
||||
|
||||
int add_filter(u_int32_t, u_int8_t, struct sockaddr *, struct sockaddr *,
|
||||
u_int16_t, u_int8_t);
|
||||
int add_nat(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
|
||||
struct sockaddr *, u_int16_t, u_int16_t, u_int8_t);
|
||||
int add_rdr(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
|
||||
struct sockaddr *, u_int16_t, u_int8_t);
|
||||
int do_commit(void);
|
||||
int do_rollback(void);
|
||||
void init_filter(char *, int);
|
||||
int prepare_commit(u_int32_t);
|
||||
int server_lookup(struct sockaddr *, struct sockaddr *, struct sockaddr *,
|
||||
u_int8_t);
|
140
contrib/pf/tftp-proxy/tftp-proxy.8
Normal file
140
contrib/pf/tftp-proxy/tftp-proxy.8
Normal file
@ -0,0 +1,140 @@
|
||||
.\" $OpenBSD: tftp-proxy.8,v 1.1 2005/12/28 19:07:07 jcs Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2005 joshua stein <jcs@openbsd.org>
|
||||
.\"
|
||||
.\" 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.
|
||||
.\" 3. The name of the author may not be used to endorse or promote products
|
||||
.\" derived from this software without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
.\"
|
||||
.Dd November 28, 2005
|
||||
.Dt TFTP-PROXY 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm tftp-proxy
|
||||
.Nd Internet Trivial File Transfer Protocol proxy
|
||||
.Sh SYNOPSIS
|
||||
.Nm tftp-proxy
|
||||
.Op Fl v
|
||||
.Op Fl w Ar transwait
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a proxy for the Internet Trivial File Transfer Protocol invoked by
|
||||
the
|
||||
.Xr inetd 8
|
||||
internet server.
|
||||
TFTP connections should be redirected to the proxy using the
|
||||
.Xr pf 4
|
||||
.Ar rdr
|
||||
command, after which the proxy connects to the server on behalf of
|
||||
the client.
|
||||
.Pp
|
||||
The proxy establishes a
|
||||
.Xr pf 4
|
||||
.Ar rdr
|
||||
rule using the
|
||||
.Ar anchor
|
||||
facility to rewrite packets between the client and the server.
|
||||
Once the rule is established,
|
||||
.Nm
|
||||
forwards the initial request from the client to the server to begin the
|
||||
transfer.
|
||||
After
|
||||
.Ar transwait
|
||||
seconds, the
|
||||
.Xr pf 4
|
||||
NAT state is assumed to have been established and the
|
||||
.Ar rdr
|
||||
rule is deleted and the program exits.
|
||||
Once the transfer between the client and the server is completed, the
|
||||
NAT state will naturally expire.
|
||||
.Pp
|
||||
Assuming the TFTP command request is from $client to $server, the
|
||||
proxy connected to the server using the $proxy source address, and
|
||||
$port is negotiated,
|
||||
.Nm
|
||||
adds the following rule to the anchor:
|
||||
.Bd -literal -offset indent
|
||||
rdr proto udp from $server to $proxy port $port -\*(Gt $client
|
||||
.Ed
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl v
|
||||
Log the connection and request information to
|
||||
.Xr syslogd 8 .
|
||||
.It Fl w Ar transwait
|
||||
Number of seconds to wait for the data transmission to begin before
|
||||
removing the
|
||||
.Xr pf 4
|
||||
.Ar rdr
|
||||
rule.
|
||||
The default is 2 seconds.
|
||||
.El
|
||||
.Sh CONFIGURATION
|
||||
To make use of the proxy,
|
||||
.Xr pf.conf 5
|
||||
needs the following rules.
|
||||
The anchors are mandatory.
|
||||
Adjust the rules as needed for your configuration.
|
||||
.Pp
|
||||
In the NAT section:
|
||||
.Bd -literal -offset indent
|
||||
nat on $ext_if from $int_if -\*(Gt ($ext_if:0)
|
||||
|
||||
no nat on $ext_if to port tftp
|
||||
|
||||
rdr-anchor "tftp-proxy/*"
|
||||
rdr on $int_if proto udp from $lan to any port tftp -\*(Gt \e
|
||||
127.0.0.1 port 6969
|
||||
.Ed
|
||||
.Pp
|
||||
In the filter section, an anchor must be added to hold the pass rules:
|
||||
.Bd -literal -offset indent
|
||||
anchor "tftp-proxy/*"
|
||||
.Ed
|
||||
.Pp
|
||||
.Xr inetd 8
|
||||
must be configured to spawn the proxy on the port that packets are
|
||||
being forwarded to by
|
||||
.Xr pf 4 .
|
||||
An example
|
||||
.Xr inetd.conf 5
|
||||
entry follows:
|
||||
.Bd -literal -offset indent
|
||||
127.0.0.1:6969 dgram udp wait root \e
|
||||
/usr/libexec/tftp-proxy tftp-proxy
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr tftp 1 ,
|
||||
.Xr pf 4 ,
|
||||
.Xr pf.conf 5 ,
|
||||
.Xr ftp-proxy 8 ,
|
||||
.Xr inetd 8 ,
|
||||
.Xr syslogd 8 ,
|
||||
.Xr tftpd 8
|
||||
.Sh CAVEATS
|
||||
.Nm
|
||||
chroots to
|
||||
.Pa /var/empty
|
||||
and changes to user
|
||||
.Dq proxy
|
||||
to drop privileges.
|
393
contrib/pf/tftp-proxy/tftp-proxy.c
Normal file
393
contrib/pf/tftp-proxy/tftp-proxy.c
Normal file
@ -0,0 +1,393 @@
|
||||
/* $OpenBSD: tftp-proxy.c,v 1.2 2006/12/20 03:33:38 joel Exp $
|
||||
*
|
||||
* Copyright (c) 2005 DLS Internet Services
|
||||
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
|
||||
*
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/tftp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <net/pfvar.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "filter.h"
|
||||
|
||||
#define CHROOT_DIR "/var/empty"
|
||||
#define NOPRIV_USER "proxy"
|
||||
|
||||
#define PF_NAT_PROXY_PORT_LOW 50001
|
||||
#define PF_NAT_PROXY_PORT_HIGH 65535
|
||||
|
||||
#define DEFTRANSWAIT 2
|
||||
#define NTOP_BUFS 4
|
||||
#define PKTSIZE SEGSIZE+4
|
||||
|
||||
const char *opcode(int);
|
||||
const char *sock_ntop(struct sockaddr *);
|
||||
u_int16_t pick_proxy_port(void);
|
||||
static void usage(void);
|
||||
|
||||
extern char *__progname;
|
||||
char ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN];
|
||||
int verbose = 0;
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int c, fd = 0, on = 1, out_fd = 0, peer, reqsize = 0;
|
||||
int transwait = DEFTRANSWAIT;
|
||||
char *p;
|
||||
struct tftphdr *tp;
|
||||
struct passwd *pw;
|
||||
|
||||
char cbuf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
|
||||
char req[PKTSIZE];
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
|
||||
struct sockaddr_storage from, proxy, server, proxy_to_server, s_in;
|
||||
struct sockaddr_in sock_out;
|
||||
socklen_t j;
|
||||
in_port_t bindport;
|
||||
|
||||
openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
||||
|
||||
while ((c = getopt(argc, argv, "vw:")) != -1)
|
||||
switch (c) {
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'w':
|
||||
transwait = strtoll(optarg, &p, 10);
|
||||
if (transwait < 1) {
|
||||
syslog(LOG_ERR, "invalid -w value");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
|
||||
/* open /dev/pf */
|
||||
init_filter(NULL, verbose);
|
||||
|
||||
tzset();
|
||||
|
||||
pw = getpwnam(NOPRIV_USER);
|
||||
if (!pw) {
|
||||
syslog(LOG_ERR, "no such user %s: %m", NOPRIV_USER);
|
||||
exit(1);
|
||||
}
|
||||
if (chroot(CHROOT_DIR) || chdir("/")) {
|
||||
syslog(LOG_ERR, "chroot %s: %m", CHROOT_DIR);
|
||||
exit(1);
|
||||
}
|
||||
if (setgroups(1, &pw->pw_gid) ||
|
||||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
|
||||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
|
||||
syslog(LOG_ERR, "can't revoke privs: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* non-blocking io */
|
||||
if (ioctl(fd, FIONBIO, &on) < 0) {
|
||||
syslog(LOG_ERR, "ioctl(FIONBIO): %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) == -1) {
|
||||
syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
j = sizeof(s_in);
|
||||
if (getsockname(fd, (struct sockaddr *)&s_in, &j) == -1) {
|
||||
syslog(LOG_ERR, "getsockname: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bindport = ((struct sockaddr_in *)&s_in)->sin_port;
|
||||
|
||||
/* req will be pushed back out at the end, unchanged */
|
||||
j = sizeof(from);
|
||||
if ((reqsize = recvfrom(fd, req, sizeof(req), MSG_PEEK,
|
||||
(struct sockaddr *)&from, &j)) < 0) {
|
||||
syslog(LOG_ERR, "recvfrom: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bzero(&msg, sizeof(msg));
|
||||
iov.iov_base = req;
|
||||
iov.iov_len = sizeof(req);
|
||||
msg.msg_name = &from;
|
||||
msg.msg_namelen = sizeof(from);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cbuf;
|
||||
msg.msg_controllen = CMSG_LEN(sizeof(struct sockaddr_storage));
|
||||
|
||||
if (recvmsg(fd, &msg, 0) < 0) {
|
||||
syslog(LOG_ERR, "recvmsg: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
close(1);
|
||||
|
||||
peer = socket(from.ss_family, SOCK_DGRAM, 0);
|
||||
if (peer < 0) {
|
||||
syslog(LOG_ERR, "socket: %m");
|
||||
exit(1);
|
||||
}
|
||||
memset(&s_in, 0, sizeof(s_in));
|
||||
s_in.ss_family = from.ss_family;
|
||||
s_in.ss_len = from.ss_len;
|
||||
|
||||
/* get local address if possible */
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == IPPROTO_IP &&
|
||||
cmsg->cmsg_type == IP_RECVDSTADDR) {
|
||||
memcpy(&((struct sockaddr_in *)&s_in)->sin_addr,
|
||||
CMSG_DATA(cmsg), sizeof(struct in_addr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(peer, (struct sockaddr *)&s_in, s_in.ss_len) < 0) {
|
||||
syslog(LOG_ERR, "bind: %m");
|
||||
exit(1);
|
||||
}
|
||||
if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
|
||||
syslog(LOG_ERR, "connect: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tp = (struct tftphdr *)req;
|
||||
if (!(ntohs(tp->th_opcode) == RRQ || ntohs(tp->th_opcode) == WRQ)) {
|
||||
/* not a tftp request, bail */
|
||||
if (verbose) {
|
||||
syslog(LOG_WARNING, "not a valid tftp request");
|
||||
exit(1);
|
||||
} else
|
||||
/* exit 0 so inetd doesn't log anything */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
j = sizeof(struct sockaddr_storage);
|
||||
if (getsockname(fd, (struct sockaddr *)&proxy, &j) == -1) {
|
||||
syslog(LOG_ERR, "getsockname: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
((struct sockaddr_in *)&proxy)->sin_port = bindport;
|
||||
|
||||
/* find the un-rdr'd server and port the client wanted */
|
||||
if (server_lookup((struct sockaddr *)&from,
|
||||
(struct sockaddr *)&proxy, (struct sockaddr *)&server,
|
||||
IPPROTO_UDP) != 0) {
|
||||
syslog(LOG_ERR, "pf connection lookup failed (no rdr?)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* establish a new outbound connection to the remote server */
|
||||
if ((out_fd = socket(((struct sockaddr *)&from)->sa_family,
|
||||
SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
||||
syslog(LOG_ERR, "couldn't create new socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bzero((char *)&sock_out, sizeof(sock_out));
|
||||
sock_out.sin_family = from.ss_family;
|
||||
sock_out.sin_port = htons(pick_proxy_port());
|
||||
if (bind(out_fd, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
|
||||
syslog(LOG_ERR, "couldn't bind to new socket: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (connect(out_fd, (struct sockaddr *)&server,
|
||||
((struct sockaddr *)&server)->sa_len) < 0 && errno != EINPROGRESS) {
|
||||
syslog(LOG_ERR, "couldn't connect to remote server: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
j = sizeof(struct sockaddr_storage);
|
||||
if ((getsockname(out_fd, (struct sockaddr *)&proxy_to_server,
|
||||
&j)) < 0) {
|
||||
syslog(LOG_ERR, "getsockname: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
syslog(LOG_INFO, "%s:%d -> %s:%d/%s:%d -> %s:%d \"%s %s\"",
|
||||
sock_ntop((struct sockaddr *)&from),
|
||||
ntohs(((struct sockaddr_in *)&from)->sin_port),
|
||||
sock_ntop((struct sockaddr *)&proxy),
|
||||
ntohs(((struct sockaddr_in *)&proxy)->sin_port),
|
||||
sock_ntop((struct sockaddr *)&proxy_to_server),
|
||||
ntohs(((struct sockaddr_in *)&proxy_to_server)->sin_port),
|
||||
sock_ntop((struct sockaddr *)&server),
|
||||
ntohs(((struct sockaddr_in *)&server)->sin_port),
|
||||
opcode(ntohs(tp->th_opcode)),
|
||||
tp->th_stuff);
|
||||
|
||||
/* get ready to add rdr and pass rules */
|
||||
if (prepare_commit(1) == -1) {
|
||||
syslog(LOG_ERR, "couldn't prepare pf commit");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* rdr from server to us on our random port -> client on its port */
|
||||
if (add_rdr(1, (struct sockaddr *)&server,
|
||||
(struct sockaddr *)&proxy_to_server, ntohs(sock_out.sin_port),
|
||||
(struct sockaddr *)&from,
|
||||
ntohs(((struct sockaddr_in *)&from)->sin_port),
|
||||
IPPROTO_UDP) == -1) {
|
||||
syslog(LOG_ERR, "couldn't add rdr");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* explicitly allow the packets to return back to the client (which pf
|
||||
* will see post-rdr) */
|
||||
if (add_filter(1, PF_IN, (struct sockaddr *)&server,
|
||||
(struct sockaddr *)&from,
|
||||
ntohs(((struct sockaddr_in *)&from)->sin_port),
|
||||
IPPROTO_UDP) == -1) {
|
||||
syslog(LOG_ERR, "couldn't add pass in");
|
||||
exit(1);
|
||||
}
|
||||
if (add_filter(1, PF_OUT, (struct sockaddr *)&server,
|
||||
(struct sockaddr *)&from,
|
||||
ntohs(((struct sockaddr_in *)&from)->sin_port),
|
||||
IPPROTO_UDP) == -1) {
|
||||
syslog(LOG_ERR, "couldn't add pass out");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* and just in case, to pass out from us to the server */
|
||||
if (add_filter(1, PF_OUT, (struct sockaddr *)&proxy_to_server,
|
||||
(struct sockaddr *)&server,
|
||||
ntohs(((struct sockaddr_in *)&server)->sin_port),
|
||||
IPPROTO_UDP) == -1) {
|
||||
syslog(LOG_ERR, "couldn't add pass out");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (do_commit() == -1) {
|
||||
syslog(LOG_ERR, "couldn't commit pf rules");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* forward the initial tftp request and start the insanity */
|
||||
if (send(out_fd, tp, reqsize, 0) < 0) {
|
||||
syslog(LOG_ERR, "couldn't forward tftp packet: %m");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* allow the transfer to start to establish a state */
|
||||
sleep(transwait);
|
||||
|
||||
/* delete our rdr rule and clean up */
|
||||
prepare_commit(1);
|
||||
do_commit();
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
const char *
|
||||
opcode(int code)
|
||||
{
|
||||
static char str[6];
|
||||
|
||||
switch (code) {
|
||||
case 1:
|
||||
(void)snprintf(str, sizeof(str), "RRQ");
|
||||
break;
|
||||
case 2:
|
||||
(void)snprintf(str, sizeof(str), "WRQ");
|
||||
break;
|
||||
default:
|
||||
(void)snprintf(str, sizeof(str), "(%d)", code);
|
||||
break;
|
||||
}
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
const char *
|
||||
sock_ntop(struct sockaddr *sa)
|
||||
{
|
||||
static int n = 0;
|
||||
|
||||
/* Cycle to next buffer. */
|
||||
n = (n + 1) % NTOP_BUFS;
|
||||
ntop_buf[n][0] = '\0';
|
||||
|
||||
if (sa->sa_family == AF_INET) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
||||
|
||||
return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
|
||||
sizeof ntop_buf[0]));
|
||||
}
|
||||
|
||||
if (sa->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
||||
|
||||
return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
|
||||
sizeof ntop_buf[0]));
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
u_int16_t
|
||||
pick_proxy_port(void)
|
||||
{
|
||||
return (IPPORT_HIFIRSTAUTO + (arc4random() %
|
||||
(IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)));
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
syslog(LOG_ERR, "usage: %s [-v] [-w transwait]", __progname);
|
||||
exit(1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user