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_flush(struct bufferevent *, short, enum bufferevent_flush_mode);
83static int be_socket_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
84
85static void be_socket_setfd(struct bufferevent *, evutil_socket_t);
86
87const struct bufferevent_ops bufferevent_ops_socket = {
88	"socket",
89	evutil_offsetof(struct bufferevent_private, bev),
90	be_socket_enable,
91	be_socket_disable,
92	NULL, /* unlink */
93	be_socket_destruct,
94	bufferevent_generic_adj_existing_timeouts_,
95	be_socket_flush,
96	be_socket_ctrl,
97};
98
99const struct sockaddr*
100bufferevent_socket_get_conn_address_(struct bufferevent *bev)
101{
102	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
103	return (struct sockaddr *)&bev_p->conn_address;
104}
105
106void
107bufferevent_socket_set_conn_address_fd_(struct bufferevent *bev,
108	evutil_socket_t fd)
109{
110	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
111
112	socklen_t len = sizeof(bev_p->conn_address);
113
114	struct sockaddr *addr = (struct sockaddr *)&bev_p->conn_address;
115	if (addr->sa_family != AF_UNSPEC)
116		getpeername(fd, addr, &len);
117}
118
119void
120bufferevent_socket_set_conn_address_(struct bufferevent *bev,
121	struct sockaddr *addr, size_t addrlen)
122{
123	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
124	EVUTIL_ASSERT(addrlen <= sizeof(bev_p->conn_address));
125	memcpy(&bev_p->conn_address, addr, addrlen);
126}
127
128static void
129bufferevent_socket_outbuf_cb(struct evbuffer *buf,
130    const struct evbuffer_cb_info *cbinfo,
131    void *arg)
132{
133	struct bufferevent *bufev = arg;
134	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
135
136	if (cbinfo->n_added &&
137	    (bufev->enabled & EV_WRITE) &&
138	    !event_pending(&bufev->ev_write, EV_WRITE, NULL) &&
139	    !bufev_p->write_suspended) {
140		/* Somebody added data to the buffer, and we would like to
141		 * write, and we were not writing.  So, start writing. */
142		if (bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1) {
143		    /* Should we log this? */
144		}
145	}
146}
147
148static void
149bufferevent_readcb(evutil_socket_t fd, short event, void *arg)
150{
151	struct bufferevent *bufev = arg;
152	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
153	struct evbuffer *input;
154	int res = 0;
155	short what = BEV_EVENT_READING;
156	ev_ssize_t howmuch = -1, readmax=-1;
157
158	bufferevent_incref_and_lock_(bufev);
159
160	if (event == EV_TIMEOUT) {
161		/* Note that we only check for event==EV_TIMEOUT. If
162		 * event==EV_TIMEOUT|EV_READ, we can safely ignore the
163		 * timeout, since a read has occurred */
164		what |= BEV_EVENT_TIMEOUT;
165		goto error;
166	}
167
168	input = bufev->input;
169
170	/*
171	 * If we have a high watermark configured then we don't want to
172	 * read more data than would make us reach the watermark.
173	 */
174	if (bufev->wm_read.high != 0) {
175		howmuch = bufev->wm_read.high - evbuffer_get_length(input);
176		/* we somehow lowered the watermark, stop reading */
177		if (howmuch <= 0) {
178			bufferevent_wm_suspend_read(bufev);
179			goto done;
180		}
181	}
182	readmax = bufferevent_get_read_max_(bufev_p);
183	if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"
184					       * uglifies this code. XXXX */
185		howmuch = readmax;
186	if (bufev_p->read_suspended)
187		goto done;
188
189	evbuffer_unfreeze(input, 0);
190	res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */
191	evbuffer_freeze(input, 0);
192
193	if (res == -1) {
194		int err = evutil_socket_geterror(fd);
195		if (EVUTIL_ERR_RW_RETRIABLE(err))
196			goto reschedule;
197		if (EVUTIL_ERR_CONNECT_REFUSED(err)) {
198			bufev_p->connection_refused = 1;
199			goto done;
200		}
201		/* error case */
202		what |= BEV_EVENT_ERROR;
203	} else if (res == 0) {
204		/* eof case */
205		what |= BEV_EVENT_EOF;
206	}
207
208	if (res <= 0)
209		goto error;
210
211	bufferevent_decrement_read_buckets_(bufev_p, res);
212
213	/* Invoke the user callback - must always be called last */
214	bufferevent_trigger_nolock_(bufev, EV_READ, 0);
215
216	goto done;
217
218 reschedule:
219	goto done;
220
221 error:
222	bufferevent_disable(bufev, EV_READ);
223	bufferevent_run_eventcb_(bufev, what, 0);
224
225 done:
226	bufferevent_decref_and_unlock_(bufev);
227}
228
229static void
230bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
231{
232	struct bufferevent *bufev = arg;
233	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
234	int res = 0;
235	short what = BEV_EVENT_WRITING;
236	int connected = 0;
237	ev_ssize_t atmost = -1;
238
239	bufferevent_incref_and_lock_(bufev);
240
241	if (event == EV_TIMEOUT) {
242		/* Note that we only check for event==EV_TIMEOUT. If
243		 * event==EV_TIMEOUT|EV_WRITE, we can safely ignore the
244		 * timeout, since a read has occurred */
245		what |= BEV_EVENT_TIMEOUT;
246		goto error;
247	}
248	if (bufev_p->connecting) {
249		int c = evutil_socket_finished_connecting_(fd);
250		/* we need to fake the error if the connection was refused
251		 * immediately - usually connection to localhost on BSD */
252		if (bufev_p->connection_refused) {
253			bufev_p->connection_refused = 0;
254			c = -1;
255		}
256
257		if (c == 0)
258			goto done;
259
260		bufev_p->connecting = 0;
261		if (c < 0) {
262			event_del(&bufev->ev_write);
263			event_del(&bufev->ev_read);
264			bufferevent_run_eventcb_(bufev, BEV_EVENT_ERROR, 0);
265			goto done;
266		} else {
267			connected = 1;
268			bufferevent_socket_set_conn_address_fd_(bufev, fd);
269#ifdef _WIN32
270			if (BEV_IS_ASYNC(bufev)) {
271				event_del(&bufev->ev_write);
272				bufferevent_async_set_connected_(bufev);
273				bufferevent_run_eventcb_(bufev,
274						BEV_EVENT_CONNECTED, 0);
275				goto done;
276			}
277#endif
278			bufferevent_run_eventcb_(bufev,
279					BEV_EVENT_CONNECTED, 0);
280			if (!(bufev->enabled & EV_WRITE) ||
281			    bufev_p->write_suspended) {
282				event_del(&bufev->ev_write);
283				goto done;
284			}
285		}
286	}
287
288	atmost = bufferevent_get_write_max_(bufev_p);
289
290	if (bufev_p->write_suspended)
291		goto done;
292
293	if (evbuffer_get_length(bufev->output)) {
294		evbuffer_unfreeze(bufev->output, 1);
295		res = evbuffer_write_atmost(bufev->output, fd, atmost);
296		evbuffer_freeze(bufev->output, 1);
297		if (res == -1) {
298			int err = evutil_socket_geterror(fd);
299			if (EVUTIL_ERR_RW_RETRIABLE(err))
300				goto reschedule;
301			what |= BEV_EVENT_ERROR;
302		} else if (res == 0) {
303			/* eof case
304			   XXXX Actually, a 0 on write doesn't indicate
305			   an EOF. An ECONNRESET might be more typical.
306			 */
307			what |= BEV_EVENT_EOF;
308		}
309		if (res <= 0)
310			goto error;
311
312		bufferevent_decrement_write_buckets_(bufev_p, res);
313	}
314
315	if (evbuffer_get_length(bufev->output) == 0) {
316		event_del(&bufev->ev_write);
317	}
318
319	/*
320	 * Invoke the user callback if our buffer is drained or below the
321	 * low watermark.
322	 */
323	if (res || !connected) {
324		bufferevent_trigger_nolock_(bufev, EV_WRITE, 0);
325	}
326
327	goto done;
328
329 reschedule:
330	if (evbuffer_get_length(bufev->output) == 0) {
331		event_del(&bufev->ev_write);
332	}
333	goto done;
334
335 error:
336	bufferevent_disable(bufev, EV_WRITE);
337	bufferevent_run_eventcb_(bufev, what, 0);
338
339 done:
340	bufferevent_decref_and_unlock_(bufev);
341}
342
343struct bufferevent *
344bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
345    int options)
346{
347	struct bufferevent_private *bufev_p;
348	struct bufferevent *bufev;
349
350#ifdef _WIN32
351	if (base && event_base_get_iocp_(base))
352		return bufferevent_async_new_(base, fd, options);
353#endif
354
355	if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)
356		return NULL;
357
358	if (bufferevent_init_common_(bufev_p, base, &bufferevent_ops_socket,
359				    options) < 0) {
360		mm_free(bufev_p);
361		return NULL;
362	}
363	bufev = &bufev_p->bev;
364	evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);
365
366	event_assign(&bufev->ev_read, bufev->ev_base, fd,
367	    EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
368	event_assign(&bufev->ev_write, bufev->ev_base, fd,
369	    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);
370
371	evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);
372
373	evbuffer_freeze(bufev->input, 0);
374	evbuffer_freeze(bufev->output, 1);
375
376	return bufev;
377}
378
379int
380bufferevent_socket_connect(struct bufferevent *bev,
381    const struct sockaddr *sa, int socklen)
382{
383	struct bufferevent_private *bufev_p = BEV_UPCAST(bev);
384
385	evutil_socket_t fd;
386	int r = 0;
387	int result=-1;
388	int ownfd = 0;
389
390	bufferevent_incref_and_lock_(bev);
391
392	fd = bufferevent_getfd(bev);
393	if (fd < 0) {
394		if (!sa)
395			goto done;
396		fd = evutil_socket_(sa->sa_family,
397		    SOCK_STREAM|EVUTIL_SOCK_NONBLOCK, 0);
398		if (fd < 0)
399			goto freesock;
400		ownfd = 1;
401	}
402	if (sa) {
403#ifdef _WIN32
404		if (bufferevent_async_can_connect_(bev)) {
405			bufferevent_setfd(bev, fd);
406			r = bufferevent_async_connect_(bev, fd, sa, socklen);
407			if (r < 0)
408				goto freesock;
409			bufev_p->connecting = 1;
410			result = 0;
411			goto done;
412		} else
413#endif
414		r = evutil_socket_connect_(&fd, sa, socklen);
415		if (r < 0)
416			goto freesock;
417	}
418#ifdef _WIN32
419	/* ConnectEx() isn't always around, even when IOCP is enabled.
420	 * Here, we borrow the socket object's write handler to fall back
421	 * on a non-blocking connect() when ConnectEx() is unavailable. */
422	if (BEV_IS_ASYNC(bev)) {
423		event_assign(&bev->ev_write, bev->ev_base, fd,
424		    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bev);
425	}
426#endif
427	bufferevent_setfd(bev, fd);
428	if (r == 0) {
429		if (! be_socket_enable(bev, EV_WRITE)) {
430			bufev_p->connecting = 1;
431			result = 0;
432			goto done;
433		}
434	} else if (r == 1) {
435		/* The connect succeeded already. How very BSD of it. */
436		result = 0;
437		bufev_p->connecting = 1;
438		bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS);
439	} else {
440		/* The connect failed already.  How very BSD of it. */
441		result = 0;
442		bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, BEV_OPT_DEFER_CALLBACKS);
443		bufferevent_disable(bev, EV_WRITE|EV_READ);
444	}
445
446	goto done;
447
448freesock:
449	if (ownfd)
450		evutil_closesocket(fd);
451done:
452	bufferevent_decref_and_unlock_(bev);
453	return result;
454}
455
456static void
457bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai,
458    void *arg)
459{
460	struct bufferevent *bev = arg;
461	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
462	int r;
463	BEV_LOCK(bev);
464
465	bufferevent_unsuspend_write_(bev, BEV_SUSPEND_LOOKUP);
466	bufferevent_unsuspend_read_(bev, BEV_SUSPEND_LOOKUP);
467
468	bev_p->dns_request = NULL;
469
470	if (result == EVUTIL_EAI_CANCEL) {
471		bev_p->dns_error = result;
472		bufferevent_decref_and_unlock_(bev);
473		return;
474	}
475	if (result != 0) {
476		bev_p->dns_error = result;
477		bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);
478		bufferevent_decref_and_unlock_(bev);
479		if (ai)
480			evutil_freeaddrinfo(ai);
481		return;
482	}
483
484	/* XXX use the other addrinfos? */
485	bufferevent_socket_set_conn_address_(bev, ai->ai_addr, (int)ai->ai_addrlen);
486	r = bufferevent_socket_connect(bev, ai->ai_addr, (int)ai->ai_addrlen);
487	if (r < 0)
488		bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);
489	bufferevent_decref_and_unlock_(bev);
490	evutil_freeaddrinfo(ai);
491}
492
493int
494bufferevent_socket_connect_hostname(struct bufferevent *bev,
495    struct evdns_base *evdns_base, int family, const char *hostname, int port)
496{
497	char portbuf[10];
498	struct evutil_addrinfo hint;
499	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
500
501	if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
502		return -1;
503	if (port < 1 || port > 65535)
504		return -1;
505
506	memset(&hint, 0, sizeof(hint));
507	hint.ai_family = family;
508	hint.ai_protocol = IPPROTO_TCP;
509	hint.ai_socktype = SOCK_STREAM;
510
511	evutil_snprintf(portbuf, sizeof(portbuf), "%d", port);
512
513	BEV_LOCK(bev);
514	bev_p->dns_error = 0;
515
516	bufferevent_suspend_write_(bev, BEV_SUSPEND_LOOKUP);
517	bufferevent_suspend_read_(bev, BEV_SUSPEND_LOOKUP);
518
519	bufferevent_incref_(bev);
520	bev_p->dns_request = evutil_getaddrinfo_async_(evdns_base, hostname,
521	    portbuf, &hint, bufferevent_connect_getaddrinfo_cb, bev);
522	BEV_UNLOCK(bev);
523
524	return 0;
525}
526
527int
528bufferevent_socket_get_dns_error(struct bufferevent *bev)
529{
530	int rv;
531	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
532
533	BEV_LOCK(bev);
534	rv = bev_p->dns_error;
535	BEV_UNLOCK(bev);
536
537	return rv;
538}
539
540/*
541 * Create a new buffered event object.
542 *
543 * The read callback is invoked whenever we read new data.
544 * The write callback is invoked whenever the output buffer is drained.
545 * The error callback is invoked on a write/read error or on EOF.
546 *
547 * Both read and write callbacks maybe NULL.  The error callback is not
548 * allowed to be NULL and have to be provided always.
549 */
550
551struct bufferevent *
552bufferevent_new(evutil_socket_t fd,
553    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
554    bufferevent_event_cb eventcb, void *cbarg)
555{
556	struct bufferevent *bufev;
557
558	if (!(bufev = bufferevent_socket_new(NULL, fd, 0)))
559		return NULL;
560
561	bufferevent_setcb(bufev, readcb, writecb, eventcb, cbarg);
562
563	return bufev;
564}
565
566
567static int
568be_socket_enable(struct bufferevent *bufev, short event)
569{
570	if (event & EV_READ &&
571	    bufferevent_add_event_(&bufev->ev_read, &bufev->timeout_read) == -1)
572			return -1;
573	if (event & EV_WRITE &&
574	    bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1)
575			return -1;
576	return 0;
577}
578
579static int
580be_socket_disable(struct bufferevent *bufev, short event)
581{
582	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
583	if (event & EV_READ) {
584		if (event_del(&bufev->ev_read) == -1)
585			return -1;
586	}
587	/* Don't actually disable the write if we are trying to connect. */
588	if ((event & EV_WRITE) && ! bufev_p->connecting) {
589		if (event_del(&bufev->ev_write) == -1)
590			return -1;
591	}
592	return 0;
593}
594
595static void
596be_socket_destruct(struct bufferevent *bufev)
597{
598	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
599	evutil_socket_t fd;
600	EVUTIL_ASSERT(BEV_IS_SOCKET(bufev));
601
602	fd = event_get_fd(&bufev->ev_read);
603
604	if ((bufev_p->options & BEV_OPT_CLOSE_ON_FREE) && fd >= 0)
605		EVUTIL_CLOSESOCKET(fd);
606
607	evutil_getaddrinfo_cancel_async_(bufev_p->dns_request);
608}
609
610static int
611be_socket_flush(struct bufferevent *bev, short iotype,
612    enum bufferevent_flush_mode mode)
613{
614	return 0;
615}
616
617
618static void
619be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd)
620{
621	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
622
623	BEV_LOCK(bufev);
624	EVUTIL_ASSERT(BEV_IS_SOCKET(bufev));
625
626	event_del(&bufev->ev_read);
627	event_del(&bufev->ev_write);
628
629	evbuffer_unfreeze(bufev->input, 0);
630	evbuffer_unfreeze(bufev->output, 1);
631
632	event_assign(&bufev->ev_read, bufev->ev_base, fd,
633	    EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
634	event_assign(&bufev->ev_write, bufev->ev_base, fd,
635	    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);
636
637	if (fd >= 0)
638		bufferevent_enable(bufev, bufev->enabled);
639
640	evutil_getaddrinfo_cancel_async_(bufev_p->dns_request);
641
642	BEV_UNLOCK(bufev);
643}
644
645/* XXXX Should non-socket bufferevents support this? */
646int
647bufferevent_priority_set(struct bufferevent *bufev, int priority)
648{
649	int r = -1;
650	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
651
652	BEV_LOCK(bufev);
653	if (BEV_IS_ASYNC(bufev) || BEV_IS_FILTER(bufev) || BEV_IS_PAIR(bufev))
654		goto done;
655
656	if (event_priority_set(&bufev->ev_read, priority) == -1)
657		goto done;
658	if (event_priority_set(&bufev->ev_write, priority) == -1)
659		goto done;
660
661	event_deferred_cb_set_priority_(&bufev_p->deferred, priority);
662
663	r = 0;
664done:
665	BEV_UNLOCK(bufev);
666	return r;
667}
668
669/* XXXX Should non-socket bufferevents support this? */
670int
671bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
672{
673	int res = -1;
674
675	BEV_LOCK(bufev);
676	if (!BEV_IS_SOCKET(bufev))
677		goto done;
678
679	bufev->ev_base = base;
680
681	res = event_base_set(base, &bufev->ev_read);
682	if (res == -1)
683		goto done;
684
685	res = event_base_set(base, &bufev->ev_write);
686done:
687	BEV_UNLOCK(bufev);
688	return res;
689}
690
691static int
692be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
693    union bufferevent_ctrl_data *data)
694{
695	switch (op) {
696	case BEV_CTRL_SET_FD:
697		be_socket_setfd(bev, data->fd);
698		return 0;
699	case BEV_CTRL_GET_FD:
700		data->fd = event_get_fd(&bev->ev_read);
701		return 0;
702	case BEV_CTRL_GET_UNDERLYING:
703	case BEV_CTRL_CANCEL_ALL:
704	default:
705		return -1;
706	}
707}
708