示例#1
0
static void ss_relay_readcb(struct bufferevent *buffev, void *_arg)
{
    redsocks_client *client = _arg;
    struct bufferevent * from = buffev;
    struct bufferevent * to   = client->client;
    size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from));
    size_t output_size = evbuffer_get_length(bufferevent_get_output(to));

    assert(buffev == client->relay);
    redsocks_touch_client(client);

    if (client->state == ss_connected)
    {
        /* decrypt and forward data to client side */
        if (output_size < to->wm_write.high)
        {
            if (input_size)
                decrypt_buffer(client, from, to);
            if (bufferevent_enable(from, EV_READ) == -1)
                redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
        }
        else
        {
            if (bufferevent_disable(from, EV_READ) == -1)
                redsocks_log_errno(client, LOG_ERR, "bufferevent_disable");
        }
    }
    else
    {
        redsocks_drop_client(client);
    }
}
示例#2
0
static void direct_relay_readcb_helper(redsocks_client *client, struct bufferevent *from, struct bufferevent *to)
{
	if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) {
		if (bufferevent_write_buffer(to, from->input) == -1)
			redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer");
	}
	else {
		if (bufferevent_disable(from, EV_READ) == -1)
			redsocks_log_errno(client, LOG_ERR, "bufferevent_disable");
	}
}
示例#3
0
static void auto_connect_relay(redsocks_client *client)
{
	autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len;
	struct timeval tv;
	tv.tv_sec = client->instance->config.timeout;
	tv.tv_usec = 0;
	time_t * acc_time = NULL;
	time_t now = redsocks_time(NULL); 	

	/* use default timeout if timeout is not configured */
	if (tv.tv_sec == 0)
		tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; 
	
	if (aclient->state == AUTOPROXY_NEW)
	{
		acc_time = get_addr_time_in_cache(&client->destaddr);
		if (acc_time)
		{
			if (now - *acc_time < CACHE_ITEM_STALE_SECONDS )
			{
				redsocks_log_error(client, LOG_DEBUG, "Found dest IP in cache");

				auto_retry(client, 0);
				return ;
			}
			else
			{
				/* stale this address in cache */
				del_addr_from_cache(&client->destaddr);
				/* update timeout value for quick detection */
				tv.tv_sec = QUICK_CONNECT_TIMEOUT_SECONDS;
			}
		}
		/* connect to target directly without going through proxy */	
		client->relay = red_connect_relay2(&client->destaddr,
						auto_relay_connected, auto_event_error, client, 
						&tv);
	
		aclient->time_connect_relay = redsocks_time(NULL);
	       
		if (!client->relay) {
			redsocks_log_errno(client, LOG_ERR, "auto_connect_relay");
			redsocks_drop_client(client);
		}
	}
	else
	{
		redsocks_log_errno(client, LOG_ERR, "invalid state: %d", aclient->state);
	}
}
示例#4
0
static void auto_connect_relay(redsocks_client *client)
{
	socks5_client *socks5 = (void*)(client + 1);
	struct timeval tv;
	tv.tv_sec = CONNECT_TIMEOUT_SECONDS;
	tv.tv_usec = 0;
	
	if (client->state == socks5_pre_detect)
	{
		if (is_addr_in_cache(&client->destaddr))
		{
			client->state = socks5_new; /* Connect SOCKS5 */
			redsocks_log_error(client, LOG_DEBUG, "Found in cache");
		}
	}
	client->relay = red_connect_relay2( client->state == socks5_pre_detect
									 ? &client->destaddr : &client->instance->config.relayaddr,
					auto_relay_connected, auto_event_error, client, 
					client->state == socks5_pre_detect ? &tv: NULL);

	socks5->time_connect_relay = redsocks_time(NULL);
       
	if (!client->relay) {
		redsocks_log_errno(client, LOG_ERR, "auto_connect_relay");
		redsocks_drop_client(client);
	}
}
示例#5
0
static void ss_relay_writecb(struct bufferevent *buffev, void *_arg)
{
    redsocks_client *client = _arg;
    struct bufferevent * from = client->client;
    struct bufferevent * to   = buffev;
    size_t input_size = evbuffer_get_contiguous_space(bufferevent_get_input(from));
    size_t output_size = evbuffer_get_length(bufferevent_get_output(to));

    assert(buffev == client->relay);
    redsocks_touch_client(client);

    if (process_shutdown_on_write_(client, from, to))
        return;

    if (client->state == ss_connected) 
    {
        /* encrypt and forward data received from client side */
        if (output_size < to->wm_write.high)
        {
            if (input_size)
                encrypt_buffer(client, from, to);
            if (!(client->client_evshut & EV_READ) && bufferevent_enable(from, EV_READ) == -1)
                redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
        }
    }
    else
    {
        redsocks_drop_client(client);
    }
}
示例#6
0
static void auto_event_error(struct bufferevent *buffev, short what, void *_arg)
{
	redsocks_client *client = _arg;
	int saved_errno = errno;
	assert(buffev == client->relay || buffev == client->client);
		
	redsocks_touch_client(client);
			
	redsocks_log_errno(client, LOG_DEBUG, "Errno: %d, State: %d, what: %x", saved_errno, client->state, what);
	if (buffev == client->relay)
	{
		if ( client->state == socks5_pre_detect 
		&& what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT))
		{
			/* In case timeout occurs for connecting relay, we try to connect
			to target with SOCKS5 proxy. It is possible that the connection to
			target can be set up a bit longer than the timeout value we set. 
			However, it is still better to make connection via proxy. */
			auto_retry(client, 1);
			return;
		}

		if (client->state == socks5_pre_detect  && saved_errno == ECONNRESET)
			if (!auto_retry_or_drop(client))
				return;

		if (client->state == socks5_direct && what == (EVBUFFER_READ|EVBUFFER_ERROR) 
				&& saved_errno == ECONNRESET )
		{
			if (!auto_retry_or_drop(client))
				return;
		}
	}	

	if (what == (EVBUFFER_READ|EVBUFFER_EOF)) {
		struct bufferevent *antiev;
		if (buffev == client->relay)
			antiev = client->client;
		else
			antiev = client->relay;
			
		redsocks_shutdown(client, buffev, SHUT_RD);
		
		if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0)
			redsocks_shutdown(client, antiev, SHUT_WR);
	}
	else {
		
		/*
		myerrno = red_socket_geterrno(buffev);
		redsocks_log_errno(client, LOG_NOTICE, "%s error, code " event_fmt_str,
				buffev == client->relay ? "relay" : "client",
				event_fmt(what));
		*/
		redsocks_drop_client(client);
	}
}																		
示例#7
0
static void auto_event_error(struct bufferevent *buffev, short what, void *_arg)
{
	redsocks_client *client = _arg;
	autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len;
	int saved_errno = errno;
	assert(buffev == client->relay || buffev == client->client);
		
	redsocks_touch_client(client);
			
	redsocks_log_errno(client, LOG_DEBUG, "%s errno(%d), State: %d, what: " event_fmt_str, 
							buffev == client->client?"client":"relay",
							saved_errno, aclient->state, event_fmt(what));
	if (buffev == client->relay)
	{
		
		if ( aclient->state == AUTOPROXY_NEW 
		&& what == (EVBUFFER_WRITE|EVBUFFER_TIMEOUT))
		{
			on_connection_blocked(client);	
			/* In case timeout occurs while connecting relay, we try to connect
			to target via SOCKS5 proxy. It is possible that the connection to
			target can be set up a bit longer than the timeout value we set. 
			However, it is still better to make connection via proxy. */
			auto_retry(client, 1);
			return;
		}

		if (aclient->state == AUTOPROXY_NEW  && saved_errno == ECONNRESET)
			if (!auto_retry_or_drop(client))
				return;

		if (aclient->state == AUTOPROXY_CONNECTED && what == (EVBUFFER_READ|EVBUFFER_ERROR) 
				&& saved_errno == ECONNRESET )
		{
			if (!auto_retry_or_drop(client))
				return;
		}
	}	

	if (what == (EVBUFFER_READ|EVBUFFER_EOF)) {
		struct bufferevent *antiev;
		if (buffev == client->relay)
			antiev = client->client;
		else
			antiev = client->relay;
			
		redsocks_shutdown(client, buffev, SHUT_RD);
		
		if (antiev != NULL && EVBUFFER_LENGTH(antiev->output) == 0)
			redsocks_shutdown(client, antiev, SHUT_WR);
	}
	else {
		redsocks_drop_client(client);
	}
}																		
示例#8
0
static void httpr_connect_relay(redsocks_client *client)
{
	int error;

	client->client->readcb = httpr_client_read_cb;
	error = bufferevent_enable(client->client, EV_READ);
	if (error) {
		redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
		redsocks_drop_client(client);
	}
}
示例#9
0
static void direct_relay_clientwritecb(struct bufferevent *to, void *_client)
{
	redsocks_client *client = _client;
	autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len;
	struct bufferevent * from = client->relay;

	redsocks_touch_client(client);

	if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) {
		redsocks_shutdown(client, to, SHUT_WR);
		return;
	}
	if (aclient->state == AUTOPROXY_CONNECTED)
	{
		if (!aclient->data_recv)
			aclient->data_recv = EVBUFFER_LENGTH(from->input);
	}
	if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) {
		if (bufferevent_write_buffer(to, from->input) == -1)
			redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer");
		if (bufferevent_enable(from, EV_READ) == -1)
			redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
	}
}
示例#10
0
static void direct_relay_clientwritecb(struct bufferevent *to, void *_client)
{
	redsocks_client *client = _client;
	socks5_client *socks5 = (void*)(client + 1);
	struct bufferevent * from = client->relay;

	redsocks_touch_client(client);

	if (EVBUFFER_LENGTH(from->input) == 0 && (client->relay_evshut & EV_READ)) {
		redsocks_shutdown(client, to, SHUT_WR);
		return;
	}
	if (client->state == socks5_direct)
	{
		if (!socks5->got_data)
			socks5->got_data = EVBUFFER_LENGTH(from->input);
	}
	if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) {
		if (bufferevent_write_buffer(to, from->input) == -1)
			redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer");
		if (bufferevent_enable(from, EV_READ) == -1)
			redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
	}
}
示例#11
0
static int direct_connect_relay(redsocks_client *client)
{
    char * interface = client->instance->config.interface;
    struct timeval tv = {client->instance->config.timeout, 0};

    // Allowing binding relay socket to specified IP for outgoing connections
    client->relay = red_connect_relay(interface, &client->destaddr, NULL,
                         redsocks_relay_connected, redsocks_event_error, client, &tv);
    if (!client->relay)
    {
        redsocks_log_errno(client, LOG_ERR, "red_connect_relay");
        redsocks_drop_client(client);
        return -1;
    }
    return 0;
}
示例#12
0
static int direct_connect_relay(redsocks_client *client)
{
    char * interface = client->instance->config.interface;
    // Allowing binding relay socket to specified IP for outgoing connections
    if (interface && strlen(interface))
    {
        client->relay = red_connect_relay_if(interface, 
                            &client->destaddr, NULL,
                            redsocks_relay_connected, redsocks_event_error, client);
    }
    else
        client->relay = red_connect_relay(&client->destaddr, NULL,
                            redsocks_relay_connected, redsocks_event_error, client);
    if (!client->relay)
    {
        redsocks_log_errno(client, LOG_ERR, "red_connect_relay");
        redsocks_drop_client(client);
        return -1;
    }
    return 0;
}
示例#13
0
static int ss_connect_relay(redsocks_client *client)
{
    struct timeval tv;

    tv.tv_sec = client->instance->config.timeout;
    tv.tv_usec = 0;
    /* use default timeout if timeout is not configured */
    if (tv.tv_sec == 0)
        tv.tv_sec = DEFAULT_CONNECT_TIMEOUT; 
    
    client->relay = red_connect_relay2(&client->instance->config.relayaddr,
                    NULL, ss_relay_connected, redsocks_event_error, client, 
                    &tv);

    if (!client->relay) {
        redsocks_log_errno(client, LOG_ERR, "ss_connect_relay");
        redsocks_drop_client(client);
        return -1;
    }
    return 0;
}
示例#14
0
static void httpc_read_cb(struct bufferevent *buffev, void *_arg)
{
	redsocks_client *client = _arg;

	assert(client->relay == buffev);
	assert(client->state == httpc_request_sent || client->state == httpc_reply_came);

	redsocks_touch_client(client);

	// evbuffer_add() triggers callbacks, so we can't write to client->client
	// till we know that we're going to ONFAIL_FORWARD_HTTP_ERR.
	// And the decision is made when all the headers are processed.
	struct evbuffer* tee = NULL;
	const bool do_errtee = client->instance->config.on_proxy_fail == ONFAIL_FORWARD_HTTP_ERR;

	if (client->state == httpc_request_sent) {
		size_t len = evbuffer_get_length(buffev->input);
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			unsigned int code;
			if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match
				if (code == 407) { // auth failed
					http_auth *auth = red_http_auth(client->instance);

					if (auth->last_auth_query != NULL && auth->last_auth_count == 1) {
						redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth failed: %s", line);
						client->state = httpc_no_way;
					} else if (client->instance->config.login == NULL || client->instance->config.password == NULL) {
						redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no login/password configured: %s", line);
						client->state = httpc_no_way;
					} else {
						if (do_errtee)
							tee = evbuffer_new();
						char *auth_request = http_auth_request_header(buffev->input, tee);
						if (!auth_request) {
							redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no <%s> header found: %s", auth_request_header, line);
							client->state = httpc_no_way;
						} else {
							free(line);
							if (tee)
								evbuffer_free(tee);
							free(auth->last_auth_query);
							char *ptr = auth_request;

							ptr += strlen(auth_request_header);
							while (isspace(*ptr))
								ptr++;

							size_t last_auth_query_len = strlen(ptr) + 1;
							auth->last_auth_query = calloc(last_auth_query_len, 1);
							memcpy(auth->last_auth_query, ptr, last_auth_query_len);
							auth->last_auth_count = 0;

							free(auth_request);

							if (bufferevent_disable(client->relay, EV_WRITE)) {
								redsocks_log_errno(client, LOG_ERR, "bufferevent_disable");
								return;
							}

							/* close relay tunnel */
							redsocks_bufferevent_free(client->relay);

							/* set to initial state*/
							client->state = httpc_new;

							/* and reconnect */
							redsocks_connect_relay(client);
							return;
						}
					}
				} else if (200 <= code && code <= 299) {
					client->state = httpc_reply_came;
				} else {
					redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy error: %s", line);
					client->state = httpc_no_way;
				}
			} else {
				redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy bad firstline: %s", line);
				client->state = httpc_no_way;
			}
			if (do_errtee && client->state == httpc_no_way) {
				if (bufferevent_write(client->client, line, strlen(line)) != 0 ||
				    bufferevent_write(client->client, "\r\n", 2) != 0)
				{
					redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write");
					goto fail;
				}
			}
			free(line);
		}
		else if (len >= HTTP_HEAD_WM_HIGH) {
			redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy reply is too long, %zu bytes", len);
			client->state = httpc_no_way;
		}
	}

	if (do_errtee && client->state == httpc_no_way) {
		if (tee) {
			if (bufferevent_write_buffer(client->client, tee) != 0) {
				redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write_buffer");
				goto fail;
			}
		}
		redsocks_shutdown(client, client->client, SHUT_RD);
		const size_t avail = evbuffer_get_length(client->client->input);
		if (avail) {
			if (evbuffer_drain(client->client->input, avail) != 0) {
				redsocks_log_errno(client, LOG_NOTICE, "evbuffer_drain");
				goto fail;
			}
		}
		redsocks_shutdown(client, client->relay, SHUT_WR);
		client->state = httpc_headers_skipped;
	}

fail:
	if (tee) {
		evbuffer_free(tee);
	}

	if (client->state == httpc_no_way) {
		redsocks_drop_client(client);
		return;
	}

	while (client->state == httpc_reply_came) {
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			if (strlen(line) == 0) {
				client->state = httpc_headers_skipped;
			}
			free(line);
		}
		else {
			break;
		}
	}

	if (client->state == httpc_headers_skipped) {
		redsocks_start_relay(client);
	}
}
示例#15
0
static void httpr_relay_write_cb(struct bufferevent *buffev, void *_arg)
{
	redsocks_client *client = _arg;
	httpr_client *httpr = (void*)(client + 1);
	int len = 0;

	assert(client->state >= httpr_recv_request_headers);

	redsocks_touch_client(client);

	if (client->state == httpr_recv_request_headers) {
		if (httpr->firstline) {
			len = bufferevent_write(client->relay, httpr->firstline, strlen(httpr->firstline));
			if (len < 0) {
				redsocks_log_errno(client, LOG_ERR, "bufferevent_write");
				redsocks_drop_client(client);
				return;
			}
		}


		http_auth *auth = (void*)(client->instance + 1);
		++auth->last_auth_count;

		const char *auth_scheme = NULL;
		char *auth_string = NULL;

		if (auth->last_auth_query != NULL) {
			/* find previous auth challange */

			if (strncasecmp(auth->last_auth_query, "Basic", 5) == 0) {
				auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password);
				auth_scheme = "Basic";
			} else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0 && httpr->firstline) {
				/* calculate method & uri */
				char *ptr = strchr(httpr->firstline, ' '), *ptr2;
				char *method = calloc(ptr - httpr->firstline + 1, 1);
				memcpy(method, httpr->firstline, ptr - httpr->firstline);
				method[ptr - httpr->firstline] = 0;

				ptr = strchr(httpr->firstline, '/');
				if (!ptr || *++ptr != '/') {
					free(method);
					redsocks_log_error(client, LOG_NOTICE, "malformed request came");
					redsocks_drop_client(client);
					return;
				}
				if (!(ptr = strchr(++ptr, '/')) || !(ptr2 = strchr(ptr, ' '))) {
					free(method);
					redsocks_log_error(client, LOG_NOTICE, "malformed request came");
					redsocks_drop_client(client);
					return;
				}
				char *uri = calloc(ptr2 - ptr + 1, 1);
				memcpy(uri, ptr, ptr2 - ptr);
				uri[ptr2 - ptr] = 0;

				/* prepare an random string for cnounce */
				char cnounce[17];
				snprintf(cnounce, sizeof(cnounce), "%08x%08x", red_randui32(), red_randui32());

				auth_string = digest_authentication_encode(auth->last_auth_query + 7, //line
						client->instance->config.login, client->instance->config.password, //user, pass
						method, uri, auth->last_auth_count, cnounce); // method, path, nc, cnounce

				free(method);
				free(uri);
				auth_scheme = "Digest";
			}
		}

		if (auth_string != NULL) {
			len = 0;
			len |= bufferevent_write(client->relay, auth_response_header, strlen(auth_response_header));
			len |= bufferevent_write(client->relay, " ", 1);
			len |= bufferevent_write(client->relay, auth_scheme, strlen(auth_scheme));
			len |= bufferevent_write(client->relay, " ", 1);
			len |= bufferevent_write(client->relay, auth_string, strlen(auth_string));
			len |= bufferevent_write(client->relay, "\r\n", 2);
			if (len) {
				redsocks_log_errno(client, LOG_ERR, "bufferevent_write");
				redsocks_drop_client(client);
				return;
			}
		}

		free(auth_string);

		len = bufferevent_write(client->relay, httpr->client_buffer.buff, httpr->client_buffer.len);
		if (len < 0) {
			redsocks_log_errno(client, LOG_ERR, "bufferevent_write");
			redsocks_drop_client(client);
			return;
		}

		client->state = httpr_request_sent;

		buffev->wm_read.low = 1;
		buffev->wm_read.high = HTTP_HEAD_WM_HIGH;
		bufferevent_enable(buffev, EV_READ);
	}
}
示例#16
0
static void httpr_relay_read_cb(struct bufferevent *buffev, void *_arg)
{
	redsocks_client *client = _arg;
	httpr_client *httpr = (void*)(client + 1);
	int dropped = 0;

	assert(client->state >= httpr_request_sent);

	redsocks_touch_client(client);

	httpr_buffer_fini(&httpr->relay_buffer);
	httpr_buffer_init(&httpr->relay_buffer);

	if (client->state == httpr_request_sent) {
		size_t len = EVBUFFER_LENGTH(buffev->input);
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			httpr_buffer_append(&httpr->relay_buffer, line, strlen(line));
			httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2);
			unsigned int code;
			if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match
				if (code == 407) { // auth failed
					http_auth *auth = (void*)(client->instance + 1);

					if (auth->last_auth_query != NULL && auth->last_auth_count == 1) {
						redsocks_log_error(client, LOG_NOTICE, "proxy auth failed");
						redsocks_drop_client(client);

						dropped = 1;
					} else if (client->instance->config.login == NULL || client->instance->config.password == NULL) {
						redsocks_log_error(client, LOG_NOTICE, "proxy auth required, but no login information provided");
						redsocks_drop_client(client);

						dropped = 1;
					} else {
						free(line);
						char *auth_request = get_auth_request_header(buffev->input);

						if (!auth_request) {
							redsocks_log_error(client, LOG_NOTICE, "403 found, but no proxy auth challenge");
							redsocks_drop_client(client);
							dropped = 1;
						} else {
							free(auth->last_auth_query);
							char *ptr = auth_request;

							ptr += strlen(auth_request_header);
							while (isspace(*ptr))
								ptr++;

							auth->last_auth_query = calloc(strlen(ptr) + 1, 1);
							strcpy(auth->last_auth_query, ptr);
							auth->last_auth_count = 0;

							free(auth_request);

							httpr_buffer_fini(&httpr->relay_buffer);

							if (bufferevent_disable(client->relay, EV_WRITE)) {
								redsocks_log_errno(client, LOG_ERR, "bufferevent_disable");
								return;
							}

							/* close relay tunnel */
							redsocks_close(EVENT_FD(&client->relay->ev_write));
							bufferevent_free(client->relay);

							/* set to initial state*/
							client->state = httpr_recv_request_headers;

							/* and reconnect */
							redsocks_connect_relay(client);
							return;
						}
					}
				} else if (100 <= code && code <= 999) {
					client->state = httpr_reply_came;
				} else {
					redsocks_log_error(client, LOG_NOTICE, "%s", line);
					redsocks_drop_client(client);
					dropped = 1;
				}
			}
			free(line);
		}
		else if (len >= HTTP_HEAD_WM_HIGH) {
			redsocks_drop_client(client);
			dropped = 1;
		}
	}

	if (dropped)
		return;

	while (client->state == httpr_reply_came) {
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			httpr_buffer_append(&httpr->relay_buffer, line, strlen(line));
			httpr_buffer_append(&httpr->relay_buffer, "\r\n", 2);
			if (strlen(line) == 0) {
				client->state = httpr_headers_skipped;
			}
			free(line);
		}
		else {
			break;
		}
	}

	if (client->state == httpr_headers_skipped) {
		if (bufferevent_write(client->client, httpr->relay_buffer.buff, httpr->relay_buffer.len) != 0) {
			redsocks_log_error(client, LOG_NOTICE, "bufferevent_write");
			redsocks_drop_client(client);
			return;
		}
		redsocks_start_relay(client);
	}

}
示例#17
0
static struct evbuffer *httpc_mkconnect(redsocks_client *client)
{
	struct evbuffer *buff = NULL, *retval = NULL;
	int len;

	buff = evbuffer_new();
	if (!buff) {
		redsocks_log_errno(client, LOG_ERR, "evbuffer_new");
		goto fail;
	}

	http_auth *auth = (void*)(client->instance + 1);
	++auth->last_auth_count;

	const char *auth_scheme = NULL;
	char *auth_string = NULL;

	if (auth->last_auth_query != NULL) {
		/* find previous auth challange */

		if (strncasecmp(auth->last_auth_query, "Basic", 5) == 0) {
			auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password);
			auth_scheme = "Basic";
		} else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0) {
			/* calculate uri */
			char uri[128];
			snprintf(uri, 128, "%s:%u", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port));

			/* prepare an random string for cnounce */
			char cnounce[17];
			srand(time(0));
			for (int i = 0; i < 16; i += 4)
				sprintf(cnounce + i, "%02x", rand() & 65535);

			auth_string = digest_authentication_encode(auth->last_auth_query + 7, //line
					client->instance->config.login, client->instance->config.password, //user, pass
					"CONNECT", uri, auth->last_auth_count, cnounce); // method, path, nc, cnounce
			auth_scheme = "Digest";
		}
	}

	if (auth_string == NULL) {
		len = evbuffer_add_printf(buff,
			"CONNECT %s:%u HTTP/1.0\r\n\r\n",
			inet_ntoa(client->destaddr.sin_addr),
			ntohs(client->destaddr.sin_port)
		);
	} else {
		len = evbuffer_add_printf(buff,
			"CONNECT %s:%u HTTP/1.0\r\n%s %s %s\r\n\r\n",
			inet_ntoa(client->destaddr.sin_addr),
			ntohs(client->destaddr.sin_port),
			auth_response_header,
			auth_scheme,
			auth_string
		);
	}

	free(auth_string);

	if (len < 0) {
		redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf");
		goto fail;
	}

	retval = buff;
	buff = NULL;

fail:
	if (buff)
		evbuffer_free(buff);
	return retval;
}
示例#18
0
static void direct_relay_relaywritecb(struct bufferevent *to, void *_client)
{
	redsocks_client *client = _client;
	autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len;
	struct bufferevent * from = client->client;

	redsocks_touch_client(client);

	if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) {
		redsocks_shutdown(client, to, SHUT_WR);
		return;
	}
	else if (aclient->state == AUTOPROXY_CONNECTED )
	{
		/* Not send or receive data. */
		if (!aclient->data_sent && !aclient->data_recv)
		{
			/* Ensure we have data to send */
			if (EVBUFFER_LENGTH(from->input))
			{
				/* copy data from input to output of relay */
				aclient->data_sent = copy_evbuffer (to, from, 0);
				redsocks_log_error(client, LOG_DEBUG, "not sent, not  got %d", EVBUFFER_LENGTH(from->input));
			}
		}
		/* 
		 * Relay reaceived data before writing to relay.
		*/
		else if (!aclient->data_sent && aclient->data_recv)
		{
			redsocks_log_error(client, LOG_DEBUG, "not sent, got");
			aclient->data_sent = copy_evbuffer(to, from, 0);
		}
		/* aclient->state = AUTOPROXY_CONFIRMED; */
		/* We have writen data to relay, but got nothing until we are requested to 
		* write to it again.
		*/
		else if (aclient->data_sent && !aclient->data_recv)
		{
			/* No response from relay and no CONNECTION RESET,
				Send more data.
			*/
			redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d",
										 evbuffer_get_length(from->input),
										 evbuffer_get_length(to->output),
											to->wm_write.high, aclient->data_sent	);
			/* Write more data util input buffer is full */
			if (EVBUFFER_LENGTH(from->input)- aclient->data_sent > 0) /* we have more data waiting to be sent  */
			{
				aclient->data_sent += copy_evbuffer(to, from, aclient->data_sent);
			}
			else if (EVBUFFER_LENGTH(to->output) < aclient->data_sent /*  data is sent out, more or less */
				&& EVBUFFER_LENGTH(from->input) == from->wm_read.high /* do not confirm unless read buffer is full */
				&& EVBUFFER_LENGTH(from->input) == aclient->data_sent /* all data in read buffer is sent */
				) 
			{
				evbuffer_drain(from->input, aclient->data_sent);
				aclient->data_sent = 0;
				aclient->state = AUTOPROXY_CONFIRMED;
				on_connection_confirmed(client);
			}
		}
		/* We sent data to and got data from relay. */
		else if (aclient->data_sent && aclient->data_recv)
		{
			/* No CONNECTION RESET error occur after sending data, good. */
			aclient->state = AUTOPROXY_CONFIRMED;
			on_connection_confirmed(client);
			redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", aclient->data_recv);
			if (evbuffer_get_length(from->input))
			{
				evbuffer_drain(from->input, aclient->data_sent);
				aclient->data_sent = 0;
			}
		}
	}

	if (aclient->state == AUTOPROXY_CONFIRMED)
	{
		if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) {
			if (bufferevent_write_buffer(to, from->input) == -1)
				redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer");
			if (bufferevent_enable(from, EV_READ) == -1)
				redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
		}	
	}
}
示例#19
0
static struct evbuffer *httpc_mkconnect(redsocks_client *client)
{
	struct evbuffer *buff = NULL, *retval = NULL;
	char *auth_string = NULL;
	int len;

	buff = evbuffer_new();
	if (!buff) {
		redsocks_log_errno(client, LOG_ERR, "evbuffer_new");
		goto fail;
	}

	http_auth *auth = red_http_auth(client->instance);
	++auth->last_auth_count;

	const char *auth_scheme = NULL;

	if (auth->last_auth_query != NULL) {
		/* find previous auth challange */

		if (strncasecmp(auth->last_auth_query, "Basic", 5) == 0) {
			auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password);
			auth_scheme = "Basic";
		} else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0) {
			/* calculate uri */
			char uri[128];
			snprintf(uri, 128, "%s:%u", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port));

			/* prepare an random string for cnounce */
			char cnounce[17];
			snprintf(cnounce, sizeof(cnounce), "%08x%08x", red_randui32(), red_randui32());

			auth_string = digest_authentication_encode(auth->last_auth_query + 7, //line
					client->instance->config.login, client->instance->config.password, //user, pass
					"CONNECT", uri, auth->last_auth_count, cnounce); // method, path, nc, cnounce
			auth_scheme = "Digest";
		}
	}

	// TODO: do accurate evbuffer_expand() while cleaning up http-auth
	len = evbuffer_add_printf(buff, "CONNECT %s:%u HTTP/1.0\r\n",
		inet_ntoa(client->destaddr.sin_addr),
		ntohs(client->destaddr.sin_port));
	if (len < 0) {
		redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf");
		goto fail;
	}

	if (auth_string) {
		len = evbuffer_add_printf(buff, "%s %s %s\r\n",
			auth_response_header, auth_scheme, auth_string);
		if (len < 0) {
			redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf");
			goto fail;
		}
		free(auth_string);
		auth_string = NULL;
	}

	const enum disclose_src_e disclose_src = client->instance->config.disclose_src;
	if (disclose_src != DISCLOSE_NONE) {
		char clientip[INET_ADDRSTRLEN];
		const char *ip = inet_ntop(client->clientaddr.sin_family, &client->clientaddr.sin_addr, clientip, sizeof(clientip));
		if (!ip) {
			redsocks_log_errno(client, LOG_ERR, "inet_ntop");
			goto fail;
		}
		if (disclose_src == DISCLOSE_X_FORWARDED_FOR) {
			len = evbuffer_add_printf(buff, "X-Forwarded-For: %s\r\n", ip);
		} else if (disclose_src == DISCLOSE_FORWARDED_IP) {
			len = evbuffer_add_printf(buff, "Forwarded: for=%s\r\n", ip);
		} else if (disclose_src == DISCLOSE_FORWARDED_IPPORT) {
			len = evbuffer_add_printf(buff, "Forwarded: for=\"%s:%d\"\r\n", ip,
				ntohs(client->clientaddr.sin_port));
		}
		if (len < 0) {
			redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf");
			goto fail;
		}
	}

	len = evbuffer_add(buff, "\r\n", 2);
	if (len < 0) {
		redsocks_log_errno(client, LOG_ERR, "evbufer_add");
		goto fail;
	}

	retval = buff;
	buff = NULL;

fail:
	if (auth_string)
		free(auth_string);
	if (buff)
		evbuffer_free(buff);
	return retval;
}
示例#20
0
static void direct_relay_relaywritecb(struct bufferevent *to, void *_client)
{
	redsocks_client *client = _client;
	socks5_client *socks5 = (void*)(client + 1);
	struct bufferevent * from = client->client;

	redsocks_touch_client(client);

	if (EVBUFFER_LENGTH(from->input) == 0 && (client->client_evshut & EV_READ)) {
		redsocks_shutdown(client, to, SHUT_WR);
		return;
	}
	else if (client->state == socks5_direct )
	{
		/* Not send or receive data. */
		if (!socks5->data_sent && !socks5->got_data)
		{
			/* Ensure we have data to send */
			if (EVBUFFER_LENGTH(from->input))
			{
				/* copy data from input to output of relay */
				socks5->data_sent = copy_evbuffer (to, from);
				redsocks_log_error(client, LOG_DEBUG, "not sent, not  got %d", EVBUFFER_LENGTH(from->input));
			}
		}
		/* 
		 * Relay reaceived data before writing to relay.
		*/
		else if (!socks5->data_sent && socks5->got_data)
		{
			redsocks_log_error(client, LOG_DEBUG, "not sent, got");
			socks5->data_sent = copy_evbuffer(to, from);
		}
		/* client->state = socks5_direct_confirmed; */
		/* We have writen data to relay, but got nothing until we are requested to 
		* write to it again.
		*/
		else if (socks5->data_sent && !socks5->got_data)
		{
			/* No response from relay and no CONNECTION RESET,
				Send more data.
			*/
			redsocks_log_error(client, LOG_DEBUG, "sent, not got in:%d out:%d high:%d sent:%d",
										 evbuffer_get_length(from->input),
										 evbuffer_get_length(to->output),
											to->wm_write.high, socks5->data_sent	);
			/* TODO: Write more data? */
			if (EVBUFFER_LENGTH(to->output) < socks5->data_sent /*  data is sent out, more or less */
				&& EVBUFFER_LENGTH(from->input) == from->wm_read.high /* read buffer is full */
				&& EVBUFFER_LENGTH(from->input) == socks5->data_sent /* all data in read buffer is sent */
				) 
			{
				evbuffer_drain(from->input, socks5->data_sent);
				socks5->data_sent = 0;
				client->state = socks5_direct_confirmed;
			}
		}
		/* We sent data to and got data from relay. */
		else if (socks5->data_sent && socks5->got_data)
		{
			/* No CONNECTION RESET error occur after sending data, good. */
			client->state = socks5_direct_confirmed;
			redsocks_log_error(client, LOG_DEBUG, "sent, got %d ", socks5->got_data);
			if (evbuffer_get_length(from->input))
			{
				evbuffer_drain(from->input, socks5->data_sent);
				socks5->data_sent = 0;
			}
		}
	}

	if (client->state == socks5_direct_confirmed)
	{
		if (EVBUFFER_LENGTH(to->output) < to->wm_write.high) {
			if (bufferevent_write_buffer(to, from->input) == -1)
				redsocks_log_errno(client, LOG_ERR, "bufferevent_write_buffer");
			if (bufferevent_enable(from, EV_READ) == -1)
				redsocks_log_errno(client, LOG_ERR, "bufferevent_enable");
		}	
	}
}