Merge branch 'buffer-read-size'

And after this patch set default evbuffer max read via bufferevent is
16K not 4K.

Here is some numbers for the single max read in evbuffer impact:
  function client() { becat "$@" | pv > /dev/null; }
  function server() { cat /dev/zero | becat -l "$@"; }

Plain bufferevent:

- 40K
  $ server -R $((40<<10)) & client -R $((40<<10))
  700MiB/s

- 16K *default now*
  $ server & client
  1.81GiB/s

- 4K
  $ server -R $((4<<10)) & client -R $((4<<10))
  1.05GiB/s

With OpenSSL (-S):

- 40K *default now*
  $ server -S -R $((40<<10)) & client -S -R $((40<<10))
  900MiB/s

- 16K *default now*
  $ server -S & client -S
  745MiB/s

- 4K
  $ server -S -R $((4<<10)) & client -S -R $((4<<10))
  593MiB/s

So as you can see without openssl 16K is faster then 40K/4K, while for
openssl 40K is still faster then 16K (I guess that this is due to with
openssl SSL_read() more at at time, while with plain we have some
allocations splits in evbuffer and maybe due to some buffer in openssl)

* buffer-read-size:
  sample/becat: bufferevent cat, ncat/nc/telnet analog
  Adjust evbuffer max read for bufferevents
  Maximum evbuffer read configuration
  Fix leaks in error path of the bufferevent_init_common_()
This commit is contained in:
Azat Khuzhin 2019-03-16 17:41:09 +03:00
commit 1f4f8769c4
No known key found for this signature in database
GPG Key ID: B86086848EF8686D
11 changed files with 716 additions and 28 deletions

View File

@ -433,6 +433,7 @@ CHECK_FUNCTION_EXISTS_EX(port_create EVENT__HAVE_PORT_CREATE)
CHECK_FUNCTION_EXISTS_EX(sendfile EVENT__HAVE_SENDFILE)
CHECK_FUNCTION_EXISTS_EX(sigaction EVENT__HAVE_SIGACTION)
CHECK_FUNCTION_EXISTS_EX(signal EVENT__HAVE_SIGNAL)
CHECK_FUNCTION_EXISTS_EX(strsignal EVENT__HAVE_STRSIGNAL)
CHECK_FUNCTION_EXISTS_EX(splice EVENT__HAVE_SPLICE)
CHECK_FUNCTION_EXISTS_EX(strlcpy EVENT__HAVE_STRLCPY)
CHECK_FUNCTION_EXISTS_EX(strsep EVENT__HAVE_STRSEP)
@ -965,6 +966,7 @@ if (NOT EVENT__DISABLE_SAMPLES)
sample/hostcheck.c)
add_sample_prog(ON le-proxy
sample/le-proxy.c)
add_sample_prog(ON becat sample/becat.c ${WIN32_GETOPT})
endif()
set(SAMPLES_WOPT

View File

@ -140,6 +140,8 @@
(ptr)->internal_.pos_in_chain = 0; \
} while (0)
#define EVBUFFER_MAX_READ_DEFAULT 4096
static void evbuffer_chain_align(struct evbuffer_chain *chain);
static int evbuffer_chain_should_realign(struct evbuffer_chain *chain,
size_t datalen);
@ -370,6 +372,7 @@ evbuffer_new(void)
LIST_INIT(&buffer->callbacks);
buffer->refcnt = 1;
buffer->last_with_datap = &buffer->first;
buffer->max_read = EVBUFFER_MAX_READ_DEFAULT;
return (buffer);
}
@ -591,6 +594,26 @@ evbuffer_free(struct evbuffer *buffer)
evbuffer_decref_and_unlock_(buffer);
}
int evbuffer_set_max_read(struct evbuffer *buf, size_t max)
{
if (max > INT_MAX) {
return -1;
}
EVBUFFER_LOCK(buf);
buf->max_read = max;
EVBUFFER_UNLOCK(buf);
return 0;
}
size_t evbuffer_get_max_read(struct evbuffer *buf)
{
size_t result;
EVBUFFER_LOCK(buf);
result = buf->max_read;
EVBUFFER_UNLOCK(buf);
return result;
}
void
evbuffer_lock(struct evbuffer *buf)
{
@ -607,13 +630,9 @@ size_t
evbuffer_get_length(const struct evbuffer *buffer)
{
size_t result;
EVBUFFER_LOCK(buffer);
result = (buffer->total_len);
result = buffer->total_len;
EVBUFFER_UNLOCK(buffer);
return result;
}
@ -2204,8 +2223,6 @@ evbuffer_expand(struct evbuffer *buf, size_t datlen)
#endif
#define NUM_READ_IOVEC 4
#define EVBUFFER_MAX_READ 4096
/** Helper function to figure out which space to use for reading data into
an evbuffer. Internal use only.
@ -2261,18 +2278,18 @@ static int
get_n_bytes_readable_on_socket(evutil_socket_t fd)
{
#if defined(FIONREAD) && defined(_WIN32)
unsigned long lng = EVBUFFER_MAX_READ;
unsigned long lng = EVBUFFER_MAX_READ_DEFAULT;
if (ioctlsocket(fd, FIONREAD, &lng) < 0)
return -1;
/* Can overflow, but mostly harmlessly. XXXX */
return (int)lng;
#elif defined(FIONREAD)
int n = EVBUFFER_MAX_READ;
int n = EVBUFFER_MAX_READ_DEFAULT;
if (ioctl(fd, FIONREAD, &n) < 0)
return -1;
return n;
#else
return EVBUFFER_MAX_READ;
return EVBUFFER_MAX_READ_DEFAULT;
#endif
}
@ -2300,8 +2317,8 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
}
n = get_n_bytes_readable_on_socket(fd);
if (n <= 0 || n > EVBUFFER_MAX_READ)
n = EVBUFFER_MAX_READ;
if (n <= 0 || n > (int)buf->max_read)
n = (int)buf->max_read;
if (howmuch < 0 || howmuch > n)
howmuch = n;

View File

@ -315,14 +315,12 @@ bufferevent_init_common_(struct bufferevent_private *bufev_private,
if (!bufev->input) {
if ((bufev->input = evbuffer_new()) == NULL)
return -1;
goto err;
}
if (!bufev->output) {
if ((bufev->output = evbuffer_new()) == NULL) {
evbuffer_free(bufev->input);
return -1;
}
if ((bufev->output = evbuffer_new()) == NULL)
goto err;
}
bufev_private->refcnt = 1;
@ -334,7 +332,8 @@ bufferevent_init_common_(struct bufferevent_private *bufev_private,
bufev->be_ops = ops;
bufferevent_ratelim_init_(bufev_private);
if (bufferevent_ratelim_init_(bufev_private))
goto err;
/*
* Set to EV_WRITE so that using bufferevent_write is going to
@ -345,20 +344,14 @@ bufferevent_init_common_(struct bufferevent_private *bufev_private,
#ifndef EVENT__DISABLE_THREAD_SUPPORT
if (options & BEV_OPT_THREADSAFE) {
if (bufferevent_enable_locking_(bufev, NULL) < 0) {
/* cleanup */
evbuffer_free(bufev->input);
evbuffer_free(bufev->output);
bufev->input = NULL;
bufev->output = NULL;
return -1;
}
if (bufferevent_enable_locking_(bufev, NULL) < 0)
goto err;
}
#endif
if ((options & (BEV_OPT_DEFER_CALLBACKS|BEV_OPT_UNLOCK_CALLBACKS))
== BEV_OPT_UNLOCK_CALLBACKS) {
event_warnx("UNLOCK_CALLBACKS requires DEFER_CALLBACKS");
return -1;
goto err;
}
if (options & BEV_OPT_UNLOCK_CALLBACKS)
event_deferred_cb_init_(
@ -379,6 +372,17 @@ bufferevent_init_common_(struct bufferevent_private *bufev_private,
evbuffer_set_parent_(bufev->output, bufev);
return 0;
err:
if (bufev->input) {
evbuffer_free(bufev->input);
bufev->input = NULL;
}
if (bufev->output) {
evbuffer_free(bufev->output);
bufev->output = NULL;
}
return -1;
}
void

View File

@ -855,14 +855,16 @@ int
bufferevent_set_max_single_read(struct bufferevent *bev, size_t size)
{
struct bufferevent_private *bevp;
int ret = 0;
BEV_LOCK(bev);
bevp = BEV_UPCAST(bev);
if (size == 0 || size > EV_SSIZE_MAX)
bevp->max_single_read = MAX_SINGLE_READ_DEFAULT;
else
bevp->max_single_read = size;
ret = evbuffer_set_max_read(bev->input, bevp->max_single_read);
BEV_UNLOCK(bev);
return 0;
return ret;
}
int
@ -1085,5 +1087,8 @@ bufferevent_ratelim_init_(struct bufferevent_private *bev)
bev->max_single_read = MAX_SINGLE_READ_DEFAULT;
bev->max_single_write = MAX_SINGLE_WRITE_DEFAULT;
if (evbuffer_set_max_read(bev->bev.input, bev->max_single_read))
return -1;
return 0;
}

View File

@ -380,6 +380,7 @@ AC_CHECK_FUNCS([ \
setrlimit \
sigaction \
signal \
strsignal \
splice \
strlcpy \
strsep \

View File

@ -100,6 +100,8 @@ struct evbuffer {
/** Total amount of bytes stored in all chains.*/
size_t total_len;
/** Maximum bytes per one read */
size_t max_read;
/** Number of bytes we have added to the buffer since we last tried to
* invoke callbacks. */

View File

@ -253,6 +253,9 @@
/* Define to 1 if you have the `signal' function. */
#cmakedefine EVENT__HAVE_SIGNAL 1
/* Define to 1 if you have the `strsignal' function. */
#cmakedefine EVENT__HAVE_STRSIGNAL 1
/* Define to 1 if you have the `splice' function. */
#cmakedefine EVENT__HAVE_SPLICE 1

View File

@ -158,6 +158,30 @@ struct evbuffer *evbuffer_new(void);
EVENT2_EXPORT_SYMBOL
void evbuffer_free(struct evbuffer *buf);
/**
Set maximum read buffer size
Default is 4096 and it works fine most of time, so before increasing the
default check carefully, since this has some negative effects (like memory
fragmentation and unfair resource distribution, i.e. some events will make
less progress than others).
@param buf pointer to the evbuffer
@param max buffer size
@return 0 on success, -1 on failure (if @max > INT_MAX).
*/
EVENT2_EXPORT_SYMBOL
int evbuffer_set_max_read(struct evbuffer *buf, size_t max);
/**
Get maximum read buffer size
@param buf pointer to the evbuffer
@return current maximum buffer read
*/
EVENT2_EXPORT_SYMBOL
size_t evbuffer_get_max_read(struct evbuffer *buf);
/**
Enable locking on an evbuffer so that it can safely be used by multiple
threads at the same time.

View File

@ -890,6 +890,8 @@ int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev);
Set to 0 for a reasonable default.
Return 0 on success and -1 on failure.
@see evbuffer_set_max_read()
*/
EVENT2_EXPORT_SYMBOL
int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size);

623
sample/becat.c Normal file
View File

@ -0,0 +1,623 @@
/**
* This is an analog of nc/ncat/telnet that uses libevent's bufferevents
*
* TODO:
* - optional SSL
*/
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/event-config.h>
#include "../util-internal.h"
#include <event2/bufferevent_ssl.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "openssl-compat.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <getopt.h>
#include <io.h>
#include <fcntl.h>
#else /* _WIN32 */
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#endif /* !_WIN32 */
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#ifdef __GNUC__
#define CHECK_FMT(a,b) __attribute__((format(printf, a, b)))
#else
#define CHECK_FMT(a,b)
#endif
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
static int verbose;
struct addr
{
int port;
char *address;
};
struct options
{
struct addr src;
struct addr dst;
int max_read;
struct {
int listen:1;
int keep:1;
int ssl:1;
};
};
struct ssl_context
{
SSL_CTX *ctx;
EVP_PKEY *pkey;
X509 *cert;
};
struct context
{
struct options *opts;
struct ssl_context ssl;
struct bufferevent *in;
struct bufferevent *out;
FILE *fout;
};
static void info(const char *fmt, ...) CHECK_FMT(1,2);
static void error(const char *fmt, ...) CHECK_FMT(1,2);
static void info(const char *fmt, ...)
{
va_list ap;
if (!verbose)
return;
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
va_end(ap);
}
static void error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
static void be_free(struct bufferevent **bevp)
{
evutil_socket_t fd;
struct bufferevent *bev = *bevp;
if (!bev)
return;
fd = bufferevent_getfd(bev);
info("Freeing bufferevent with fd=%d\n", fd);
bufferevent_free(bev);
*bevp = NULL;
}
static struct bufferevent *
be_new(struct context *ctx, struct event_base *base, evutil_socket_t fd)
{
SSL *ssl = NULL;
struct bufferevent *bev = NULL;
int flags = BEV_OPT_CLOSE_ON_FREE;
enum bufferevent_ssl_state state = BUFFEREVENT_SSL_CONNECTING;
if (fd != -1)
state = BUFFEREVENT_SSL_ACCEPTING;
if (ctx->opts->ssl) {
ssl = SSL_new(ctx->ssl.ctx);
if (!ssl)
goto err;
if (SSL_use_certificate(ssl, ctx->ssl.cert) != 1)
goto err;
if (SSL_use_PrivateKey(ssl, ctx->ssl.pkey) != 1)
goto err;
bev = bufferevent_openssl_socket_new(base, fd, ssl, state, flags);
} else {
bev = bufferevent_socket_new(base, fd, flags);
}
if (ctx->opts->max_read != -1) {
if (bufferevent_set_max_single_read(bev, ctx->opts->max_read))
goto err;
}
return bev;
err:
if (ssl)
SSL_free(ssl);
return NULL;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
static inline void ssl_init(void)
{
SSL_library_init();
ERR_load_crypto_strings();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
}
#else /* OPENSSL_VERSION_NUMBER */
static inline void ssl_init(void) {}
#endif /* OPENSSL_VERSION_NUMBER */
static void ssl_ctx_free(struct ssl_context *ssl)
{
SSL_CTX_free(ssl->ctx);
EVP_PKEY_free(ssl->pkey);
X509_free(ssl->cert);
}
static int ssl_load_key(struct ssl_context *ssl)
{
int err = 1;
BIGNUM *bn;
RSA *key;
ssl->pkey = EVP_PKEY_new();
bn = BN_new();
if (BN_set_word(bn, RSA_F4) != 1)
goto err;
/** Will be freed with ctx.pkey */
key = RSA_new();
if (RSA_generate_key_ex(key, 2048, bn, NULL) != 1)
goto err;
if (EVP_PKEY_assign_RSA(ssl->pkey, key) != 1)
goto err;
err = 0;
err:
BN_free(bn);
return err;
}
static int ssl_load_cert(struct ssl_context *ssl)
{
X509_NAME *name;
ssl->cert = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(ssl->cert), 1);
X509_gmtime_adj(X509_get_notBefore(ssl->cert), 0);
/** 1 year lifetime */
X509_gmtime_adj(X509_get_notAfter(ssl->cert),
(long)time(NULL) + 365 * 86400);
X509_set_pubkey(ssl->cert, ssl->pkey);
name = X509_get_subject_name(ssl->cert);
X509_NAME_add_entry_by_txt(
name, "C", MBSTRING_ASC, (unsigned char *)"--", -1, -1, 0);
X509_NAME_add_entry_by_txt(
name, "O", MBSTRING_ASC, (unsigned char *)"<NULL>", -1, -1, 0);
X509_NAME_add_entry_by_txt(
name, "CN", MBSTRING_ASC, (unsigned char *)"*", -1, -1, 0);
X509_set_issuer_name(ssl->cert, name);
X509_sign(ssl->cert, ssl->pkey, EVP_sha1());
return 0;
}
static int ssl_ctx_init(struct ssl_context *ssl)
{
const SSL_METHOD *method;
ssl_init();
method = TLS_method();
if (!method)
goto err;
ssl->ctx = SSL_CTX_new(method);
SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_cipher_list(ssl->ctx,
"RC4:AES128-SHA:AES:CAMELLIA128-SHA:!ADH:!aNULL:!DH:!EDH:!eNULL:!LOW:!SSLv2:!EXP:!NULL");
SSL_CTX_set_options(ssl->ctx,
SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_COMPRESSION);
if (ssl_load_key(ssl))
goto err;
if (ssl_load_cert(ssl))
goto err;
return 0;
err:
ssl_ctx_free(ssl);
return 1;
}
static void print_usage(FILE *out, const char *name)
{
fprintf(out, "Syntax: %s [ OPTS ] [hostname] [port]\n", name);
fprintf(out,
"\n"
"ncat like utility\n"
"\n"
" -p Specify source port to use\n"
" -s Specify source address to use (*does* affect -l, unlike ncat)\n"
" -l Bind and listen for incoming connections\n"
" -k Accept multiple connections in listen mode\n"
" -S Connect or listen with SSL\n"
"\n"
" -v Increase verbosity\n"
" -h Print usage\n"
);
}
static struct options parse_opts(int argc, char **argv)
{
struct options o;
int opt;
memset(&o, 0, sizeof(o));
o.src.port = o.dst.port = 10024;
o.max_read = -1;
while ((opt = getopt(argc, argv, "p:s:R:" "lkSvh")) != -1) {
switch (opt) {
case 'p': o.src.port = atoi(optarg); break;
case 's': o.src.address = strdup("127.1"); break;
case 'R': o.max_read = atoi(optarg); break;
case 'l': o.listen = 1; break;
case 'k': o.keep = 1; break;
case 'S': o.ssl = 1; break;
/**
* TODO: implement other bits:
* - filters
* - pair
* - watermarks
* - ratelimits
*/
case 'v': ++verbose; break;
case 'h':
print_usage(stdout, argv[0]);
exit(EXIT_SUCCESS);
default:
fprintf(stderr, "Unknown option -%c\n", opt); break;
exit(EXIT_FAILURE);
}
}
if ((argc-optind) > 1) {
o.dst.address = strdup(argv[optind]);
++optind;
}
if ((argc-optind) > 1) {
o.dst.port = atoi(optarg);
++optind;
}
if ((argc-optind) > 1) {
print_usage(stderr, argv[0]);
exit(1);
}
if (!o.src.address)
o.src.address = strdup("127.1");
if (!o.dst.address)
o.dst.address = strdup("127.1");
return o;
}
#ifndef EVENT__HAVE_STRSIGNAL
static inline const char* strsignal(evutil_socket_t sig) { return "Signal"; }
#endif
static void do_term(evutil_socket_t sig, short events, void *arg)
{
struct event_base *base = arg;
event_base_loopexit(base, NULL);
fprintf(stderr, "%s(" EV_SOCK_FMT "), Terminating\n",
strsignal(sig), EV_SOCK_ARG(sig));
}
static ev_socklen_t
make_address(struct sockaddr_storage *ss, const char *address, ev_uint16_t port)
{
struct evutil_addrinfo *ai = NULL;
struct evutil_addrinfo hints;
char strport[NI_MAXSERV];
int ai_result;
ev_socklen_t ret = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG;
evutil_snprintf(strport, sizeof(strport), "%d", port);
if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai)) != 0) {
return 0;
}
if (!ai)
return 0;
if (ai->ai_addrlen > sizeof(*ss)) {
evutil_freeaddrinfo(ai);
return 0;
}
memcpy(ss, ai->ai_addr, ai->ai_addrlen);
/** Hm.. I do not like this cast. */
ret = (ev_socklen_t)ai->ai_addrlen;
evutil_freeaddrinfo(ai);
return ret;
}
static void be_ssl_errors(struct bufferevent *bev)
{
int err;
while ((err = bufferevent_get_openssl_error(bev))) {
const char *msg = ERR_reason_error_string(err);
const char *lib = ERR_lib_error_string(err);
const char *func = ERR_func_error_string(err);
error("ssl/err=%d/%s in %s %s\n", err, msg, lib, func);
}
}
static int event_cb_(struct bufferevent *bev, short what, int ssl, int stop)
{
struct event_base *base = bufferevent_get_base(bev);
evutil_socket_t fd = bufferevent_getfd(bev);
if (what & BEV_EVENT_CONNECTED) {
info("Connected\n");
return 0;
}
if (ssl && what & BEV_EVENT_ERROR) {
be_ssl_errors(bev);
}
if (stop)
event_base_loopexit(base, NULL);
error("Got 0x%x event on fd=%d. Terminating connection\n", what, fd);
be_free(&bev);
return 1;
}
static void read_cb(struct bufferevent *bev, void *arg)
{
struct context *ctx = arg;
struct evbuffer *in = bufferevent_get_input(bev);
evbuffer_write(in, fileno(ctx->fout));
}
static void write_cb(struct bufferevent *bev, void *arg)
{
struct context *ctx = arg;
bufferevent_write_buffer(bev, bufferevent_get_input(ctx->in));
}
static void server_event_cb(struct bufferevent *bev, short what, void *arg)
{
struct context *ctx = arg;
EVUTIL_ASSERT(bev == ctx->out);
if (!event_cb_(bev, what, ctx->opts->ssl, !ctx->opts->keep))
return;
ctx->out = NULL;
}
static void
accept_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *arg)
{
char buffer[128];
struct context *ctx = arg;
struct bufferevent *bev = NULL;
struct event_base *base = evconnlistener_get_base(listener);
if (!ctx->opts->keep)
evconnlistener_disable(listener);
info("Accepting %s (fd=%d)\n",
evutil_format_sockaddr_port_(sa, buffer, sizeof(buffer)-1), fd);
bev = be_new(ctx, base, fd);
if (!bev) {
error("Cannot make bufferevent for fd=%d\n", fd);
goto err;
}
bufferevent_setcb(bev, read_cb, write_cb, server_event_cb, ctx);
bufferevent_enable(bev, EV_READ|EV_WRITE);
/** TODO: support multiple bevs */
EVUTIL_ASSERT(!ctx->out);
ctx->out = bev;
if (bufferevent_enable(ctx->in, EV_READ)) {
error("Cannot monitor EV_READ on input\n");
goto err;
}
return;
err:
be_free(&bev);
}
static void client_event_cb(struct bufferevent *bev, short what, void *arg)
{
struct context *ctx = arg;
if (!event_cb_(bev, what, ctx->opts->ssl, 1))
return;
ctx->out = NULL;
}
static void in_event_cb(struct bufferevent *bev, short what, void *arg)
{
struct context *ctx = arg;
if (!event_cb_(bev, what, ctx->opts->ssl, 1))
return;
ctx->in = NULL;
be_free(&ctx->out);
}
static void trigger_bev_write_cb(struct bufferevent *bev, void *arg)
{
struct context *ctx = arg;
if (!ctx->out)
return;
bufferevent_trigger(ctx->out, EV_WRITE, 0);
}
int main(int argc, char **argv)
{
struct event_base *base = NULL;
struct event *term = NULL;
struct evconnlistener *listener = NULL;
struct bufferevent *bev = NULL;
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
ev_socklen_t ss_len;
char buffer[128];
struct context ctx;
struct options o = parse_opts(argc, argv);
int err = EXIT_SUCCESS;
memset(&ctx, 0, sizeof(ctx));
ctx.opts = &o;
base = event_base_new();
if (!base)
goto err;
term = evsignal_new(base, SIGINT, do_term, base);
if (!term)
goto err;
if (event_add(term, NULL))
goto err;
#ifdef _WIN32
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
}
#else
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
goto err;
#endif
if (o.ssl && ssl_ctx_init(&ctx.ssl))
goto err;
if (o.listen) {
int flags = 0;
flags |= LEV_OPT_CLOSE_ON_FREE;
flags |= LEV_OPT_CLOSE_ON_EXEC;
flags |= LEV_OPT_REUSEABLE;
ss_len = make_address(&ss, o.src.address, o.src.port);
if (!ss_len) {
error("Cannot make address from %s:%d\n",
o.src.address, o.src.port);
goto err;
}
info("Listening on %s\n",
evutil_format_sockaddr_port_(sa, buffer, sizeof(buffer)-1));
listener = evconnlistener_new_bind(base, accept_cb, &ctx, flags, -1, sa, ss_len);
if (!listener) {
error("Cannot listen\n");
goto err;
}
} else {
ss_len = make_address(&ss, o.dst.address, o.dst.port);
if (!ss_len) {
error("Cannot make address from %s:%d\n",
o.src.address, o.src.port);
goto err;
}
info("Connecting to %s\n",
evutil_format_sockaddr_port_(sa, buffer, sizeof(buffer)-1));
bev = be_new(&ctx, base, -1);
if (!bev) {
error("Cannot make bufferevent\n");
goto err;
}
bufferevent_setcb(bev, read_cb, write_cb, client_event_cb, &ctx);
if (bufferevent_enable(bev, EV_READ|EV_WRITE)) {
error("Cannot monitor EV_READ|EV_WRITE for client\n");
goto err;
}
if (bufferevent_socket_connect(bev, sa, ss_len)) {
info("Connection failed\n");
goto err;
}
}
ctx.out = bev;
ctx.fout = stdout;
ctx.in = bufferevent_socket_new(base, STDIN_FILENO, 0);
if (o.max_read != -1) {
if (bufferevent_set_max_single_read(ctx.in, o.max_read))
goto err;
}
if (!ctx.in) {
error("Cannot create input bufferevent\n");
goto err;
}
bufferevent_setcb(ctx.in, trigger_bev_write_cb, NULL, in_event_cb, &ctx);
if (ctx.out && bufferevent_enable(ctx.in, EV_READ)) {
error("Cannot monitor EV_READ on input\n");
goto err;
}
bufferevent_disable(ctx.in, EV_WRITE);
if (!event_base_dispatch(base))
goto out;
err:
if (!err)
err = EXIT_FAILURE;
out:
if (term)
event_free(term);
be_free(&ctx.in);
be_free(&ctx.out);
if (listener)
evconnlistener_free(listener);
if (base)
event_base_free(base);
free(o.src.address);
free(o.dst.address);
ssl_ctx_free(&ctx.ssl);
#ifdef _WIN32
WSACleanup();
#endif
return err;
}

View File

@ -19,6 +19,11 @@ sample_le_proxy_SOURCES = sample/le-proxy.c
sample_le_proxy_LDADD = libevent.la libevent_openssl.la $(OPENSSL_LIBS) $(OPENSSL_LIBADD)
sample_le_proxy_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS)
SAMPLES += sample/becat
sample_becat_SOURCES = sample/becat.c
sample_becat_LDADD = libevent.la libevent_openssl.la $(OPENSSL_LIBS) $(OPENSSL_LIBADD)
sample_becat_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS)
SAMPLES += sample/https-client
sample_https_client_SOURCES = \
sample/https-client.c \