diff --git a/contrib/pf/ftp-proxy/filter.c b/contrib/pf/ftp-proxy/filter.c new file mode 100644 index 000000000000..f86429db51d3 --- /dev/null +++ b/contrib/pf/ftp-proxy/filter.c @@ -0,0 +1,387 @@ +/* $OpenBSD: filter.c,v 1.5 2006/12/01 07:31:21 camield Exp $ */ + +/* + * Copyright (c) 2004, 2005 Camiel Dobbelaar, + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/contrib/pf/ftp-proxy/filter.h b/contrib/pf/ftp-proxy/filter.h new file mode 100644 index 000000000000..6779c5974467 --- /dev/null +++ b/contrib/pf/ftp-proxy/filter.h @@ -0,0 +1,31 @@ +/* $OpenBSD: filter.h,v 1.3 2005/06/07 14:12:07 camield Exp $ */ + +/* + * Copyright (c) 2004, 2005 Camiel Dobbelaar, + * + * 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 *); diff --git a/contrib/pf/libevent/buffer.c b/contrib/pf/libevent/buffer.c new file mode 100644 index 000000000000..77efd0cfa3e2 --- /dev/null +++ b/contrib/pf/libevent/buffer.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2002, 2003 Niels Provos + * 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 + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include +#include +#include +#include +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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; +} diff --git a/contrib/pf/libevent/evbuffer.c b/contrib/pf/libevent/evbuffer.c new file mode 100644 index 000000000000..52712bce5856 --- /dev/null +++ b/contrib/pf/libevent/evbuffer.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2002-2004 Niels Provos + * 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 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#ifdef HAVE_STDARG_H +#include +#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); +} diff --git a/contrib/pf/libevent/event-internal.h b/contrib/pf/libevent/event-internal.h new file mode 100644 index 000000000000..becb6691d52a --- /dev/null +++ b/contrib/pf/libevent/event-internal.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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_ */ diff --git a/contrib/pf/libevent/event.c b/contrib/pf/libevent/event.c new file mode 100644 index 000000000000..f6d2b1cc42ca --- /dev/null +++ b/contrib/pf/libevent/event.c @@ -0,0 +1,878 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 +#undef WIN32_LEAN_AND_MEAN +#include "misc.h" +#endif +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include +#include +#include + +#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); +} diff --git a/contrib/pf/libevent/event.h b/contrib/pf/libevent/event.h new file mode 100644 index 000000000000..3f2032dd068e --- /dev/null +++ b/contrib/pf/libevent/event.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#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 */ +#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_ */ diff --git a/contrib/pf/libevent/evsignal.h b/contrib/pf/libevent/evsignal.h new file mode 100644 index 000000000000..5b92bd6e55fb --- /dev/null +++ b/contrib/pf/libevent/evsignal.h @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2002 Niels Provos + * 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_ */ diff --git a/contrib/pf/libevent/kqueue.c b/contrib/pf/libevent/kqueue.c new file mode 100644 index 000000000000..08369c6dd51d --- /dev/null +++ b/contrib/pf/libevent/kqueue.c @@ -0,0 +1,413 @@ +/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */ + +/* + * Copyright 2000-2002 Niels Provos + * 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 +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_INTTYPES_H +#include +#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); +} diff --git a/contrib/pf/libevent/log.c b/contrib/pf/libevent/log.c new file mode 100644 index 000000000000..c9275e363fc5 --- /dev/null +++ b/contrib/pf/libevent/log.c @@ -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 + * + * Copyright (c) 2000 Dug Song + * + * 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 +#undef WIN32_LEAN_AND_MEAN +#include "misc.h" +#endif +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#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); + } +} diff --git a/contrib/pf/libevent/log.h b/contrib/pf/libevent/log.h new file mode 100644 index 000000000000..1f843cf984e0 --- /dev/null +++ b/contrib/pf/libevent/log.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 diff --git a/contrib/pf/libevent/poll.c b/contrib/pf/libevent/poll.c new file mode 100644 index 000000000000..14ca8453739b --- /dev/null +++ b/contrib/pf/libevent/poll.c @@ -0,0 +1,388 @@ +/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2003 Niels Provos + * 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 +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CHECK_INVARIANTS +#include +#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); +} diff --git a/contrib/pf/libevent/select.c b/contrib/pf/libevent/select.c new file mode 100644 index 000000000000..6ce81a232bbe --- /dev/null +++ b/contrib/pf/libevent/select.c @@ -0,0 +1,370 @@ +/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2002 Niels Provos + * 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 +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CHECK_INVARIANTS +#include +#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); +} diff --git a/contrib/pf/libevent/signal.c b/contrib/pf/libevent/signal.c new file mode 100644 index 000000000000..71bcffcba5b3 --- /dev/null +++ b/contrib/pf/libevent/signal.c @@ -0,0 +1,180 @@ +/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2002 Niels Provos + * 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 +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#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; + } + } +} + diff --git a/contrib/pf/pfctl/pfctl_osfp.c b/contrib/pf/pfctl/pfctl_osfp.c index 23e3cccc0bf1..7018d6cd3657 100644 --- a/contrib/pf/pfctl/pfctl_osfp.c +++ b/contrib/pf/pfctl/pfctl_osfp.c @@ -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 @@ -23,6 +23,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -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 */ diff --git a/contrib/pf/pfctl/pfctl_radix.c b/contrib/pf/pfctl/pfctl_radix.c index ba004b83910a..01ad4758ff1a 100644 --- a/contrib/pf/pfctl/pfctl_radix.c +++ b/contrib/pf/pfctl/pfctl_radix.c @@ -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) }; /* diff --git a/contrib/pf/pflogd/pflogd.h b/contrib/pf/pflogd/pflogd.h index 3baecb66fe67..596e69692614 100644 --- a/contrib/pf/pflogd/pflogd.h +++ b/contrib/pf/pflogd/pflogd.h @@ -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); diff --git a/contrib/pf/tftp-proxy/filter.c b/contrib/pf/tftp-proxy/filter.c new file mode 100644 index 000000000000..cd6ce3cd1e7a --- /dev/null +++ b/contrib/pf/tftp-proxy/filter.c @@ -0,0 +1,397 @@ +/* $OpenBSD: filter.c,v 1.1 2005/12/28 19:07:07 jcs Exp $ */ + +/* + * Copyright (c) 2004, 2005 Camiel Dobbelaar, + * + * 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 + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/contrib/pf/tftp-proxy/filter.h b/contrib/pf/tftp-proxy/filter.h new file mode 100644 index 000000000000..04d43f737cbb --- /dev/null +++ b/contrib/pf/tftp-proxy/filter.h @@ -0,0 +1,32 @@ +/* $OpenBSD: filter.h,v 1.1 2005/12/28 19:07:07 jcs Exp $ */ + +/* + * Copyright (c) 2004, 2005 Camiel Dobbelaar, + * + * 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); diff --git a/contrib/pf/tftp-proxy/tftp-proxy.8 b/contrib/pf/tftp-proxy/tftp-proxy.8 new file mode 100644 index 000000000000..b9098ef4d17d --- /dev/null +++ b/contrib/pf/tftp-proxy/tftp-proxy.8 @@ -0,0 +1,140 @@ +.\" $OpenBSD: tftp-proxy.8,v 1.1 2005/12/28 19:07:07 jcs Exp $ +.\" +.\" Copyright (c) 2005 joshua stein +.\" +.\" 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. diff --git a/contrib/pf/tftp-proxy/tftp-proxy.c b/contrib/pf/tftp-proxy/tftp-proxy.c new file mode 100644 index 000000000000..18d3323e9117 --- /dev/null +++ b/contrib/pf/tftp-proxy/tftp-proxy.c @@ -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, + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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); +}