Skip to content

Instantly share code, notes, and snippets.

@valmat
Created December 1, 2022 10:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save valmat/077d1bcd401820b6a8d20daf4a567fd3 to your computer and use it in GitHub Desktop.
Save valmat/077d1bcd401820b6a8d20daf4a567fd3 to your computer and use it in GitHub Desktop.
Simple and fast async libevent tcp proxy
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
static struct event_base *base;
static struct sockaddr_storage listen_on_addr;
static struct sockaddr_storage connect_to_addr;
static int connect_to_addrlen;
#define MAX_OUTPUT (512*1024)
static void drained_writecb(struct bufferevent *bev, void *ctx);
static void eventcb(struct bufferevent *bev, short what, void *ctx);
static void readcb(struct bufferevent *bev, void *ctx)
{
struct bufferevent *partner = ctx;
struct evbuffer *src, *dst;
size_t len;
src = bufferevent_get_input(bev);
len = evbuffer_get_length(src);
if (!partner) {
printf("Failed to connect with Partner\n");
evbuffer_drain(src, len);
return;
}
dst = bufferevent_get_output(partner);
evbuffer_add_buffer(dst, src);
if (evbuffer_get_length(dst) >= MAX_OUTPUT) {
bufferevent_setcb(partner, readcb, drained_writecb,
eventcb, bev);
bufferevent_setwatermark(partner, EV_WRITE, MAX_OUTPUT/2,
MAX_OUTPUT);
bufferevent_disable(bev, EV_READ);
}
}
static void drained_writecb(struct bufferevent *bev, void *ctx)
{
struct bufferevent *partner = ctx;
bufferevent_setcb(bev, readcb, NULL, eventcb, partner);
bufferevent_setwatermark(bev, EV_WRITE, 0, 0);
if (partner)
bufferevent_enable(partner, EV_READ);
}
static void
close_on_finished_writecb(struct bufferevent *bev, void *ctx)
{
struct evbuffer *b = bufferevent_get_output(bev);
if (evbuffer_get_length(b) == 0) {
bufferevent_free(bev);
}
}
static void
eventcb(struct bufferevent *bev, short what, void *ctx)
{
struct bufferevent *partner = ctx;
if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
if (what & BEV_EVENT_ERROR) {
perror("connection error");
}
if (partner) {
readcb(bev, ctx);
if (evbuffer_get_length(
bufferevent_get_output(partner))) {
bufferevent_setcb(partner,
NULL, close_on_finished_writecb,
eventcb, NULL);
bufferevent_disable(partner, EV_READ);
} else {
bufferevent_free(partner);
}
}
bufferevent_free(bev);
}
}
static void
syntax(void)
{
fputs("Syntax:\n", stderr);
fputs(" program-name <listen-on-addr> <connect-to-addr>\n", stderr);
fputs("Example:\n", stderr);
fputs(" program-name 0.0.0.0:1010 127.0.0.1:5050\n", stderr);
exit(1);
}
static void
accept_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *a, int slen, void *p)
{
struct bufferevent *b_out, *b_in;
b_in = bufferevent_socket_new(base, fd,
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
b_out = bufferevent_socket_new(base, -1,
BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
assert(b_in && b_out);
if (bufferevent_socket_connect(b_out,
(struct sockaddr*)&connect_to_addr, connect_to_addrlen)<0) {
perror("bufferevent_socket_connect");
bufferevent_free(b_out);
bufferevent_free(b_in);
return;
}
bufferevent_setcb(b_in, readcb, NULL, eventcb, b_out);
bufferevent_setcb(b_out, readcb, NULL, eventcb, b_in);
bufferevent_enable(b_in, EV_READ|EV_WRITE);
bufferevent_enable(b_out, EV_READ|EV_WRITE);
}
int main(int argc, char **argv)
{
int i;
int socklen;
struct evconnlistener *listener;
if (argc < 3)
syntax();
for (i=1; i < argc; ++i) {
if (argv[i][0] == '-') {
syntax();
} else
break;
}
if (i+2 != argc)
syntax();
memset(&listen_on_addr, 0, sizeof(listen_on_addr));
socklen = sizeof(listen_on_addr);
if (evutil_parse_sockaddr_port(argv[i],
(struct sockaddr*)&listen_on_addr, &socklen)<0)
syntax();
memset(&connect_to_addr, 0, sizeof(connect_to_addr));
connect_to_addrlen = sizeof(connect_to_addr);
if (evutil_parse_sockaddr_port(argv[i+1],
(struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0)
syntax();
base = event_base_new();
if (!base) {
perror("event_base_new()");
return 1;
}
listener = evconnlistener_new_bind(base, accept_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE,
-1, (struct sockaddr*)&listen_on_addr, socklen);
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
/*
gcc -Wall -Wextra -O3 -flto -I"../../libevent-2.1.12-stable/include/" -c proxy.c -o proxy.o
gcc -Wall -Wextra -O3 -flto proxy.o ../../libevent-2.1.12-stable/.libs/libevent.a -o proxy.bin
strip --strip-all proxy.bin
gcc -Wall -Wextra -O3 -flto -c proxy.c -o proxy.o
gcc -Wall -Wextra -O3 -flto proxy.o /usr/lib/x86_64-linux-gnu/libevent.a -o proxy.bin
strip --strip-all proxy.bin
rm proxy.o
source: // https://imrahulg.wordpress.com/2016/02/28/simple-and-fast-proxy/
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment