xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/bufferevent_sock.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: bufferevent_sock.c,v 1.6 2024/08/18 20:47:21 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*
48585484eSchristos  * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
58585484eSchristos  * Copyright (c) 2002-2006 Niels Provos <provos@citi.umich.edu>
68585484eSchristos  * All rights reserved.
78585484eSchristos  *
88585484eSchristos  * Redistribution and use in source and binary forms, with or without
98585484eSchristos  * modification, are permitted provided that the following conditions
108585484eSchristos  * are met:
118585484eSchristos  * 1. Redistributions of source code must retain the above copyright
128585484eSchristos  *    notice, this list of conditions and the following disclaimer.
138585484eSchristos  * 2. Redistributions in binary form must reproduce the above copyright
148585484eSchristos  *    notice, this list of conditions and the following disclaimer in the
158585484eSchristos  *    documentation and/or other materials provided with the distribution.
168585484eSchristos  * 3. The name of the author may not be used to endorse or promote products
178585484eSchristos  *    derived from this software without specific prior written permission.
188585484eSchristos  *
198585484eSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
208585484eSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
218585484eSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
228585484eSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
238585484eSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
248585484eSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
258585484eSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
268585484eSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
278585484eSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
288585484eSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
298585484eSchristos  */
308585484eSchristos 
318585484eSchristos #include "event2/event-config.h"
328585484eSchristos #include "evconfig-private.h"
338585484eSchristos 
348585484eSchristos #include <sys/types.h>
358585484eSchristos 
368585484eSchristos #ifdef EVENT__HAVE_SYS_TIME_H
378585484eSchristos #include <sys/time.h>
388585484eSchristos #endif
398585484eSchristos 
408585484eSchristos #include <errno.h>
418585484eSchristos #include <stdio.h>
428585484eSchristos #include <stdlib.h>
438585484eSchristos #include <string.h>
448585484eSchristos #ifdef EVENT__HAVE_STDARG_H
458585484eSchristos #include <stdarg.h>
468585484eSchristos #endif
478585484eSchristos #ifdef EVENT__HAVE_UNISTD_H
488585484eSchristos #include <unistd.h>
498585484eSchristos #endif
508585484eSchristos 
518585484eSchristos #ifdef _WIN32
528585484eSchristos #include <winsock2.h>
538585484eSchristos #include <ws2tcpip.h>
548585484eSchristos #endif
558585484eSchristos 
568585484eSchristos #ifdef EVENT__HAVE_SYS_SOCKET_H
578585484eSchristos #include <sys/socket.h>
588585484eSchristos #endif
598585484eSchristos #ifdef EVENT__HAVE_NETINET_IN_H
608585484eSchristos #include <netinet/in.h>
618585484eSchristos #endif
628585484eSchristos #ifdef EVENT__HAVE_NETINET_IN6_H
638585484eSchristos #include <netinet/in6.h>
648585484eSchristos #endif
658585484eSchristos 
668585484eSchristos #include "event2/util.h"
678585484eSchristos #include "event2/bufferevent.h"
688585484eSchristos #include "event2/buffer.h"
698585484eSchristos #include "event2/bufferevent_struct.h"
708585484eSchristos #include "event2/bufferevent_compat.h"
718585484eSchristos #include "event2/event.h"
728585484eSchristos #include "log-internal.h"
738585484eSchristos #include "mm-internal.h"
748585484eSchristos #include "bufferevent-internal.h"
758585484eSchristos #include "util-internal.h"
768585484eSchristos #ifdef _WIN32
778585484eSchristos #include "iocp-internal.h"
788585484eSchristos #endif
798585484eSchristos 
808585484eSchristos /* prototypes */
818585484eSchristos static int be_socket_enable(struct bufferevent *, short);
828585484eSchristos static int be_socket_disable(struct bufferevent *, short);
838585484eSchristos static void be_socket_destruct(struct bufferevent *);
848585484eSchristos static int be_socket_flush(struct bufferevent *, short, enum bufferevent_flush_mode);
858585484eSchristos static int be_socket_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
868585484eSchristos 
878585484eSchristos static void be_socket_setfd(struct bufferevent *, evutil_socket_t);
888585484eSchristos 
898585484eSchristos const struct bufferevent_ops bufferevent_ops_socket = {
908585484eSchristos 	"socket",
918585484eSchristos 	evutil_offsetof(struct bufferevent_private, bev),
928585484eSchristos 	be_socket_enable,
938585484eSchristos 	be_socket_disable,
94b8ecfcfeSchristos 	NULL, /* unlink */
958585484eSchristos 	be_socket_destruct,
96*eabc0478Schristos 	bufferevent_generic_adj_existing_timeouts_,
978585484eSchristos 	be_socket_flush,
988585484eSchristos 	be_socket_ctrl,
998585484eSchristos };
1008585484eSchristos 
101*eabc0478Schristos const struct sockaddr*
102*eabc0478Schristos bufferevent_socket_get_conn_address_(struct bufferevent *bev)
103*eabc0478Schristos {
104*eabc0478Schristos 	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
105*eabc0478Schristos 	return (struct sockaddr *)&bev_p->conn_address;
106*eabc0478Schristos }
107*eabc0478Schristos 
108*eabc0478Schristos void
109*eabc0478Schristos bufferevent_socket_set_conn_address_fd_(struct bufferevent *bev,
110*eabc0478Schristos 	evutil_socket_t fd)
111*eabc0478Schristos {
112*eabc0478Schristos 	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
113*eabc0478Schristos 
114*eabc0478Schristos 	socklen_t len = sizeof(bev_p->conn_address);
115*eabc0478Schristos 
116*eabc0478Schristos 	struct sockaddr *addr = (struct sockaddr *)&bev_p->conn_address;
117*eabc0478Schristos 	if (addr->sa_family != AF_UNSPEC)
118*eabc0478Schristos 		getpeername(fd, addr, &len);
119*eabc0478Schristos }
120*eabc0478Schristos 
121*eabc0478Schristos void
122*eabc0478Schristos bufferevent_socket_set_conn_address_(struct bufferevent *bev,
123*eabc0478Schristos 	struct sockaddr *addr, size_t addrlen)
124*eabc0478Schristos {
125*eabc0478Schristos 	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
126*eabc0478Schristos 	EVUTIL_ASSERT(addrlen <= sizeof(bev_p->conn_address));
127*eabc0478Schristos 	memcpy(&bev_p->conn_address, addr, addrlen);
128*eabc0478Schristos }
1298585484eSchristos 
1308585484eSchristos static void
1318585484eSchristos bufferevent_socket_outbuf_cb(struct evbuffer *buf,
1328585484eSchristos     const struct evbuffer_cb_info *cbinfo,
1338585484eSchristos     void *arg)
1348585484eSchristos {
1358585484eSchristos 	struct bufferevent *bufev = arg;
136*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
1378585484eSchristos 
1388585484eSchristos 	if (cbinfo->n_added &&
1398585484eSchristos 	    (bufev->enabled & EV_WRITE) &&
1408585484eSchristos 	    !event_pending(&bufev->ev_write, EV_WRITE, NULL) &&
1418585484eSchristos 	    !bufev_p->write_suspended) {
1428585484eSchristos 		/* Somebody added data to the buffer, and we would like to
1438585484eSchristos 		 * write, and we were not writing.  So, start writing. */
144*eabc0478Schristos 		if (bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1) {
1458585484eSchristos 		    /* Should we log this? */
1468585484eSchristos 		}
1478585484eSchristos 	}
1488585484eSchristos }
1498585484eSchristos 
1508585484eSchristos static void
1518585484eSchristos bufferevent_readcb(evutil_socket_t fd, short event, void *arg)
1528585484eSchristos {
1538585484eSchristos 	struct bufferevent *bufev = arg;
154*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
1558585484eSchristos 	struct evbuffer *input;
1568585484eSchristos 	int res = 0;
1578585484eSchristos 	short what = BEV_EVENT_READING;
1588585484eSchristos 	ev_ssize_t howmuch = -1, readmax=-1;
1598585484eSchristos 
1608585484eSchristos 	bufferevent_incref_and_lock_(bufev);
1618585484eSchristos 
1628585484eSchristos 	if (event == EV_TIMEOUT) {
1638585484eSchristos 		/* Note that we only check for event==EV_TIMEOUT. If
1648585484eSchristos 		 * event==EV_TIMEOUT|EV_READ, we can safely ignore the
1658585484eSchristos 		 * timeout, since a read has occurred */
1668585484eSchristos 		what |= BEV_EVENT_TIMEOUT;
1678585484eSchristos 		goto error;
1688585484eSchristos 	}
1698585484eSchristos 
1708585484eSchristos 	input = bufev->input;
1718585484eSchristos 
1728585484eSchristos 	/*
1738585484eSchristos 	 * If we have a high watermark configured then we don't want to
1748585484eSchristos 	 * read more data than would make us reach the watermark.
1758585484eSchristos 	 */
1768585484eSchristos 	if (bufev->wm_read.high != 0) {
1778585484eSchristos 		howmuch = bufev->wm_read.high - evbuffer_get_length(input);
1788585484eSchristos 		/* we somehow lowered the watermark, stop reading */
1798585484eSchristos 		if (howmuch <= 0) {
1808585484eSchristos 			bufferevent_wm_suspend_read(bufev);
1818585484eSchristos 			goto done;
1828585484eSchristos 		}
1838585484eSchristos 	}
1848585484eSchristos 	readmax = bufferevent_get_read_max_(bufev_p);
1858585484eSchristos 	if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"
1868585484eSchristos 					       * uglifies this code. XXXX */
1878585484eSchristos 		howmuch = readmax;
1888585484eSchristos 	if (bufev_p->read_suspended)
1898585484eSchristos 		goto done;
1908585484eSchristos 
1918585484eSchristos 	evbuffer_unfreeze(input, 0);
1928585484eSchristos 	res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */
1938585484eSchristos 	evbuffer_freeze(input, 0);
1948585484eSchristos 
1958585484eSchristos 	if (res == -1) {
1968585484eSchristos 		int err = evutil_socket_geterror(fd);
1978585484eSchristos 		if (EVUTIL_ERR_RW_RETRIABLE(err))
1988585484eSchristos 			goto reschedule;
199*eabc0478Schristos 		if (EVUTIL_ERR_CONNECT_REFUSED(err)) {
200*eabc0478Schristos 			bufev_p->connection_refused = 1;
201*eabc0478Schristos 			goto done;
202*eabc0478Schristos 		}
2038585484eSchristos 		/* error case */
2048585484eSchristos 		what |= BEV_EVENT_ERROR;
2058585484eSchristos 	} else if (res == 0) {
2068585484eSchristos 		/* eof case */
2078585484eSchristos 		what |= BEV_EVENT_EOF;
2088585484eSchristos 	}
2098585484eSchristos 
2108585484eSchristos 	if (res <= 0)
2118585484eSchristos 		goto error;
2128585484eSchristos 
2138585484eSchristos 	bufferevent_decrement_read_buckets_(bufev_p, res);
2148585484eSchristos 
2158585484eSchristos 	/* Invoke the user callback - must always be called last */
216b8ecfcfeSchristos 	bufferevent_trigger_nolock_(bufev, EV_READ, 0);
2178585484eSchristos 
2188585484eSchristos 	goto done;
2198585484eSchristos 
2208585484eSchristos  reschedule:
2218585484eSchristos 	goto done;
2228585484eSchristos 
2238585484eSchristos  error:
2248585484eSchristos 	bufferevent_disable(bufev, EV_READ);
225b8ecfcfeSchristos 	bufferevent_run_eventcb_(bufev, what, 0);
2268585484eSchristos 
2278585484eSchristos  done:
2288585484eSchristos 	bufferevent_decref_and_unlock_(bufev);
2298585484eSchristos }
2308585484eSchristos 
2318585484eSchristos static void
2328585484eSchristos bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
2338585484eSchristos {
2348585484eSchristos 	struct bufferevent *bufev = arg;
235*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
2368585484eSchristos 	int res = 0;
2378585484eSchristos 	short what = BEV_EVENT_WRITING;
2388585484eSchristos 	int connected = 0;
2398585484eSchristos 	ev_ssize_t atmost = -1;
2408585484eSchristos 
2418585484eSchristos 	bufferevent_incref_and_lock_(bufev);
2428585484eSchristos 
2438585484eSchristos 	if (event == EV_TIMEOUT) {
2448585484eSchristos 		/* Note that we only check for event==EV_TIMEOUT. If
2458585484eSchristos 		 * event==EV_TIMEOUT|EV_WRITE, we can safely ignore the
2468585484eSchristos 		 * timeout, since a read has occurred */
2478585484eSchristos 		what |= BEV_EVENT_TIMEOUT;
2488585484eSchristos 		goto error;
2498585484eSchristos 	}
2508585484eSchristos 	if (bufev_p->connecting) {
2518585484eSchristos 		int c = evutil_socket_finished_connecting_(fd);
2528585484eSchristos 		/* we need to fake the error if the connection was refused
2538585484eSchristos 		 * immediately - usually connection to localhost on BSD */
2548585484eSchristos 		if (bufev_p->connection_refused) {
2558585484eSchristos 			bufev_p->connection_refused = 0;
2568585484eSchristos 			c = -1;
2578585484eSchristos 		}
2588585484eSchristos 
2598585484eSchristos 		if (c == 0)
2608585484eSchristos 			goto done;
2618585484eSchristos 
2628585484eSchristos 		bufev_p->connecting = 0;
2638585484eSchristos 		if (c < 0) {
2648585484eSchristos 			event_del(&bufev->ev_write);
2658585484eSchristos 			event_del(&bufev->ev_read);
266b8ecfcfeSchristos 			bufferevent_run_eventcb_(bufev, BEV_EVENT_ERROR, 0);
2678585484eSchristos 			goto done;
2688585484eSchristos 		} else {
2698585484eSchristos 			connected = 1;
270*eabc0478Schristos 			bufferevent_socket_set_conn_address_fd_(bufev, fd);
2718585484eSchristos #ifdef _WIN32
2728585484eSchristos 			if (BEV_IS_ASYNC(bufev)) {
2738585484eSchristos 				event_del(&bufev->ev_write);
2748585484eSchristos 				bufferevent_async_set_connected_(bufev);
2758585484eSchristos 				bufferevent_run_eventcb_(bufev,
276b8ecfcfeSchristos 						BEV_EVENT_CONNECTED, 0);
2778585484eSchristos 				goto done;
2788585484eSchristos 			}
2798585484eSchristos #endif
2808585484eSchristos 			bufferevent_run_eventcb_(bufev,
281b8ecfcfeSchristos 					BEV_EVENT_CONNECTED, 0);
2828585484eSchristos 			if (!(bufev->enabled & EV_WRITE) ||
2838585484eSchristos 			    bufev_p->write_suspended) {
2848585484eSchristos 				event_del(&bufev->ev_write);
2858585484eSchristos 				goto done;
2868585484eSchristos 			}
2878585484eSchristos 		}
2888585484eSchristos 	}
2898585484eSchristos 
2908585484eSchristos 	atmost = bufferevent_get_write_max_(bufev_p);
2918585484eSchristos 
2928585484eSchristos 	if (bufev_p->write_suspended)
2938585484eSchristos 		goto done;
2948585484eSchristos 
2958585484eSchristos 	if (evbuffer_get_length(bufev->output)) {
2968585484eSchristos 		evbuffer_unfreeze(bufev->output, 1);
2978585484eSchristos 		res = evbuffer_write_atmost(bufev->output, fd, atmost);
2988585484eSchristos 		evbuffer_freeze(bufev->output, 1);
2998585484eSchristos 		if (res == -1) {
3008585484eSchristos 			int err = evutil_socket_geterror(fd);
3018585484eSchristos 			if (EVUTIL_ERR_RW_RETRIABLE(err))
3028585484eSchristos 				goto reschedule;
3038585484eSchristos 			what |= BEV_EVENT_ERROR;
3048585484eSchristos 		} else if (res == 0) {
3058585484eSchristos 			/* eof case
3068585484eSchristos 			   XXXX Actually, a 0 on write doesn't indicate
3078585484eSchristos 			   an EOF. An ECONNRESET might be more typical.
3088585484eSchristos 			 */
3098585484eSchristos 			what |= BEV_EVENT_EOF;
3108585484eSchristos 		}
3118585484eSchristos 		if (res <= 0)
3128585484eSchristos 			goto error;
3138585484eSchristos 
3148585484eSchristos 		bufferevent_decrement_write_buckets_(bufev_p, res);
3158585484eSchristos 	}
3168585484eSchristos 
3178585484eSchristos 	if (evbuffer_get_length(bufev->output) == 0) {
3188585484eSchristos 		event_del(&bufev->ev_write);
3198585484eSchristos 	}
3208585484eSchristos 
3218585484eSchristos 	/*
3228585484eSchristos 	 * Invoke the user callback if our buffer is drained or below the
3238585484eSchristos 	 * low watermark.
3248585484eSchristos 	 */
325b8ecfcfeSchristos 	if (res || !connected) {
326b8ecfcfeSchristos 		bufferevent_trigger_nolock_(bufev, EV_WRITE, 0);
3278585484eSchristos 	}
3288585484eSchristos 
3298585484eSchristos 	goto done;
3308585484eSchristos 
3318585484eSchristos  reschedule:
3328585484eSchristos 	if (evbuffer_get_length(bufev->output) == 0) {
3338585484eSchristos 		event_del(&bufev->ev_write);
3348585484eSchristos 	}
3358585484eSchristos 	goto done;
3368585484eSchristos 
3378585484eSchristos  error:
3388585484eSchristos 	bufferevent_disable(bufev, EV_WRITE);
339b8ecfcfeSchristos 	bufferevent_run_eventcb_(bufev, what, 0);
3408585484eSchristos 
3418585484eSchristos  done:
3428585484eSchristos 	bufferevent_decref_and_unlock_(bufev);
3438585484eSchristos }
3448585484eSchristos 
3458585484eSchristos struct bufferevent *
3468585484eSchristos bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
3478585484eSchristos     int options)
3488585484eSchristos {
3498585484eSchristos 	struct bufferevent_private *bufev_p;
3508585484eSchristos 	struct bufferevent *bufev;
3518585484eSchristos 
3528585484eSchristos #ifdef _WIN32
3538585484eSchristos 	if (base && event_base_get_iocp_(base))
3548585484eSchristos 		return bufferevent_async_new_(base, fd, options);
3558585484eSchristos #endif
3568585484eSchristos 
3578585484eSchristos 	if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)
3588585484eSchristos 		return NULL;
3598585484eSchristos 
3608585484eSchristos 	if (bufferevent_init_common_(bufev_p, base, &bufferevent_ops_socket,
3618585484eSchristos 				    options) < 0) {
3628585484eSchristos 		mm_free(bufev_p);
3638585484eSchristos 		return NULL;
3648585484eSchristos 	}
3658585484eSchristos 	bufev = &bufev_p->bev;
3668585484eSchristos 	evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);
3678585484eSchristos 
3688585484eSchristos 	event_assign(&bufev->ev_read, bufev->ev_base, fd,
369b8ecfcfeSchristos 	    EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
3708585484eSchristos 	event_assign(&bufev->ev_write, bufev->ev_base, fd,
371b8ecfcfeSchristos 	    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);
3728585484eSchristos 
3738585484eSchristos 	evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);
3748585484eSchristos 
3758585484eSchristos 	evbuffer_freeze(bufev->input, 0);
3768585484eSchristos 	evbuffer_freeze(bufev->output, 1);
3778585484eSchristos 
3788585484eSchristos 	return bufev;
3798585484eSchristos }
3808585484eSchristos 
3818585484eSchristos int
3828585484eSchristos bufferevent_socket_connect(struct bufferevent *bev,
383*eabc0478Schristos     const struct sockaddr *sa, int socklen)
3848585484eSchristos {
385*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bev);
3868585484eSchristos 
3878585484eSchristos 	evutil_socket_t fd;
3888585484eSchristos 	int r = 0;
3898585484eSchristos 	int result=-1;
3908585484eSchristos 	int ownfd = 0;
3918585484eSchristos 
3928585484eSchristos 	bufferevent_incref_and_lock_(bev);
3938585484eSchristos 
3948585484eSchristos 	fd = bufferevent_getfd(bev);
3958585484eSchristos 	if (fd < 0) {
3968585484eSchristos 		if (!sa)
3978585484eSchristos 			goto done;
3988585484eSchristos 		fd = evutil_socket_(sa->sa_family,
3998585484eSchristos 		    SOCK_STREAM|EVUTIL_SOCK_NONBLOCK, 0);
4008585484eSchristos 		if (fd < 0)
401*eabc0478Schristos 			goto freesock;
4028585484eSchristos 		ownfd = 1;
4038585484eSchristos 	}
4048585484eSchristos 	if (sa) {
4058585484eSchristos #ifdef _WIN32
4068585484eSchristos 		if (bufferevent_async_can_connect_(bev)) {
4078585484eSchristos 			bufferevent_setfd(bev, fd);
4088585484eSchristos 			r = bufferevent_async_connect_(bev, fd, sa, socklen);
4098585484eSchristos 			if (r < 0)
4108585484eSchristos 				goto freesock;
4118585484eSchristos 			bufev_p->connecting = 1;
4128585484eSchristos 			result = 0;
4138585484eSchristos 			goto done;
4148585484eSchristos 		} else
4158585484eSchristos #endif
4168585484eSchristos 		r = evutil_socket_connect_(&fd, sa, socklen);
4178585484eSchristos 		if (r < 0)
4188585484eSchristos 			goto freesock;
4198585484eSchristos 	}
4208585484eSchristos #ifdef _WIN32
4218585484eSchristos 	/* ConnectEx() isn't always around, even when IOCP is enabled.
4228585484eSchristos 	 * Here, we borrow the socket object's write handler to fall back
4238585484eSchristos 	 * on a non-blocking connect() when ConnectEx() is unavailable. */
4248585484eSchristos 	if (BEV_IS_ASYNC(bev)) {
4258585484eSchristos 		event_assign(&bev->ev_write, bev->ev_base, fd,
426b8ecfcfeSchristos 		    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bev);
4278585484eSchristos 	}
4288585484eSchristos #endif
4298585484eSchristos 	bufferevent_setfd(bev, fd);
4308585484eSchristos 	if (r == 0) {
4318585484eSchristos 		if (! be_socket_enable(bev, EV_WRITE)) {
4328585484eSchristos 			bufev_p->connecting = 1;
4338585484eSchristos 			result = 0;
4348585484eSchristos 			goto done;
4358585484eSchristos 		}
4368585484eSchristos 	} else if (r == 1) {
4378585484eSchristos 		/* The connect succeeded already. How very BSD of it. */
4388585484eSchristos 		result = 0;
4398585484eSchristos 		bufev_p->connecting = 1;
440*eabc0478Schristos 		bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS);
4418585484eSchristos 	} else {
4428585484eSchristos 		/* The connect failed already.  How very BSD of it. */
4438585484eSchristos 		result = 0;
444*eabc0478Schristos 		bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, BEV_OPT_DEFER_CALLBACKS);
445*eabc0478Schristos 		bufferevent_disable(bev, EV_WRITE|EV_READ);
4468585484eSchristos 	}
4478585484eSchristos 
4488585484eSchristos 	goto done;
4498585484eSchristos 
4508585484eSchristos freesock:
4518585484eSchristos 	if (ownfd)
4528585484eSchristos 		evutil_closesocket(fd);
4538585484eSchristos done:
4548585484eSchristos 	bufferevent_decref_and_unlock_(bev);
4558585484eSchristos 	return result;
4568585484eSchristos }
4578585484eSchristos 
4588585484eSchristos static void
4598585484eSchristos bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai,
4608585484eSchristos     void *arg)
4618585484eSchristos {
4628585484eSchristos 	struct bufferevent *bev = arg;
463*eabc0478Schristos 	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
4648585484eSchristos 	int r;
4658585484eSchristos 	BEV_LOCK(bev);
4668585484eSchristos 
4678585484eSchristos 	bufferevent_unsuspend_write_(bev, BEV_SUSPEND_LOOKUP);
4688585484eSchristos 	bufferevent_unsuspend_read_(bev, BEV_SUSPEND_LOOKUP);
4698585484eSchristos 
470*eabc0478Schristos 	bev_p->dns_request = NULL;
471*eabc0478Schristos 
472*eabc0478Schristos 	if (result == EVUTIL_EAI_CANCEL) {
473*eabc0478Schristos 		bev_p->dns_error = result;
474*eabc0478Schristos 		bufferevent_decref_and_unlock_(bev);
475*eabc0478Schristos 		return;
476*eabc0478Schristos 	}
4778585484eSchristos 	if (result != 0) {
4788585484eSchristos 		bev_p->dns_error = result;
479b8ecfcfeSchristos 		bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);
4808585484eSchristos 		bufferevent_decref_and_unlock_(bev);
4818585484eSchristos 		if (ai)
4828585484eSchristos 			evutil_freeaddrinfo(ai);
4838585484eSchristos 		return;
4848585484eSchristos 	}
4858585484eSchristos 
4868585484eSchristos 	/* XXX use the other addrinfos? */
487*eabc0478Schristos 	bufferevent_socket_set_conn_address_(bev, ai->ai_addr, (int)ai->ai_addrlen);
4888585484eSchristos 	r = bufferevent_socket_connect(bev, ai->ai_addr, (int)ai->ai_addrlen);
489*eabc0478Schristos 	if (r < 0)
490*eabc0478Schristos 		bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);
4918585484eSchristos 	bufferevent_decref_and_unlock_(bev);
4928585484eSchristos 	evutil_freeaddrinfo(ai);
4938585484eSchristos }
4948585484eSchristos 
4958585484eSchristos int
4968585484eSchristos bufferevent_socket_connect_hostname(struct bufferevent *bev,
4978585484eSchristos     struct evdns_base *evdns_base, int family, const char *hostname, int port)
4988585484eSchristos {
4998585484eSchristos 	char portbuf[10];
5008585484eSchristos 	struct evutil_addrinfo hint;
501*eabc0478Schristos 	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
5028585484eSchristos 
5038585484eSchristos 	if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
5048585484eSchristos 		return -1;
5058585484eSchristos 	if (port < 1 || port > 65535)
5068585484eSchristos 		return -1;
5078585484eSchristos 
5088585484eSchristos 	memset(&hint, 0, sizeof(hint));
5098585484eSchristos 	hint.ai_family = family;
5108585484eSchristos 	hint.ai_protocol = IPPROTO_TCP;
5118585484eSchristos 	hint.ai_socktype = SOCK_STREAM;
5128585484eSchristos 
513*eabc0478Schristos 	evutil_snprintf(portbuf, sizeof(portbuf), "%d", port);
514*eabc0478Schristos 
515*eabc0478Schristos 	BEV_LOCK(bev);
516*eabc0478Schristos 	bev_p->dns_error = 0;
517*eabc0478Schristos 
5188585484eSchristos 	bufferevent_suspend_write_(bev, BEV_SUSPEND_LOOKUP);
5198585484eSchristos 	bufferevent_suspend_read_(bev, BEV_SUSPEND_LOOKUP);
5208585484eSchristos 
5218585484eSchristos 	bufferevent_incref_(bev);
522*eabc0478Schristos 	bev_p->dns_request = evutil_getaddrinfo_async_(evdns_base, hostname,
523*eabc0478Schristos 	    portbuf, &hint, bufferevent_connect_getaddrinfo_cb, bev);
524*eabc0478Schristos 	BEV_UNLOCK(bev);
5258585484eSchristos 
5268585484eSchristos 	return 0;
5278585484eSchristos }
5288585484eSchristos 
5298585484eSchristos int
5308585484eSchristos bufferevent_socket_get_dns_error(struct bufferevent *bev)
5318585484eSchristos {
5328585484eSchristos 	int rv;
533*eabc0478Schristos 	struct bufferevent_private *bev_p = BEV_UPCAST(bev);
5348585484eSchristos 
5358585484eSchristos 	BEV_LOCK(bev);
5368585484eSchristos 	rv = bev_p->dns_error;
5378585484eSchristos 	BEV_UNLOCK(bev);
5388585484eSchristos 
5398585484eSchristos 	return rv;
5408585484eSchristos }
5418585484eSchristos 
5428585484eSchristos /*
5438585484eSchristos  * Create a new buffered event object.
5448585484eSchristos  *
5458585484eSchristos  * The read callback is invoked whenever we read new data.
5468585484eSchristos  * The write callback is invoked whenever the output buffer is drained.
5478585484eSchristos  * The error callback is invoked on a write/read error or on EOF.
5488585484eSchristos  *
5498585484eSchristos  * Both read and write callbacks maybe NULL.  The error callback is not
5508585484eSchristos  * allowed to be NULL and have to be provided always.
5518585484eSchristos  */
5528585484eSchristos 
5538585484eSchristos struct bufferevent *
5548585484eSchristos bufferevent_new(evutil_socket_t fd,
5558585484eSchristos     bufferevent_data_cb readcb, bufferevent_data_cb writecb,
5568585484eSchristos     bufferevent_event_cb eventcb, void *cbarg)
5578585484eSchristos {
5588585484eSchristos 	struct bufferevent *bufev;
5598585484eSchristos 
5608585484eSchristos 	if (!(bufev = bufferevent_socket_new(NULL, fd, 0)))
5618585484eSchristos 		return NULL;
5628585484eSchristos 
5638585484eSchristos 	bufferevent_setcb(bufev, readcb, writecb, eventcb, cbarg);
5648585484eSchristos 
5658585484eSchristos 	return bufev;
5668585484eSchristos }
5678585484eSchristos 
5688585484eSchristos 
5698585484eSchristos static int
5708585484eSchristos be_socket_enable(struct bufferevent *bufev, short event)
5718585484eSchristos {
572*eabc0478Schristos 	if (event & EV_READ &&
573*eabc0478Schristos 	    bufferevent_add_event_(&bufev->ev_read, &bufev->timeout_read) == -1)
5748585484eSchristos 			return -1;
575*eabc0478Schristos 	if (event & EV_WRITE &&
576*eabc0478Schristos 	    bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1)
5778585484eSchristos 			return -1;
5788585484eSchristos 	return 0;
5798585484eSchristos }
5808585484eSchristos 
5818585484eSchristos static int
5828585484eSchristos be_socket_disable(struct bufferevent *bufev, short event)
5838585484eSchristos {
584*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
5858585484eSchristos 	if (event & EV_READ) {
5868585484eSchristos 		if (event_del(&bufev->ev_read) == -1)
5878585484eSchristos 			return -1;
5888585484eSchristos 	}
5898585484eSchristos 	/* Don't actually disable the write if we are trying to connect. */
5908585484eSchristos 	if ((event & EV_WRITE) && ! bufev_p->connecting) {
5918585484eSchristos 		if (event_del(&bufev->ev_write) == -1)
5928585484eSchristos 			return -1;
5938585484eSchristos 	}
5948585484eSchristos 	return 0;
5958585484eSchristos }
5968585484eSchristos 
5978585484eSchristos static void
5988585484eSchristos be_socket_destruct(struct bufferevent *bufev)
5998585484eSchristos {
600*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
6018585484eSchristos 	evutil_socket_t fd;
602*eabc0478Schristos 	EVUTIL_ASSERT(BEV_IS_SOCKET(bufev));
6038585484eSchristos 
6048585484eSchristos 	fd = event_get_fd(&bufev->ev_read);
6058585484eSchristos 
6068585484eSchristos 	if ((bufev_p->options & BEV_OPT_CLOSE_ON_FREE) && fd >= 0)
6078585484eSchristos 		EVUTIL_CLOSESOCKET(fd);
6088585484eSchristos 
609*eabc0478Schristos 	evutil_getaddrinfo_cancel_async_(bufev_p->dns_request);
6108585484eSchristos }
6118585484eSchristos 
6128585484eSchristos static int
6138585484eSchristos be_socket_flush(struct bufferevent *bev, short iotype,
6148585484eSchristos     enum bufferevent_flush_mode mode)
6158585484eSchristos {
6168585484eSchristos 	return 0;
6178585484eSchristos }
6188585484eSchristos 
6198585484eSchristos 
6208585484eSchristos static void
6218585484eSchristos be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd)
6228585484eSchristos {
623*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
624*eabc0478Schristos 
6258585484eSchristos 	BEV_LOCK(bufev);
626*eabc0478Schristos 	EVUTIL_ASSERT(BEV_IS_SOCKET(bufev));
6278585484eSchristos 
6288585484eSchristos 	event_del(&bufev->ev_read);
6298585484eSchristos 	event_del(&bufev->ev_write);
6308585484eSchristos 
631*eabc0478Schristos 	evbuffer_unfreeze(bufev->input, 0);
632*eabc0478Schristos 	evbuffer_unfreeze(bufev->output, 1);
633*eabc0478Schristos 
6348585484eSchristos 	event_assign(&bufev->ev_read, bufev->ev_base, fd,
635b8ecfcfeSchristos 	    EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
6368585484eSchristos 	event_assign(&bufev->ev_write, bufev->ev_base, fd,
637b8ecfcfeSchristos 	    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);
6388585484eSchristos 
6398585484eSchristos 	if (fd >= 0)
6408585484eSchristos 		bufferevent_enable(bufev, bufev->enabled);
6418585484eSchristos 
642*eabc0478Schristos 	evutil_getaddrinfo_cancel_async_(bufev_p->dns_request);
643*eabc0478Schristos 
6448585484eSchristos 	BEV_UNLOCK(bufev);
6458585484eSchristos }
6468585484eSchristos 
6478585484eSchristos /* XXXX Should non-socket bufferevents support this? */
6488585484eSchristos int
6498585484eSchristos bufferevent_priority_set(struct bufferevent *bufev, int priority)
6508585484eSchristos {
6518585484eSchristos 	int r = -1;
652*eabc0478Schristos 	struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);
6538585484eSchristos 
6548585484eSchristos 	BEV_LOCK(bufev);
655*eabc0478Schristos 	if (BEV_IS_ASYNC(bufev) || BEV_IS_FILTER(bufev) || BEV_IS_PAIR(bufev))
6568585484eSchristos 		goto done;
6578585484eSchristos 
6588585484eSchristos 	if (event_priority_set(&bufev->ev_read, priority) == -1)
6598585484eSchristos 		goto done;
6608585484eSchristos 	if (event_priority_set(&bufev->ev_write, priority) == -1)
6618585484eSchristos 		goto done;
6628585484eSchristos 
6638585484eSchristos 	event_deferred_cb_set_priority_(&bufev_p->deferred, priority);
6648585484eSchristos 
6658585484eSchristos 	r = 0;
6668585484eSchristos done:
6678585484eSchristos 	BEV_UNLOCK(bufev);
6688585484eSchristos 	return r;
6698585484eSchristos }
6708585484eSchristos 
6718585484eSchristos /* XXXX Should non-socket bufferevents support this? */
6728585484eSchristos int
6738585484eSchristos bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
6748585484eSchristos {
6758585484eSchristos 	int res = -1;
6768585484eSchristos 
6778585484eSchristos 	BEV_LOCK(bufev);
678*eabc0478Schristos 	if (!BEV_IS_SOCKET(bufev))
6798585484eSchristos 		goto done;
6808585484eSchristos 
6818585484eSchristos 	bufev->ev_base = base;
6828585484eSchristos 
6838585484eSchristos 	res = event_base_set(base, &bufev->ev_read);
6848585484eSchristos 	if (res == -1)
6858585484eSchristos 		goto done;
6868585484eSchristos 
6878585484eSchristos 	res = event_base_set(base, &bufev->ev_write);
6888585484eSchristos done:
6898585484eSchristos 	BEV_UNLOCK(bufev);
6908585484eSchristos 	return res;
6918585484eSchristos }
6928585484eSchristos 
6938585484eSchristos static int
6948585484eSchristos be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
6958585484eSchristos     union bufferevent_ctrl_data *data)
6968585484eSchristos {
6978585484eSchristos 	switch (op) {
6988585484eSchristos 	case BEV_CTRL_SET_FD:
6998585484eSchristos 		be_socket_setfd(bev, data->fd);
7008585484eSchristos 		return 0;
7018585484eSchristos 	case BEV_CTRL_GET_FD:
7028585484eSchristos 		data->fd = event_get_fd(&bev->ev_read);
7038585484eSchristos 		return 0;
7048585484eSchristos 	case BEV_CTRL_GET_UNDERLYING:
7058585484eSchristos 	case BEV_CTRL_CANCEL_ALL:
7068585484eSchristos 	default:
7078585484eSchristos 		return -1;
7088585484eSchristos 	}
7098585484eSchristos }
710