Compare commits
5 Commits
bd73061462
...
6f0371c46f
Author | SHA1 | Date | |
---|---|---|---|
6f0371c46f
|
|||
ad6ffe5425
|
|||
ab7e5cc722
|
|||
40310faa4b
|
|||
7a3adc6ed3
|
@@ -27,7 +27,6 @@ See [doc/example.conf](doc/example.conf) for more details.
|
|||||||
### Global directives
|
### Global directives
|
||||||
|
|
||||||
* `geoip_dir` (optional) - path to a directory containing GeoIP databases
|
* `geoip_dir` (optional) - path to a directory containing GeoIP databases
|
||||||
* `dns_server` (optional) - address of a DNS server
|
|
||||||
|
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
@@ -61,9 +61,6 @@ static int config_parse_line(char *line, char *section, int *i, int *j) {
|
|||||||
if (len > 10 && strncmp(ptr, "geoip_dir", 9) == 0 && (ptr[9] == ' ' || ptr[9] == '\t')) {
|
if (len > 10 && strncmp(ptr, "geoip_dir", 9) == 0 && (ptr[9] == ' ' || ptr[9] == '\t')) {
|
||||||
source = ptr + 9;
|
source = ptr + 9;
|
||||||
target = config.geoip_dir;
|
target = config.geoip_dir;
|
||||||
} else if (len > 11 && strncmp(ptr, "dns_server", 10) == 0 && (ptr[10] == ' ' || ptr[10] == '\t')) {
|
|
||||||
source = ptr + 10;
|
|
||||||
target = config.dns_server;
|
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@@ -53,7 +53,6 @@ typedef struct {
|
|||||||
host_config_t hosts[CONFIG_MAX_HOST_CONFIG];
|
host_config_t hosts[CONFIG_MAX_HOST_CONFIG];
|
||||||
cert_config_t certs[CONFIG_MAX_CERT_CONFIG];
|
cert_config_t certs[CONFIG_MAX_CERT_CONFIG];
|
||||||
char geoip_dir[256];
|
char geoip_dir[256];
|
||||||
char dns_server[256];
|
|
||||||
} config_t;
|
} config_t;
|
||||||
|
|
||||||
extern config_t config;
|
extern config_t config;
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
extern const char *sock_error_str(unsigned long err);
|
extern const char *sock_error_str(unsigned long err);
|
||||||
extern const char *http_error_str(int err);
|
extern const char *http_error_str(int err);
|
||||||
@@ -29,23 +30,14 @@ static unsigned long error_decompress(int err) {
|
|||||||
|
|
||||||
const char *error_str(int err_no, char *buf, int buf_len) {
|
const char *error_str(int err_no, char *buf, int buf_len) {
|
||||||
buf[0] = 0;
|
buf[0] = 0;
|
||||||
unsigned char mode = (unsigned char) (err_no >> 24);
|
|
||||||
int e = err_no & 0x00FFFFFF;
|
int e = err_no & 0x00FFFFFF;
|
||||||
if (mode == 0x00) {
|
switch (err_no >> 24) {
|
||||||
// normal
|
case 0x00: return strerror_r(e, buf, buf_len);
|
||||||
return strerror_r(e, buf, buf_len);
|
case 0x01: return sock_error_str(error_decompress(e));
|
||||||
} else if (mode == 0x01) {
|
case 0x02: return ERR_reason_error_string(error_decompress(e));
|
||||||
// ssl
|
case 0x03: return MMDB_strerror(e);
|
||||||
return sock_error_str(error_decompress(e));
|
case 0x04: return http_error_str(e);
|
||||||
} else if (mode == 0x02) {
|
case 0x05: return gai_strerror(e);
|
||||||
// ssl err
|
|
||||||
return ERR_reason_error_string(error_decompress(e));
|
|
||||||
} else if (mode == 0x03) {
|
|
||||||
// mmdb
|
|
||||||
return MMDB_strerror(e);
|
|
||||||
} else if (mode == 0x04) {
|
|
||||||
// http
|
|
||||||
return http_error_str(e);
|
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@@ -66,6 +58,10 @@ void error_http(int err) {
|
|||||||
errno = 0x04000000 | err;
|
errno = 0x04000000 | err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void error_gai(int err) {
|
||||||
|
errno = 0x05000000 | err;
|
||||||
|
}
|
||||||
|
|
||||||
static int error_get(unsigned char prefix) {
|
static int error_get(unsigned char prefix) {
|
||||||
return (errno >> 24 != prefix) ? 0 : errno & 0x00FFFFFF;
|
return (errno >> 24 != prefix) ? 0 : errno & 0x00FFFFFF;
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,8 @@ void error_mmdb(int err);
|
|||||||
|
|
||||||
void error_http(int err);
|
void error_http(int err);
|
||||||
|
|
||||||
|
void error_gai(int err);
|
||||||
|
|
||||||
int error_get_sys();
|
int error_get_sys();
|
||||||
|
|
||||||
int error_get_ssl();
|
int error_get_ssl();
|
||||||
|
369
src/lib/proxy.c
369
src/lib/proxy.c
@@ -19,7 +19,6 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netdb.h>
|
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
|
|
||||||
static SSL_CTX *proxy_ctx = NULL;
|
static SSL_CTX *proxy_ctx = NULL;
|
||||||
@@ -300,63 +299,13 @@ int proxy_response_header(http_req *req, http_res *res, host_config_t *conf) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg) {
|
static int proxy_connect(proxy_ctx_t *proxy, host_config_t *conf, http_res *res, http_status_ctx *ctx, char *err_msg) {
|
||||||
char buffer[CHUNK_SIZE], err_buf[256];
|
char err_buf[256], addr_buf[1024];
|
||||||
const char *connection, *upgrade, *ws_version;
|
|
||||||
long ret;
|
|
||||||
int tries = 0, retry = 0;
|
|
||||||
|
|
||||||
*proxy_ptr = proxy_get_by_conf(conf);
|
info(BLUE_STR "Connecting to " BLD_STR "[%s]:%i" CLR_STR BLUE_STR "...", conf->proxy.hostname, conf->proxy.port);
|
||||||
proxy_ctx_t *proxy = *proxy_ptr;
|
|
||||||
proxy->client = NULL;
|
|
||||||
|
|
||||||
if (proxy->initialized && sock_has_pending(&proxy->proxy) == 0)
|
|
||||||
goto proxy;
|
|
||||||
|
|
||||||
retry:
|
|
||||||
if (proxy->initialized)
|
|
||||||
proxy_close(proxy);
|
|
||||||
|
|
||||||
retry = 0;
|
|
||||||
tries++;
|
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) {
|
if ((fd = sock_connect(conf->proxy.hostname, conf->proxy.port, SERVER_TIMEOUT_INIT, addr_buf, sizeof(addr_buf))) == -1) {
|
||||||
error("Unable to create socket");
|
|
||||||
res->status = http_get_status(500);
|
|
||||||
ctx->origin = INTERNAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sock_init(&proxy->proxy, fd, 0);
|
|
||||||
|
|
||||||
if (sock_set_socket_timeout(&proxy->proxy, 1) != 0 || sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT_INIT) != 0)
|
|
||||||
goto proxy_timeout_err;
|
|
||||||
|
|
||||||
struct hostent *host_ent = gethostbyname2(conf->proxy.hostname, AF_INET6);
|
|
||||||
if (host_ent == NULL) {
|
|
||||||
host_ent = gethostbyname2(conf->proxy.hostname, AF_INET);
|
|
||||||
if (host_ent == NULL) {
|
|
||||||
res->status = http_get_status(502);
|
|
||||||
ctx->origin = SERVER_REQ;
|
|
||||||
error("Unable to connect to server: Name or service not known");
|
|
||||||
sprintf(err_msg, "Unable to connect to server: Name or service not known.");
|
|
||||||
goto proxy_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in6 address = {.sin6_family = AF_INET6, .sin6_port = htons(conf->proxy.port)};
|
|
||||||
if (host_ent->h_addrtype == AF_INET6) {
|
|
||||||
memcpy(&address.sin6_addr, host_ent->h_addr_list[0], host_ent->h_length);
|
|
||||||
} else if (host_ent->h_addrtype == AF_INET) {
|
|
||||||
unsigned char addr[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0};
|
|
||||||
memcpy(addr + 12, host_ent->h_addr_list[0], host_ent->h_length);
|
|
||||||
memcpy(&address.sin6_addr, addr, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
inet_ntop(address.sin6_family, (void *) &address.sin6_addr, buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
info(BLUE_STR "Connecting to " BLD_STR "[%s]:%i" CLR_STR BLUE_STR "...", buffer, conf->proxy.port);
|
|
||||||
if (connect(proxy->proxy.socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
|
|
||||||
if (errno == ETIMEDOUT || errno == EINPROGRESS) {
|
if (errno == ETIMEDOUT || errno == EINPROGRESS) {
|
||||||
res->status = http_get_status(504);
|
res->status = http_get_status(504);
|
||||||
ctx->origin = SERVER_REQ;
|
ctx->origin = SERVER_REQ;
|
||||||
@@ -367,18 +316,19 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
|||||||
res->status = http_get_status(500);
|
res->status = http_get_status(500);
|
||||||
ctx->origin = INTERNAL;
|
ctx->origin = INTERNAL;
|
||||||
}
|
}
|
||||||
error("Unable to connect to [%s]:%i", buffer, conf->proxy.port);
|
error("Unable to connect to [%s]:%i", addr_buf, conf->proxy.port);
|
||||||
sprintf(err_msg, "Unable to connect to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
sprintf(err_msg, "Unable to connect to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||||
goto proxy_err;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sock_init(&proxy->proxy, fd, 0);
|
||||||
|
|
||||||
if (sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT) != 0) {
|
if (sock_set_timeout(&proxy->proxy, SERVER_TIMEOUT) != 0) {
|
||||||
proxy_timeout_err:
|
|
||||||
res->status = http_get_status(500);
|
res->status = http_get_status(500);
|
||||||
ctx->origin = INTERNAL;
|
ctx->origin = INTERNAL;
|
||||||
error("Unable to set timeout for reverse proxy socket");
|
error("Unable to set timeout for reverse proxy socket");
|
||||||
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", error_str(errno, err_buf, sizeof(err_buf)));
|
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", error_str(errno, err_buf, sizeof(err_buf)));
|
||||||
goto proxy_err;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf->proxy.enc) {
|
if (conf->proxy.enc) {
|
||||||
@@ -386,6 +336,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
|||||||
SSL_set_fd(proxy->proxy.ssl, proxy->proxy.socket);
|
SSL_set_fd(proxy->proxy.ssl, proxy->proxy.socket);
|
||||||
SSL_set_connect_state(proxy->proxy.ssl);
|
SSL_set_connect_state(proxy->proxy.ssl);
|
||||||
|
|
||||||
|
int ret;
|
||||||
if ((ret = SSL_do_handshake(proxy->proxy.ssl)) != 1) {
|
if ((ret = SSL_do_handshake(proxy->proxy.ssl)) != 1) {
|
||||||
sock_error(&proxy->proxy, (int) ret);
|
sock_error(&proxy->proxy, (int) ret);
|
||||||
SSL_free(proxy->proxy.ssl);
|
SSL_free(proxy->proxy.ssl);
|
||||||
@@ -393,7 +344,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
|||||||
ctx->origin = SERVER_REQ;
|
ctx->origin = SERVER_REQ;
|
||||||
error("Unable to perform handshake");
|
error("Unable to perform handshake");
|
||||||
sprintf(err_msg, "Unable to perform handshake: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
sprintf(err_msg, "Unable to perform handshake: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||||
goto proxy_err;
|
return -1;
|
||||||
}
|
}
|
||||||
proxy->proxy.enc = 1;
|
proxy->proxy.enc = 1;
|
||||||
}
|
}
|
||||||
@@ -401,173 +352,197 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
|||||||
proxy->initialized = 1;
|
proxy->initialized = 1;
|
||||||
proxy->cnx_s = clock_micros();
|
proxy->cnx_s = clock_micros();
|
||||||
proxy->host = conf->name;
|
proxy->host = conf->name;
|
||||||
info(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i", buffer, conf->proxy.port);
|
|
||||||
|
|
||||||
proxy:
|
info(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i", addr_buf, conf->proxy.port);
|
||||||
connection = http_get_header_field(&req->hdr, "Connection");
|
|
||||||
if (strcontains(connection, "upgrade") || strcontains(connection, "Upgrade")) {
|
return 0;
|
||||||
upgrade = http_get_header_field(&req->hdr, "Upgrade");
|
}
|
||||||
ws_version = http_get_header_field(&req->hdr, "Sec-WebSocket-Version");
|
|
||||||
if (streq(upgrade, "websocket") && streq(ws_version, "13")) {
|
int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg) {
|
||||||
ctx->ws_key = http_get_header_field(&req->hdr, "Sec-WebSocket-Key");
|
char buffer[CHUNK_SIZE], err_buf[256];
|
||||||
|
long ret;
|
||||||
|
int tries = 0, retry = 1;
|
||||||
|
|
||||||
|
*proxy_ptr = proxy_get_by_conf(conf);
|
||||||
|
proxy_ctx_t *proxy = *proxy_ptr;
|
||||||
|
proxy->client = NULL;
|
||||||
|
|
||||||
|
while (retry) {
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
if (!proxy->initialized || sock_has_pending(&proxy->proxy) != 0) {
|
||||||
|
if (proxy->initialized)
|
||||||
|
proxy_close(proxy);
|
||||||
|
|
||||||
|
retry = 0;
|
||||||
|
tries++;
|
||||||
|
|
||||||
|
if (proxy_connect(proxy, conf, res, ctx, err_msg) != 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *connection = http_get_header_field(&req->hdr, "Connection");
|
||||||
|
if (strcontains(connection, "upgrade") || strcontains(connection, "Upgrade")) {
|
||||||
|
const char *upgrade = http_get_header_field(&req->hdr, "Upgrade");
|
||||||
|
const char *ws_version = http_get_header_field(&req->hdr, "Sec-WebSocket-Version");
|
||||||
|
if (streq(upgrade, "websocket") && streq(ws_version, "13")) {
|
||||||
|
ctx->ws_key = http_get_header_field(&req->hdr, "Sec-WebSocket-Key");
|
||||||
|
} else {
|
||||||
|
res->status = http_get_status(501);
|
||||||
|
ctx->origin = INTERNAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
res->status = http_get_status(501);
|
http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL);
|
||||||
|
http_add_header_field(&req->hdr, "Connection", "keep-alive");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = proxy_request_header(req, client);
|
||||||
|
if (ret != 0) {
|
||||||
|
res->status = http_get_status(500);
|
||||||
ctx->origin = INTERNAL;
|
ctx->origin = INTERNAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL);
|
|
||||||
http_add_header_field(&req->hdr, "Connection", "keep-alive");
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = proxy_request_header(req, client);
|
ret = http_send_request(&proxy->proxy, req);
|
||||||
if (ret != 0) {
|
if (ret < 0) {
|
||||||
res->status = http_get_status(500);
|
|
||||||
ctx->origin = INTERNAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = http_send_request(&proxy->proxy, req);
|
|
||||||
if (ret < 0) {
|
|
||||||
res->status = http_get_status(502);
|
|
||||||
ctx->origin = SERVER_REQ;
|
|
||||||
error("Unable to send request to server (1)");
|
|
||||||
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
|
||||||
retry = tries < 4;
|
|
||||||
goto proxy_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *content_length = http_get_header_field(&req->hdr, "Content-Length");
|
|
||||||
unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0;
|
|
||||||
const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding");
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
if (content_len > 0) {
|
|
||||||
ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len);
|
|
||||||
} else if (strcontains(transfer_encoding, "chunked")) {
|
|
||||||
ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0 || (content_len != 0 && ret != content_len)) {
|
|
||||||
if (ret == -1 && errno != EPROTO) {
|
|
||||||
res->status = http_get_status(502);
|
res->status = http_get_status(502);
|
||||||
ctx->origin = SERVER_REQ;
|
ctx->origin = SERVER_REQ;
|
||||||
error("Unable to send request to server (2)");
|
error("Unable to send request to server (1)");
|
||||||
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||||
retry = tries < 4;
|
retry = tries < 4;
|
||||||
goto proxy_err;
|
continue;
|
||||||
} else if (ret == -1) {
|
}
|
||||||
res->status = http_get_status(400);
|
|
||||||
ctx->origin = CLIENT_REQ;
|
const char *content_length = http_get_header_field(&req->hdr, "Content-Length");
|
||||||
error("Unable to receive request from client");
|
unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0;
|
||||||
sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding");
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
if (content_len > 0) {
|
||||||
|
ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len);
|
||||||
|
} else if (strcontains(transfer_encoding, "chunked")) {
|
||||||
|
ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0 || (content_len != 0 && ret != content_len)) {
|
||||||
|
if (ret == -1 && errno != EPROTO) {
|
||||||
|
res->status = http_get_status(502);
|
||||||
|
ctx->origin = SERVER_REQ;
|
||||||
|
error("Unable to send request to server (2)");
|
||||||
|
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||||
|
retry = tries < 4;
|
||||||
|
continue;
|
||||||
|
} else if (ret == -1) {
|
||||||
|
res->status = http_get_status(400);
|
||||||
|
ctx->origin = CLIENT_REQ;
|
||||||
|
error("Unable to receive request from client");
|
||||||
|
sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
res->status = http_get_status(500);
|
||||||
|
ctx->origin = INTERNAL;
|
||||||
|
error("Unknown Error");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
res->status = http_get_status(500);
|
|
||||||
ctx->origin = INTERNAL;
|
|
||||||
error("Unknown Error");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK);
|
ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
int e_sys = error_get_sys(), e_ssl = error_get_ssl();
|
int e_sys = error_get_sys(), e_ssl = error_get_ssl();
|
||||||
if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) {
|
if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) {
|
||||||
res->status = http_get_status(504);
|
res->status = http_get_status(504);
|
||||||
ctx->origin = SERVER_RES;
|
ctx->origin = SERVER_RES;
|
||||||
} else {
|
} else {
|
||||||
|
res->status = http_get_status(502);
|
||||||
|
ctx->origin = SERVER_RES;
|
||||||
|
}
|
||||||
|
error("Unable to receive response from server");
|
||||||
|
sprintf(err_msg, "Unable to receive response from server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||||
|
retry = tries < 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buffer[ret] = 0;
|
||||||
|
|
||||||
|
char *buf = buffer;
|
||||||
|
unsigned short header_len = (unsigned short) (strstr(buffer, "\r\n\r\n") - buffer + 4);
|
||||||
|
|
||||||
|
if (header_len <= 0) {
|
||||||
res->status = http_get_status(502);
|
res->status = http_get_status(502);
|
||||||
ctx->origin = SERVER_RES;
|
ctx->origin = SERVER_RES;
|
||||||
|
error("Unable to parse header: End of header not found");
|
||||||
|
sprintf(err_msg, "Unable to parser header: End of header not found.");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
error("Unable to receive response from server");
|
|
||||||
sprintf(err_msg, "Unable to receive response from server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
|
||||||
retry = tries < 4;
|
|
||||||
goto proxy_err;
|
|
||||||
}
|
|
||||||
buffer[ret] = 0;
|
|
||||||
|
|
||||||
char *buf = buffer;
|
for (int i = 0; i < header_len; i++) {
|
||||||
unsigned short header_len = (unsigned short) (strstr(buffer, "\r\n\r\n") - buffer + 4);
|
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) {
|
||||||
|
res->status = http_get_status(502);
|
||||||
if (header_len <= 0) {
|
ctx->origin = SERVER_RES;
|
||||||
res->status = http_get_status(502);
|
error("Unable to parse header: Header contains illegal characters");
|
||||||
ctx->origin = SERVER_RES;
|
sprintf(err_msg, "Unable to parse header: Header contains illegal characters.");
|
||||||
error("Unable to parse header: End of header not found");
|
continue;
|
||||||
sprintf(err_msg, "Unable to parser header: End of header not found.");
|
}
|
||||||
goto proxy_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < header_len; i++) {
|
|
||||||
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) {
|
|
||||||
res->status = http_get_status(502);
|
|
||||||
ctx->origin = SERVER_RES;
|
|
||||||
error("Unable to parse header: Header contains illegal characters");
|
|
||||||
sprintf(err_msg, "Unable to parse header: Header contains illegal characters.");
|
|
||||||
goto proxy_err;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
char *ptr = buf;
|
char *ptr = buf;
|
||||||
while (header_len != (ptr - buf)) {
|
while (header_len != (ptr - buf)) {
|
||||||
char *pos0 = strstr(ptr, "\r\n");
|
char *pos0 = strstr(ptr, "\r\n");
|
||||||
if (pos0 == NULL) {
|
if (pos0 == NULL) {
|
||||||
res->status = http_get_status(502);
|
|
||||||
ctx->origin = SERVER_RES;
|
|
||||||
error("Unable to parse header: Invalid header format");
|
|
||||||
sprintf(err_msg, "Unable to parse header: Invalid header format.");
|
|
||||||
goto proxy_err;
|
|
||||||
}
|
|
||||||
if (ptr == buf) {
|
|
||||||
if (!strstarts(ptr, "HTTP/")) {
|
|
||||||
res->status = http_get_status(502);
|
res->status = http_get_status(502);
|
||||||
ctx->origin = SERVER_RES;
|
ctx->origin = SERVER_RES;
|
||||||
error("Unable to parse header: Invalid header format");
|
error("Unable to parse header: Invalid header format");
|
||||||
sprintf(err_msg, "Unable to parse header: Invalid header format.");
|
sprintf(err_msg, "Unable to parse header: Invalid header format.");
|
||||||
goto proxy_err;
|
continue;
|
||||||
}
|
}
|
||||||
int status_code = (int) strtol(ptr + 9, NULL, 10);
|
if (ptr == buf) {
|
||||||
res->status = http_get_status(status_code);
|
if (!strstarts(ptr, "HTTP/")) {
|
||||||
if (res->status == NULL && status_code >= 100 && status_code <= 999) {
|
res->status = http_get_status(502);
|
||||||
custom_status->code = status_code;
|
ctx->origin = SERVER_RES;
|
||||||
custom_status->type = 0;
|
error("Unable to parse header: Invalid header format");
|
||||||
snprintf(custom_status->msg, sizeof(custom_status->msg), "%.*s",
|
sprintf(err_msg, "Unable to parse header: Invalid header format.");
|
||||||
(int) (strchr(ptr, '\r') - ptr - 13), ptr + 13);
|
continue;
|
||||||
res->status = custom_status;
|
}
|
||||||
} else if (res->status == NULL) {
|
int status_code = (int) strtol(ptr + 9, NULL, 10);
|
||||||
res->status = http_get_status(502);
|
res->status = http_get_status(status_code);
|
||||||
ctx->origin = SERVER_RES;
|
if (res->status == NULL && status_code >= 100 && status_code <= 999) {
|
||||||
error("Unable to parse header: Invalid or unknown status code");
|
custom_status->code = status_code;
|
||||||
sprintf(err_msg, "Unable to parse header: Invalid or unknown status code.");
|
custom_status->type = 0;
|
||||||
goto proxy_err;
|
snprintf(custom_status->msg, sizeof(custom_status->msg), "%.*s",
|
||||||
|
(int) (strchr(ptr, '\r') - ptr - 13), ptr + 13);
|
||||||
|
res->status = custom_status;
|
||||||
|
} else if (res->status == NULL) {
|
||||||
|
res->status = http_get_status(502);
|
||||||
|
ctx->origin = SERVER_RES;
|
||||||
|
error("Unable to parse header: Invalid or unknown status code");
|
||||||
|
sprintf(err_msg, "Unable to parse header: Invalid or unknown status code.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (http_parse_header_field(&res->hdr, ptr, pos0, 0) != 0) {
|
||||||
|
res->status = http_get_status(502);
|
||||||
|
ctx->origin = SERVER_RES;
|
||||||
|
error("Unable to parse header");
|
||||||
|
sprintf(err_msg, "Unable to parse header.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
if (pos0[2] == '\r' && pos0[3] == '\n') {
|
||||||
if (http_parse_header_field(&res->hdr, ptr, pos0, 0) != 0) {
|
break;
|
||||||
res->status = http_get_status(502);
|
|
||||||
ctx->origin = SERVER_RES;
|
|
||||||
error("Unable to parse header");
|
|
||||||
sprintf(err_msg, "Unable to parse header.");
|
|
||||||
goto proxy_err;
|
|
||||||
}
|
}
|
||||||
|
ptr = pos0 + 2;
|
||||||
}
|
}
|
||||||
if (pos0[2] == '\r' && pos0[3] == '\n') {
|
sock_recv_x(&proxy->proxy, buffer, header_len, 0);
|
||||||
break;
|
|
||||||
}
|
|
||||||
ptr = pos0 + 2;
|
|
||||||
}
|
|
||||||
sock_recv_x(&proxy->proxy, buffer, header_len, 0);
|
|
||||||
|
|
||||||
ret = proxy_response_header(req, res, conf);
|
ret = proxy_response_header(req, res, conf);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
res->status = http_get_status(500);
|
res->status = http_get_status(500);
|
||||||
ctx->origin = INTERNAL;
|
ctx->origin = INTERNAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
proxy_err:
|
|
||||||
errno = 0;
|
|
||||||
if (retry) goto retry;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
104
src/lib/sock.c
104
src/lib/sock.c
@@ -9,6 +9,7 @@
|
|||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "../logger.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
@@ -18,8 +19,9 @@
|
|||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
static void ssl_error(unsigned long err) {
|
static void sock_ssl_error(unsigned long err) {
|
||||||
if (err == SSL_ERROR_NONE) {
|
if (err == SSL_ERROR_NONE) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
} else if (err == SSL_ERROR_SYSCALL) {
|
} else if (err == SSL_ERROR_SYSCALL) {
|
||||||
@@ -32,7 +34,18 @@ static void ssl_error(unsigned long err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sock_error(sock *s, int ret) {
|
void sock_error(sock *s, int ret) {
|
||||||
ssl_error(SSL_get_error(s->ssl, ret));
|
sock_ssl_error(SSL_get_error(s->ssl, ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock_gai_error(int ret) {
|
||||||
|
if (ret == 0) {
|
||||||
|
errno = 0;
|
||||||
|
} else if (ret == EAI_SYSTEM) {
|
||||||
|
// errno already set
|
||||||
|
} else {
|
||||||
|
error_gai(ret);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *sock_error_str(unsigned long err) {
|
const char *sock_error_str(unsigned long err) {
|
||||||
@@ -78,21 +91,100 @@ int sock_init(sock *s, int fd, int flags) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros) {
|
int sock_connect(const char *hostname, unsigned short port, double timeout_sec, char *addr_buf, size_t addr_buf_size) {
|
||||||
|
char buf[INET6_ADDRSTRLEN + 1];
|
||||||
|
int ret, fd, e = 0;
|
||||||
|
long timeout_micros = (long) (timeout_sec * 1000000L);
|
||||||
|
struct addrinfo *result, *rp,
|
||||||
|
hints = {
|
||||||
|
.ai_family = AF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_protocol = 0,
|
||||||
|
.ai_flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (addr_buf && addr_buf_size > 1)
|
||||||
|
addr_buf[0] = 0;
|
||||||
|
|
||||||
|
if ((ret = getaddrinfo(hostname, NULL, &hints, &result)) != 0)
|
||||||
|
return sock_gai_error(ret);
|
||||||
|
|
||||||
|
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||||
|
switch (rp->ai_family) {
|
||||||
|
case AF_INET:
|
||||||
|
((struct sockaddr_in *) rp->ai_addr)->sin_port = htons(port);
|
||||||
|
inet_ntop(rp->ai_family, &((struct sockaddr_in *) rp->ai_addr)->sin_addr, buf, addr_buf_size);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
((struct sockaddr_in6 *) rp->ai_addr)->sin6_port = htons(port);
|
||||||
|
inet_ntop(rp->ai_family, &((struct sockaddr_in6 *) rp->ai_addr)->sin6_addr, buf, addr_buf_size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("Trying [%s]:%i", buf, port);
|
||||||
|
|
||||||
|
if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) {
|
||||||
|
if (e == 0) {
|
||||||
|
e = errno;
|
||||||
|
} else if (e != errno) {
|
||||||
|
e = -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock_set_socket_timeout_micros(fd, timeout_micros, timeout_micros) == -1) {
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
|
||||||
|
e = errno;
|
||||||
|
close(fd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
|
if (addr_buf && addr_buf_size > 1 && addr_buf[0] == 0)
|
||||||
|
strncpy(addr_buf, buf, addr_buf_size);
|
||||||
|
|
||||||
|
errno = e;
|
||||||
|
return (e == 0) ? fd : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock_reverse_lookup(const sock *s, char *host, size_t host_size) {
|
||||||
|
memset(host, 0, host_size);
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
if ((ret = getnameinfo(&s->_addr.sock, sizeof(s->_addr), host, host_size, NULL, 0, 0)) != 0) {
|
||||||
|
if (ret == EAI_NONAME) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return sock_gai_error(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock_set_socket_timeout_micros(int fd, long recv_micros, long send_micros) {
|
||||||
struct timeval recv_to = {.tv_sec = recv_micros / 1000000, .tv_usec = recv_micros % 1000000},
|
struct timeval recv_to = {.tv_sec = recv_micros / 1000000, .tv_usec = recv_micros % 1000000},
|
||||||
send_to = {.tv_sec = send_micros / 1000000, .tv_usec = send_micros % 1000000};
|
send_to = {.tv_sec = send_micros / 1000000, .tv_usec = send_micros % 1000000};
|
||||||
|
|
||||||
if (setsockopt(s->socket, SOL_SOCKET, SO_RCVTIMEO, &recv_to, sizeof(recv_to)) != 0)
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &recv_to, sizeof(recv_to)) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (setsockopt(s->socket, SOL_SOCKET, SO_SNDTIMEO, &send_to, sizeof(send_to)) != 0)
|
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &send_to, sizeof(send_to)) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_socket_timeout(sock *s, double sec) {
|
int sock_set_socket_timeout(sock *s, double sec) {
|
||||||
return sock_set_socket_timeout_micros(s, (long) (sec * 1000000L), (long) (sec * 1000000L));
|
return sock_set_socket_timeout_micros(s->socket, (long) (sec * 1000000L), (long) (sec * 1000000L));
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_timeout_micros(sock *s, long micros) {
|
int sock_set_timeout_micros(sock *s, long micros) {
|
||||||
|
@@ -38,7 +38,13 @@ const char *sock_error_str(unsigned long err);
|
|||||||
|
|
||||||
int sock_init(sock *s, int fd, int enc);
|
int sock_init(sock *s, int fd, int enc);
|
||||||
|
|
||||||
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros);
|
int sock_connect(const char *hostname, unsigned short port, double timeout_sec, char *addr_buf, size_t addr_buf_size);
|
||||||
|
|
||||||
|
int sock_reverse_lookup(const sock *s, char *host, size_t host_size);
|
||||||
|
|
||||||
|
int sock_init_addr_str(const sock *s, char *c_addr, size_t c_addr_size, char *s_addr, size_t s_addr_size);
|
||||||
|
|
||||||
|
int sock_set_socket_timeout_micros(int fd, long recv_micros, long send_micros);
|
||||||
|
|
||||||
int sock_set_socket_timeout(sock *s, double sec);
|
int sock_set_socket_timeout(sock *s, double sec);
|
||||||
|
|
||||||
|
@@ -28,32 +28,6 @@ void tcp_acceptor_func(client_ctx_t *ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dig(const char *addr, char *host, size_t host_size) {
|
|
||||||
char buf[1024];
|
|
||||||
FILE *out;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
sprintf(buf, "dig @%s +short +time=1 -x %s", config.dns_server, addr);
|
|
||||||
if ((out = popen(buf, "r")) == NULL) {
|
|
||||||
error("Unable to start dig: %s");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long read = fread(buf, 1, sizeof(buf), out);
|
|
||||||
if ((ret = pclose(out)) != 0) {
|
|
||||||
error("Dig terminated with exit code %i", ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *ptr = memchr(buf, '\n', read);
|
|
||||||
if (ptr == buf || ptr == NULL) return -1;
|
|
||||||
|
|
||||||
ptr[-1] = 0;
|
|
||||||
strncpy(host, buf, host_size);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tcp_acceptor(client_ctx_t *ctx) {
|
static int tcp_acceptor(client_ctx_t *ctx) {
|
||||||
struct sockaddr_in6 server_addr;
|
struct sockaddr_in6 server_addr;
|
||||||
|
|
||||||
@@ -84,9 +58,7 @@ static int tcp_acceptor(client_ctx_t *ctx) {
|
|||||||
sock *client = &ctx->socket;
|
sock *client = &ctx->socket;
|
||||||
ctx->cnx_s = clock_micros();
|
ctx->cnx_s = clock_micros();
|
||||||
|
|
||||||
ctx->host[0] = 0;
|
sock_reverse_lookup(&ctx->socket, ctx->host, sizeof(ctx->host));
|
||||||
if (config.dns_server[0] != 0)
|
|
||||||
dig(ctx->socket.addr, ctx->host, sizeof(ctx->host));
|
|
||||||
|
|
||||||
ctx->cc[0] = 0;
|
ctx->cc[0] = 0;
|
||||||
geoip_lookup_country(&client->_addr.sock, ctx->cc);
|
geoip_lookup_country(&client->_addr.sock, ctx->cc);
|
||||||
|
Reference in New Issue
Block a user