Path: blob/main/contrib/libevent/bufferevent_sock.c
39475 views
/*1* Copyright (c) 2007-2012 Niels Provos and Nick Mathewson2* Copyright (c) 2002-2006 Niels Provos <[email protected]>3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13* 3. The name of the author may not be used to endorse or promote products14* derived from this software without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR17* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES18* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.19* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,20* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT21* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,22* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY23* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF25* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.26*/2728#include "event2/event-config.h"29#include "evconfig-private.h"3031#include <sys/types.h>3233#ifdef EVENT__HAVE_SYS_TIME_H34#include <sys/time.h>35#endif3637#include <errno.h>38#include <stdio.h>39#include <stdlib.h>40#include <string.h>41#ifdef EVENT__HAVE_STDARG_H42#include <stdarg.h>43#endif44#ifdef EVENT__HAVE_UNISTD_H45#include <unistd.h>46#endif4748#ifdef _WIN3249#include <winsock2.h>50#include <ws2tcpip.h>51#endif5253#ifdef EVENT__HAVE_SYS_SOCKET_H54#include <sys/socket.h>55#endif56#ifdef EVENT__HAVE_NETINET_IN_H57#include <netinet/in.h>58#endif59#ifdef EVENT__HAVE_NETINET_IN6_H60#include <netinet/in6.h>61#endif6263#include "event2/util.h"64#include "event2/bufferevent.h"65#include "event2/buffer.h"66#include "event2/bufferevent_struct.h"67#include "event2/bufferevent_compat.h"68#include "event2/event.h"69#include "log-internal.h"70#include "mm-internal.h"71#include "bufferevent-internal.h"72#include "util-internal.h"73#ifdef _WIN3274#include "iocp-internal.h"75#endif7677/* prototypes */78static int be_socket_enable(struct bufferevent *, short);79static int be_socket_disable(struct bufferevent *, short);80static void be_socket_destruct(struct bufferevent *);81static int be_socket_flush(struct bufferevent *, short, enum bufferevent_flush_mode);82static int be_socket_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);8384static void be_socket_setfd(struct bufferevent *, evutil_socket_t);8586const struct bufferevent_ops bufferevent_ops_socket = {87"socket",88evutil_offsetof(struct bufferevent_private, bev),89be_socket_enable,90be_socket_disable,91NULL, /* unlink */92be_socket_destruct,93bufferevent_generic_adj_existing_timeouts_,94be_socket_flush,95be_socket_ctrl,96};9798const struct sockaddr*99bufferevent_socket_get_conn_address_(struct bufferevent *bev)100{101struct bufferevent_private *bev_p = BEV_UPCAST(bev);102return (struct sockaddr *)&bev_p->conn_address;103}104105void106bufferevent_socket_set_conn_address_fd_(struct bufferevent *bev,107evutil_socket_t fd)108{109struct bufferevent_private *bev_p = BEV_UPCAST(bev);110111socklen_t len = sizeof(bev_p->conn_address);112113struct sockaddr *addr = (struct sockaddr *)&bev_p->conn_address;114if (addr->sa_family != AF_UNSPEC)115getpeername(fd, addr, &len);116}117118void119bufferevent_socket_set_conn_address_(struct bufferevent *bev,120struct sockaddr *addr, size_t addrlen)121{122struct bufferevent_private *bev_p = BEV_UPCAST(bev);123EVUTIL_ASSERT(addrlen <= sizeof(bev_p->conn_address));124memcpy(&bev_p->conn_address, addr, addrlen);125}126127static void128bufferevent_socket_outbuf_cb(struct evbuffer *buf,129const struct evbuffer_cb_info *cbinfo,130void *arg)131{132struct bufferevent *bufev = arg;133struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);134135if (cbinfo->n_added &&136(bufev->enabled & EV_WRITE) &&137!event_pending(&bufev->ev_write, EV_WRITE, NULL) &&138!bufev_p->write_suspended) {139/* Somebody added data to the buffer, and we would like to140* write, and we were not writing. So, start writing. */141if (bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1) {142/* Should we log this? */143}144}145}146147static void148bufferevent_readcb(evutil_socket_t fd, short event, void *arg)149{150struct bufferevent *bufev = arg;151struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);152struct evbuffer *input;153int res = 0;154short what = BEV_EVENT_READING;155ev_ssize_t howmuch = -1, readmax=-1;156157bufferevent_incref_and_lock_(bufev);158159if (event == EV_TIMEOUT) {160/* Note that we only check for event==EV_TIMEOUT. If161* event==EV_TIMEOUT|EV_READ, we can safely ignore the162* timeout, since a read has occurred */163what |= BEV_EVENT_TIMEOUT;164goto error;165}166167input = bufev->input;168169/*170* If we have a high watermark configured then we don't want to171* read more data than would make us reach the watermark.172*/173if (bufev->wm_read.high != 0) {174howmuch = bufev->wm_read.high - evbuffer_get_length(input);175/* we somehow lowered the watermark, stop reading */176if (howmuch <= 0) {177bufferevent_wm_suspend_read(bufev);178goto done;179}180}181readmax = bufferevent_get_read_max_(bufev_p);182if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"183* uglifies this code. XXXX */184howmuch = readmax;185if (bufev_p->read_suspended)186goto done;187188evbuffer_unfreeze(input, 0);189res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */190evbuffer_freeze(input, 0);191192if (res == -1) {193int err = evutil_socket_geterror(fd);194if (EVUTIL_ERR_RW_RETRIABLE(err))195goto reschedule;196if (EVUTIL_ERR_CONNECT_REFUSED(err)) {197bufev_p->connection_refused = 1;198goto done;199}200/* error case */201what |= BEV_EVENT_ERROR;202} else if (res == 0) {203/* eof case */204what |= BEV_EVENT_EOF;205}206207if (res <= 0)208goto error;209210bufferevent_decrement_read_buckets_(bufev_p, res);211212/* Invoke the user callback - must always be called last */213bufferevent_trigger_nolock_(bufev, EV_READ, 0);214215goto done;216217reschedule:218goto done;219220error:221bufferevent_disable(bufev, EV_READ);222bufferevent_run_eventcb_(bufev, what, 0);223224done:225bufferevent_decref_and_unlock_(bufev);226}227228static void229bufferevent_writecb(evutil_socket_t fd, short event, void *arg)230{231struct bufferevent *bufev = arg;232struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);233int res = 0;234short what = BEV_EVENT_WRITING;235int connected = 0;236ev_ssize_t atmost = -1;237238bufferevent_incref_and_lock_(bufev);239240if (event == EV_TIMEOUT) {241/* Note that we only check for event==EV_TIMEOUT. If242* event==EV_TIMEOUT|EV_WRITE, we can safely ignore the243* timeout, since a read has occurred */244what |= BEV_EVENT_TIMEOUT;245goto error;246}247if (bufev_p->connecting) {248int c = evutil_socket_finished_connecting_(fd);249/* we need to fake the error if the connection was refused250* immediately - usually connection to localhost on BSD */251if (bufev_p->connection_refused) {252bufev_p->connection_refused = 0;253c = -1;254}255256if (c == 0)257goto done;258259bufev_p->connecting = 0;260if (c < 0) {261event_del(&bufev->ev_write);262event_del(&bufev->ev_read);263bufferevent_run_eventcb_(bufev, BEV_EVENT_ERROR, 0);264goto done;265} else {266connected = 1;267bufferevent_socket_set_conn_address_fd_(bufev, fd);268#ifdef _WIN32269if (BEV_IS_ASYNC(bufev)) {270event_del(&bufev->ev_write);271bufferevent_async_set_connected_(bufev);272bufferevent_run_eventcb_(bufev,273BEV_EVENT_CONNECTED, 0);274goto done;275}276#endif277bufferevent_run_eventcb_(bufev,278BEV_EVENT_CONNECTED, 0);279if (!(bufev->enabled & EV_WRITE) ||280bufev_p->write_suspended) {281event_del(&bufev->ev_write);282goto done;283}284}285}286287atmost = bufferevent_get_write_max_(bufev_p);288289if (bufev_p->write_suspended)290goto done;291292if (evbuffer_get_length(bufev->output)) {293evbuffer_unfreeze(bufev->output, 1);294res = evbuffer_write_atmost(bufev->output, fd, atmost);295evbuffer_freeze(bufev->output, 1);296if (res == -1) {297int err = evutil_socket_geterror(fd);298if (EVUTIL_ERR_RW_RETRIABLE(err))299goto reschedule;300what |= BEV_EVENT_ERROR;301} else if (res == 0) {302/* eof case303XXXX Actually, a 0 on write doesn't indicate304an EOF. An ECONNRESET might be more typical.305*/306what |= BEV_EVENT_EOF;307}308if (res <= 0)309goto error;310311bufferevent_decrement_write_buckets_(bufev_p, res);312}313314if (evbuffer_get_length(bufev->output) == 0) {315event_del(&bufev->ev_write);316}317318/*319* Invoke the user callback if our buffer is drained or below the320* low watermark.321*/322if (res || !connected) {323bufferevent_trigger_nolock_(bufev, EV_WRITE, 0);324}325326goto done;327328reschedule:329if (evbuffer_get_length(bufev->output) == 0) {330event_del(&bufev->ev_write);331}332goto done;333334error:335bufferevent_disable(bufev, EV_WRITE);336bufferevent_run_eventcb_(bufev, what, 0);337338done:339bufferevent_decref_and_unlock_(bufev);340}341342struct bufferevent *343bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,344int options)345{346struct bufferevent_private *bufev_p;347struct bufferevent *bufev;348349#ifdef _WIN32350if (base && event_base_get_iocp_(base))351return bufferevent_async_new_(base, fd, options);352#endif353354if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)355return NULL;356357if (bufferevent_init_common_(bufev_p, base, &bufferevent_ops_socket,358options) < 0) {359mm_free(bufev_p);360return NULL;361}362bufev = &bufev_p->bev;363evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);364365event_assign(&bufev->ev_read, bufev->ev_base, fd,366EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);367event_assign(&bufev->ev_write, bufev->ev_base, fd,368EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);369370evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);371372evbuffer_freeze(bufev->input, 0);373evbuffer_freeze(bufev->output, 1);374375return bufev;376}377378int379bufferevent_socket_connect(struct bufferevent *bev,380const struct sockaddr *sa, int socklen)381{382struct bufferevent_private *bufev_p = BEV_UPCAST(bev);383384evutil_socket_t fd;385int r = 0;386int result=-1;387int ownfd = 0;388389bufferevent_incref_and_lock_(bev);390391fd = bufferevent_getfd(bev);392if (fd < 0) {393if (!sa)394goto done;395fd = evutil_socket_(sa->sa_family,396SOCK_STREAM|EVUTIL_SOCK_NONBLOCK, 0);397if (fd < 0)398goto freesock;399ownfd = 1;400}401if (sa) {402#ifdef _WIN32403if (bufferevent_async_can_connect_(bev)) {404bufferevent_setfd(bev, fd);405r = bufferevent_async_connect_(bev, fd, sa, socklen);406if (r < 0)407goto freesock;408bufev_p->connecting = 1;409result = 0;410goto done;411} else412#endif413r = evutil_socket_connect_(&fd, sa, socklen);414if (r < 0)415goto freesock;416}417#ifdef _WIN32418/* ConnectEx() isn't always around, even when IOCP is enabled.419* Here, we borrow the socket object's write handler to fall back420* on a non-blocking connect() when ConnectEx() is unavailable. */421if (BEV_IS_ASYNC(bev)) {422event_assign(&bev->ev_write, bev->ev_base, fd,423EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bev);424}425#endif426bufferevent_setfd(bev, fd);427if (r == 0) {428if (! be_socket_enable(bev, EV_WRITE)) {429bufev_p->connecting = 1;430result = 0;431goto done;432}433} else if (r == 1) {434/* The connect succeeded already. How very BSD of it. */435result = 0;436bufev_p->connecting = 1;437bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS);438} else {439/* The connect failed already. How very BSD of it. */440result = 0;441bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, BEV_OPT_DEFER_CALLBACKS);442bufferevent_disable(bev, EV_WRITE|EV_READ);443}444445goto done;446447freesock:448if (ownfd)449evutil_closesocket(fd);450done:451bufferevent_decref_and_unlock_(bev);452return result;453}454455static void456bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai,457void *arg)458{459struct bufferevent *bev = arg;460struct bufferevent_private *bev_p = BEV_UPCAST(bev);461int r;462BEV_LOCK(bev);463464bufferevent_unsuspend_write_(bev, BEV_SUSPEND_LOOKUP);465bufferevent_unsuspend_read_(bev, BEV_SUSPEND_LOOKUP);466467bev_p->dns_request = NULL;468469if (result == EVUTIL_EAI_CANCEL) {470bev_p->dns_error = result;471bufferevent_decref_and_unlock_(bev);472return;473}474if (result != 0) {475bev_p->dns_error = result;476bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);477bufferevent_decref_and_unlock_(bev);478if (ai)479evutil_freeaddrinfo(ai);480return;481}482483/* XXX use the other addrinfos? */484bufferevent_socket_set_conn_address_(bev, ai->ai_addr, (int)ai->ai_addrlen);485r = bufferevent_socket_connect(bev, ai->ai_addr, (int)ai->ai_addrlen);486if (r < 0)487bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, 0);488bufferevent_decref_and_unlock_(bev);489evutil_freeaddrinfo(ai);490}491492int493bufferevent_socket_connect_hostname(struct bufferevent *bev,494struct evdns_base *evdns_base, int family, const char *hostname, int port)495{496char portbuf[10];497struct evutil_addrinfo hint;498struct bufferevent_private *bev_p = BEV_UPCAST(bev);499500if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)501return -1;502if (port < 1 || port > 65535)503return -1;504505memset(&hint, 0, sizeof(hint));506hint.ai_family = family;507hint.ai_protocol = IPPROTO_TCP;508hint.ai_socktype = SOCK_STREAM;509510evutil_snprintf(portbuf, sizeof(portbuf), "%d", port);511512BEV_LOCK(bev);513bev_p->dns_error = 0;514515bufferevent_suspend_write_(bev, BEV_SUSPEND_LOOKUP);516bufferevent_suspend_read_(bev, BEV_SUSPEND_LOOKUP);517518bufferevent_incref_(bev);519bev_p->dns_request = evutil_getaddrinfo_async_(evdns_base, hostname,520portbuf, &hint, bufferevent_connect_getaddrinfo_cb, bev);521BEV_UNLOCK(bev);522523return 0;524}525526int527bufferevent_socket_get_dns_error(struct bufferevent *bev)528{529int rv;530struct bufferevent_private *bev_p = BEV_UPCAST(bev);531532BEV_LOCK(bev);533rv = bev_p->dns_error;534BEV_UNLOCK(bev);535536return rv;537}538539/*540* Create a new buffered event object.541*542* The read callback is invoked whenever we read new data.543* The write callback is invoked whenever the output buffer is drained.544* The error callback is invoked on a write/read error or on EOF.545*546* Both read and write callbacks maybe NULL. The error callback is not547* allowed to be NULL and have to be provided always.548*/549550struct bufferevent *551bufferevent_new(evutil_socket_t fd,552bufferevent_data_cb readcb, bufferevent_data_cb writecb,553bufferevent_event_cb eventcb, void *cbarg)554{555struct bufferevent *bufev;556557if (!(bufev = bufferevent_socket_new(NULL, fd, 0)))558return NULL;559560bufferevent_setcb(bufev, readcb, writecb, eventcb, cbarg);561562return bufev;563}564565566static int567be_socket_enable(struct bufferevent *bufev, short event)568{569if (event & EV_READ &&570bufferevent_add_event_(&bufev->ev_read, &bufev->timeout_read) == -1)571return -1;572if (event & EV_WRITE &&573bufferevent_add_event_(&bufev->ev_write, &bufev->timeout_write) == -1)574return -1;575return 0;576}577578static int579be_socket_disable(struct bufferevent *bufev, short event)580{581struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);582if (event & EV_READ) {583if (event_del(&bufev->ev_read) == -1)584return -1;585}586/* Don't actually disable the write if we are trying to connect. */587if ((event & EV_WRITE) && ! bufev_p->connecting) {588if (event_del(&bufev->ev_write) == -1)589return -1;590}591return 0;592}593594static void595be_socket_destruct(struct bufferevent *bufev)596{597struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);598evutil_socket_t fd;599EVUTIL_ASSERT(BEV_IS_SOCKET(bufev));600601fd = event_get_fd(&bufev->ev_read);602603if ((bufev_p->options & BEV_OPT_CLOSE_ON_FREE) && fd >= 0)604EVUTIL_CLOSESOCKET(fd);605606evutil_getaddrinfo_cancel_async_(bufev_p->dns_request);607}608609static int610be_socket_flush(struct bufferevent *bev, short iotype,611enum bufferevent_flush_mode mode)612{613return 0;614}615616617static void618be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd)619{620struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);621622BEV_LOCK(bufev);623EVUTIL_ASSERT(BEV_IS_SOCKET(bufev));624625event_del(&bufev->ev_read);626event_del(&bufev->ev_write);627628evbuffer_unfreeze(bufev->input, 0);629evbuffer_unfreeze(bufev->output, 1);630631event_assign(&bufev->ev_read, bufev->ev_base, fd,632EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);633event_assign(&bufev->ev_write, bufev->ev_base, fd,634EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);635636if (fd >= 0)637bufferevent_enable(bufev, bufev->enabled);638639evutil_getaddrinfo_cancel_async_(bufev_p->dns_request);640641BEV_UNLOCK(bufev);642}643644/* XXXX Should non-socket bufferevents support this? */645int646bufferevent_priority_set(struct bufferevent *bufev, int priority)647{648int r = -1;649struct bufferevent_private *bufev_p = BEV_UPCAST(bufev);650651BEV_LOCK(bufev);652if (BEV_IS_ASYNC(bufev) || BEV_IS_FILTER(bufev) || BEV_IS_PAIR(bufev))653goto done;654655if (event_priority_set(&bufev->ev_read, priority) == -1)656goto done;657if (event_priority_set(&bufev->ev_write, priority) == -1)658goto done;659660event_deferred_cb_set_priority_(&bufev_p->deferred, priority);661662r = 0;663done:664BEV_UNLOCK(bufev);665return r;666}667668/* XXXX Should non-socket bufferevents support this? */669int670bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)671{672int res = -1;673674BEV_LOCK(bufev);675if (!BEV_IS_SOCKET(bufev))676goto done;677678bufev->ev_base = base;679680res = event_base_set(base, &bufev->ev_read);681if (res == -1)682goto done;683684res = event_base_set(base, &bufev->ev_write);685done:686BEV_UNLOCK(bufev);687return res;688}689690static int691be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,692union bufferevent_ctrl_data *data)693{694switch (op) {695case BEV_CTRL_SET_FD:696be_socket_setfd(bev, data->fd);697return 0;698case BEV_CTRL_GET_FD:699data->fd = event_get_fd(&bev->ev_read);700return 0;701case BEV_CTRL_GET_UNDERLYING:702case BEV_CTRL_CANCEL_ALL:703default:704return -1;705}706}707708709