Exemple #1
0
static void auto_retry(redsocks_client * client, int updcache)
{
	autoproxy_client * aclient = (void*)(client + 1) + client->instance->relay_ss->payload_len;

	if (aclient->state == AUTOPROXY_CONNECTED)
		bufferevent_disable(client->client, EV_READ| EV_WRITE); 
	/* drop relay and update state, then retry with specified relay */
	if (updcache)
	{
		/* only add IP to cache when the IP is not in cache */
		if (get_addr_time_in_cache(&client->destaddr) == NULL)
		{
			add_addr_to_cache(&client->destaddr);
			redsocks_log_error(client, LOG_DEBUG, "ADD IP to cache: %s", 
							inet_ntoa(client->destaddr.sin_addr));
		}
	}
	auto_drop_relay(client);

	// restore callbacks for ordinary client.
	bufferevent_setcb(client->client, NULL, NULL, client->client->errorcb, client);
	// enable reading to handle EOF from client
	bufferevent_enable(client->client, EV_READ); 
	/* connect to relay */
	if (client->instance->relay_ss->connect_relay)
		client->instance->relay_ss->connect_relay(client);
	else
		redsocks_connect_relay(client);
	// 
	if (EVBUFFER_LENGTH(client->client->input) && client->client->readcb)
		client->client->readcb(client->client, client);
}
Exemple #2
0
static void httpr_client_read_cb(struct bufferevent *buffev, void *_arg)
{
	redsocks_client *client = _arg;
	httpr_client *httpr = (void*)(client + 1);

	redsocks_touch_client(client);

	if (client->state >= httpr_recv_request_headers) {
		httpr_client_read_content(buffev, client);
		return;
	}

	char *line = NULL;
	int connect_relay = 0;

	while (!connect_relay && (line = redsocks_evbuffer_readline(buffev->input))) {
		int skip_line = 0;
		int do_drop = 0;

		if (strlen(line) > 0) {
			if (!httpr->firstline) {
				httpr->firstline = line;
				line = 0;
			}
			else if (strncasecmp(line, "Host", 4) == 0) {
				httpr->has_host = 1;
				char *ptr = line + 5;
				while (*ptr && isspace(*ptr))
					ptr ++;
				httpr->host = calloc(strlen(ptr) + 1, 1);
				strcpy(httpr->host, ptr);
			} else if (strncasecmp(line, "Proxy-Connection", 16) == 0)
				skip_line = 1;
			else if (strncasecmp(line, "Connection", 10) == 0)
				skip_line = 1;

		}
		else { // last line of request
			if (!httpr->firstline || httpr_toss_http_firstline(client) < 0)
				do_drop = 1;

			if (!httpr->has_host) {
				char host[32]; // "Host: 123.456.789.012:34567"
				int written_wo_null = snprintf(host, sizeof(host), "Host: %s",
				                               fmt_http_host(client->destaddr));
				UNUSED(written_wo_null);
				assert(0 < written_wo_null && written_wo_null < sizeof(host));
				if (httpr_append_header(client, host) < 0)
					do_drop = 1;
			}

			if (httpr_append_header(client, "Proxy-Connection: close") < 0)
				do_drop = 1;

			if (httpr_append_header(client, "Connection: close") < 0)
				do_drop = 1;

			connect_relay = 1;
		}

		if (line && !skip_line)
			if (httpr_append_header(client, line) < 0)
				do_drop = 1;

		free(line);

		if (do_drop) {
			redsocks_drop_client(client);
			return;
		}
	}

	if (connect_relay) {
		client->state = httpr_recv_request_headers;
		httpr_client_read_content(buffev, client);
		redsocks_connect_relay(client);
	}
}
Exemple #3
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);
	}

}
Exemple #4
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);
	}
}