Beispiel #1
0
/**
   Print a CMETHOD/SMETHOD line saying that we launched 'transport'
   with 'cfg' under 'proxy'.
*/
static void
print_method_line(const char *transport, const managed_proxy_t *proxy,
                  config_t *cfg)
{
  struct evconnlistener *listener = get_evconnlistener_by_config(cfg);
  obfs_assert(listener);

  struct sockaddr_in saddr;
  memset(&saddr,0,sizeof(struct sockaddr_in));
  socklen_t slen = sizeof(saddr);

  obfs_assert(!(getsockname(evconnlistener_get_fd(listener),
                            (struct sockaddr *)&saddr, &slen) < 0));

  if (proxy->is_server) {
    print_protocol_line("%s %s %s:%hu\n",
                        PROTO_SMETHOD,
                        transport,
                        inet_ntoa(saddr.sin_addr),
                        ntohs(saddr.sin_port));
  } else {
    print_protocol_line("%s %s %s %s:%hu\n",
                        PROTO_CMETHOD,
                        transport,
                        "socks5",
                        inet_ntoa(saddr.sin_addr),
                        ntohs(saddr.sin_port));

  }
}
Beispiel #2
0
int
regress_get_listener_addr(struct evconnlistener *lev,
    struct sockaddr *sa, ev_socklen_t *socklen)
{
	evutil_socket_t s = evconnlistener_get_fd(lev);
	if (s <= 0)
		return -1;
	return getsockname(s, sa, socklen);
}
Beispiel #3
0
/* Start the server listening on a random port and start the first client. */
static void
start_loop(void)
{
	struct event_base *base;
	struct evconnlistener *listener;
	struct sockaddr_storage ss;
	ev_socklen_t socklen = sizeof(ss);
	evutil_socket_t fd;

	base = event_base_new();
	if (base == NULL) {
		puts("Could not open event base!");
		exit(1);
	}

	listener = evconnlistener_new_bind(base, listener_accept_cb, NULL,
	    LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
	    -1, (struct sockaddr *)&saddr, sizeof(saddr));
	if (listener == NULL) {
		my_perror("Could not create listener!");
		exit(1);
	}
	fd = evconnlistener_get_fd(listener);
	if (fd < 0) {
		puts("Couldn't get fd from listener");
		exit(1);
	}
	if (getsockname(fd, (struct sockaddr *)&ss, &socklen) < 0) {
		my_perror("getsockname()");
		exit(1);
	}
	memcpy(&saddr, &ss, sizeof(saddr));
	if (saddr.sin_family != AF_INET) {
		puts("AF mismatch from getsockname().");
		exit(1);
	}

	start_client(base);

	event_base_dispatch(base);
}
Beispiel #4
0
int main(int argc, char* argv[])
{
	daemon(1, 1);
	glog_init(argv[0]);

	sockaddr addr;
	int len = sizeof(sockaddr);
	evutil_parse_sockaddr_port("127.0.0.1:5000", &addr, &len);

	event_base* pbase = event_base_new();
	char test[32] = "test data";
	evconnlistener* plistener 
		= evconnlistener_new_bind(pbase, listener_cb, reinterpret_cast<void*>(test), LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10, &addr, len);

	LOG(INFO) << evconnlistener_get_fd(plistener);

	event_base_dispatch(pbase);

	evconnlistener_free(plistener);
	event_base_free(pbase);

	return 0;
}
Beispiel #5
0
static void
regress_pick_a_port(void *arg)
{
	struct basic_test_data *data = arg;
	struct event_base *base = data->base;
	struct evconnlistener *listener1 = NULL, *listener2 = NULL;
	//struct event *connecting;
	struct sockaddr_in sin;
	int count1 = 2, count2 = 1;
	struct sockaddr_storage ss1, ss2;
	struct sockaddr_in *sin1, *sin2;
	ev_socklen_t slen1 = sizeof(ss1), slen2 = sizeof(ss2);
	unsigned int flags =
	    LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC;

	evutil_socket_t fd1 = -1, fd2 = -1, fd3 = -1;

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
	sin.sin_port = 0; /* "You pick!" */

	listener1 = evconnlistener_new_bind(base, acceptcb, &count1,
	    flags, -1, (struct sockaddr *)&sin, sizeof(sin));
	tt_assert(listener1);
	listener2 = evconnlistener_new_bind(base, acceptcb, &count2,
	    flags, -1, (struct sockaddr *)&sin, sizeof(sin));
	tt_assert(listener2);

	tt_int_op(evconnlistener_get_fd(listener1), >=, 0);
	tt_int_op(evconnlistener_get_fd(listener2), >=, 0);
	tt_assert(getsockname(evconnlistener_get_fd(listener1),
		(struct sockaddr*)&ss1, &slen1) == 0);
	tt_assert(getsockname(evconnlistener_get_fd(listener2),
		(struct sockaddr*)&ss2, &slen2) == 0);
	tt_int_op(ss1.ss_family, ==, AF_INET);
	tt_int_op(ss2.ss_family, ==, AF_INET);

	sin1 = (struct sockaddr_in*)&ss1;
	sin2 = (struct sockaddr_in*)&ss2;
	tt_int_op(ntohl(sin1->sin_addr.s_addr), ==, 0x7f000001);
	tt_int_op(ntohl(sin2->sin_addr.s_addr), ==, 0x7f000001);
	tt_int_op(sin1->sin_port, !=, sin2->sin_port);

	tt_ptr_op(evconnlistener_get_base(listener1), ==, base);
	tt_ptr_op(evconnlistener_get_base(listener2), ==, base);

	fd1 = fd2 = fd3 = -1;
	evutil_socket_connect(&fd1, (struct sockaddr*)&ss1, slen1);
	evutil_socket_connect(&fd2, (struct sockaddr*)&ss1, slen1);
	evutil_socket_connect(&fd3, (struct sockaddr*)&ss2, slen2);

#ifdef WIN32
	Sleep(100); /* XXXX this is a stupid stopgap. */
#endif
	event_base_dispatch(base);

	tt_int_op(count1, ==, 0);
	tt_int_op(count2, ==, 0);

end:
	if (fd1>=0)
		EVUTIL_CLOSESOCKET(fd1);
	if (fd2>=0)
		EVUTIL_CLOSESOCKET(fd2);
	if (fd3>=0)
		EVUTIL_CLOSESOCKET(fd3);
}
Beispiel #6
0
static int
test_ratelimiting(void)
{
	struct event_base *base;
	struct sockaddr_in sin;
	struct evconnlistener *listener;

	struct sockaddr_storage ss;
	ev_socklen_t slen;

	struct bufferevent **bevs;
	struct client_state *states;
	struct bufferevent_rate_limit_group *group = NULL;

	int i;

	struct timeval tv;

	ev_uint64_t total_received;
	double total_sq_persec, total_persec;
	double variance;
	double expected_total_persec = -1.0, expected_avg_persec = -1.0;
	int ok = 1;
	struct event_config *base_cfg;

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
	sin.sin_port = 0; /* unspecified port */

	if (0)
		event_enable_debug_mode();

	base_cfg = event_config_new();

#ifdef _WIN32
	if (cfg_enable_iocp) {
		evthread_use_windows_threads();
		event_config_set_flag(base_cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
	}
#endif

	base = event_base_new_with_config(base_cfg);

	listener = evconnlistener_new_bind(base, echo_listenercb, base,
	    LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
	    (struct sockaddr *)&sin, sizeof(sin));

	slen = sizeof(ss);
	if (getsockname(evconnlistener_get_fd(listener), (struct sockaddr *)&ss,
		&slen) < 0) {
		perror("getsockname");
		return 1;
	}

	if (cfg_connlimit > 0) {
		conn_bucket_cfg = ev_token_bucket_cfg_new(
			cfg_connlimit, cfg_connlimit * 4,
			cfg_connlimit, cfg_connlimit * 4,
			&cfg_tick);
		assert(conn_bucket_cfg);
	}

	if (cfg_grouplimit > 0) {
		group_bucket_cfg = ev_token_bucket_cfg_new(
			cfg_grouplimit, cfg_grouplimit * 4,
			cfg_grouplimit, cfg_grouplimit * 4,
			&cfg_tick);
		group = ratelim_group = bufferevent_rate_limit_group_new(
			base, group_bucket_cfg);
		expected_total_persec = cfg_grouplimit;
		expected_avg_persec = cfg_grouplimit / cfg_n_connections;
		if (cfg_connlimit > 0 && expected_avg_persec > cfg_connlimit)
			expected_avg_persec = cfg_connlimit;
		if (cfg_min_share >= 0)
			bufferevent_rate_limit_group_set_min_share(
				ratelim_group, cfg_min_share);
	}

	if (expected_avg_persec < 0 && cfg_connlimit > 0)
		expected_avg_persec = cfg_connlimit;

	if (expected_avg_persec > 0)
		expected_avg_persec /= seconds_per_tick;
	if (expected_total_persec > 0)
		expected_total_persec /= seconds_per_tick;

	bevs = calloc(cfg_n_connections, sizeof(struct bufferevent *));
	states = calloc(cfg_n_connections, sizeof(struct client_state));

	for (i = 0; i < cfg_n_connections; ++i) {
		bevs[i] = bufferevent_socket_new(base, -1,
		    BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);
		assert(bevs[i]);
		bufferevent_setcb(bevs[i], discard_readcb, loud_writecb,
		    write_on_connectedcb, &states[i]);
		bufferevent_enable(bevs[i], EV_READ|EV_WRITE);
		bufferevent_socket_connect(bevs[i], (struct sockaddr *)&ss,
		    slen);
	}

	tv.tv_sec = cfg_duration - 1;
	tv.tv_usec = 995000;

	event_base_loopexit(base, &tv);

	event_base_dispatch(base);

	ratelim_group = NULL; /* So no more responders get added */

	for (i = 0; i < cfg_n_connections; ++i) {
		bufferevent_free(bevs[i]);
	}
	evconnlistener_free(listener);

	/* Make sure no new echo_conns get added to the group. */
	ratelim_group = NULL;

	/* This should get _everybody_ freed */
	while (n_echo_conns_open) {
		printf("waiting for %d conns\n", n_echo_conns_open);
		tv.tv_sec = 0;
		tv.tv_usec = 300000;
		event_base_loopexit(base, &tv);
		event_base_dispatch(base);
	}

	if (group)
		bufferevent_rate_limit_group_free(group);

	total_received = 0;
	total_persec = 0.0;
	total_sq_persec = 0.0;
	for (i=0; i < cfg_n_connections; ++i) {
		double persec = states[i].received;
		persec /= cfg_duration;
		total_received += states[i].received;
		total_persec += persec;
		total_sq_persec += persec*persec;
		printf("%d: %f per second\n", i+1, persec);
	}
	printf("   total: %f per second\n",
	    ((double)total_received)/cfg_duration);
	if (expected_total_persec > 0) {
		double diff = expected_total_persec -
		    ((double)total_received/cfg_duration);
		printf("  [Off by %lf]\n", diff);
		if (cfg_grouplimit_tolerance > 0 &&
		    fabs(diff) > cfg_grouplimit_tolerance) {
			fprintf(stderr, "Group bandwidth out of bounds\n");
			ok = 0;
		}
	}

	printf(" average: %f per second\n",
	    (((double)total_received)/cfg_duration)/cfg_n_connections);
	if (expected_avg_persec > 0) {
		double diff = expected_avg_persec - (((double)total_received)/cfg_duration)/cfg_n_connections;
		printf("  [Off by %lf]\n", diff);
		if (cfg_connlimit_tolerance > 0 &&
		    fabs(diff) > cfg_connlimit_tolerance) {
			fprintf(stderr, "Connection bandwidth out of bounds\n");
			ok = 0;
		}
	}

	variance = total_sq_persec/cfg_n_connections - total_persec*total_persec/(cfg_n_connections*cfg_n_connections);

	printf("  stddev: %f per second\n", sqrt(variance));
	if (cfg_stddev_tolerance > 0 &&
	    sqrt(variance) > cfg_stddev_tolerance) {
		fprintf(stderr, "Connection variance out of bounds\n");
		ok = 0;
	}

	event_base_free(base);
	free(bevs);
	free(states);

	return ok ? 0 : 1;
}
Beispiel #7
0
static void
test_bufferevent_connect_hostname(void *arg)
{
	struct basic_test_data *data = arg;
	struct evconnlistener *listener = NULL;
	struct bufferevent *be1=NULL, *be2=NULL, *be3=NULL, *be4=NULL, *be5=NULL;
	int be1_outcome=0, be2_outcome=0, be3_outcome=0, be4_outcome=0,
	    be5_outcome=0;
	struct evdns_base *dns=NULL;
	struct evdns_server_port *port=NULL;
	evutil_socket_t server_fd=-1;
	struct sockaddr_in sin;
	int listener_port=-1;
	ev_uint16_t dns_port=0;
	int n_accept=0, n_dns=0;
	char buf[128];

	be_connect_hostname_base = data->base;

	/* Bind an address and figure out what port it's on. */
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
	sin.sin_port = 0;
	listener = evconnlistener_new_bind(data->base, nil_accept_cb,
	    &n_accept,
	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC,
	    -1, (struct sockaddr *)&sin, sizeof(sin));
	listener_port = regress_get_socket_port(
		evconnlistener_get_fd(listener));

	port = regress_get_dnsserver(data->base, &dns_port, NULL,
	    be_getaddrinfo_server_cb, &n_dns);
	tt_assert(port);
	tt_int_op(dns_port, >=, 0);

	/* Start an evdns_base that uses the server as its resolver. */
	dns = evdns_base_new(data->base, 0);
	evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", dns_port);
	evdns_base_nameserver_ip_add(dns, buf);

	/* Now, finally, at long last, launch the bufferevents.  One should do
	 * a failing lookup IP, one should do a successful lookup by IP,
	 * and one should do a successful lookup by hostname. */
	be1 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE);
	be2 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE);
	be3 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE);
	be4 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE);
	be5 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE);

	bufferevent_setcb(be1, NULL, NULL, be_connect_hostname_event_cb,
	    &be1_outcome);
	bufferevent_setcb(be2, NULL, NULL, be_connect_hostname_event_cb,
	    &be2_outcome);
	bufferevent_setcb(be3, NULL, NULL, be_connect_hostname_event_cb,
	    &be3_outcome);
	bufferevent_setcb(be4, NULL, NULL, be_connect_hostname_event_cb,
	    &be4_outcome);
	bufferevent_setcb(be5, NULL, NULL, be_connect_hostname_event_cb,
	    &be5_outcome);

	/* Launch an async resolve that will fail. */
	tt_assert(!bufferevent_socket_connect_hostname(be1, dns, AF_INET,
		"nosuchplace.example.com", listener_port));
	/* Connect to the IP without resolving. */
	tt_assert(!bufferevent_socket_connect_hostname(be2, dns, AF_INET,
		"127.0.0.1", listener_port));
	/* Launch an async resolve that will succeed. */
	tt_assert(!bufferevent_socket_connect_hostname(be3, dns, AF_INET,
		"nobodaddy.example.com", listener_port));
	/* Use the blocking resolver.  This one will fail if your resolver
	 * can't resolve localhost to 127.0.0.1 */
	tt_assert(!bufferevent_socket_connect_hostname(be4, NULL, AF_INET,
		"localhost", listener_port));
	/* Use the blocking resolver with a nonexistent hostname. */
	tt_assert(!bufferevent_socket_connect_hostname(be5, NULL, AF_INET,
		"nonesuch.nowhere.example.com", 80));

	event_base_dispatch(data->base);

	tt_int_op(be1_outcome, ==, BEV_EVENT_ERROR);
	tt_int_op(be2_outcome, ==, BEV_EVENT_CONNECTED);
	tt_int_op(be3_outcome, ==, BEV_EVENT_CONNECTED);
	tt_int_op(be4_outcome, ==, BEV_EVENT_CONNECTED);
	tt_int_op(be5_outcome, ==, BEV_EVENT_ERROR);

	tt_int_op(n_accept, ==, 3);
	tt_int_op(n_dns, ==, 2);

end:
	if (listener)
		evconnlistener_free(listener);
	if (server_fd>=0)
		EVUTIL_CLOSESOCKET(server_fd);
	if (port)
                evdns_close_server_port(port);
	if (dns)
		evdns_base_free(dns, 0);
	if (be1)
		bufferevent_free(be1);
	if (be2)
		bufferevent_free(be2);
	if (be3)
		bufferevent_free(be3);
	if (be4)
		bufferevent_free(be4);
	if (be5)
		bufferevent_free(be5);
}
inline
static
void setup_relays(void)
{
    if(!*options.relay_ports)
    {
        debug("relays: missing ports");
        teardown_control();
        return;
    }

    const char *w = options.relay_ports;
    for(context.relays_count = 1; *w; ++ w)
        context.relays_count += *w == ',';

    debug("relays: setting up tcp channels port");
    evconnlistener_set_cb(  context.listener.tcp,
                            accept_tcp_channel,
                            NULL);

    debug("relays: setting up %d relay ports", context.relays_count);
    context.relays =
        (struct RelayListener *) malloc(
            context.relays_count * sizeof(struct RelayListener));

    w = options.relay_ports;
    struct RelayListener *cur = context.relays;
    uint8_t udp = 0;
    for(int c = 0; c < context.relays_count; ++ c)
    {
        char        proto[4];
        int32_t     bytes;

        if(sscanf(w, "%[^:]:%hu,%n", proto, &cur->port, &bytes) != 2)
        {
            debug("relays: invalid relay ports format");
            context.relays_count = c;
            teardown_control();
            return;
        }

        if(!strcmp(proto, "tcp"))
        {
            debug("relays: setting up tcp relay %d", cur->port);

            cur->proto = IPPROTO_TCP;
            struct sockaddr_in  relay; memset(&relay, 0, sizeof(relay));
            relay.sin_family = AF_INET;
            if(options.address)
            {
                debug("relays: binding to address %s", options.address);
                inet_pton(AF_INET, options.address, &relay.sin_addr);
            }

            else
                relay.sin_addr.s_addr = INADDR_ANY;

            relay.sin_port = htons(cur->port);

            cur->tcp_listener = evconnlistener_new_bind(
                context.events,
                accept_tcp_peer, cur,
                LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
                (struct sockaddr *) &relay, sizeof(relay));

            if(!cur->tcp_listener)
            {
                perror("evconnlistener_new_bind");
                context.relays_count = c;
                teardown_control();
                return;
            }

            if(options.interface)
            {
                evutil_socket_t fd =
                    evconnlistener_get_fd(cur->tcp_listener);

                debug(  "relays: binding fd:%d to interface %s",
                        fd,
                        options.interface);

                assert(fd != -1);
                struct ifreq ifr; memset(&ifr, 0, sizeof(ifr));
                strncpy(ifr.ifr_name, options.interface, sizeof(ifr.ifr_name));
                if(setsockopt(  fd,
                                SOL_SOCKET,
                                SO_BINDTODEVICE,
                                &ifr,
                                sizeof(ifr)) == -1)
                {
                    perror("setsockopt");
                    context.relays_count = c;
                    teardown_control();
                    return;
                }
            }

            evconnlistener_set_error_cb(cur->tcp_listener,
                                        error_on_tcp_peer_listener);

            ++ cur;
            w += bytes;
        }

        else if(!strcmp(proto, "udp"))
        {
            udp = 1;
            debug("relays: setting up udp relay %d", cur->port);

            cur->proto = IPPROTO_UDP;
            struct sockaddr_in  relay; memset(&relay, 0, sizeof(relay));
            relay.sin_family = AF_INET;
            if(options.address)
            {
                debug("relays: binding to address %s", options.address);
                inet_pton(AF_INET, options.address, &relay.sin_addr);
            }

            else
                relay.sin_addr.s_addr = INADDR_ANY;

            relay.sin_port = htons(cur->port);

            evutil_socket_t ufd = socket(AF_INET, SOCK_DGRAM, 0);
            assert(ufd != -1);
            //evutil_make_socket_nonblocking(ufd); maybe?
            if(options.interface)
            {
                debug(  "relays: binding fd:%d to interface %s",
                        ufd,
                        options.interface);

                struct ifreq ifr; memset(&ifr, 0, sizeof(ifr));
                strncpy(ifr.ifr_name, options.interface, sizeof(ifr.ifr_name));
                if(setsockopt(  ufd,
                                SOL_SOCKET,
                                SO_BINDTODEVICE,
                                &ifr,
                                sizeof(ifr)) == -1)
                {
                    perror("setsockopt");
                    context.relays_count = c;
                    teardown_control();
                    return;
                }
            }

            if(bind(ufd, (struct sockaddr *) &relay, sizeof(relay)) == -1)
            {
                perror("bind");
                context.relays_count = c;
                teardown_control();
                return;
            }

            cur->udp_listener = event_new(  context.events,
                                            ufd,
                                            EV_READ | EV_PERSIST,
                                            read_udp_peer,
                                            cur);

            event_add(cur->udp_listener, NULL);

            ++ cur;
            w += bytes;
        }

        else
        {
            debug("relays: unsupported protocol %s", proto);
            context.relays_count = c;
            teardown_control();
            return;
        }
    }

    if(udp)
    {
        debug("relays: setting up udp channels port");
        struct sockaddr_in relay; memset(&relay, 0, sizeof(relay));
        relay.sin_family = AF_INET;
        if(options.address)
        {
            debug("relays: binding to address %s", options.address);
            inet_pton(AF_INET, options.address, &relay.sin_addr);
        }

        else
            relay.sin_addr.s_addr = INADDR_ANY;

        relay.sin_port = htons(options.control_port);

        evutil_socket_t ufd = socket(AF_INET, SOCK_DGRAM, 0);
        //evutil_make_socket_nonblocking(ufd); maybe?
        if(options.interface)
        {
            debug(  "relays: binding fd:%d to interface %s",
                    ufd,
                    options.interface);

            struct ifreq ifr; memset(&ifr, 0, sizeof(ifr));
            strncpy(ifr.ifr_name, options.interface, sizeof(ifr.ifr_name));
            if(setsockopt(  ufd,
                            SOL_SOCKET,
                            SO_BINDTODEVICE,
                            &ifr,
                            sizeof(ifr)) == -1)
            {
                perror("setsockopt");
                teardown_control();
                return;
            }
        }

        if(bind(ufd, (struct sockaddr *) &relay, sizeof(relay)) == -1)
        {
            perror("bind");
            teardown_control();
            return;
        }

        context.listener.udp = event_new(   context.events,
                                            ufd,
                                            EV_READ | EV_PERSIST,
                                            read_udp_channel,
                                            NULL);

        event_add(context.listener.udp, NULL);
    }
}
int
obfsproxyssh_client_init(obfsproxyssh_t *state)
{
	obfsproxyssh_client_t *client;
	struct sockaddr_storage addr;
	evutil_socket_t sock;
	uint16_t port;
	int rval, len;

	client = calloc(1, sizeof(obfsproxyssh_client_t));
	if (NULL == client) {
		fprintf(stdout, "CMETHOD-ERROR %s Out of memory allocating state\n",
						OBFSPROXYSSH_METHOD);
		return -1;
	}
	client->state = state;

	memset(&addr, 0, sizeof(addr));
	len = sizeof(addr);
	evutil_parse_sockaddr_port(OBFSPROXYSSH_CLIENT_BIND_ADDR ":8080",
					(struct sockaddr *) &addr, &len);
	((struct sockaddr_in *) &addr)->sin_port = 0;	/* Use ephemeral port */
	client->listener = evconnlistener_new_bind(state->base,
			socks_accept_cb, client,
			LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
			-1, (struct sockaddr *) &addr, len);
	if (NULL == client->listener) {
		fprintf(stdout, "CMETHOD-ERROR %s Failed to bind SOCKS socket\n",
						OBFSPROXYSSH_METHOD);
		goto out_error;
	}
	evconnlistener_set_error_cb(client->listener, client_error_cb);

	sock = evconnlistener_get_fd(client->listener);
	len = sizeof(addr);
	rval = getsockname(sock, (struct sockaddr *) &addr, (socklen_t *) &len);
	if (rval) {
		fprintf(stdout, "CMETHOD-ERROR %s Failed to get SOCKS socket addr %d\n",
						OBFSPROXYSSH_METHOD, rval);
		goto out_free_listener;
	}
	port = htons(((struct sockaddr_in *) &addr)->sin_port);

	rval = libssh2_init(0);
	if (rval) {
		fprintf(stdout, "CMETHOD-ERROR %s Failed to initialize libssh2 (%d)\n",
						OBFSPROXYSSH_METHOD, rval);
		goto out_free_listener;
	}

	rval = ssh_client_profile_init(client);
	if (rval) {
		fprintf(stdout, "CMETHOD-ERROR %s Failed to initialize libssh2 algorithms (%d)\n",
						OBFSPROXYSSH_METHOD, rval);
		goto out_free_listener;
	}

	if (0 == state->unsafe_logging)
		log_f(state, "SOCKS: Listening on: TCP port %d", port);
	else
		log_f(state, "SOCKS: Listening on: %s:%d",
				OBFSPROXYSSH_CLIENT_BIND_ADDR, port);

	fprintf(stdout, "CMETHOD %s socks4 %s:%d %s\n", OBFSPROXYSSH_METHOD,
			OBFSPROXYSSH_CLIENT_BIND_ADDR, port,
			OBFSPROXYSSH_CLIENT_CMETHOD_ARGS);

	LIST_INIT(&client->sessions);
	LIST_INIT(&client->arg_cache);
	state->ctxt = client;
	state->shutdown_fn = client_on_shutdown;
	return 0;

out_free_listener:
	evconnlistener_free(client->listener);
out_error:
	free(client);
	return -1;
}