1ea4b8724SNick Mathewson /*
2e49e2891SNick Mathewson * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
3eda27f95SNick Mathewson * Copyright (c) 2002-2006 Niels Provos <[email protected]>
4ea4b8724SNick Mathewson * All rights reserved.
5ea4b8724SNick Mathewson *
6ea4b8724SNick Mathewson * Redistribution and use in source and binary forms, with or without
7ea4b8724SNick Mathewson * modification, are permitted provided that the following conditions
8ea4b8724SNick Mathewson * are met:
9ea4b8724SNick Mathewson * 1. Redistributions of source code must retain the above copyright
10ea4b8724SNick Mathewson * notice, this list of conditions and the following disclaimer.
11ea4b8724SNick Mathewson * 2. Redistributions in binary form must reproduce the above copyright
12ea4b8724SNick Mathewson * notice, this list of conditions and the following disclaimer in the
13ea4b8724SNick Mathewson * documentation and/or other materials provided with the distribution.
14ea4b8724SNick Mathewson * 3. The name of the author may not be used to endorse or promote products
15ea4b8724SNick Mathewson * derived from this software without specific prior written permission.
16ea4b8724SNick Mathewson *
17ea4b8724SNick Mathewson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18ea4b8724SNick Mathewson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19ea4b8724SNick Mathewson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20ea4b8724SNick Mathewson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21ea4b8724SNick Mathewson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22ea4b8724SNick Mathewson * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23ea4b8724SNick Mathewson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24ea4b8724SNick Mathewson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25ea4b8724SNick Mathewson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26ea4b8724SNick Mathewson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27ea4b8724SNick Mathewson */
28ea4b8724SNick Mathewson
29ded0a090SKevin Bowling #include "evconfig-private.h"
30ded0a090SKevin Bowling
31ea4b8724SNick Mathewson #include <sys/types.h>
32ea4b8724SNick Mathewson
33ec347b92SNick Mathewson #include "event2/event-config.h"
34ea4b8724SNick Mathewson
3568120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_TIME_H
36ea4b8724SNick Mathewson #include <sys/time.h>
37ea4b8724SNick Mathewson #endif
38ea4b8724SNick Mathewson
39ea4b8724SNick Mathewson #include <errno.h>
40ea4b8724SNick Mathewson #include <stdio.h>
41ea4b8724SNick Mathewson #include <stdlib.h>
42ea4b8724SNick Mathewson #include <string.h>
4368120d9bSNick Mathewson #ifdef EVENT__HAVE_STDARG_H
44ea4b8724SNick Mathewson #include <stdarg.h>
45ea4b8724SNick Mathewson #endif
46ea4b8724SNick Mathewson
479f560bfaSNick Mathewson #ifdef _WIN32
48ea4b8724SNick Mathewson #include <winsock2.h>
49ea4b8724SNick Mathewson #endif
50ea4b8724SNick Mathewson
51ea4b8724SNick Mathewson #include "event2/util.h"
52ea4b8724SNick Mathewson #include "event2/bufferevent.h"
53ea4b8724SNick Mathewson #include "event2/buffer.h"
54ea4b8724SNick Mathewson #include "event2/bufferevent_struct.h"
55ea4b8724SNick Mathewson #include "event2/event.h"
56ea4b8724SNick Mathewson #include "log-internal.h"
57ea4b8724SNick Mathewson #include "mm-internal.h"
58ea4b8724SNick Mathewson #include "bufferevent-internal.h"
59ea4b8724SNick Mathewson #include "util-internal.h"
60ea4b8724SNick Mathewson
61ea4b8724SNick Mathewson /* prototypes */
62ea4b8724SNick Mathewson static int be_filter_enable(struct bufferevent *, short);
63ea4b8724SNick Mathewson static int be_filter_disable(struct bufferevent *, short);
6402fbf687SNick Mathewson static void be_filter_unlink(struct bufferevent *);
65ea4b8724SNick Mathewson static void be_filter_destruct(struct bufferevent *);
66ea4b8724SNick Mathewson
67ea4b8724SNick Mathewson static void be_filter_readcb(struct bufferevent *, void *);
68ea4b8724SNick Mathewson static void be_filter_writecb(struct bufferevent *, void *);
695232cfa3SNick Mathewson static void be_filter_eventcb(struct bufferevent *, short, void *);
70ea4b8724SNick Mathewson static int be_filter_flush(struct bufferevent *bufev,
71ea4b8724SNick Mathewson short iotype, enum bufferevent_flush_mode mode);
7231d89f27SNick Mathewson static int be_filter_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
73ea4b8724SNick Mathewson
74b627ad88SEduardo Panisset static void bufferevent_filtered_inbuf_cb(struct evbuffer *buf,
75b627ad88SEduardo Panisset const struct evbuffer_cb_info *cbinfo, void *arg);
76b627ad88SEduardo Panisset
77ea4b8724SNick Mathewson static void bufferevent_filtered_outbuf_cb(struct evbuffer *buf,
78f1b1bad4SNick Mathewson const struct evbuffer_cb_info *info, void *arg);
79ea4b8724SNick Mathewson
80ea4b8724SNick Mathewson struct bufferevent_filtered {
811becc4c4SNick Mathewson struct bufferevent_private bev;
82ea4b8724SNick Mathewson
83e3fd294aSNick Mathewson /** The bufferevent that we read/write filtered data from/to. */
84ea4b8724SNick Mathewson struct bufferevent *underlying;
85b627ad88SEduardo Panisset /** A callback on our inbuf to notice somebory removes data */
86b627ad88SEduardo Panisset struct evbuffer_cb_entry *inbuf_cb;
87ea4b8724SNick Mathewson /** A callback on our outbuf to notice when somebody adds data */
88ea4b8724SNick Mathewson struct evbuffer_cb_entry *outbuf_cb;
89ea4b8724SNick Mathewson /** True iff we have received an EOF callback from the underlying
90ea4b8724SNick Mathewson * bufferevent. */
91ea4b8724SNick Mathewson unsigned got_eof;
92ea4b8724SNick Mathewson
93ea4b8724SNick Mathewson /** Function to free context when we're done. */
94ea4b8724SNick Mathewson void (*free_context)(void *);
95ea4b8724SNick Mathewson /** Input filter */
96ea4b8724SNick Mathewson bufferevent_filter_cb process_in;
97ea4b8724SNick Mathewson /** Output filter */
98ea4b8724SNick Mathewson bufferevent_filter_cb process_out;
99ea4b8724SNick Mathewson /** User-supplied argument to the filters. */
100ea4b8724SNick Mathewson void *context;
101ea4b8724SNick Mathewson };
102ea4b8724SNick Mathewson
10323085c92SNick Mathewson const struct bufferevent_ops bufferevent_ops_filter = {
104ea4b8724SNick Mathewson "filter",
105657d1b6dSNick Mathewson evutil_offsetof(struct bufferevent_filtered, bev.bev),
106ea4b8724SNick Mathewson be_filter_enable,
107ea4b8724SNick Mathewson be_filter_disable,
10802fbf687SNick Mathewson be_filter_unlink,
109ea4b8724SNick Mathewson be_filter_destruct,
110cb9da0bfSNick Mathewson bufferevent_generic_adj_timeouts_,
111ea4b8724SNick Mathewson be_filter_flush,
11231d89f27SNick Mathewson be_filter_ctrl,
113ea4b8724SNick Mathewson };
114ea4b8724SNick Mathewson
115ea4b8724SNick Mathewson /* Given a bufferevent that's really the bev filter of a bufferevent_filtered,
116ea4b8724SNick Mathewson * return that bufferevent_filtered. Returns NULL otherwise.*/
117ea4b8724SNick Mathewson static inline struct bufferevent_filtered *
upcast(struct bufferevent * bev)118ea4b8724SNick Mathewson upcast(struct bufferevent *bev)
119ea4b8724SNick Mathewson {
120ea4b8724SNick Mathewson struct bufferevent_filtered *bev_f;
12156faf02bSDominic Chen if (!BEV_IS_FILTER(bev))
122ea4b8724SNick Mathewson return NULL;
123ea4b8724SNick Mathewson bev_f = (void*)( ((char*)bev) -
1241becc4c4SNick Mathewson evutil_offsetof(struct bufferevent_filtered, bev.bev));
12556faf02bSDominic Chen EVUTIL_ASSERT(BEV_IS_FILTER(&bev_f->bev.bev));
126ea4b8724SNick Mathewson return bev_f;
127ea4b8724SNick Mathewson }
128ea4b8724SNick Mathewson
1291becc4c4SNick Mathewson #define downcast(bev_f) (&(bev_f)->bev.bev)
130ea4b8724SNick Mathewson
131ea4b8724SNick Mathewson /** Return 1 iff bevf's underlying bufferevent's output buffer is at or
132ea4b8724SNick Mathewson * over its high watermark such that we should not write to it in a given
133ea4b8724SNick Mathewson * flush mode. */
134ea4b8724SNick Mathewson static int
be_underlying_writebuf_full(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state)135ea4b8724SNick Mathewson be_underlying_writebuf_full(struct bufferevent_filtered *bevf,
136ea4b8724SNick Mathewson enum bufferevent_flush_mode state)
137ea4b8724SNick Mathewson {
138ea4b8724SNick Mathewson struct bufferevent *u = bevf->underlying;
139ea4b8724SNick Mathewson return state == BEV_NORMAL &&
140ea4b8724SNick Mathewson u->wm_write.high &&
141a8f6d961SNick Mathewson evbuffer_get_length(u->output) >= u->wm_write.high;
142ea4b8724SNick Mathewson }
143ea4b8724SNick Mathewson
144ea4b8724SNick Mathewson /** Return 1 if our input buffer is at or over its high watermark such that we
145ea4b8724SNick Mathewson * should not write to it in a given flush mode. */
146ea4b8724SNick Mathewson static int
be_readbuf_full(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state)147ea4b8724SNick Mathewson be_readbuf_full(struct bufferevent_filtered *bevf,
148ea4b8724SNick Mathewson enum bufferevent_flush_mode state)
149ea4b8724SNick Mathewson {
150ea4b8724SNick Mathewson struct bufferevent *bufev = downcast(bevf);
151ea4b8724SNick Mathewson return state == BEV_NORMAL &&
152ea4b8724SNick Mathewson bufev->wm_read.high &&
153a8f6d961SNick Mathewson evbuffer_get_length(bufev->input) >= bufev->wm_read.high;
154ea4b8724SNick Mathewson }
155ea4b8724SNick Mathewson
156ea4b8724SNick Mathewson
157ea4b8724SNick Mathewson /* Filter to use when we're created with a NULL filter. */
158ea4b8724SNick Mathewson static enum bufferevent_filter_result
be_null_filter(struct evbuffer * src,struct evbuffer * dst,ev_ssize_t lim,enum bufferevent_flush_mode state,void * ctx)1590b22ca19SNick Mathewson be_null_filter(struct evbuffer *src, struct evbuffer *dst, ev_ssize_t lim,
160ea4b8724SNick Mathewson enum bufferevent_flush_mode state, void *ctx)
161ea4b8724SNick Mathewson {
162ea4b8724SNick Mathewson (void)state;
163*b92b0792SGreg Hazel if (evbuffer_remove_buffer(src, dst, lim) >= 0)
164ea4b8724SNick Mathewson return BEV_OK;
165ea4b8724SNick Mathewson else
166ea4b8724SNick Mathewson return BEV_ERROR;
167ea4b8724SNick Mathewson }
168ea4b8724SNick Mathewson
169ea4b8724SNick Mathewson struct bufferevent *
bufferevent_filter_new(struct bufferevent * underlying,bufferevent_filter_cb input_filter,bufferevent_filter_cb output_filter,int options,void (* free_context)(void *),void * ctx)170ea4b8724SNick Mathewson bufferevent_filter_new(struct bufferevent *underlying,
171ea4b8724SNick Mathewson bufferevent_filter_cb input_filter,
172ea4b8724SNick Mathewson bufferevent_filter_cb output_filter,
173b73ad7bcSNick Mathewson int options,
174ea4b8724SNick Mathewson void (*free_context)(void *),
175ea4b8724SNick Mathewson void *ctx)
176ea4b8724SNick Mathewson {
177ea4b8724SNick Mathewson struct bufferevent_filtered *bufev_f;
178b73ad7bcSNick Mathewson int tmp_options = options & ~BEV_OPT_THREADSAFE;
179ea4b8724SNick Mathewson
180ac27eb82SNick Mathewson if (!underlying)
181ac27eb82SNick Mathewson return NULL;
182ac27eb82SNick Mathewson
183ea4b8724SNick Mathewson if (!input_filter)
184ea4b8724SNick Mathewson input_filter = be_null_filter;
185ea4b8724SNick Mathewson if (!output_filter)
186ea4b8724SNick Mathewson output_filter = be_null_filter;
187ea4b8724SNick Mathewson
188ea4b8724SNick Mathewson bufev_f = mm_calloc(1, sizeof(struct bufferevent_filtered));
189ea4b8724SNick Mathewson if (!bufev_f)
190ea4b8724SNick Mathewson return NULL;
191ea4b8724SNick Mathewson
1928ac3c4c2SNick Mathewson if (bufferevent_init_common_(&bufev_f->bev, underlying->ev_base,
193915193e7SNick Mathewson &bufferevent_ops_filter, tmp_options) < 0) {
194ea4b8724SNick Mathewson mm_free(bufev_f);
195ea4b8724SNick Mathewson return NULL;
196ea4b8724SNick Mathewson }
197915193e7SNick Mathewson if (options & BEV_OPT_THREADSAFE) {
1988ac3c4c2SNick Mathewson bufferevent_enable_locking_(downcast(bufev_f), NULL);
199915193e7SNick Mathewson }
200ea4b8724SNick Mathewson
201ea4b8724SNick Mathewson bufev_f->underlying = underlying;
202f1bc125eSNick Mathewson
203ea4b8724SNick Mathewson bufev_f->process_in = input_filter;
204ea4b8724SNick Mathewson bufev_f->process_out = output_filter;
205ea4b8724SNick Mathewson bufev_f->free_context = free_context;
206ea4b8724SNick Mathewson bufev_f->context = ctx;
207ea4b8724SNick Mathewson
208ea4b8724SNick Mathewson bufferevent_setcb(bufev_f->underlying,
2095232cfa3SNick Mathewson be_filter_readcb, be_filter_writecb, be_filter_eventcb, bufev_f);
210ea4b8724SNick Mathewson
211b627ad88SEduardo Panisset bufev_f->inbuf_cb = evbuffer_add_cb(downcast(bufev_f)->input,
212b627ad88SEduardo Panisset bufferevent_filtered_inbuf_cb, bufev_f);
213b627ad88SEduardo Panisset evbuffer_cb_clear_flags(downcast(bufev_f)->input, bufev_f->inbuf_cb,
214b627ad88SEduardo Panisset EVBUFFER_CB_ENABLED);
215b627ad88SEduardo Panisset
2161becc4c4SNick Mathewson bufev_f->outbuf_cb = evbuffer_add_cb(downcast(bufev_f)->output,
217ea4b8724SNick Mathewson bufferevent_filtered_outbuf_cb, bufev_f);
218ea4b8724SNick Mathewson
219cb9da0bfSNick Mathewson bufferevent_init_generic_timeout_cbs_(downcast(bufev_f));
2208ac3c4c2SNick Mathewson bufferevent_incref_(underlying);
22134574db0SNick Mathewson
222ac27eb82SNick Mathewson bufferevent_enable(underlying, EV_READ|EV_WRITE);
2238ac3c4c2SNick Mathewson bufferevent_suspend_read_(underlying, BEV_SUSPEND_FILT_READ);
224ac27eb82SNick Mathewson
2251becc4c4SNick Mathewson return downcast(bufev_f);
226ea4b8724SNick Mathewson }
227ea4b8724SNick Mathewson
228ea4b8724SNick Mathewson static void
be_filter_unlink(struct bufferevent * bev)22902fbf687SNick Mathewson be_filter_unlink(struct bufferevent *bev)
230ea4b8724SNick Mathewson {
231ea4b8724SNick Mathewson struct bufferevent_filtered *bevf = upcast(bev);
2322e36dbe1SNick Mathewson EVUTIL_ASSERT(bevf);
233ea4b8724SNick Mathewson
234f1bc125eSNick Mathewson if (bevf->bev.options & BEV_OPT_CLOSE_ON_FREE) {
2358ac3c4c2SNick Mathewson /* Yes, there is also a decref in bufferevent_decref_.
236f1bc125eSNick Mathewson * That decref corresponds to the incref when we set
237f1bc125eSNick Mathewson * underlying for the first time. This decref is an
238f1bc125eSNick Mathewson * extra one to remove the last reference.
239f1bc125eSNick Mathewson */
240f1bc125eSNick Mathewson if (BEV_UPCAST(bevf->underlying)->refcnt < 2) {
241f1bc125eSNick Mathewson event_warnx("BEV_OPT_CLOSE_ON_FREE set on an "
242f1bc125eSNick Mathewson "bufferevent with too few references");
243f1bc125eSNick Mathewson } else {
244ea4b8724SNick Mathewson bufferevent_free(bevf->underlying);
245f1bc125eSNick Mathewson }
246ac27eb82SNick Mathewson } else {
247ac27eb82SNick Mathewson if (bevf->underlying) {
2481ac5b230SNick Mathewson if (bevf->underlying->errorcb == be_filter_eventcb)
249fc7b1b00SNick Mathewson bufferevent_setcb(bevf->underlying,
250fc7b1b00SNick Mathewson NULL, NULL, NULL, NULL);
2518ac3c4c2SNick Mathewson bufferevent_unsuspend_read_(bevf->underlying,
252ac27eb82SNick Mathewson BEV_SUSPEND_FILT_READ);
253ac27eb82SNick Mathewson }
254f1bc125eSNick Mathewson }
25502fbf687SNick Mathewson }
25634574db0SNick Mathewson
25702fbf687SNick Mathewson static void
be_filter_destruct(struct bufferevent * bev)25802fbf687SNick Mathewson be_filter_destruct(struct bufferevent *bev)
25902fbf687SNick Mathewson {
26002fbf687SNick Mathewson struct bufferevent_filtered *bevf = upcast(bev);
26102fbf687SNick Mathewson EVUTIL_ASSERT(bevf);
26202fbf687SNick Mathewson if (bevf->free_context)
26302fbf687SNick Mathewson bevf->free_context(bevf->context);
264b627ad88SEduardo Panisset
265b627ad88SEduardo Panisset if (bevf->inbuf_cb)
266b627ad88SEduardo Panisset evbuffer_remove_cb_entry(bev->input, bevf->inbuf_cb);
267b627ad88SEduardo Panisset
268b627ad88SEduardo Panisset if (bevf->outbuf_cb)
269b627ad88SEduardo Panisset evbuffer_remove_cb_entry(bev->output, bevf->outbuf_cb);
270ea4b8724SNick Mathewson }
271ea4b8724SNick Mathewson
272ea4b8724SNick Mathewson static int
be_filter_enable(struct bufferevent * bev,short event)273ea4b8724SNick Mathewson be_filter_enable(struct bufferevent *bev, short event)
274ea4b8724SNick Mathewson {
275ea4b8724SNick Mathewson struct bufferevent_filtered *bevf = upcast(bev);
276d3288293SNick Mathewson if (event & EV_WRITE)
277d3288293SNick Mathewson BEV_RESET_GENERIC_WRITE_TIMEOUT(bev);
278ac27eb82SNick Mathewson
279ac27eb82SNick Mathewson if (event & EV_READ) {
280ac27eb82SNick Mathewson BEV_RESET_GENERIC_READ_TIMEOUT(bev);
2818ac3c4c2SNick Mathewson bufferevent_unsuspend_read_(bevf->underlying,
282ac27eb82SNick Mathewson BEV_SUSPEND_FILT_READ);
283ac27eb82SNick Mathewson }
284ac27eb82SNick Mathewson return 0;
285ea4b8724SNick Mathewson }
286ea4b8724SNick Mathewson
287ea4b8724SNick Mathewson static int
be_filter_disable(struct bufferevent * bev,short event)288ea4b8724SNick Mathewson be_filter_disable(struct bufferevent *bev, short event)
289ea4b8724SNick Mathewson {
290ea4b8724SNick Mathewson struct bufferevent_filtered *bevf = upcast(bev);
291d3288293SNick Mathewson if (event & EV_WRITE)
292d3288293SNick Mathewson BEV_DEL_GENERIC_WRITE_TIMEOUT(bev);
293ac27eb82SNick Mathewson if (event & EV_READ) {
294ac27eb82SNick Mathewson BEV_DEL_GENERIC_READ_TIMEOUT(bev);
2958ac3c4c2SNick Mathewson bufferevent_suspend_read_(bevf->underlying,
296ac27eb82SNick Mathewson BEV_SUSPEND_FILT_READ);
297ac27eb82SNick Mathewson }
298ac27eb82SNick Mathewson return 0;
299ea4b8724SNick Mathewson }
300ea4b8724SNick Mathewson
301ea4b8724SNick Mathewson static enum bufferevent_filter_result
be_filter_process_input(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state,int * processed_out)302ea4b8724SNick Mathewson be_filter_process_input(struct bufferevent_filtered *bevf,
303ea4b8724SNick Mathewson enum bufferevent_flush_mode state,
304ea4b8724SNick Mathewson int *processed_out)
305ea4b8724SNick Mathewson {
306ea4b8724SNick Mathewson enum bufferevent_filter_result res;
3071becc4c4SNick Mathewson struct bufferevent *bev = downcast(bevf);
308ea4b8724SNick Mathewson
309ea4b8724SNick Mathewson if (state == BEV_NORMAL) {
310ea4b8724SNick Mathewson /* If we're in 'normal' mode, don't urge data on the filter
311ea4b8724SNick Mathewson * unless we're reading data and under our high-water mark.*/
3121becc4c4SNick Mathewson if (!(bev->enabled & EV_READ) ||
313ea4b8724SNick Mathewson be_readbuf_full(bevf, state))
314ea4b8724SNick Mathewson return BEV_OK;
315ea4b8724SNick Mathewson }
316ea4b8724SNick Mathewson
317ea4b8724SNick Mathewson do {
3180b22ca19SNick Mathewson ev_ssize_t limit = -1;
3191becc4c4SNick Mathewson if (state == BEV_NORMAL && bev->wm_read.high)
3201becc4c4SNick Mathewson limit = bev->wm_read.high -
321a8f6d961SNick Mathewson evbuffer_get_length(bev->input);
322ea4b8724SNick Mathewson
323ea4b8724SNick Mathewson res = bevf->process_in(bevf->underlying->input,
3241becc4c4SNick Mathewson bev->input, limit, state, bevf->context);
325ea4b8724SNick Mathewson
326ea4b8724SNick Mathewson if (res == BEV_OK)
327ea4b8724SNick Mathewson *processed_out = 1;
328ea4b8724SNick Mathewson } while (res == BEV_OK &&
3291becc4c4SNick Mathewson (bev->enabled & EV_READ) &&
330a8f6d961SNick Mathewson evbuffer_get_length(bevf->underlying->input) &&
331ea4b8724SNick Mathewson !be_readbuf_full(bevf, state));
332ea4b8724SNick Mathewson
33334574db0SNick Mathewson if (*processed_out)
33434574db0SNick Mathewson BEV_RESET_GENERIC_READ_TIMEOUT(bev);
33534574db0SNick Mathewson
336ea4b8724SNick Mathewson return res;
337ea4b8724SNick Mathewson }
338ea4b8724SNick Mathewson
339ea4b8724SNick Mathewson
340ea4b8724SNick Mathewson static enum bufferevent_filter_result
be_filter_process_output(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state,int * processed_out)341ea4b8724SNick Mathewson be_filter_process_output(struct bufferevent_filtered *bevf,
342ea4b8724SNick Mathewson enum bufferevent_flush_mode state,
343ea4b8724SNick Mathewson int *processed_out)
344ea4b8724SNick Mathewson {
345a62283a9SNick Mathewson /* Requires references and lock: might call writecb */
346ea4b8724SNick Mathewson enum bufferevent_filter_result res = BEV_OK;
347ea4b8724SNick Mathewson struct bufferevent *bufev = downcast(bevf);
348ea4b8724SNick Mathewson int again = 0;
349ea4b8724SNick Mathewson
350ea4b8724SNick Mathewson if (state == BEV_NORMAL) {
351ea4b8724SNick Mathewson /* If we're in 'normal' mode, don't urge data on the
352ea4b8724SNick Mathewson * filter unless we're writing data, and the underlying
353ea4b8724SNick Mathewson * bufferevent is accepting data, and we have data to
354ea4b8724SNick Mathewson * give the filter. If we're in 'flush' or 'finish',
355ea4b8724SNick Mathewson * call the filter no matter what. */
356ea4b8724SNick Mathewson if (!(bufev->enabled & EV_WRITE) ||
357ea4b8724SNick Mathewson be_underlying_writebuf_full(bevf, state) ||
358a8f6d961SNick Mathewson !evbuffer_get_length(bufev->output))
359ea4b8724SNick Mathewson return BEV_OK;
360ea4b8724SNick Mathewson }
361ea4b8724SNick Mathewson
362ea4b8724SNick Mathewson /* disable the callback that calls this function
363ea4b8724SNick Mathewson when the user adds to the output buffer. */
364c031215dSSimon Perreault evbuffer_cb_clear_flags(bufev->output, bevf->outbuf_cb,
365c031215dSSimon Perreault EVBUFFER_CB_ENABLED);
366ea4b8724SNick Mathewson
367ea4b8724SNick Mathewson do {
368ea4b8724SNick Mathewson int processed = 0;
369ea4b8724SNick Mathewson again = 0;
370ea4b8724SNick Mathewson
371ea4b8724SNick Mathewson do {
3720b22ca19SNick Mathewson ev_ssize_t limit = -1;
373ea4b8724SNick Mathewson if (state == BEV_NORMAL &&
374ea4b8724SNick Mathewson bevf->underlying->wm_write.high)
375ea4b8724SNick Mathewson limit = bevf->underlying->wm_write.high -
376a8f6d961SNick Mathewson evbuffer_get_length(bevf->underlying->output);
377ea4b8724SNick Mathewson
3781becc4c4SNick Mathewson res = bevf->process_out(downcast(bevf)->output,
379ea4b8724SNick Mathewson bevf->underlying->output,
380ea4b8724SNick Mathewson limit,
381ea4b8724SNick Mathewson state,
382ea4b8724SNick Mathewson bevf->context);
383ea4b8724SNick Mathewson
384ea4b8724SNick Mathewson if (res == BEV_OK)
385ea4b8724SNick Mathewson processed = *processed_out = 1;
386ea4b8724SNick Mathewson } while (/* Stop if the filter wasn't successful...*/
387ea4b8724SNick Mathewson res == BEV_OK &&
388ea4b8724SNick Mathewson /* Or if we aren't writing any more. */
389ea4b8724SNick Mathewson (bufev->enabled & EV_WRITE) &&
390ea4b8724SNick Mathewson /* Of if we have nothing more to write and we are
391ea4b8724SNick Mathewson * not flushing. */
392a8f6d961SNick Mathewson evbuffer_get_length(bufev->output) &&
393ea4b8724SNick Mathewson /* Or if we have filled the underlying output buffer. */
394ea4b8724SNick Mathewson !be_underlying_writebuf_full(bevf,state));
395ea4b8724SNick Mathewson
39661ee18b8SOndřej Kuzník if (processed) {
397ea4b8724SNick Mathewson /* call the write callback.*/
39861ee18b8SOndřej Kuzník bufferevent_trigger_nolock_(bufev, EV_WRITE, 0);
399ea4b8724SNick Mathewson
400ea4b8724SNick Mathewson if (res == BEV_OK &&
401ea4b8724SNick Mathewson (bufev->enabled & EV_WRITE) &&
402a8f6d961SNick Mathewson evbuffer_get_length(bufev->output) &&
403ea4b8724SNick Mathewson !be_underlying_writebuf_full(bevf, state)) {
404ea4b8724SNick Mathewson again = 1;
405ea4b8724SNick Mathewson }
406ea4b8724SNick Mathewson }
407ea4b8724SNick Mathewson } while (again);
408ea4b8724SNick Mathewson
409ea4b8724SNick Mathewson /* reenable the outbuf_cb */
410ea4b8724SNick Mathewson evbuffer_cb_set_flags(bufev->output,bevf->outbuf_cb,
411ea4b8724SNick Mathewson EVBUFFER_CB_ENABLED);
412ea4b8724SNick Mathewson
41334574db0SNick Mathewson if (*processed_out)
41434574db0SNick Mathewson BEV_RESET_GENERIC_WRITE_TIMEOUT(bufev);
41534574db0SNick Mathewson
416ea4b8724SNick Mathewson return res;
417ea4b8724SNick Mathewson }
418ea4b8724SNick Mathewson
419ea4b8724SNick Mathewson /* Called when the size of our outbuf changes. */
420ea4b8724SNick Mathewson static void
bufferevent_filtered_outbuf_cb(struct evbuffer * buf,const struct evbuffer_cb_info * cbinfo,void * arg)421ea4b8724SNick Mathewson bufferevent_filtered_outbuf_cb(struct evbuffer *buf,
422f1b1bad4SNick Mathewson const struct evbuffer_cb_info *cbinfo, void *arg)
423ea4b8724SNick Mathewson {
424ea4b8724SNick Mathewson struct bufferevent_filtered *bevf = arg;
425a62283a9SNick Mathewson struct bufferevent *bev = downcast(bevf);
426ea4b8724SNick Mathewson
427f1b1bad4SNick Mathewson if (cbinfo->n_added) {
428ea4b8724SNick Mathewson int processed_any = 0;
429ea4b8724SNick Mathewson /* Somebody added more data to the output buffer. Try to
430ea4b8724SNick Mathewson * process it, if we should. */
431cb9da0bfSNick Mathewson bufferevent_incref_and_lock_(bev);
432ea4b8724SNick Mathewson be_filter_process_output(bevf, BEV_NORMAL, &processed_any);
433cb9da0bfSNick Mathewson bufferevent_decref_and_unlock_(bev);
434ea4b8724SNick Mathewson }
435ea4b8724SNick Mathewson }
436ea4b8724SNick Mathewson
437ea4b8724SNick Mathewson static void
be_filter_read_nolock_(struct bufferevent * underlying,void * me_)438b627ad88SEduardo Panisset be_filter_read_nolock_(struct bufferevent *underlying, void *me_)
439ea4b8724SNick Mathewson {
440946b5841SNick Mathewson struct bufferevent_filtered *bevf = me_;
441ea4b8724SNick Mathewson enum bufferevent_filter_result res;
442ea4b8724SNick Mathewson enum bufferevent_flush_mode state;
443ea4b8724SNick Mathewson struct bufferevent *bufev = downcast(bevf);
4442c82aa0fSJohn Ohl struct bufferevent_private *bufev_private = BEV_UPCAST(bufev);
445ea4b8724SNick Mathewson int processed_any = 0;
446ea4b8724SNick Mathewson
4472c82aa0fSJohn Ohl // It's possible our refcount is 0 at this point if another thread free'd our filterevent
4482c82aa0fSJohn Ohl EVUTIL_ASSERT(bufev_private->refcnt >= 0);
4492c82aa0fSJohn Ohl
4502c82aa0fSJohn Ohl // If our refcount is > 0
4512c82aa0fSJohn Ohl if (bufev_private->refcnt > 0) {
452a62283a9SNick Mathewson
453ea4b8724SNick Mathewson if (bevf->got_eof)
454ea4b8724SNick Mathewson state = BEV_FINISHED;
455ea4b8724SNick Mathewson else
456ea4b8724SNick Mathewson state = BEV_NORMAL;
457ea4b8724SNick Mathewson
4587bcace2dSNick Mathewson /* XXXX use return value */
459ea4b8724SNick Mathewson res = be_filter_process_input(bevf, state, &processed_any);
46065707d7cSSebastian Hahn (void)res;
461ea4b8724SNick Mathewson
462a62283a9SNick Mathewson /* XXX This should be in process_input, not here. There are
463a62283a9SNick Mathewson * other places that can call process-input, and they should
464a62283a9SNick Mathewson * force readcb calls as needed. */
465b627ad88SEduardo Panisset if (processed_any) {
46661ee18b8SOndřej Kuzník bufferevent_trigger_nolock_(bufev, EV_READ, 0);
467b627ad88SEduardo Panisset if (evbuffer_get_length(underlying->input) > 0 &&
468b627ad88SEduardo Panisset be_readbuf_full(bevf, state)) {
469b627ad88SEduardo Panisset /* data left in underlying buffer and filter input buffer
470b627ad88SEduardo Panisset * hit its read high watermark.
471b627ad88SEduardo Panisset * Schedule callback to avoid data gets stuck in underlying
472b627ad88SEduardo Panisset * input buffer.
473b627ad88SEduardo Panisset */
474b627ad88SEduardo Panisset evbuffer_cb_set_flags(bufev->input, bevf->inbuf_cb,
475b627ad88SEduardo Panisset EVBUFFER_CB_ENABLED);
476b627ad88SEduardo Panisset }
477b627ad88SEduardo Panisset }
478b627ad88SEduardo Panisset }
4792c82aa0fSJohn Ohl }
480a62283a9SNick Mathewson
481b627ad88SEduardo Panisset /* Called when the size of our inbuf changes. */
482b627ad88SEduardo Panisset static void
bufferevent_filtered_inbuf_cb(struct evbuffer * buf,const struct evbuffer_cb_info * cbinfo,void * arg)483b627ad88SEduardo Panisset bufferevent_filtered_inbuf_cb(struct evbuffer *buf,
484b627ad88SEduardo Panisset const struct evbuffer_cb_info *cbinfo, void *arg)
485b627ad88SEduardo Panisset {
486b627ad88SEduardo Panisset struct bufferevent_filtered *bevf = arg;
487b627ad88SEduardo Panisset enum bufferevent_flush_mode state;
488b627ad88SEduardo Panisset struct bufferevent *bev = downcast(bevf);
489b627ad88SEduardo Panisset
490b627ad88SEduardo Panisset BEV_LOCK(bev);
491b627ad88SEduardo Panisset
492b627ad88SEduardo Panisset if (bevf->got_eof)
493b627ad88SEduardo Panisset state = BEV_FINISHED;
494b627ad88SEduardo Panisset else
495b627ad88SEduardo Panisset state = BEV_NORMAL;
496b627ad88SEduardo Panisset
497b627ad88SEduardo Panisset
498b627ad88SEduardo Panisset if (!be_readbuf_full(bevf, state)) {
499b627ad88SEduardo Panisset /* opportunity to read data which was left in underlying
500b627ad88SEduardo Panisset * input buffer because filter input buffer hit read
501b627ad88SEduardo Panisset * high watermark.
502b627ad88SEduardo Panisset */
503b627ad88SEduardo Panisset evbuffer_cb_clear_flags(bev->input, bevf->inbuf_cb,
504b627ad88SEduardo Panisset EVBUFFER_CB_ENABLED);
505b627ad88SEduardo Panisset if (evbuffer_get_length(bevf->underlying->input) > 0)
506b627ad88SEduardo Panisset be_filter_read_nolock_(bevf->underlying, bevf);
507b627ad88SEduardo Panisset }
508b627ad88SEduardo Panisset
509b627ad88SEduardo Panisset BEV_UNLOCK(bev);
510b627ad88SEduardo Panisset }
511b627ad88SEduardo Panisset
512b627ad88SEduardo Panisset /* Called when the underlying socket has read. */
513b627ad88SEduardo Panisset static void
be_filter_readcb(struct bufferevent * underlying,void * me_)514b627ad88SEduardo Panisset be_filter_readcb(struct bufferevent *underlying, void *me_)
515b627ad88SEduardo Panisset {
516b627ad88SEduardo Panisset struct bufferevent_filtered *bevf = me_;
517b627ad88SEduardo Panisset struct bufferevent *bev = downcast(bevf);
518b627ad88SEduardo Panisset
519b627ad88SEduardo Panisset BEV_LOCK(bev);
520b627ad88SEduardo Panisset
521b627ad88SEduardo Panisset be_filter_read_nolock_(underlying, me_);
522b627ad88SEduardo Panisset
523b627ad88SEduardo Panisset BEV_UNLOCK(bev);
524ea4b8724SNick Mathewson }
525ea4b8724SNick Mathewson
526ea4b8724SNick Mathewson /* Called when the underlying socket has drained enough that we can write to
527ea4b8724SNick Mathewson it. */
528ea4b8724SNick Mathewson static void
be_filter_writecb(struct bufferevent * underlying,void * me_)529946b5841SNick Mathewson be_filter_writecb(struct bufferevent *underlying, void *me_)
530ea4b8724SNick Mathewson {
531946b5841SNick Mathewson struct bufferevent_filtered *bevf = me_;
532a62283a9SNick Mathewson struct bufferevent *bev = downcast(bevf);
5332c82aa0fSJohn Ohl struct bufferevent_private *bufev_private = BEV_UPCAST(bev);
534ea4b8724SNick Mathewson int processed_any = 0;
535ea4b8724SNick Mathewson
5362c82aa0fSJohn Ohl BEV_LOCK(bev);
5372c82aa0fSJohn Ohl
5382c82aa0fSJohn Ohl // It's possible our refcount is 0 at this point if another thread free'd our filterevent
5392c82aa0fSJohn Ohl EVUTIL_ASSERT(bufev_private->refcnt >= 0);
5402c82aa0fSJohn Ohl
5412c82aa0fSJohn Ohl // If our refcount is > 0
5422c82aa0fSJohn Ohl if (bufev_private->refcnt > 0) {
543ea4b8724SNick Mathewson be_filter_process_output(bevf, BEV_NORMAL, &processed_any);
5442c82aa0fSJohn Ohl }
5452c82aa0fSJohn Ohl
5462c82aa0fSJohn Ohl BEV_UNLOCK(bev);
547ea4b8724SNick Mathewson }
548ea4b8724SNick Mathewson
549ea4b8724SNick Mathewson /* Called when the underlying socket has given us an error */
550ea4b8724SNick Mathewson static void
be_filter_eventcb(struct bufferevent * underlying,short what,void * me_)551946b5841SNick Mathewson be_filter_eventcb(struct bufferevent *underlying, short what, void *me_)
552ea4b8724SNick Mathewson {
553946b5841SNick Mathewson struct bufferevent_filtered *bevf = me_;
5541becc4c4SNick Mathewson struct bufferevent *bev = downcast(bevf);
5552c82aa0fSJohn Ohl struct bufferevent_private *bufev_private = BEV_UPCAST(bev);
556ea4b8724SNick Mathewson
5572c82aa0fSJohn Ohl BEV_LOCK(bev);
5582c82aa0fSJohn Ohl
5592c82aa0fSJohn Ohl // It's possible our refcount is 0 at this point if another thread free'd our filterevent
5602c82aa0fSJohn Ohl EVUTIL_ASSERT(bufev_private->refcnt >= 0);
5612c82aa0fSJohn Ohl
5622c82aa0fSJohn Ohl // If our refcount is > 0
5632c82aa0fSJohn Ohl if (bufev_private->refcnt > 0) {
5642c82aa0fSJohn Ohl
5655232cfa3SNick Mathewson /* All we can really to is tell our own eventcb. */
566a7384c78SOndřej Kuzník bufferevent_run_eventcb_(bev, what, 0);
5672c82aa0fSJohn Ohl }
5682c82aa0fSJohn Ohl
5692c82aa0fSJohn Ohl BEV_UNLOCK(bev);
570ea4b8724SNick Mathewson }
571ea4b8724SNick Mathewson
572ea4b8724SNick Mathewson static int
be_filter_flush(struct bufferevent * bufev,short iotype,enum bufferevent_flush_mode mode)573ea4b8724SNick Mathewson be_filter_flush(struct bufferevent *bufev,
574ea4b8724SNick Mathewson short iotype, enum bufferevent_flush_mode mode)
575ea4b8724SNick Mathewson {
576ea4b8724SNick Mathewson struct bufferevent_filtered *bevf = upcast(bufev);
577ea4b8724SNick Mathewson int processed_any = 0;
5782e36dbe1SNick Mathewson EVUTIL_ASSERT(bevf);
579ea4b8724SNick Mathewson
580cb9da0bfSNick Mathewson bufferevent_incref_and_lock_(bufev);
581a62283a9SNick Mathewson
582ea4b8724SNick Mathewson if (iotype & EV_READ) {
583ea4b8724SNick Mathewson be_filter_process_input(bevf, mode, &processed_any);
584ea4b8724SNick Mathewson }
585ea4b8724SNick Mathewson if (iotype & EV_WRITE) {
586ea4b8724SNick Mathewson be_filter_process_output(bevf, mode, &processed_any);
587ea4b8724SNick Mathewson }
588ea4b8724SNick Mathewson /* XXX check the return value? */
589ea4b8724SNick Mathewson /* XXX does this want to recursively call lower-level flushes? */
590ea4b8724SNick Mathewson bufferevent_flush(bevf->underlying, iotype, mode);
591ea4b8724SNick Mathewson
592cb9da0bfSNick Mathewson bufferevent_decref_and_unlock_(bufev);
593a62283a9SNick Mathewson
594ea4b8724SNick Mathewson return processed_any;
595ea4b8724SNick Mathewson }
59631d89f27SNick Mathewson
59731d89f27SNick Mathewson static int
be_filter_ctrl(struct bufferevent * bev,enum bufferevent_ctrl_op op,union bufferevent_ctrl_data * data)59831d89f27SNick Mathewson be_filter_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
59931d89f27SNick Mathewson union bufferevent_ctrl_data *data)
60031d89f27SNick Mathewson {
60131d89f27SNick Mathewson struct bufferevent_filtered *bevf;
60231d89f27SNick Mathewson switch (op) {
60331d89f27SNick Mathewson case BEV_CTRL_GET_UNDERLYING:
60431d89f27SNick Mathewson bevf = upcast(bev);
60531d89f27SNick Mathewson data->ptr = bevf->underlying;
60631d89f27SNick Mathewson return 0;
60731d89f27SNick Mathewson case BEV_CTRL_SET_FD:
608ebfac517SGreg Hazel case BEV_CTRL_GET_FD:
609c2aa7dcbSMark Ellzey bevf = upcast(bev);
610c2aa7dcbSMark Ellzey
611c2aa7dcbSMark Ellzey if (bevf->underlying &&
612c2aa7dcbSMark Ellzey bevf->underlying->be_ops &&
613c2aa7dcbSMark Ellzey bevf->underlying->be_ops->ctrl) {
614c2aa7dcbSMark Ellzey return (bevf->underlying->be_ops->ctrl)(bevf->underlying, op, data);
615c2aa7dcbSMark Ellzey }
61640da44bdSAzat Khuzhin EVUTIL_FALLTHROUGH;
617c2aa7dcbSMark Ellzey
618e6af35d7SNick Mathewson case BEV_CTRL_CANCEL_ALL:
61940da44bdSAzat Khuzhin EVUTIL_FALLTHROUGH;
62031d89f27SNick Mathewson default:
62131d89f27SNick Mathewson return -1;
62231d89f27SNick Mathewson }
623c2aa7dcbSMark Ellzey
624c2aa7dcbSMark Ellzey return -1;
62531d89f27SNick Mathewson }
626