Edit

kc3-lang/libevent/sample/le-proxy.c

Branch :

  • Show log

    Commit

  • Author : Nick Mathewson
    Date : 2009-07-28 04:03:57
    Hash : 709c21c4
    Message : Bufferevent support for openssl. This code adds a new Bufferevent type that is only compiled when the openssl library is present. It supports using an SSL object and an event alert mechanism, which can either be an fd or an underlying bufferevent. There is still more work to do: the unit tests are incomplete, and we need to support flush and shutdown much better. Sometimes events are generated needlessly: this will hose performance. There's a new encrypting proxy in sample/le-proxy.c. This code has only been tested on OSX, and nowhere else. svn:r1382

  • sample/le-proxy.c
  • #include <stdio.h>
    #include <assert.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #include <event2/bufferevent_ssl.h>
    #include <event2/bufferevent.h>
    #include <event2/buffer.h>
    #include <event2/listener.h>
    #include <event2/util.h>
    
    #include <openssl/ssl.h>
    #include <openssl/err.h>
    #include <openssl/rand.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;
    static int use_wrapper = 1;
    
    static SSL_CTX *ssl_ctx = NULL;
    
    static void
    readcb(struct bufferevent *bev, void *ctx)
    {
    	struct bufferevent *partner = ctx;
    	struct evbuffer *src, *dst;
    	src = bufferevent_get_input(bev);
    	if (!partner) {
    		evbuffer_drain(src, evbuffer_get_length(src));
    		return;
    	}
    	dst = bufferevent_get_output(partner);
    
    	evbuffer_add_buffer(dst, src);
    }
    
    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("maybe an error");
    
    		if (partner) {
    			/* Flush all pending data */
    			readcb(bev, ctx);
    
    			/* Disconnect this one from the partner. */
    			bufferevent_setcb(partner,
    			    NULL, close_on_finished_writecb,
    			    eventcb, NULL);
    		}
    		bufferevent_free(bev);
    	}
    }
    
    static void
    syntax(void)
    {
    	fputs("Syntax:\n", stderr);
    	fputs("   le-proxy [-s] [-W] <listen-on-addr> <connect-to-addr>\n", stderr);
    	fputs("Example:\n", stderr);
    	fputs("   le-proxy 127.0.0.1:8888 1.2.3.4:80\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;
    	/* Create two linked bufferevent objects: one to connect, one for the
    	 * new connection */
    	b_in = bufferevent_socket_new(base, fd,
    	    BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
    
    	if (!ssl_ctx || use_wrapper)
    		b_out = bufferevent_socket_new(base, -1,
    		    BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
    	else {
    		SSL *ssl = SSL_new(ssl_ctx);
     		b_out = bufferevent_openssl_socket_new(base, -1, ssl,
    		    BUFFEREVENT_SSL_CONNECTING,
    		    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;
    	}
    
    	if (ssl_ctx && use_wrapper) {
    		struct bufferevent *b_ssl;
    		SSL *ssl = SSL_new(ssl_ctx);
    		b_ssl = bufferevent_openssl_filter_new(base,
    		    b_out, ssl, BUFFEREVENT_SSL_CONNECTING,
    		    BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
    		if (!b_ssl) {
    			perror("Bufferevent_openssl_new");
    			bufferevent_free(b_out);
    			bufferevent_free(b_in);
    		}
    		b_out = b_ssl;
    	}
    
    	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;
    
    	int use_ssl = 0;
    	struct evconnlistener *listener;
    
    	if (argc < 3)
    		syntax();
    
    	for (i=1; i < argc; ++i) {
    		if (!strcmp(argv[i], "-s")) {
    			use_ssl = 1;
    		} else if (!strcmp(argv[i], "-W")) {
    			use_wrapper = 0;
    		} else 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) {
    		int p = atoi(argv[i]);
    		struct sockaddr_in *sin = (struct sockaddr_in*)&listen_on_addr;
    		if (p < 1 || p > 65535)
    			syntax();
    		sin->sin_port = htons(p);
    		sin->sin_addr.s_addr = htonl(0x7f000001);
    		sin->sin_family = AF_INET;
    		socklen = sizeof(struct sockaddr_in);
    	}
    
    	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;
    	}
    
    	if (use_ssl) {
    		int r;
    		SSL_library_init();
    		ERR_load_crypto_strings();
    		SSL_load_error_strings();
    		OpenSSL_add_all_algorithms();
    		r = RAND_poll();
    		if (r == 0) {
    			fprintf(stderr, "RAND_poll() failed.\n");
    			return 1;
    		}
    		ssl_ctx = SSL_CTX_new(SSLv23_method());
    	}
    
    	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);
    
    	return 0;
    }