1/*
2 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
3 * Copyright (c) 2002-2006 Niels Provos <provos@citi.umich.edu>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "event2/event-config.h"
30#include "evconfig-private.h"
31
32#include <sys/types.h>
33
34#ifdef EVENT__HAVE_SYS_TIME_H
35#include <sys/time.h>
36#endif
37
38#include <errno.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#ifdef EVENT__HAVE_STDARG_H
43#include <stdarg.h>
44#endif
45#ifdef EVENT__HAVE_UNISTD_H
46#include <unistd.h>
47#endif
48
49#ifdef _WIN32
50#include <winsock2.h>
51#include <ws2tcpip.h>
52#endif
53
54#ifdef EVENT__HAVE_SYS_SOCKET_H
55#include <sys/socket.h>
56#endif
57#ifdef EVENT__HAVE_NETINET_IN_H
58#include <netinet/in.h>
59#endif
60#ifdef EVENT__HAVE_NETINET_IN6_H
61#include <netinet/in6.h>
62#endif
63
64#include "event2/util.h"
65#include "event2/bufferevent.h"
66#include "event2/buffer.h"
67#include "event2/bufferevent_struct.h"
68#include "event2/bufferevent_compat.h"
69#include "event2/event.h"
70#include "log-internal.h"
71#include "mm-internal.h"
72#include "bufferevent-internal.h"
73#include "util-internal.h"
74#ifdef _WIN32
75#include "iocp-internal.h"
76#endif
77
78/* prototypes */
79static int be_socket_enable(struct bufferevent *, short);
80static int be_socket_disable(struct bufferevent *, short);
81static void be_socket_destruct(struct bufferevent *);
82static int be_socket_adj_timeouts(struct bufferevent *);
83static int be_socket_flush(struct bufferevent *, short, enum bufferevent_flush_mode);
84static int be_socket_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
85
86static void be_socket_setfd(struct bufferevent *, evutil_socket_t);
87
88const struct bufferevent_ops bufferevent_ops_socket = {
89	"socket",
90	evutil_offsetof(struct bufferevent_private, bev),
91	be_socket_enable,
92	be_socket_disable,
93	NULL, /* unlink */
94	be_socket_destruct,
95	be_socket_adj_timeouts,
96	be_socket_flush,
97	be_socket_ctrl,
98};
99
100#define be_socket_add(ev, t)			\
101	bufferevent_add_event_((ev), (t))
102
103static void
104bufferevent_socket_outbuf_cb(struct evbuffer *buf,
105    const struct evbuffer_cb_info *cbinfo,
106    void *arg)
107{
108	struct bufferevent *bufev = arg;
109	struct bufferevent_private *bufev_p =
110	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
111
112	if (cbinfo->n_added &&
113	    (bufev->enabled & EV_WRITE) &&
114	    !event_pending(&bufev->ev_write, EV_WRITE, NULL) &&
115	    !bufev_p->write_suspended) {
116		/* Somebody added data to the buffer, and we would like to
117		 * write, and we were not writing.  So, start writing. */
118		if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) == -1) {
119		    /* Should we log this? */
120		}
121	}
122}
123
124static void
125bufferevent_readcb(evutil_socket_t fd, short event, void *arg)
126{
127	struct bufferevent *bufev = arg;
128	struct bufferevent_private *bufev_p =
129	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
130	struct evbuffer *input;
131	int res = 0;
132	short what = BEV_EVENT_READING;
133	ev_ssize_t howmuch = -1, readmax=-1;
134
135	bufferevent_incref_and_lock_(bufev);
136
137	if (event == EV_TIMEOUT) {
138		/* Note that we only check for event==EV_TIMEOUT. If
139		 * event==EV_TIMEOUT|EV_READ, we can safely ignore the
140		 * timeout, since a read has occurred */
141		what |= BEV_EVENT_TIMEOUT;
142		goto error;
143	}
144
145	input = bufev->input;
146
147	/*
148	 * If we have a high watermark configured then we don't want to
149	 * read more data than would make us reach the watermark.
150	 */
151	if (bufev->wm_read.high != 0) {
152		howmuch = bufev->wm_read.high - evbuffer_get_length(input);
153		/* we somehow lowered the watermark, stop reading */
154		if (howmuch <= 0) {
155			bufferevent_wm_suspend_read(bufev);
156			goto done;
157		}
158	}
159	readmax = bufferevent_get_read_max_(bufev_p);
160	if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"
161					       * uglifies this code. XXXX */
162		howmuch = readmax;
163	if (bufev_p->read_suspended)
164		goto done;
165
166	evbuffer_unfreeze(input, 0);
167	res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */
168	evbuffer_freeze(input, 0);
169
170	if (res == -1) {
171		int err = evutil_socket_geterror(fd);
172		if (EVUTIL_ERR_RW_RETRIABLE(err))
173			goto reschedule;
174		/* error case */
175		what |= BEV_EVENT_ERROR;
176	} else if (res == 0) {
177		/* eof case */
178		what |= BEV_EVENT_EOF;
179	}
180
181	if (res <= 0)
182		goto error;
183
184	bufferevent_decrement_read_buckets_(bufev_p, res);
185
186	/* Invoke the user callback - must always be called last */
187	bufferevent_trigger_nolock_(bufev, EV_READ, 0);
188
189	goto done;
190
191 reschedule:
192	goto done;
193
194 error:
195	bufferevent_disable(bufev, EV_READ);
196	bufferevent_run_eventcb_(bufev, what, 0);
197
198 done:
199	bufferevent_decref_and_unlock_(bufev);
200}
201
202static void
203bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
204{
205	struct bufferevent *bufev = arg;
206	struct bufferevent_private *bufev_p =
207	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
208	int res = 0;
209	short what = BEV_EVENT_WRITING;
210	int connected = 0;
211	ev_ssize_t atmost = -1;
212
213	bufferevent_incref_and_lock_(bufev);
214
215	if (event == EV_TIMEOUT) {
216		/* Note that we only check for event==EV_TIMEOUT. If
217		 * event==EV_TIMEOUT|EV_WRITE, we can safely ignore the
218		 * timeout, since a read has occurred */
219		what |= BEV_EVENT_TIMEOUT;
220		goto error;
221	}
222	if (bufev_p->connecting) {
223		int c = evutil_socket_finished_connecting_(fd);
224		/* we need to fake the error if the connection was refused
225		 * immediately - usually connection to localhost on BSD */
226		if (bufev_p->connection_refused) {
227		  bufev_p->connection_refused = 0;
228		  c = -1;
229		}
230
231		if (c == 0)
232			goto done;
233
234		bufev_p->connecting = 0;
235		if (c < 0) {
236			event_del(&bufev->ev_write);
237			event_del(&bufev->ev_read);
238			bufferevent_run_eventcb_(bufev, BEV_EVENT_ERROR, 0);
239			goto done;
240		} else {
241			connected = 1;
242#ifdef _WIN32
243			if (BEV_IS_ASYNC(bufev)) {
244				event_del(&bufev->ev_write);
245				bufferevent_async_set_connected_(bufev);
246				bufferevent_run_eventcb_(bufev,
247						BEV_EVENT_CONNECTED, 0);
248				goto done;
249			}
250#endif
251			bufferevent_run_eventcb_(bufev,
252					BEV_EVENT_CONNECTED, 0);
253			if (!(bufev->enabled & EV_WRITE) ||
254			    bufev_p->write_suspended) {
255				event_del(&bufev->ev_write);
256				goto done;
257			}
258		}
259	}
260
261	atmost = bufferevent_get_write_max_(bufev_p);
262
263	if (bufev_p->write_suspended)
264		goto done;
265
266	if (evbuffer_get_length(bufev->output)) {
267		evbuffer_unfreeze(bufev->output, 1);
268		res = evbuffer_write_atmost(bufev->output, fd, atmost);
269		evbuffer_freeze(bufev->output, 1);
270		if (res == -1) {
271			int err = evutil_socket_geterror(fd);
272			if (EVUTIL_ERR_RW_RETRIABLE(err))
273				goto reschedule;
274			what |= BEV_EVENT_ERROR;
275		} else if (res == 0) {
276			/* eof case
277			   XXXX Actually, a 0 on write doesn't indicate
278			   an EOF. An ECONNRESET might be more typical.
279			 */
280			what |= BEV_EVENT_EOF;
281		}
282		if (res <= 0)
283			goto error;
284
285		bufferevent_decrement_write_buckets_(bufev_p, res);
286	}
287
288	if (evbuffer_get_length(bufev->output) == 0) {
289		event_del(&bufev->ev_write);
290	}
291
292	/*
293	 * Invoke the user callback if our buffer is drained or below the
294	 * low watermark.
295	 */
296	if (res || !connected) {
297		bufferevent_trigger_nolock_(bufev, EV_WRITE, 0);
298	}
299
300	goto done;
301
302 reschedule:
303	if (evbuffer_get_length(bufev->output) == 0) {
304		event_del(&bufev->ev_write);
305	}
306	goto done;
307
308 error:
309	bufferevent_disable(bufev, EV_WRITE);
310	bufferevent_run_eventcb_(bufev, what, 0);
311
312 done:
313	bufferevent_decref_and_unlock_(bufev);
314}
315
316struct bufferevent *
317bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
318    int options)
319{
320	struct bufferevent_private *bufev_p;
321	struct bufferevent *bufev;
322
323#ifdef _WIN32
324	if (base && event_base_get_iocp_(base))
325		return bufferevent_async_new_(base, fd, options);
326#endif
327
328	if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)
329		return NULL;
330
331	if (bufferevent_init_common_(bufev_p, base, &bufferevent_ops_socket,
332				    options) < 0) {
333		mm_free(bufev_p);
334		return NULL;
335	}
336	bufev = &bufev_p->bev;
337	evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);
338
339	event_assign(&bufev->ev_read, bufev->ev_base, fd,
340	    EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
341	event_assign(&bufev->ev_write, bufev->ev_base, fd,
342	    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);
343
344	evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);
345
346	evbuffer_freeze(bufev->input, 0);
347	evbuffer_freeze(bufev->output, 1);
348
349	return bufev;
350}
351
352int
353bufferevent_socket_connect(struct bufferevent *bev,
354    struct sockaddr *sa, int socklen)
355{
356	struct bufferevent_private *bufev_p =
357	    EVUTIL_UPCAST(bev, struct bufferevent_private, bev);
358
359	evutil_socket_t fd;
360	int r = 0;
361	int result=-1;
362	int ownfd = 0;
363
364	bufferevent_incref_and_lock_(bev);
365
366	if (!bufev_p)
367		goto done;
368
369	fd = bufferevent_getfd(bev);
370	if (fd < 0) {
371		if (!sa)
372			goto done;
373		fd = evutil_socket_(sa->sa_family,
374		    SOCK_STREAM|EVUTIL_SOCK_NONBLOCK, 0);
375		if (fd < 0)
376			goto done;
377		ownfd = 1;
378	}
379	if (sa) {
380#ifdef _WIN32
381		if (bufferevent_async_can_connect_(bev)) {
382			bufferevent_setfd(bev, fd);
383			r = bufferevent_async_connect_(bev, fd, sa, socklen);
384			if (r < 0)
385				goto freesock;
386			bufev_p->connecting = 1;
387			result = 0;
388			goto done;
389		} else
390#endif
391		r = evutil_socket_connect_(&fd, sa, socklen);
392		if (r < 0)
393			goto freesock;
394	}
395#ifdef _WIN32
396	/* ConnectEx() isn't always around, even when IOCP is enabled.
397	 * Here, we borrow the socket object's write handler to fall back
398	 * on a non-blocking connect() when ConnectEx() is unavailable. */
399	if (BEV_IS_ASYNC(bev)) {
400		event_assign(&bev->ev_write, bev->ev_base, fd,
401		    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bev);
402	}
403#endif
404	bufferevent_setfd(bev, fd);
405	if (r == 0) {
406		if (! be_socket_enable(bev, EV_WRITE)) {
407			bufev_p->connecting = 1;
408			result = 0;
409			goto done;
410		}
411	} else if (r == 1) {
412		/* The connect succeeded already. How very BSD of it. */
413		result = 0;
414		bufev_p->connecting = 1;
415		event_active(&bev->ev_write, EV_WRITE, 1);
416	} else {
417		/* The connect failed already.  How very BSD of it. */
418		bufev_p->connection_refused = 1;
419		bufev_p->connecting = 1;
420		result = 0;
421		event_active(&bev->ev_write, EV_WRITE, 1);
422	}
423
424	goto done;
425
426freesock:
427	bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);
428	if (ownfd)
429		evutil_closesocket(fd);
430	/* do something about the error? */
431done:
432	bufferevent_decref_and_unlock_(bev);
433	return result;
434}
435
436static void
437bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai,
438    void *arg)
439{
440	struct bufferevent *bev = arg;
441	struct bufferevent_private *bev_p =
442	    EVUTIL_UPCAST(bev, struct bufferevent_private, bev);
443	int r;
444	BEV_LOCK(bev);
445
446	bufferevent_unsuspend_write_(bev, BEV_SUSPEND_LOOKUP);
447	bufferevent_unsuspend_read_(bev, BEV_SUSPEND_LOOKUP);
448
449	if (result != 0) {
450		bev_p->dns_error = result;
451		bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);
452		bufferevent_decref_and_unlock_(bev);
453		if (ai)
454			evutil_freeaddrinfo(ai);
455		return;
456	}
457
458	/* XXX use the other addrinfos? */
459	/* XXX use this return value */
460	r = bufferevent_socket_connect(bev, ai->ai_addr, (int)ai->ai_addrlen);
461	(void)r;
462	bufferevent_decref_and_unlock_(bev);
463	evutil_freeaddrinfo(ai);
464}
465
466int
467bufferevent_socket_connect_hostname(struct bufferevent *bev,
468    struct evdns_base *evdns_base, int family, const char *hostname, int port)
469{
470	char portbuf[10];
471	struct evutil_addrinfo hint;
472	int err;
473	struct bufferevent_private *bev_p =
474	    EVUTIL_UPCAST(bev, struct bufferevent_private, bev);
475
476	if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
477		return -1;
478	if (port < 1 || port > 65535)
479		return -1;
480
481	BEV_LOCK(bev);
482	bev_p->dns_error = 0;
483	BEV_UNLOCK(bev);
484
485	evutil_snprintf(portbuf, sizeof(portbuf), "%d", port);
486
487	memset(&hint, 0, sizeof(hint));
488	hint.ai_family = family;
489	hint.ai_protocol = IPPROTO_TCP;
490	hint.ai_socktype = SOCK_STREAM;
491
492	bufferevent_suspend_write_(bev, BEV_SUSPEND_LOOKUP);
493	bufferevent_suspend_read_(bev, BEV_SUSPEND_LOOKUP);
494
495	bufferevent_incref_(bev);
496	err = evutil_getaddrinfo_async_(evdns_base, hostname, portbuf,
497	    &hint, bufferevent_connect_getaddrinfo_cb, bev);
498
499	if (err == 0) {
500		return 0;
501	} else {
502		bufferevent_unsuspend_write_(bev, BEV_SUSPEND_LOOKUP);
503		bufferevent_unsuspend_read_(bev, BEV_SUSPEND_LOOKUP);
504		bufferevent_decref_(bev);
505		return -1;
506	}
507}
508
509int
510bufferevent_socket_get_dns_error(struct bufferevent *bev)
511{
512	int rv;
513	struct bufferevent_private *bev_p =
514	    EVUTIL_UPCAST(bev, struct bufferevent_private, bev);
515
516	BEV_LOCK(bev);
517	rv = bev_p->dns_error;
518	BEV_UNLOCK(bev);
519
520	return rv;
521}
522
523/*
524 * Create a new buffered event object.
525 *
526 * The read callback is invoked whenever we read new data.
527 * The write callback is invoked whenever the output buffer is drained.
528 * The error callback is invoked on a write/read error or on EOF.
529 *
530 * Both read and write callbacks maybe NULL.  The error callback is not
531 * allowed to be NULL and have to be provided always.
532 */
533
534struct bufferevent *
535bufferevent_new(evutil_socket_t fd,
536    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
537    bufferevent_event_cb eventcb, void *cbarg)
538{
539	struct bufferevent *bufev;
540
541	if (!(bufev = bufferevent_socket_new(NULL, fd, 0)))
542		return NULL;
543
544	bufferevent_setcb(bufev, readcb, writecb, eventcb, cbarg);
545
546	return bufev;
547}
548
549
550static int
551be_socket_enable(struct bufferevent *bufev, short event)
552{
553	if (event & EV_READ) {
554		if (be_socket_add(&bufev->ev_read,&bufev->timeout_read) == -1)
555			return -1;
556	}
557	if (event & EV_WRITE) {
558		if (be_socket_add(&bufev->ev_write,&bufev->timeout_write) == -1)
559			return -1;
560	}
561	return 0;
562}
563
564static int
565be_socket_disable(struct bufferevent *bufev, short event)
566{
567	struct bufferevent_private *bufev_p =
568	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
569	if (event & EV_READ) {
570		if (event_del(&bufev->ev_read) == -1)
571			return -1;
572	}
573	/* Don't actually disable the write if we are trying to connect. */
574	if ((event & EV_WRITE) && ! bufev_p->connecting) {
575		if (event_del(&bufev->ev_write) == -1)
576			return -1;
577	}
578	return 0;
579}
580
581static void
582be_socket_destruct(struct bufferevent *bufev)
583{
584	struct bufferevent_private *bufev_p =
585	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
586	evutil_socket_t fd;
587	EVUTIL_ASSERT(bufev->be_ops == &bufferevent_ops_socket);
588
589	fd = event_get_fd(&bufev->ev_read);
590
591	if ((bufev_p->options & BEV_OPT_CLOSE_ON_FREE) && fd >= 0)
592		EVUTIL_CLOSESOCKET(fd);
593}
594
595static int
596be_socket_adj_timeouts(struct bufferevent *bufev)
597{
598	int r = 0;
599	if (event_pending(&bufev->ev_read, EV_READ, NULL)) {
600		if (evutil_timerisset(&bufev->timeout_read)) {
601			    if (be_socket_add(&bufev->ev_read, &bufev->timeout_read) < 0)
602				    r = -1;
603		} else {
604			event_remove_timer(&bufev->ev_read);
605		}
606	}
607	if (event_pending(&bufev->ev_write, EV_WRITE, NULL)) {
608		if (evutil_timerisset(&bufev->timeout_write)) {
609			if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) < 0)
610				r = -1;
611		} else {
612			event_remove_timer(&bufev->ev_write);
613		}
614	}
615	return r;
616}
617
618static int
619be_socket_flush(struct bufferevent *bev, short iotype,
620    enum bufferevent_flush_mode mode)
621{
622	return 0;
623}
624
625
626static void
627be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd)
628{
629	BEV_LOCK(bufev);
630	EVUTIL_ASSERT(bufev->be_ops == &bufferevent_ops_socket);
631
632	event_del(&bufev->ev_read);
633	event_del(&bufev->ev_write);
634
635	event_assign(&bufev->ev_read, bufev->ev_base, fd,
636	    EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
637	event_assign(&bufev->ev_write, bufev->ev_base, fd,
638	    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);
639
640	if (fd >= 0)
641		bufferevent_enable(bufev, bufev->enabled);
642
643	BEV_UNLOCK(bufev);
644}
645
646/* XXXX Should non-socket bufferevents support this? */
647int
648bufferevent_priority_set(struct bufferevent *bufev, int priority)
649{
650	int r = -1;
651	struct bufferevent_private *bufev_p =
652	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
653
654	BEV_LOCK(bufev);
655	if (bufev->be_ops != &bufferevent_ops_socket)
656		goto done;
657
658	if (event_priority_set(&bufev->ev_read, priority) == -1)
659		goto done;
660	if (event_priority_set(&bufev->ev_write, priority) == -1)
661		goto done;
662
663	event_deferred_cb_set_priority_(&bufev_p->deferred, priority);
664
665	r = 0;
666done:
667	BEV_UNLOCK(bufev);
668	return r;
669}
670
671/* XXXX Should non-socket bufferevents support this? */
672int
673bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
674{
675	int res = -1;
676
677	BEV_LOCK(bufev);
678	if (bufev->be_ops != &bufferevent_ops_socket)
679		goto done;
680
681	bufev->ev_base = base;
682
683	res = event_base_set(base, &bufev->ev_read);
684	if (res == -1)
685		goto done;
686
687	res = event_base_set(base, &bufev->ev_write);
688done:
689	BEV_UNLOCK(bufev);
690	return res;
691}
692
693static int
694be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
695    union bufferevent_ctrl_data *data)
696{
697	switch (op) {
698	case BEV_CTRL_SET_FD:
699		be_socket_setfd(bev, data->fd);
700		return 0;
701	case BEV_CTRL_GET_FD:
702		data->fd = event_get_fd(&bev->ev_read);
703		return 0;
704	case BEV_CTRL_GET_UNDERLYING:
705	case BEV_CTRL_CANCEL_ALL:
706	default:
707		return -1;
708	}
709}
710