7 Commits

21 changed files with 430 additions and 226 deletions

View File

@@ -63,7 +63,7 @@ bin/sesimos: bin/server.o bin/logger.o bin/cache_handler.o bin/async.o bin/worke
bin/lib/http_static.o bin/res/default.o bin/res/proxy.o bin/res/style.o \
bin/res/icon_error.o bin/res/icon_info.o bin/res/icon_success.o bin/res/icon_warning.o \
bin/res/globe.o \
bin/lib/compress.o bin/lib/config.o bin/lib/fastcgi.o bin/lib/geoip.o \
bin/lib/compress.o bin/lib/config.o bin/lib/fastcgi.o bin/lib/geoip.o bin/lib/error.o \
bin/lib/http.o bin/lib/proxy.o bin/lib/sock.o bin/lib/uri.o \
bin/lib/utils.o bin/lib/websocket.o bin/lib/mpmc.o bin/lib/list.o
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
@@ -99,6 +99,8 @@ bin/lib/config.o: src/lib/config.h src/lib/utils.h src/lib/uri.h src/logger.h
bin/lib/fastcgi.o: src/lib/fastcgi.h src/server.h src/lib/utils.h src/lib/compress.h src/lib/http.h \
src/lib/uri.h src/lib/include/fastcgi.h src/logger.h
bin/lib/error.o: src/lib/error.h
bin/lib/geoip.o: src/lib/geoip.h
bin/lib/http.o: src/lib/http.h src/lib/utils.h src/lib/compress.h src/lib/sock.h src/logger.h

View File

@@ -9,6 +9,7 @@
#include "async.h"
#include "logger.h"
#include "lib/list.h"
#include "lib/utils.h"
#include <poll.h>
#include <sys/epoll.h>
@@ -28,6 +29,7 @@ typedef struct {
int flags;
void *arg;
void (*cb)(void *);
void (*to_cb)(void *);
void (*err_cb)(void *);
} evt_listen_t;
@@ -154,7 +156,7 @@ static int async_add(evt_listen_t *evt) {
return ret;
}
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *)) {
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *)) {
evt_listen_t evt = {
.fd = fd,
.socket = NULL,
@@ -162,12 +164,13 @@ int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *),
.flags = flags,
.arg = arg,
.cb = cb,
.to_cb = to_cb,
.err_cb = err_cb,
};
return async_add(&evt);
}
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *)) {
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *)) {
evt_listen_t evt = {
.fd = s->socket,
.socket = s,
@@ -175,6 +178,7 @@ int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), vo
.flags = flags,
.arg = arg,
.cb = cb,
.to_cb = to_cb,
.err_cb = err_cb,
};
return async_add(&evt);
@@ -206,6 +210,8 @@ void async_free(void) {
void async_thread(void) {
struct epoll_event ev, events[ASYNC_MAX_EVENTS];
int num_fds;
long ts, min_ts, cur_ts;
listen_queue_t *l;
evt_listen_t **local = list_create(sizeof(evt_listen_t *), 16);
if (local == NULL) {
critical("Unable to create async local list");
@@ -217,7 +223,7 @@ void async_thread(void) {
// main event loop
while (alive) {
// swap listen queue
listen_queue_t *l = listen_q;
l = listen_q;
listen_q = (listen_q == &listen1) ? &listen2 : &listen1;
// fill local list and epoll instance with previously added queue entries
@@ -240,7 +246,17 @@ void async_thread(void) {
// reset size of queue
l->n = 0;
if ((num_fds = epoll_wait(epoll_fd, events, ASYNC_MAX_EVENTS, -1)) == -1) {
// calculate wait timeout
min_ts = -1000, cur_ts = clock_micros();;
for (int i = 0; i < list_size(local); i++) {
evt_listen_t *evt = local[i];
if (!evt->socket) continue;
ts = evt->socket->ts_last + evt->socket->timeout_us - cur_ts;
if (min_ts == -1000 || ts < min_ts) min_ts = ts;
}
if ((num_fds = epoll_wait(epoll_fd, events, ASYNC_MAX_EVENTS, (int) (min_ts / 1000))) == -1) {
if (errno == EINTR) {
// interrupt
errno = 0;
@@ -274,6 +290,31 @@ void async_thread(void) {
free(evt);
}
}
// check, if some socket ran into a timeout
cur_ts = clock_micros();
for (int i = 0; i < list_size(local); i++) {
evt_listen_t *evt = local[i];
if (!evt->socket) continue;
if ((cur_ts - evt->socket->ts_last) >= evt->socket->timeout_us) {
evt->to_cb(evt->arg);
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, evt->fd, NULL) == -1) {
if (errno == EBADF) {
// already closed fd, do not die
errno = 0;
} else {
critical("Unable to remove file descriptor from epoll instance");
return;
}
}
local = list_remove(local, i--);
}
}
logger_set_prefix("");
errno = 0;
}
// cleanup

View File

@@ -24,9 +24,9 @@
typedef unsigned int async_evt_t;
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *));
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *));
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *));
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *));
int async_init(void);

134
src/lib/error.c Normal file
View File

@@ -0,0 +1,134 @@
/**
* sesimos - secure, simple, modern web server
* @brief Error interface
* @file src/lib/error.c
* @author Lorenz Stechauner
* @date 2023-01-08
*/
#include "error.h"
#include "http.h"
#include <errno.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <maxminddb.h>
static const char *error_ssl_strerror(int err) {
switch (err) {
case SSL_ERROR_ZERO_RETURN:
return "closed";
case SSL_ERROR_WANT_READ:
return "want read";
case SSL_ERROR_WANT_WRITE:
return "want write";
case SSL_ERROR_WANT_CONNECT:
return "want connect";
case SSL_ERROR_WANT_ACCEPT:
return "want accept";
case SSL_ERROR_WANT_X509_LOOKUP:
return "want x509 lookup";
case SSL_ERROR_WANT_ASYNC:
return "want async";
case SSL_ERROR_WANT_ASYNC_JOB:
return "want async job";
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
return "want client hello callback";
case SSL_ERROR_WANT_RETRY_VERIFY:
return "want retry verify";
case SSL_ERROR_SSL:
return ERR_reason_error_string(ERR_get_error());
default:
return "unknown error";
}
}
static const char *error_http_strerror(int err) {
switch (err) {
case HTTP_ERROR_TOO_MANY_HEADER_FIELDS:
return "too many header fields";
case HTTP_ERROR_EOH_NOT_FOUND:
return "end of http header not found";
case HTTP_ERROR_HEADER_MALFORMED:
return "http header malformed";
case HTTP_ERROR_INVALID_VERSION:
return "invalid http version";
case HTTP_ERROR_URI_TOO_LONG:
return "uri too long";
case HTTP_ERROR_GENERAL:
default:
return "unknown error";
}
}
const char *error_str(int err_no, char *buf, int buf_len) {
buf[0] = 0;
unsigned char mode = (unsigned char) (err_no >> 24);
int e = err_no & 0x00FFFFFF;
if (mode == 0x00) {
// normal
strerror_r(e, buf, buf_len);
return buf;
} else if (mode == 0x01) {
// ssl
return error_ssl_strerror(e);
} else if (mode == 0x02) {
// mmdb
return MMDB_strerror(e);
} else if (mode == 0x03) {
// http
return error_http_strerror(e);
}
return buf;
}
void error_ssl(int err) {
if (err == SSL_ERROR_NONE) {
errno = 0;
} else if (err == SSL_ERROR_SYSCALL) {
// errno already set
} else {
errno = 0x01000000 | err;
}
}
void error_mmdb(int err) {
if (err == MMDB_SUCCESS) {
errno = 0;
} else if (err == MMDB_IO_ERROR) {
// errno already set
} else {
errno = 0x02000000 | err;
}
}
int error_http(int err) {
if (err == 0) {
errno = 0;
} else if (err == HTTP_ERROR_SYSCALL) {
// errno already set
} else {
errno = 0x03000000 | err;
}
return -1;
}
static int error_get(unsigned char prefix) {
return (errno >> 24 != prefix) ? 0 : errno & 0x00FFFFFF;
}
int error_get_sys() {
return error_get(0x00);
}
int error_get_ssl() {
return error_get(0x01);
}
int error_get_mmdb() {
return error_get(0x02);
}
int error_get_http() {
return error_get(0x03);
}

28
src/lib/error.h Normal file
View File

@@ -0,0 +1,28 @@
/**
* sesimos - secure, simple, modern web server
* @brief Error interface (header fie)
* @file src/lib/error.h
* @author Lorenz Stechauner
* @date 2023-01-08
*/
#ifndef SESIMOS_ERROR_H
#define SESIMOS_ERROR_H
const char *error_str(int err_no, char *buf, int buf_len);
void error_ssl(int err);
void error_mmdb(int err);
int error_http(int err);
int error_get_sys();
int error_get_ssl();
int error_get_mmdb();
int error_get_http();
#endif //SESIMOS_ERROR_H

View File

@@ -383,6 +383,7 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) {
ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
if (ret != 0) return (int) ret;
if (pos0[2] == '\r' && pos0[3] == '\n') {
return 0;
}
@@ -593,7 +594,7 @@ int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) {
while (rcv_len < len) {
ret = sock_recv(client, buf, sizeof(buf), 0);
if (ret <= 0) {
error("Unable to receive: %s", sock_strerror(client));
error("Unable to receive");
return -1;
}

View File

@@ -8,6 +8,7 @@
#include "geoip.h"
#include "../logger.h"
#include "error.h"
#include <memory.h>
#include <dirent.h>
@@ -101,7 +102,8 @@ int geoip_init(const char *directory) {
sprintf(buf, "%s/%s", directory, entry->d_name);
if ((status = MMDB_open(buf, 0, &mmdbs[i])) != MMDB_SUCCESS) {
critical("Unable to initialize geoip: Unable to open .mmdb file: %s", MMDB_strerror(status));
error_mmdb(status);
critical("Unable to initialize geoip: Unable to open .mmdb file");
closedir(geoip_dir);
return 1;
}
@@ -164,7 +166,8 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) {
int mmdb_res;
MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdbs[i], addr, &mmdb_res);
if (mmdb_res != MMDB_SUCCESS) {
error("Unable to lookup geoip info: %s", MMDB_strerror(mmdb_res));
error_mmdb(mmdb_res);
error("Unable to lookup geoip info");
continue;
} else if (!result.found_entry) {
continue;
@@ -172,7 +175,8 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) {
MMDB_entry_data_list_s *list;
if ((mmdb_res = MMDB_get_entry_data_list(&result.entry, &list)) != MMDB_SUCCESS) {
error("Unable to lookup geoip info: %s", MMDB_strerror(mmdb_res));
error_mmdb(mmdb_res);
error("Unable to lookup geoip info");
continue;
}

View File

@@ -6,11 +6,11 @@
* @date 2020-12-09
*/
#include "../logger.h"
#include "http.h"
#include "utils.h"
#include "compress.h"
#include "list.h"
#include "error.h"
#include <string.h>
@@ -90,23 +90,20 @@ void http_free_res(http_res *res) {
int http_init_hdr(http_hdr *hdr) {
hdr->fields = list_create(sizeof(http_field), HTTP_INIT_HEADER_FIELD_NUM);
if (hdr->fields == NULL)
return -1;
return error_http(HTTP_ERROR_SYSCALL);
return 0;
}
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags) {
if (hdr->last_field_num > list_size(hdr->fields)) {
error("Unable to parse header: Invalid state");
return 3;
}
if (hdr->last_field_num > list_size(hdr->fields))
return error_http(HTTP_ERROR_GENERAL);
char *pos1 = (char *) buf, *pos2 = (char *) end_ptr;
if (buf[0] == ' ' || buf[0] == '\t') {
if (hdr->last_field_num == -1) {
error("Unable to parse header");
return 3;
}
if (hdr->last_field_num == -1)
return error_http(HTTP_ERROR_GENERAL);
http_field *f = &hdr->fields[(int) hdr->last_field_num];
str_trim_lws(&pos1, &pos2);
@@ -116,10 +113,9 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr,
}
pos1 = memchr(buf, ':', end_ptr - buf);
if (pos1 == NULL) {
error("Unable to parse header");
return 3;
}
if (pos1 == NULL)
return error_http(HTTP_ERROR_GENERAL);
long len1 = pos1 - buf;
pos1++;
@@ -129,10 +125,8 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr,
int field_num = list_size(hdr->fields);
int found = http_get_header_field_num_len(hdr, buf, len1);
if (!(flags & HTTP_MERGE_FIELDS) || found == -1) {
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) {
error("Unable to parse header: Too many header fields");
return 3;
}
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0)
return error_http(HTTP_ERROR_TOO_MANY_HEADER_FIELDS);
} else {
field_num = found;
http_append_to_header_field(&hdr->fields[found], ", ", 2);
@@ -148,62 +142,52 @@ int http_parse_request(char *buf, http_req *req) {
long len;
unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4;
if (header_len <= 0) {
error("Unable to parse http header: End of header not found");
return -5;
}
if (header_len <= 0)
return error_http(HTTP_ERROR_EOH_NOT_FOUND);
for (int i = 0; i < header_len; i++) {
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) {
error("Unable to parse http header: Header contains illegal characters");
return -4;
}
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F)
return error_http(HTTP_ERROR_HEADER_MALFORMED);
}
ptr = buf;
while (header_len > (ptr - buf + 2)) {
pos0 = strstr(ptr, "\r\n");
if (pos0 == NULL) {
error("Unable to parse http header: Invalid header format");
return -1;
}
if (pos0 == NULL)
return error_http(HTTP_ERROR_HEADER_MALFORMED);
if (req->version[0] == 0) {
pos1 = (char *) strchr(ptr, ' ') + 1;
if (pos1 == NULL) goto err_hdr_fmt;
if (pos1 - ptr - 1 >= sizeof(req->method)) {
error("Unable to parse http header: Method name too long");
return -2;
}
if (pos1 - ptr - 1 >= sizeof(req->method))
return error_http(HTTP_ERROR_HEADER_MALFORMED);
for (int i = 0; i < (pos1 - ptr - 1); i++) {
if (ptr[i] < 'A' || ptr[i] > 'Z') {
error("Unable to parse http header: Invalid method");
return -2;
}
if (ptr[i] < 'A' || ptr[i] > 'Z')
return error_http(HTTP_ERROR_HEADER_MALFORMED);
}
snprintf(req->method, sizeof(req->method), "%.*s", (int) (pos1 - ptr - 1), ptr);
pos2 = (char *) strchr(pos1, ' ') + 1;
if (pos2 == NULL) {
err_hdr_fmt:
error("Unable to parse http header: Invalid header format");
return -1;
return error_http(HTTP_ERROR_HEADER_MALFORMED);
}
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) {
error("Unable to parse http header: Invalid version");
return -3;
}
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0)
return error_http(HTTP_ERROR_INVALID_VERSION);
len = pos2 - pos1 - 1;
if (len >= 2048)
return error_http(HTTP_ERROR_URI_TOO_LONG);
req->uri = malloc(len + 1);
sprintf(req->uri, "%.*s", (int) len, pos1);
sprintf(req->version, "%.3s", pos2 + 5);
} else {
int ret = http_parse_header_field(&req->hdr, ptr, pos0, HTTP_MERGE_FIELDS);
if (ret != 0) return -ret;
if (http_parse_header_field(&req->hdr, ptr, pos0, HTTP_MERGE_FIELDS) != 0)
return -1;
}
ptr = pos0 + 2;
}
@@ -212,7 +196,7 @@ int http_parse_request(char *buf, http_req *req) {
return (int) header_len;
}
return -1;
return error_http(HTTP_ERROR_GENERAL);
}
int http_receive_request(sock *client, http_req *req) {
@@ -226,10 +210,9 @@ int http_receive_request(sock *client, http_req *req) {
http_init_hdr(&req->hdr);
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE - 1, MSG_PEEK);
if (rcv_len <= 0) {
error("Unable to receive http header: %s", sock_strerror(client));
if (rcv_len <= 0)
return -1;
}
buf[rcv_len] = 0;
long header_len = http_parse_request(buf, req);
@@ -351,9 +334,9 @@ int http_send_response(sock *client, http_res *res) {
off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
}
off += sprintf(buf + off, "\r\n");
if (sock_send(client, buf, off, 0) < 0) {
if (sock_send(client, buf, off, 0) != off)
return -1;
}
return 0;
}
@@ -365,10 +348,9 @@ int http_send_request(sock *server, http_req *req) {
off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
}
off += sprintf(buf + off, "\r\n");
long ret = sock_send(server, buf, off, 0);
if (ret <= 0) {
if (sock_send(server, buf, off, 0) != off)
return -1;
}
return 0;
}

View File

@@ -45,6 +45,15 @@
#define HTTP_TYPE_CLIENT_ERROR 4
#define HTTP_TYPE_SERVER_ERROR 5
#define HTTP_ERROR_GENERAL 1
#define HTTP_ERROR_SYSCALL 2
#define HTTP_ERROR_TOO_MANY_HEADER_FIELDS 3
#define HTTP_ERROR_EOH_NOT_FOUND 4
#define HTTP_ERROR_HEADER_MALFORMED 5
#define HTTP_ERROR_INVALID_VERSION 6
#define HTTP_ERROR_URI_TOO_LONG 7
#define HTTP_ERROR_
#ifndef SERVER_STR
# define SERVER_STR "sesimos"
#endif

View File

@@ -24,7 +24,7 @@ const http_status http_statuses[] = {
{206, HTTP_TYPE_SUCCESS, "Partial Content"},
{207, HTTP_TYPE_SUCCESS, "Multi-Status"},
{208, HTTP_TYPE_SUCCESS, "Already Reported"},
{226, HTTP_TYPE_SUCCESS, "IM Used"},
{226, HTTP_TYPE_SUCCESS, "Instance Manipulation Used"},
{300, HTTP_TYPE_REDIRECTION, "Multiple Choices"},
{301, HTTP_TYPE_REDIRECTION, "Moved Permanently"},
@@ -76,7 +76,6 @@ const http_status http_statuses[] = {
{511, HTTP_TYPE_SERVER_ERROR, "Network Authentication Required"},
};
// TODO add remaining descriptions
const http_status_msg http_status_messages[] = {
{100, "The client SHOULD continue with its request. The server MUST send a final response after the request "
"has been completed."},
@@ -146,15 +145,21 @@ const http_status_msg http_status_messages[] = {
"next-hop server."},
{421, "The server is not able to produce a response. The client MAY retry the request over a different "
"connection."},
{422, ""},
{423, ""},
{424, ""},
{425, ""},
{426, ""},
{428, ""},
{429, ""},
{431, ""},
{451, ""},
{422, "The server understands the content type of the request content, and the syntax of the request content "
"is correct, but the server was unable to process the contained information."},
{423, "The source or destination resource of a method is locked."},
{424, "The method could not be performed on the resource because the requested action depended on another "
"action and that action failed."},
{425, "The server is unwilling to risk processing a request that might be replayed."},
{426, "The server refuses to perform the request using the current protocol but might be willing to do so "
"after the client upgrades to a different protocol. The server MUST send an Upgrade header field to"
"indicate the required protocol(s)."},
{428, "The origin server requires the request to be conditional. By requiring requests to be conditional, the "
"server can assure that clients are working with the correct copies and thus avoiding a lost update."},
{429, "The client has sent too many requests in a given amount of time."},
{431, "The server is unwilling to process the request because its header fields are too large. The request MAY "
"be resubmitted after reducing the size of the request header fields."},
{451, "The server is denying access to the resource as a consequence of a legal demand."},
{500, "The server encountered an unexpected condition which prevented it from fulfilling the request."},
{501, "The server does not support the functionality required to fulfill the request."},
@@ -167,10 +172,16 @@ const http_status_msg http_status_messages[] = {
"complete the request."},
{505, "The server does not support, or refuses to support, the HTTP protocol version that was used in the "
"request message."},
{506, ""},
{507, ""},
{508, ""},
{511, ""},
{506, "The server has an internal configuration error: the chosen variant resource is configured to engage in "
"transparent content negotiation itself, and is therefore not a proper end point in the negotiation "
"process."},
{507, "The method could not be performed on the resource because the server is unable to store the "
"representation needed to successfully complete the request. This condition is considered to be "
"temporary."},
{508, "The server terminated an operation because it encountered an infinite loop while processing the "
"request."},
{511, "The client needs to authenticate to gain network access. The response representation SHOULD contain a "
"link to a resource that allows the user to submit credentials."},
};
const int http_statuses_size = sizeof(http_statuses) / sizeof(http_status);

View File

@@ -13,6 +13,7 @@
#include "utils.h"
#include "compress.h"
#include "config.h"
#include "error.h"
#include <openssl/ssl.h>
#include <string.h>
@@ -293,7 +294,7 @@ int proxy_response_header(http_req *req, http_res *res, host_config_t *conf) {
}
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) {
char buffer[CHUNK_SIZE];
char buffer[CHUNK_SIZE], err_buf[256];
const char *connection, *upgrade, *ws_version;
long ret;
int tries = 0, retry = 0;
@@ -329,7 +330,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
if (host_ent == NULL) {
host_ent = gethostbyname2(conf->proxy.hostname, AF_INET);
if (host_ent == NULL) {
res->status = http_get_status(504);
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.");
@@ -354,14 +355,14 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
res->status = http_get_status(504);
ctx->origin = SERVER_REQ;
} else if (errno == ECONNREFUSED) {
res->status = http_get_status(504);
res->status = http_get_status(502);
ctx->origin = SERVER_REQ;
} else {
res->status = http_get_status(500);
ctx->origin = INTERNAL;
}
error("Unable to connect to [%s]:%i: %s", buffer, conf->proxy.port, strerror(errno));
sprintf(err_msg, "Unable to connect to server: %s.", strerror(errno));
error("Unable to connect to [%s]:%i", buffer, conf->proxy.port);
sprintf(err_msg, "Unable to connect to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
goto proxy_err;
}
@@ -370,7 +371,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
res->status = http_get_status(500);
ctx->origin = INTERNAL;
error("Unable to set timeout for reverse proxy socket");
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", strerror(errno));
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", error_str(errno, err_buf, sizeof(err_buf)));
goto proxy_err;
}
@@ -380,17 +381,15 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
SSL_set_connect_state(proxy->proxy.ssl);
ret = SSL_do_handshake(proxy->proxy.ssl);
proxy->proxy._last_ret = ret;
proxy->proxy._errno = errno;
proxy->proxy._ssl_error = ERR_get_error();
proxy->proxy.enc = 1;
if (ret < 0) {
if (ret != 1) {
error_ssl(SSL_get_error(proxy->proxy.ssl, (int) ret));
res->status = http_get_status(502);
ctx->origin = SERVER_REQ;
error("Unable to perform handshake: %s", sock_strerror(&proxy->proxy));
sprintf(err_msg, "Unable to perform handshake: %s.", sock_strerror(&proxy->proxy));
error("Unable to perform handshake");
sprintf(err_msg, "Unable to perform handshake: %s.", error_str(errno, err_buf, sizeof(err_buf)));
goto proxy_err;
}
proxy->proxy.enc = 1;
}
proxy->initialized = 1;
@@ -425,8 +424,8 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
if (ret < 0) {
res->status = http_get_status(502);
ctx->origin = SERVER_REQ;
error("Unable to send request to server (1): %s", sock_strerror(&proxy->proxy));
sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&proxy->proxy));
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;
}
@@ -446,15 +445,15 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
if (ret == -1) {
res->status = http_get_status(502);
ctx->origin = SERVER_REQ;
error("Unable to send request to server (2): %s", sock_strerror(&proxy->proxy));
sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&proxy->proxy));
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;
goto proxy_err;
} else if (ret == -2) {
res->status = http_get_status(400);
ctx->origin = CLIENT_REQ;
error("Unable to receive request from client: %s", sock_strerror(client));
sprintf(err_msg, "Unable to receive request from client: %s.", sock_strerror(client));
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);
@@ -465,18 +464,16 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer), MSG_PEEK);
if (ret <= 0) {
int enc_err = sock_enc_error(&proxy->proxy);
if (errno == EAGAIN || errno == EINPROGRESS || enc_err == SSL_ERROR_WANT_READ ||
enc_err == SSL_ERROR_WANT_WRITE)
{
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) {
res->status = http_get_status(504);
ctx->origin = SERVER_RES;
} else {
res->status = http_get_status(502);
ctx->origin = SERVER_RES;
}
error("Unable to receive response from server: %s", sock_strerror(&proxy->proxy));
sprintf(err_msg, "Unable to receive response from server: %s.", sock_strerror(&proxy->proxy));
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;
}
@@ -536,8 +533,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
goto proxy_err;
}
} else {
ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
if (ret != 0) {
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");
@@ -595,7 +591,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
if (ret == -1) {
error("Unable to receive from server: Malformed chunk header");
} else {
error("Unable to receive from server: %s", sock_strerror(&proxy->proxy));
error("Unable to receive from server");
}
break;
}
@@ -615,7 +611,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
unsigned long avail_in, avail_out;
ret = sock_recv(&proxy->proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0);
if (ret <= 0) {
error("Unable to receive from server: %s", sock_strerror(&proxy->proxy));
error("Unable to receive from server");
break;
}
len = ret;
@@ -646,7 +642,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
if (flags & PROXY_CHUNKED) ret = sock_send(client, "\r\n", 2, 0);
if (ret <= 0) {
err:
error("Unable to send: %s", sock_strerror(client));
error("Unable to send");
break;
}
}
@@ -663,7 +659,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
if (flags & PROXY_CHUNKED) {
ret = sock_send(client, "0\r\n\r\n", 5, 0);
if (ret <= 0) {
error("Unable to send: %s", sock_strerror(client));
error("Unable to send");
return -1;
}
}

View File

@@ -8,58 +8,15 @@
#include "sock.h"
#include "utils.h"
#include "error.h"
#include <openssl/err.h>
#include <errno.h>
#include <openssl/ssl.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int sock_enc_error(sock *s) {
return (int) s->enc ? SSL_get_error(s->ssl, (int) s->_last_ret) : 0;
}
const char *sock_strerror(sock *s) {
// FIXME sock_strerror not Thread Safe!
// (and ugly)
errno = 0;
if (s->_last_ret == 0) {
return "closed";
} else if (s->enc) {
if (s->_last_ret > 0) {
return NULL;
}
const char *err1 = ERR_reason_error_string(s->_ssl_error);
const char *err2 = strerror(s->_errno);
switch (sock_enc_error(s)) {
case SSL_ERROR_NONE:
return NULL;
case SSL_ERROR_ZERO_RETURN:
return "closed";
case SSL_ERROR_WANT_READ:
return "want read";
case SSL_ERROR_WANT_WRITE:
return "want write";
case SSL_ERROR_WANT_CONNECT:
return "want connect";
case SSL_ERROR_WANT_ACCEPT:
return "want accept";
case SSL_ERROR_WANT_X509_LOOKUP:
return "want x509 lookup";
case SSL_ERROR_SYSCALL:
return ((s->_ssl_error == 0) ? ((s->_last_ret == 0) ? "protocol violation" : err2) : err1);
case SSL_ERROR_SSL:
return err1;
default:
return "unknown error";
}
} else {
return strerror(s->_errno);
}
}
int sock_set_timeout_micros(sock *s, long recv_micros, long send_micros) {
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros) {
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};
@@ -72,21 +29,37 @@ int sock_set_timeout_micros(sock *s, long recv_micros, long send_micros) {
return 0;
}
int sock_set_timeout(sock *s, int sec) {
return sock_set_timeout_micros(s, sec * 1000000L, sec * 1000000L);
int sock_set_socket_timeout(sock *s, double sec) {
return sock_set_socket_timeout_micros(s, (long) (sec * 1000000L), (long) (sec * 1000000L));
}
int sock_set_timeout_micros(sock *s, long micros) {
if (micros < 0)
return -1;
s->timeout_us = micros;
return 0;
}
int sock_set_timeout(sock *s, double sec) {
return sock_set_timeout_micros(s, (long) (sec * 1000000));
}
long sock_send(sock *s, void *buf, unsigned long len, int flags) {
long ret;
if (s->enc) {
ret = SSL_write(s->ssl, buf, (int) len);
s->_ssl_error = ERR_get_error();
if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret));
} else {
ret = send(s->socket, buf, len, flags);
}
s->_last_ret = ret;
s->_errno = errno;
return ret >= 0 ? ret : -1;
if (ret >= 0) {
s->ts_last = clock_micros();
return ret;
} else {
return -1;
}
}
long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
@@ -94,13 +67,17 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
if (s->enc) {
int (*func)(SSL*, void*, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read;
ret = func(s->ssl, buf, (int) len);
s->_ssl_error = ERR_get_error();
if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret));
} else {
ret = recv(s->socket, buf, len, flags);
}
s->_last_ret = ret;
s->_errno = errno;
return ret >= 0 ? ret : -1;
if (ret >= 0) {
s->ts_last = clock_micros();
return ret;
} else {
return -1;
}
}
long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len) {
@@ -142,7 +119,7 @@ long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len)
int sock_close(sock *s) {
int e = errno;
if (s->enc && s->ssl != NULL) {
if (s->_last_ret >= 0) SSL_shutdown(s->ssl);
SSL_shutdown(s->ssl);
SSL_free(s->ssl);
s->ssl = NULL;
}

View File

@@ -23,18 +23,16 @@ typedef struct {
char *addr, *s_addr;
SSL_CTX *ctx;
SSL *ssl;
long _last_ret;
int _errno;
unsigned long _ssl_error;
long ts_start, ts_last, timeout_us;
} sock;
int sock_enc_error(sock *s);
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros);
const char *sock_strerror(sock *s);
int sock_set_socket_timeout(sock *s, double sec);
int sock_set_timeout_micros(sock *s, long recv_micros, long send_micros);
int sock_set_timeout_micros(sock *s, long micros);
int sock_set_timeout(sock *s, int sec);
int sock_set_timeout(sock *s, double sec);
long sock_send(sock *s, void *buf, unsigned long len, int flags);

View File

@@ -211,7 +211,6 @@ long clock_micros(void) {
return time.tv_sec * 1000000 + time.tv_nsec / 1000;
}
long clock_cpu(void) {
struct timespec time;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time);

View File

@@ -8,6 +8,7 @@
#include "logger.h"
#include "lib/utils.h"
#include "lib/error.h"
#include <stdio.h>
#include <pthread.h>
@@ -59,8 +60,8 @@ static const char *level_keywords[] = {
static void err(const char *restrict msg) {
char err_buf[64];
strerror_r(errno, err_buf, sizeof(err_buf));
fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", "logger", level_keywords[LOG_CRITICAL], msg, err_buf);
fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", "logger",
level_keywords[LOG_CRITICAL], msg, error_str(errno, err_buf, sizeof(err_buf)));
}
void logmsgf(log_lvl_t level, const char *restrict format, ...) {
@@ -71,8 +72,7 @@ void logmsgf(log_lvl_t level, const char *restrict format, ...) {
const char *color = (level <= LOG_ERROR) ? ERR_STR : ((level <= LOG_WARNING) ? WRN_STR : "");
if (errno != 0) {
strerror_r(errno, err_buf, sizeof(err_buf));
snprintf(buf, sizeof(buf), "%s%s: %s" CLR_STR, color, format, err_buf);
snprintf(buf, sizeof(buf), "%s%s: %s" CLR_STR, color, format, error_str(errno, err_buf, sizeof(err_buf)));
} else {
snprintf(buf, sizeof(buf), "%s%s" CLR_STR, color, format);
}

View File

@@ -18,6 +18,7 @@
#include "workers.h"
#include "worker/func.h"
#include "lib/list.h"
#include "lib/utils.h"
#include <stdio.h>
#include <getopt.h>
@@ -98,6 +99,10 @@ static void accept_cb(void *arg) {
client->socket = client_fd;
client->enc = (i == 1);
client->ts_start = clock_micros();
client->ts_last = client->ts_start;
client_ctx->cnx_s = client->ts_start;
client_ctx->cnx_e = -1, client_ctx->req_s = -1, client_ctx->req_e = -1, client_ctx->res_ts = -1;
tcp_accept(client_ctx);
}
@@ -301,12 +306,11 @@ int main(int argc, char *const argv[]) {
workers_init();
for (int i = 0; i < NUM_SOCKETS; i++) {
async_fd(sockets[i], ASYNC_WAIT_READ, ASYNC_KEEP, &sockets[i], accept_cb, accept_err_cb);
async_fd(sockets[i], ASYNC_WAIT_READ, ASYNC_KEEP, &sockets[i], accept_cb, accept_err_cb, accept_err_cb);
}
notice("Ready to accept connections");
// TODO handle timeouts in epoll
async_thread();
notice("Goodbye!");

View File

@@ -58,6 +58,8 @@ int respond(client_ctx_t *ctx);
void request_complete(client_ctx_t *ctx);
void timeout_request(client_ctx_t *ctx);
void tcp_close(client_ctx_t *ctx);
void proxy_close(proxy_ctx_t *ctx);

View File

@@ -22,6 +22,8 @@ static int proxy_handler_2(client_ctx_t *ctx);
void proxy_handler_func(client_ctx_t *ctx) {
logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix);
// TODO handle 1xx responses
int ret = proxy_handler_1(ctx);
respond(ctx);

View File

@@ -14,9 +14,11 @@
#include "../lib/utils.h"
#include "../server.h"
#include "../lib/res.h"
#include "../lib/error.h"
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
static int request_handler(client_ctx_t *ctx);
@@ -41,15 +43,7 @@ void request_handler_func(client_ctx_t *ctx) {
}
}
static int request_handler(client_ctx_t *ctx) {
sock *client = &ctx->socket;
char *err_msg = ctx->err_msg;
long ret;
char buf0[1024], buf1[1024];
err_msg[0] = 0;
static void init_ctx(client_ctx_t *ctx) {
ctx->conf = NULL;
ctx->file = NULL;
ctx->proxy = NULL;
@@ -62,24 +56,35 @@ static int request_handler(client_ctx_t *ctx) {
ctx->msg_buf_ptr = NULL;
ctx->req_host[0] = 0;
ctx->err_msg[0] = 0;
ctx->req_s = ctx->socket.ts_last;
memset(&ctx->uri, 0, sizeof(ctx->uri));
memset(&ctx->req, 0, sizeof(ctx->req));
memset(&ctx->res, 0, sizeof(ctx->res));
http_res *res = &ctx->res;
res->status = http_get_status(501);
http_init_hdr(&res->hdr);
res->hdr.last_field_num = -1;
sprintf(res->version, "1.1");
http_status_ctx *status = &ctx->status;
status->status = 0;
status->origin = NONE;
status->ws_key = NULL;
ctx->res.status = http_get_status(501);
http_init_hdr(&ctx->res.hdr);
ctx->res.hdr.last_field_num = -1;
sprintf(ctx->res.version, "1.1");
ctx->status.status = 0;
ctx->status.origin = NONE;
ctx->status.ws_key = NULL;
}
static int request_handler(client_ctx_t *ctx) {
sock *client = &ctx->socket;
char *err_msg = ctx->err_msg;
http_res *res = &ctx->res;
long ret;
char buf0[1024], buf1[1024];
ctx->req_s = clock_micros();
init_ctx(ctx);
http_add_header_field(&res->hdr, "Date", http_get_date(buf0, sizeof(buf0)));
http_add_header_field(&res->hdr, "Server", SERVER_STR);
/*if (ret <= 0) {
@@ -94,20 +99,11 @@ static int request_handler(client_ctx_t *ctx) {
ret = http_receive_request(client, req);
if (ret != 0) {
ctx->c_keep_alive = 0;
if (ret < 0) {
return -1;
} else if (ret == 1) {
sprintf(err_msg, "Unable to parse http header: Invalid header format.");
} else if (ret == 2) {
sprintf(err_msg, "Unable to parse http header: Invalid method.");
} else if (ret == 3) {
sprintf(err_msg, "Unable to parse http header: Invalid version.");
} else if (ret == 4) {
sprintf(err_msg, "Unable to parse http header: Header contains illegal characters.");
} else if (ret == 5) {
sprintf(err_msg, "Unable to parse http header: End of header not found.");
}
res->status = http_get_status(400);
error("Unable to receive http header");
sprintf(err_msg, "Unable to receive http header: %s.", error_str(errno, buf0, sizeof(buf0)));
int err = error_get_http();
res->status = http_get_status(err == HTTP_ERROR_URI_TOO_LONG ? 414 : (err == HTTP_ERROR_TOO_MANY_HEADER_FIELDS ? 431 : 400));
errno = 0;
return 0;
}
@@ -170,6 +166,7 @@ static int request_handler(client_ctx_t *ctx) {
ctx->conf = get_host_config(ctx->req_host);
if (ctx->conf == NULL) {
res->status = http_get_status(421);
strcpy(ctx->err_msg, "The requested host name is not configured on the server.");
return 0;
}
@@ -348,7 +345,7 @@ int respond(client_ctx_t *ctx) {
if (ctx->msg_buf != NULL) {
ret = sock_send(client, ctx->msg_buf, ctx->content_length, 0);
if (ret <= 0) {
error("Unable to send: %s", sock_strerror(client));
error("Unable to send");
}
} else if (ctx->file != NULL) {
unsigned long len, snd_len = 0;
@@ -359,7 +356,7 @@ int respond(client_ctx_t *ctx) {
}
ret = sock_send(client, buffer, len, feof(ctx->file) ? 0 : MSG_MORE);
if (ret <= 0) {
error("Unable to send: %s", sock_strerror(client));
error("Unable to send");
break;
}
snd_len += ret;
@@ -386,3 +383,14 @@ void request_complete(client_ctx_t *ctx) {
http_free_req(&ctx->req);
http_free_res(&ctx->res);
}
void timeout_request(client_ctx_t *ctx) {
init_ctx(ctx);
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix);
ctx->s_keep_alive = 0;
ctx->res.status = http_get_status(408);
respond(ctx);
tcp_close(ctx);
}

View File

@@ -12,6 +12,7 @@
#include "../lib/geoip.h"
#include "../workers.h"
#include "../server.h"
#include "../lib/error.h"
#include <string.h>
#include <errno.h>
@@ -63,7 +64,7 @@ static int tcp_acceptor(client_ctx_t *ctx) {
sprintf(buf, "dig @%s +short +time=1 -x %s", config.dns_server, ctx->socket.addr);
FILE *dig = popen(buf, "r");
if (dig == NULL) {
error("Unable to start dig: %s", strerror(errno));
error("Unable to start dig: %s");
goto dig_err;
}
unsigned long read = fread(buf, 1, sizeof(buf), dig);
@@ -90,7 +91,7 @@ static int tcp_acceptor(client_ctx_t *ctx) {
ctx->host[0] != 0 ? ctx->host : "", ctx->host[0] != 0 ? ") " : "",
ctx->cc[0] != 0 ? ctx->cc : "N/A");
if (sock_set_timeout(client, CLIENT_TIMEOUT)) {
if (sock_set_socket_timeout(client, 1) != 0 || sock_set_timeout(client, CLIENT_TIMEOUT) != 0) {
error("Unable to set timeout for socket");
return -1;
}
@@ -101,13 +102,12 @@ static int tcp_acceptor(client_ctx_t *ctx) {
SSL_set_accept_state(client->ssl);
ret = SSL_accept(client->ssl);
client->_last_ret = ret;
client->_errno = errno;
client->_ssl_error = ERR_get_error();
if (ret <= 0) {
info("Unable to perform handshake: %s", sock_strerror(client));
if (ret != 1) {
error_ssl(SSL_get_error(client->ssl, ret));
info("Unable to perform handshake");
return - 1;
}
client->ts_last = clock_micros();
}
ctx->req_num = 0;

View File

@@ -53,7 +53,10 @@ static int handle_request_cb(client_ctx_t *ctx) {
int handle_request(client_ctx_t *ctx) {
if (ctx->c_keep_alive && ctx->s_keep_alive) {
return async(&ctx->socket, ASYNC_WAIT_READ, 0, ctx, (void (*)(void *)) handle_request_cb, (void (*)(void *)) tcp_close);
return async(&ctx->socket, ASYNC_WAIT_READ, 0, ctx,
(void (*)(void *)) handle_request_cb,
(void (*)(void *)) timeout_request,
(void (*)(void *)) tcp_close);
} else {
tcp_close(ctx);
return 0;
@@ -77,5 +80,8 @@ static int ws_handle_frame_cb(ws_ctx_t *ctx) {
}
int ws_handle_frame(ws_ctx_t *ctx) {
return async(ctx->socket, ASYNC_WAIT_READ, 0, ctx, (void (*)(void *)) ws_handle_frame_cb, (void (*)(void *)) ws_close);
return async(ctx->socket, ASYNC_WAIT_READ, 0, ctx,
(void (*)(void *)) ws_handle_frame_cb,
(void (*)(void *)) ws_close,
(void (*)(void *)) ws_close);
}