Compare commits
13 Commits
c060ee5bb6
...
b6ba58d406
Author | SHA1 | Date | |
---|---|---|---|
b6ba58d406
|
|||
abe0e326cb
|
|||
4fe067ed7d
|
|||
41e12d6293
|
|||
a738f1abfe
|
|||
b6309eec39
|
|||
9923a76ba7
|
|||
ea4cdff233
|
|||
6526b5cbcb
|
|||
0119945e03
|
|||
3fe1fe023a
|
|||
bc1c6d3498
|
|||
50bb537074
|
6
Makefile
6
Makefile
@@ -1,6 +1,6 @@
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-std=gnu11 -Wall
|
||||
CFLAGS=-std=gnu11 -Wall -Wno-unused-but-set-variable
|
||||
LIBS=-lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc
|
||||
|
||||
DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.4-fpm.sock\""
|
||||
@@ -16,11 +16,11 @@ permit:
|
||||
compile:
|
||||
@mkdir -p bin
|
||||
$(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS)
|
||||
$(CC) src/necronda-server.c -o bin/necronda-server $(CFLAGS) $(LIBS) \
|
||||
$(CC) src/server.c src/client.c -o bin/necronda-server $(CFLAGS) $(LIBS) \
|
||||
-Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin
|
||||
|
||||
compile-prod:
|
||||
@mkdir -p bin
|
||||
$(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3
|
||||
$(CC) src/necronda-server.c -o bin/necronda-server $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 \
|
||||
$(CC) src/server.c src/client.c -o bin/necronda-server $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 \
|
||||
-Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin
|
||||
|
@@ -7,9 +7,12 @@ Necronda web server
|
||||
* Full IPv4 and IPv6 support
|
||||
* TLS Server Name Inspection (SNI)
|
||||
* Serving local files via HTTP and HTTPS
|
||||
* File compression ([gzip](https://www.gzip.org/), [Brotli](https://www.brotli.org/)) and disk cache for compressed files
|
||||
* File compression ([gzip](https://www.gzip.org/), [Brotli](https://www.brotli.org/))
|
||||
* Disk cache for compressed files
|
||||
* Reverse proxy for other HTTP and HTTPS servers
|
||||
* Transparent WebSocket reverse proxy **[WIP]**
|
||||
* FastCGI support (e.g. [PHP-FPM](https://php-fpm.org/))
|
||||
* Automatic path info detection (e.g. `/my/file/extra/path` -> script: `/my/file.php`, path info: `extra/path`)
|
||||
* Support for [MaxMind's GeoIP Database](https://www.maxmind.com/en/geoip2-services-and-databases)
|
||||
* Optional DNS reverse lookup for connecting hosts
|
||||
* Automatic URL rewrite (e.g. `/index.html` -> `/`, `/test.php` -> `/test`)
|
||||
@@ -29,7 +32,7 @@ See [docs/example.conf](docs/example.conf) for more details.
|
||||
|
||||
### Configuration
|
||||
|
||||
* `[cert <cert-name]` - begins section for a certificate
|
||||
* `[cert <cert-name>]` - begins section for a certificate
|
||||
* `certificate` - path to SSL certificate (or certificate chain)
|
||||
* `private_key` - path to SSL private key
|
||||
* `[host <host>]` - begins section for the virtual host `<host>`
|
||||
|
@@ -1,19 +1,24 @@
|
||||
|
||||
certificate /var/cert/cert.pem
|
||||
private_key /var/cert/cert.key
|
||||
#geoip_dir /var/dir
|
||||
#dns_server 192.168.0.1
|
||||
|
||||
[localhost]
|
||||
[cert cert1]
|
||||
certificate /var/cert/cert.pem
|
||||
private_key /var/cert/cert.key
|
||||
|
||||
[host localhost]
|
||||
webroot /var/www/localhost
|
||||
dir_mode forbidden
|
||||
cert cert1
|
||||
|
||||
[me.local]
|
||||
[host me.local]
|
||||
hostname www.example.com
|
||||
port 80
|
||||
cert cert1
|
||||
http
|
||||
|
||||
[secure.local]
|
||||
[host secure.local]
|
||||
hostname www.example.com
|
||||
port 443
|
||||
cert cert1
|
||||
https
|
||||
|
70
src/client.c
70
src/client.c
@@ -5,6 +5,10 @@
|
||||
* Lorenz Stechauner, 2020-12-03
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "necronda.h"
|
||||
#include "server.h"
|
||||
|
||||
#include "lib/utils.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/sock.h"
|
||||
@@ -63,7 +67,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
char msg_buf[8192], msg_pre_buf_1[4096], msg_pre_buf_2[4096], err_msg[256];
|
||||
char msg_content[1024];
|
||||
char buffer[CHUNK_SIZE];
|
||||
char host[256], *host_ptr, *hdr_connection;
|
||||
char host[256];
|
||||
const char *host_ptr, *hdr_connection;
|
||||
|
||||
msg_buf[0] = 0;
|
||||
err_msg[0] = 0;
|
||||
@@ -81,7 +86,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
fastcgi_conn fcgi_conn = {.socket = 0, .req_id = 0};
|
||||
http_status custom_status;
|
||||
|
||||
http_res res = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0};
|
||||
http_res res = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0, .hdr.last_field_num = -1};
|
||||
http_status_ctx ctx = {.status = 0, .origin = NONE};
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &begin);
|
||||
@@ -95,9 +100,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
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) {
|
||||
if (errno != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (errno != 0) return 1;
|
||||
|
||||
client_keep_alive = 0;
|
||||
res.status = http_get_status(408);
|
||||
goto respond;
|
||||
@@ -126,8 +130,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
}
|
||||
|
||||
hdr_connection = http_get_header_field(&req.hdr, "Connection");
|
||||
client_keep_alive = hdr_connection != NULL &&
|
||||
(strcmp(hdr_connection, "keep-alive") == 0 || strcmp(hdr_connection, "Keep-Alive") == 0);
|
||||
client_keep_alive = (hdr_connection != NULL && (strcmp(hdr_connection, "keep-alive") == 0 || strcmp(hdr_connection, "Keep-Alive") == 0));
|
||||
host_ptr = http_get_header_field(&req.hdr, "Host");
|
||||
if (host_ptr != NULL && strlen(host_ptr) > 255) {
|
||||
host[0] = 0;
|
||||
@@ -153,7 +156,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
|
||||
conf = get_host_config(host);
|
||||
if (conf == NULL) {
|
||||
print("Host unknown, redirecting to default");
|
||||
print("Unknown host, redirecting to default");
|
||||
res.status = http_get_status(307);
|
||||
sprintf(buf0, "https://%s%s", DEFAULT_HOST, req.uri);
|
||||
http_add_header_field(&res.hdr, "Location", buf0);
|
||||
@@ -161,7 +164,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
}
|
||||
|
||||
http_uri uri;
|
||||
unsigned char dir_mode = conf->type == CONFIG_TYPE_LOCAL ? conf->local.dir_mode : URI_DIR_MODE_NO_VALIDATION;
|
||||
unsigned char dir_mode = (conf->type == CONFIG_TYPE_LOCAL ? conf->local.dir_mode : URI_DIR_MODE_NO_VALIDATION);
|
||||
ret = uri_init(&uri, conf->local.webroot, req.uri, dir_mode);
|
||||
if (ret != 0) {
|
||||
if (ret == 1) {
|
||||
@@ -218,11 +221,10 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
res.status = http_get_status(200);
|
||||
http_add_header_field(&res.hdr, "Content-Type", "message/http");
|
||||
|
||||
content_length = snprintf(msg_buf, sizeof(msg_buf) - content_length, "%s %s HTTP/%s\r\n",
|
||||
req.method, req.uri, req.version);
|
||||
content_length = snprintf(msg_buf, sizeof(msg_buf) - content_length, "%s %s HTTP/%s\r\n", req.method, req.uri, req.version);
|
||||
for (int i = 0; i < req.hdr.field_num; i++) {
|
||||
content_length += snprintf(msg_buf + content_length, sizeof(msg_buf) - content_length, "%s: %s\r\n",
|
||||
req.hdr.fields[i][0], req.hdr.fields[i][1]);
|
||||
const http_field *f = &req.hdr.fields[i];
|
||||
content_length += snprintf(msg_buf + content_length, sizeof(msg_buf) - content_length, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
|
||||
}
|
||||
|
||||
goto respond;
|
||||
@@ -273,13 +275,13 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
sprintf(err_msg, "Unable to communicate with internal file cache.");
|
||||
goto respond;
|
||||
}
|
||||
char *last_modified = http_format_date(uri.meta->stat.st_mtime, buf0, sizeof(buf0));
|
||||
const char *last_modified = http_format_date(uri.meta->stat.st_mtime, buf0, sizeof(buf0));
|
||||
http_add_header_field(&res.hdr, "Last-Modified", last_modified);
|
||||
sprintf(buf1, "%s; charset=%s", uri.meta->type, uri.meta->charset);
|
||||
http_add_header_field(&res.hdr, "Content-Type", buf1);
|
||||
|
||||
|
||||
char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding");
|
||||
const char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding");
|
||||
int enc = 0;
|
||||
if (accept_encoding != NULL) {
|
||||
if (uri.meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != NULL) {
|
||||
@@ -306,8 +308,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
|
||||
if (uri.meta->etag[0] != 0) {
|
||||
if (enc) {
|
||||
sprintf(buf0, "%s-%s", uri.meta->etag,
|
||||
(enc & COMPRESS_BR) ? "br" : (enc & COMPRESS_GZ) ? "gzip" : "");
|
||||
sprintf(buf0, "%s-%s", uri.meta->etag, (enc & COMPRESS_BR) ? "br" : (enc & COMPRESS_GZ) ? "gzip" : "");
|
||||
http_add_header_field(&res.hdr, "ETag", buf0);
|
||||
} else {
|
||||
http_add_header_field(&res.hdr, "ETag", uri.meta->etag);
|
||||
@@ -320,16 +321,16 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=86400");
|
||||
}
|
||||
|
||||
char *if_modified_since = http_get_header_field(&req.hdr, "If-Modified-Since");
|
||||
char *if_none_match = http_get_header_field(&req.hdr, "If-None-Match");
|
||||
const char *if_modified_since = http_get_header_field(&req.hdr, "If-Modified-Since");
|
||||
const char *if_none_match = http_get_header_field(&req.hdr, "If-None-Match");
|
||||
if ((if_none_match != NULL && strstr(if_none_match, uri.meta->etag) == NULL) ||
|
||||
(accept_if_modified_since && if_modified_since != NULL &&
|
||||
strcmp(if_modified_since, last_modified) == 0)) {
|
||||
(accept_if_modified_since && if_modified_since != NULL && strcmp(if_modified_since, last_modified) == 0))
|
||||
{
|
||||
res.status = http_get_status(304);
|
||||
goto respond;
|
||||
}
|
||||
|
||||
char *range = http_get_header_field(&req.hdr, "Range");
|
||||
const char *range = http_get_header_field(&req.hdr, "Range");
|
||||
if (range != NULL) {
|
||||
if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) {
|
||||
res.status = http_get_status(416);
|
||||
@@ -404,7 +405,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
goto respond;
|
||||
}
|
||||
|
||||
char *client_content_length = http_get_header_field(&req.hdr, "Content-Length");
|
||||
const char *client_content_length = http_get_header_field(&req.hdr, "Content-Length");
|
||||
if (client_content_length != NULL) {
|
||||
unsigned long client_content_len = strtoul(client_content_length, NULL, 10);
|
||||
ret = fastcgi_receive(&fcgi_conn, client, client_content_len);
|
||||
@@ -426,7 +427,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
goto respond;
|
||||
}
|
||||
|
||||
char *status = http_get_header_field(&res.hdr, "Status");
|
||||
const char *status = http_get_header_field(&res.hdr, "Status");
|
||||
if (status != NULL) {
|
||||
int status_code = (int) strtoul(status, NULL, 10);
|
||||
res.status = http_get_status(status_code);
|
||||
@@ -497,9 +498,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
const char *content_type = http_get_header_field(&res.hdr, "Content-Type");
|
||||
const char *content_length_f = http_get_header_field(&res.hdr, "Content-Length");
|
||||
const char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding");
|
||||
if (content_encoding == NULL && content_type != NULL && content_length_f != NULL &&
|
||||
strncmp(content_type, "text/html", 9) == 0)
|
||||
{
|
||||
if (content_encoding == NULL && content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0) {
|
||||
long content_len = strtol(content_length_f, NULL, 10);
|
||||
if (content_len <= sizeof(msg_content) - 1) {
|
||||
ctx.status = res.status->code;
|
||||
@@ -596,8 +595,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
rev_proxy_doc = msg_pre_buf_2;
|
||||
}
|
||||
|
||||
sprintf(msg_pre_buf_1, info->doc, res.status->code, res.status->msg,
|
||||
http_msg != NULL ? http_msg->msg : "", err_msg[0] != 0 ? err_msg : "");
|
||||
sprintf(msg_pre_buf_1, info->doc, res.status->code, res.status->msg, http_msg != NULL ? http_msg->msg : "", err_msg[0] != 0 ? err_msg : "");
|
||||
content_length = snprintf(msg_buf, sizeof(msg_buf), http_default_document, res.status->code,
|
||||
res.status->msg, msg_pre_buf_1, info->mode, info->icon, info->color, host,
|
||||
rev_proxy_doc, msg_content[0] != 0 ? msg_content : "");
|
||||
@@ -611,8 +609,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
}
|
||||
}
|
||||
|
||||
char *conn = http_get_header_field(&res.hdr, "Connection");
|
||||
int close_proxy = conn == NULL || (strcmp(conn, "keep-alive") != 0 && strcmp(conn, "Keep-Alive") != 0);
|
||||
const char *conn = http_get_header_field(&res.hdr, "Connection");
|
||||
int close_proxy = (conn == NULL || (strcmp(conn, "keep-alive") != 0 && strcmp(conn, "Keep-Alive") != 0));
|
||||
http_remove_header_field(&res.hdr, "Connection", HTTP_REMOVE_ALL);
|
||||
http_remove_header_field(&res.hdr, "Keep-Alive", HTTP_REMOVE_ALL);
|
||||
if (server_keep_alive && client_keep_alive) {
|
||||
@@ -625,7 +623,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
|
||||
http_send_response(client, &res);
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
char *location = http_get_header_field(&res.hdr, "Location");
|
||||
const char *location = http_get_header_field(&res.hdr, "Location");
|
||||
unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
||||
print("%s%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), use_rev_proxy ? "-> " : "", res.status->code,
|
||||
res.status->msg, location != NULL ? " -> " : "", location != NULL ? location : "",
|
||||
@@ -656,16 +654,16 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
||||
snd_len += ret;
|
||||
}
|
||||
} else if (use_fastcgi) {
|
||||
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
|
||||
const char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||
int chunked = (transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0);
|
||||
|
||||
int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD));
|
||||
ret = fastcgi_send(&fcgi_conn, client, flags);
|
||||
} else if (use_rev_proxy) {
|
||||
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||
const char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||
int chunked = transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL;
|
||||
|
||||
char *content_len = http_get_header_field(&res.hdr, "Content-Length");
|
||||
const char *content_len = http_get_header_field(&res.hdr, "Content-Length");
|
||||
unsigned long len_to_send = 0;
|
||||
if (content_len != NULL) {
|
||||
len_to_send = strtol(content_len, NULL, 10);
|
||||
|
20
src/client.h
Normal file
20
src/client.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Necronda Web Server
|
||||
* Client connection and request handlers (header file)
|
||||
* src/client.h
|
||||
* Lorenz Stechauner, 2022-08-16
|
||||
*/
|
||||
|
||||
#ifndef NECRONDA_SERVER_NECRONDA_CLIENT_H
|
||||
#define NECRONDA_SERVER_NECRONDA_CLIENT_H
|
||||
|
||||
#include "lib/config.h"
|
||||
#include "lib/sock.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
host_config *get_host_config(const char *host);
|
||||
|
||||
int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *client_addr);
|
||||
|
||||
#endif //NECRONDA_SERVER_NECRONDA_CLIENT_H
|
@@ -45,14 +45,14 @@ int cache_process() {
|
||||
|
||||
int shm_id = shmget(CACHE_SHM_KEY, CACHE_ENTRIES * sizeof(cache_entry), 0);
|
||||
if (shm_id < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to create cache shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
shmdt(cache);
|
||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||
if (shm_rw == (void *) -1) {
|
||||
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to attach cache shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
cache = shm_rw;
|
||||
@@ -226,20 +226,20 @@ int cache_init() {
|
||||
|
||||
int shm_id = shmget(CACHE_SHM_KEY, CACHE_ENTRIES * sizeof(cache_entry), IPC_CREAT | IPC_EXCL | 0600);
|
||||
if (shm_id < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to create cache shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
void *shm = shmat(shm_id, NULL, SHM_RDONLY);
|
||||
if (shm == (void *) -1) {
|
||||
fprintf(stderr, ERR_STR "Unable to attach shared memory (ro): %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to attach cache shared memory (ro): %s" CLR_STR "\n", strerror(errno));
|
||||
return -3;
|
||||
}
|
||||
cache = shm;
|
||||
|
||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||
if (shm_rw == (void *) -1) {
|
||||
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to attach cache shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
return -4;
|
||||
}
|
||||
cache = shm_rw;
|
||||
@@ -268,11 +268,11 @@ int cache_init() {
|
||||
int cache_unload() {
|
||||
int shm_id = shmget(CACHE_SHM_KEY, 0, 0);
|
||||
if (shm_id < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to get cache shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||
shmdt(cache);
|
||||
return -1;
|
||||
} else if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to configure shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to configure cache shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
shmdt(cache);
|
||||
return -1;
|
||||
}
|
||||
@@ -285,7 +285,7 @@ int cache_update_entry(int entry_num, const char *filename, const char *webroot)
|
||||
int shm_id = shmget(CACHE_SHM_KEY, 0, 0);
|
||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||
if (shm_rw == (void *) -1) {
|
||||
print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno));
|
||||
print(ERR_STR "Unable to attach cache shared memory (rw): %s" CLR_STR, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
cache = shm_rw;
|
||||
@@ -328,7 +328,7 @@ int cache_filename_comp_invalid(const char *filename) {
|
||||
int shm_id = shmget(CACHE_SHM_KEY, 0, 0);
|
||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||
if (shm_rw == (void *) -1) {
|
||||
print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno));
|
||||
print(ERR_STR "Unable to attach cache shared memory (rw): %s" CLR_STR, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
cache = shm_rw;
|
||||
|
@@ -20,20 +20,20 @@ char geoip_dir[256], dns_server[256];
|
||||
int config_init() {
|
||||
int shm_id = shmget(CONFIG_SHM_KEY, sizeof(t_config), IPC_CREAT | IPC_EXCL | 0640);
|
||||
if (shm_id < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to create config shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *shm = shmat(shm_id, NULL, SHM_RDONLY);
|
||||
if (shm == (void *) -1) {
|
||||
fprintf(stderr, ERR_STR "Unable to attach shared memory (ro): %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to attach config shared memory (ro): %s" CLR_STR "\n", strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
config = shm;
|
||||
|
||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||
if (shm_rw == (void *) -1) {
|
||||
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to attach config shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
return -3;
|
||||
}
|
||||
config = shm_rw;
|
||||
@@ -46,11 +46,11 @@ int config_init() {
|
||||
int config_unload() {
|
||||
int shm_id = shmget(CONFIG_SHM_KEY, 0, 0);
|
||||
if (shm_id < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to get config shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||
shmdt(config);
|
||||
return -1;
|
||||
} else if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to configure shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to configure config shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||
shmdt(config);
|
||||
return -1;
|
||||
}
|
||||
@@ -256,7 +256,7 @@ int config_load(const char *filename) {
|
||||
|
||||
int shm_id = shmget(CONFIG_SHM_KEY, 0, 0);
|
||||
if (shm_id < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to get config shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||
shmdt(config);
|
||||
return -3;
|
||||
}
|
||||
@@ -264,7 +264,7 @@ int config_load(const char *filename) {
|
||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||
if (shm_rw == (void *) -1) {
|
||||
free(tmp_config);
|
||||
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to attach config shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||
return -4;
|
||||
}
|
||||
memcpy(shm_rw, tmp_config, sizeof(t_config));
|
||||
|
@@ -8,7 +8,7 @@
|
||||
#include "fastcgi.h"
|
||||
#include "utils.h"
|
||||
#include "compress.h"
|
||||
#include "../necronda-server.h"
|
||||
#include "../server.h"
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
@@ -41,9 +41,9 @@ char *fastcgi_add_param(char *buf, const char *key, const char *value) {
|
||||
ptr += 4;
|
||||
}
|
||||
|
||||
memcpy(ptr, key, key_len);
|
||||
strcpy(ptr, key);
|
||||
ptr += key_len;
|
||||
memcpy(ptr, value, val_len);
|
||||
strcpy(ptr, value);
|
||||
ptr += val_len;
|
||||
|
||||
return ptr;
|
||||
@@ -149,19 +149,21 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned
|
||||
param_ptr = fastcgi_add_param(param_ptr, "PATH_INFO", buf0);
|
||||
|
||||
//param_ptr = fastcgi_add_param(param_ptr, "AUTH_TYPE", "");
|
||||
char *content_length = http_get_header_field(&req->hdr, "Content-Length");
|
||||
const char *content_length = http_get_header_field(&req->hdr, "Content-Length");
|
||||
param_ptr = fastcgi_add_param(param_ptr, "CONTENT_LENGTH", content_length != NULL ? content_length : "");
|
||||
char *content_type = http_get_header_field(&req->hdr, "Content-Type");
|
||||
const char *content_type = http_get_header_field(&req->hdr, "Content-Type");
|
||||
param_ptr = fastcgi_add_param(param_ptr, "CONTENT_TYPE", content_type != NULL ? content_type : "");
|
||||
if (client_geoip != NULL) {
|
||||
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", client_geoip);
|
||||
}
|
||||
|
||||
for (int i = 0; i < req->hdr.field_num; i++) {
|
||||
const http_field *f = &req->hdr.fields[i];
|
||||
const char *name = http_field_get_name(f);
|
||||
char *ptr = buf0;
|
||||
ptr += sprintf(ptr, "HTTP_");
|
||||
for (int j = 0; j < strlen(req->hdr.fields[i][0]); j++, ptr++) {
|
||||
char ch = req->hdr.fields[i][0][j];
|
||||
for (int j = 0; j < strlen(name); j++, ptr++) {
|
||||
char ch = name[j];
|
||||
if ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
|
||||
ch = ch;
|
||||
} else if (ch >= 'a' && ch <= 'z') {
|
||||
@@ -172,7 +174,7 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned
|
||||
ptr[0] = ch;
|
||||
ptr[1] = 0;
|
||||
}
|
||||
param_ptr = fastcgi_add_param(param_ptr, buf0, req->hdr.fields[i][1]);
|
||||
param_ptr = fastcgi_add_param(param_ptr, buf0, http_field_get_value(f));
|
||||
}
|
||||
|
||||
unsigned short param_len = param_ptr - param_buf - sizeof(header);
|
||||
@@ -219,7 +221,8 @@ int fastcgi_close_stdin(fastcgi_conn *conn) {
|
||||
int fastcgi_php_error(const fastcgi_conn *conn, const char *msg, int msg_len, char *err_msg) {
|
||||
char *msg_str = malloc(msg_len + 1);
|
||||
char *ptr0 = msg_str;
|
||||
strncpy(msg_str, msg, msg_len);
|
||||
memcpy(msg_str, msg, msg_len);
|
||||
msg_str[msg_len] = 0;
|
||||
char *ptr1 = NULL;
|
||||
int len;
|
||||
int err = 0;
|
||||
|
230
src/lib/http.c
230
src/lib/http.c
@@ -11,8 +11,10 @@
|
||||
#include <string.h>
|
||||
|
||||
void http_to_camel_case(char *str, int mode) {
|
||||
char last = '-';
|
||||
char ch;
|
||||
if (mode == HTTP_PRESERVE)
|
||||
return;
|
||||
|
||||
char ch, last = '-';
|
||||
for (int i = 0; i < strlen(str); i++) {
|
||||
ch = str[i];
|
||||
if (mode == HTTP_CAMEL && last == '-' && ch >= 'a' && ch <= 'z') {
|
||||
@@ -24,12 +26,50 @@ void http_to_camel_case(char *str, int mode) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *http_field_get_name(const http_field *field) {
|
||||
if (field->type == HTTP_FIELD_NORMAL) {
|
||||
return field->normal.name;
|
||||
} else if (field->type == HTTP_FIELD_EX_VALUE) {
|
||||
return field->ex_value.name;
|
||||
} else if (field->type == HTTP_FIELD_EX_NAME) {
|
||||
return field->ex_name.name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *http_field_get_value(const http_field *field) {
|
||||
if (field->type == HTTP_FIELD_NORMAL) {
|
||||
return field->normal.value;
|
||||
} else if (field->type == HTTP_FIELD_EX_VALUE) {
|
||||
return field->ex_value.value;
|
||||
} else if (field->type == HTTP_FIELD_EX_NAME) {
|
||||
return field->ex_name.value;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void http_free_field(http_field *f) {
|
||||
if (f->type == HTTP_FIELD_NORMAL) {
|
||||
f->normal.name[0] = 0;
|
||||
f->normal.value[0] = 0;
|
||||
} else if (f->type == HTTP_FIELD_EX_VALUE) {
|
||||
f->ex_value.name[0] = 0;
|
||||
free(f->ex_value.value);
|
||||
f->ex_value.value = NULL;
|
||||
} else if (f->type == HTTP_FIELD_EX_NAME) {
|
||||
free(f->ex_name.name);
|
||||
free(f->ex_name.value);
|
||||
f->ex_name.name = NULL;
|
||||
f->ex_name.value = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void http_free_hdr(http_hdr *hdr) {
|
||||
for (int i = 0; i < hdr->field_num; i++) {
|
||||
free(hdr->fields[i][0]);
|
||||
free(hdr->fields[i][1]);
|
||||
http_free_field(&hdr->fields[i]);
|
||||
}
|
||||
hdr->field_num = 0;
|
||||
hdr->last_field_num = -1;
|
||||
}
|
||||
|
||||
void http_free_req(http_req *req) {
|
||||
@@ -43,44 +83,63 @@ void http_free_res(http_res *res) {
|
||||
}
|
||||
|
||||
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) {
|
||||
char *pos1 = memchr(buf, ':', end_ptr - buf);
|
||||
char *pos2;
|
||||
if (hdr->last_field_num > hdr->field_num) {
|
||||
print(ERR_STR "Unable to parse header: Invalid state" CLR_STR);
|
||||
return 3;
|
||||
}
|
||||
|
||||
char *pos1 = (char *) buf, *pos2 = (char *) end_ptr;
|
||||
if (buf[0] == ' ' || buf[0] == '\t') {
|
||||
if (hdr->last_field_num == -1) {
|
||||
print(ERR_STR "Unable to parse header" CLR_STR);
|
||||
return 3;
|
||||
}
|
||||
http_field *f = &hdr->fields[(int) hdr->last_field_num];
|
||||
|
||||
str_trim_lws(&pos1, &pos2);
|
||||
http_append_to_header_field(f, pos1, pos2 - pos1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pos1 = memchr(buf, ':', end_ptr - buf);
|
||||
if (pos1 == NULL) {
|
||||
print(ERR_STR "Unable to parse header" CLR_STR);
|
||||
return 3;
|
||||
}
|
||||
|
||||
long len = pos1 - buf;
|
||||
hdr->fields[(int) hdr->field_num][0] = malloc(len + 1);
|
||||
sprintf(hdr->fields[(int) hdr->field_num][0], "%.*s", (int) len, buf);
|
||||
http_to_camel_case(hdr->fields[(int) hdr->field_num][0], HTTP_CAMEL);
|
||||
long len1 = pos1 - buf;
|
||||
|
||||
pos1++;
|
||||
pos2 = (char *) end_ptr - 1;
|
||||
while (pos1[0] == ' ') pos1++;
|
||||
while (pos2[0] == ' ') pos2--;
|
||||
len = pos2 - pos1 + 1;
|
||||
str_trim_lws(&pos1, &pos2);
|
||||
long len2 = pos2 - pos1;
|
||||
|
||||
if (len <= 0) {
|
||||
hdr->fields[(int) hdr->field_num][1] = malloc(1);
|
||||
hdr->fields[(int) hdr->field_num][1][0] = 0;
|
||||
char field_num = hdr->field_num;
|
||||
int found = http_get_header_field_num_len(hdr, buf, len1);
|
||||
if (found == -1) {
|
||||
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) {
|
||||
print(ERR_STR "Unable to parse header: Too many header fields" CLR_STR);
|
||||
return 3;
|
||||
}
|
||||
} else {
|
||||
hdr->fields[(int) hdr->field_num][1] = malloc(len + 1);
|
||||
sprintf(hdr->fields[(int) hdr->field_num][1], "%.*s", (int) len, pos1);
|
||||
field_num = (char) found;
|
||||
http_append_to_header_field(&hdr->fields[found], ", ", 2);
|
||||
http_append_to_header_field(&hdr->fields[found], pos1, len2);
|
||||
}
|
||||
hdr->field_num++;
|
||||
|
||||
hdr->last_field_num = (char) field_num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_receive_request(sock *client, http_req *req) {
|
||||
long rcv_len, len;
|
||||
char *ptr, *pos0, *pos1, *pos2;
|
||||
char buf[CLIENT_MAX_HEADER_SIZE];
|
||||
char *ptr, *pos0 = buf, *pos1, *pos2;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(req->method, 0, sizeof(req->method));
|
||||
memset(req->version, 0, sizeof(req->version));
|
||||
req->uri = NULL;
|
||||
req->hdr.field_num = 0;
|
||||
req->hdr.last_field_num = -1;
|
||||
|
||||
while (1) {
|
||||
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE, 0);
|
||||
@@ -164,31 +223,95 @@ int http_receive_request(sock *client, http_req *req) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *http_get_header_field(const http_hdr *hdr, const char *field_name) {
|
||||
char field_name_1[256], field_name_2[256];
|
||||
strcpy(field_name_1, field_name);
|
||||
http_to_camel_case(field_name_1, HTTP_LOWER);
|
||||
for (int i = 0; i < hdr->field_num; i++) {
|
||||
strcpy(field_name_2, hdr->fields[i][0]);
|
||||
http_to_camel_case(field_name_2, HTTP_LOWER);
|
||||
if (strcmp(field_name_1, field_name_2) == 0) {
|
||||
return hdr->fields[i][1];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
const char *http_get_header_field(const http_hdr *hdr, const char *field_name) {
|
||||
return http_get_header_field_len(hdr, field_name, strlen(field_name));
|
||||
}
|
||||
|
||||
void http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value) {
|
||||
size_t len_name = strlen(field_name);
|
||||
size_t len_value = strlen(field_value);
|
||||
char *_field_name = malloc(len_name + 1);
|
||||
char *_field_value = malloc(len_value + 1);
|
||||
strcpy(_field_name, field_name);
|
||||
strcpy(_field_value, field_value);
|
||||
http_to_camel_case(_field_name, HTTP_PRESERVE);
|
||||
hdr->fields[(int) hdr->field_num][0] = _field_name;
|
||||
hdr->fields[(int) hdr->field_num][1] = _field_value;
|
||||
const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len) {
|
||||
int num = http_get_header_field_num_len(hdr, field_name, len);
|
||||
return (num >= 0 && num < HTTP_MAX_HEADER_FIELD_NUM) ? http_field_get_value(&hdr->fields[num]) : NULL;
|
||||
}
|
||||
|
||||
int http_get_header_field_num(const http_hdr *hdr, const char *field_name) {
|
||||
return http_get_header_field_num_len(hdr, field_name, strlen(field_name));
|
||||
}
|
||||
|
||||
int http_get_header_field_num_len(const http_hdr *hdr, const char *field_name, unsigned long len) {
|
||||
char field_name_1[256], field_name_2[256];
|
||||
memcpy(field_name_1, field_name, len);
|
||||
field_name_1[len] = 0;
|
||||
http_to_camel_case(field_name_1, HTTP_LOWER);
|
||||
|
||||
for (int i = 0; i < hdr->field_num; i++) {
|
||||
strcpy(field_name_2, http_field_get_name(&hdr->fields[i]));
|
||||
http_to_camel_case(field_name_2, HTTP_LOWER);
|
||||
|
||||
if (strcmp(field_name_1, field_name_2) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value) {
|
||||
return http_add_header_field_len(hdr, field_name, strlen(field_name), field_value, strlen(field_value));
|
||||
}
|
||||
|
||||
int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long name_len, const char *value, unsigned long value_len) {
|
||||
if (hdr->field_num >= HTTP_MAX_HEADER_FIELD_NUM)
|
||||
return -1;
|
||||
|
||||
http_field *f = &hdr->fields[(int) hdr->field_num];
|
||||
|
||||
if (name_len < sizeof(f->normal.name) && value_len < sizeof(f->normal.value)) {
|
||||
f->type = HTTP_FIELD_NORMAL;
|
||||
memcpy(f->normal.name, name, name_len);
|
||||
memcpy(f->normal.value, value, value_len);
|
||||
f->normal.name[name_len] = 0;
|
||||
f->normal.value[value_len] = 0;
|
||||
http_to_camel_case(f->normal.name, HTTP_PRESERVE);
|
||||
} else if (name_len < sizeof(f->ex_value.name)) {
|
||||
f->type = HTTP_FIELD_EX_VALUE;
|
||||
f->ex_value.value = malloc(value_len + 1);
|
||||
memcpy(f->ex_value.name, name, name_len);
|
||||
memcpy(f->ex_value.value, value, value_len);
|
||||
f->ex_value.name[name_len] = 0;
|
||||
f->ex_value.value[value_len] = 0;
|
||||
http_to_camel_case(f->ex_value.name, HTTP_PRESERVE);
|
||||
} else {
|
||||
f->type = HTTP_FIELD_EX_NAME;
|
||||
f->ex_name.name = malloc(name_len + 1);
|
||||
f->ex_name.value = malloc(value_len + 1);
|
||||
memcpy(f->ex_name.name, name, name_len);
|
||||
memcpy(f->ex_name.value, value, value_len);
|
||||
f->ex_name.name[name_len] = 0;
|
||||
f->ex_name.value[value_len] = 0;
|
||||
http_to_camel_case(f->ex_name.name, HTTP_PRESERVE);
|
||||
}
|
||||
|
||||
hdr->field_num++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void http_append_to_header_field(http_field *field, const char *value, unsigned long len) {
|
||||
if (field->type == HTTP_FIELD_NORMAL) {
|
||||
unsigned long total_len = strlen(field->normal.value) + len + 1;
|
||||
if (total_len < sizeof(field->normal.value)) {
|
||||
strncat(field->normal.value, value, len);
|
||||
} else {
|
||||
field->type = HTTP_FIELD_EX_VALUE;
|
||||
char *new = malloc(total_len);
|
||||
strcpy(new, field->normal.value);
|
||||
strncat(new, value, len);
|
||||
field->ex_value.value = new;
|
||||
}
|
||||
} else if (field->type == HTTP_FIELD_EX_VALUE) {
|
||||
field->ex_value.value = realloc(field->ex_value.value, strlen(field->ex_value.value) + len + 1);
|
||||
strncat(field->ex_value.value, value, len);
|
||||
} else if (field->type == HTTP_FIELD_EX_NAME) {
|
||||
field->ex_name.value = realloc(field->ex_name.value, strlen(field->ex_name.value) + len + 1);
|
||||
strncat(field->ex_name.value, value, len);
|
||||
}
|
||||
}
|
||||
|
||||
void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode) {
|
||||
@@ -203,12 +326,11 @@ void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode) {
|
||||
diff = -1;
|
||||
}
|
||||
for (; i < hdr->field_num && i >= 0; i += diff) {
|
||||
strcpy(field_name_2, hdr->fields[i][0]);
|
||||
strcpy(field_name_2, http_field_get_name(&hdr->fields[i]));
|
||||
http_to_camel_case(field_name_2, HTTP_LOWER);
|
||||
if (strcmp(field_name_1, field_name_2) == 0) {
|
||||
for (int j = i; j < hdr->field_num - 1; j++) {
|
||||
memcpy(hdr->fields[j], hdr->fields[j + 1], sizeof(hdr->fields[0]));
|
||||
}
|
||||
http_free_field(&hdr->fields[i]);
|
||||
memmove(&hdr->fields[i], &hdr->fields[i + 1], sizeof(hdr->fields[0]) * (hdr->field_num - i));
|
||||
hdr->field_num--;
|
||||
if (mode == HTTP_REMOVE_ALL) {
|
||||
i -= diff;
|
||||
@@ -223,7 +345,8 @@ int http_send_response(sock *client, http_res *res) {
|
||||
char buf[CLIENT_MAX_HEADER_SIZE];
|
||||
long off = sprintf(buf, "HTTP/%s %03i %s\r\n", res->version, res->status->code, res->status->msg);
|
||||
for (int i = 0; i < res->hdr.field_num; i++) {
|
||||
off += sprintf(buf + off, "%s: %s\r\n", res->hdr.fields[i][0], res->hdr.fields[i][1]);
|
||||
const http_field *f = &res->hdr.fields[i];
|
||||
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) {
|
||||
@@ -236,7 +359,8 @@ int http_send_request(sock *server, http_req *req) {
|
||||
char buf[CLIENT_MAX_HEADER_SIZE];
|
||||
long off = sprintf(buf, "%s %s HTTP/%s\r\n", req->method, req->uri, req->version);
|
||||
for (int i = 0; i < req->hdr.field_num; i++) {
|
||||
off += sprintf(buf + off, "%s: %s\r\n", req->hdr.fields[i][0], req->hdr.fields[i][1]);
|
||||
const http_field *f = &req->hdr.fields[i];
|
||||
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);
|
||||
@@ -314,9 +438,9 @@ const http_doc_info *http_get_status_info(const http_status *status) {
|
||||
}
|
||||
|
||||
int http_get_compression(const http_req *req, const http_res *res) {
|
||||
char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding");
|
||||
char *content_type = http_get_header_field(&res->hdr, "Content-Type");
|
||||
char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||
const char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding");
|
||||
const char *content_type = http_get_header_field(&res->hdr, "Content-Type");
|
||||
const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||
if (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) {
|
||||
if (strstr(accept_encoding, "br") != NULL) {
|
||||
return COMPRESS_BR;
|
||||
|
@@ -18,6 +18,10 @@
|
||||
#define HTTP_REMOVE_ALL 1
|
||||
#define HTTP_REMOVE_LAST 2
|
||||
|
||||
#define HTTP_FIELD_NORMAL 0
|
||||
#define HTTP_FIELD_EX_VALUE 1
|
||||
#define HTTP_FIELD_EX_NAME 2
|
||||
|
||||
#define HTTP_1XX_STR "\x1B[1;32m"
|
||||
#define HTTP_2XX_STR "\x1B[1;32m"
|
||||
#define HTTP_3XX_STR "\x1B[1;33m"
|
||||
@@ -30,6 +34,7 @@
|
||||
#define HTTP_COLOR_ERROR "#C00000"
|
||||
|
||||
#define CLIENT_MAX_HEADER_SIZE 8192
|
||||
#define HTTP_MAX_HEADER_FIELD_NUM 64
|
||||
|
||||
#ifndef SERVER_STR
|
||||
# define SERVER_STR "Necronda"
|
||||
@@ -57,9 +62,28 @@ typedef struct {
|
||||
const char *doc;
|
||||
} http_doc_info;
|
||||
|
||||
typedef struct {
|
||||
char type;
|
||||
union {
|
||||
struct {
|
||||
char name[64];
|
||||
char value[192];
|
||||
} normal;
|
||||
struct {
|
||||
char name[192];
|
||||
char *value;
|
||||
} ex_value;
|
||||
struct {
|
||||
char *name;
|
||||
char *value;
|
||||
} ex_name;
|
||||
};
|
||||
} http_field;
|
||||
|
||||
typedef struct {
|
||||
char field_num;
|
||||
char *fields[64][2];
|
||||
char last_field_num;
|
||||
http_field fields[HTTP_MAX_HEADER_FIELD_NUM];
|
||||
} http_hdr;
|
||||
|
||||
typedef struct {
|
||||
@@ -102,6 +126,12 @@ extern const char http_info_icon[];
|
||||
|
||||
void http_to_camel_case(char *str, int mode);
|
||||
|
||||
const char *http_field_get_name(const http_field *field);
|
||||
|
||||
const char *http_field_get_value(const http_field *field);
|
||||
|
||||
void http_free_field(http_field *f);
|
||||
|
||||
void http_free_hdr(http_hdr *hdr);
|
||||
|
||||
void http_free_req(http_req *req);
|
||||
@@ -112,9 +142,19 @@ int http_receive_request(sock *client, http_req *req);
|
||||
|
||||
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) ;
|
||||
|
||||
char *http_get_header_field(const http_hdr *hdr, const char *field_name);
|
||||
const char *http_get_header_field(const http_hdr *hdr, const char *field_name);
|
||||
|
||||
void http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value);
|
||||
const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len);
|
||||
|
||||
int http_get_header_field_num(const http_hdr *hdr, const char *field_name);
|
||||
|
||||
int http_get_header_field_num_len(const http_hdr *hdr, const char *field_name, unsigned long len);
|
||||
|
||||
int http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value);
|
||||
|
||||
int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long name_len, const char *value, unsigned long value_len);
|
||||
|
||||
void http_append_to_header_field(http_field *field, const char *value, unsigned long len);
|
||||
|
||||
void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode);
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
#include "rev_proxy.h"
|
||||
#include "utils.h"
|
||||
#include "compress.h"
|
||||
#include "../necronda-server.h"
|
||||
#include "../server.h"
|
||||
#include <openssl/ssl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
@@ -29,13 +29,12 @@ int rev_proxy_preload() {
|
||||
}
|
||||
|
||||
int rev_proxy_request_header(http_req *req, int enc) {
|
||||
char buf1[256];
|
||||
char buf2[256];
|
||||
char buf1[256], buf2[256];
|
||||
int p_len;
|
||||
http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL);
|
||||
http_add_header_field(&req->hdr, "Connection", "keep-alive");
|
||||
|
||||
char *via = http_get_header_field(&req->hdr, "Via");
|
||||
const char *via = http_get_header_field(&req->hdr, "Via");
|
||||
sprintf(buf1, "HTTP/%s %s", req->version, SERVER_NAME);
|
||||
if (via == NULL) {
|
||||
http_add_header_field(&req->hdr, "Via", buf1);
|
||||
@@ -49,10 +48,10 @@ int rev_proxy_request_header(http_req *req, int enc) {
|
||||
http_add_header_field(&req->hdr, "Via", buf2);
|
||||
}
|
||||
|
||||
char *host = http_get_header_field(&req->hdr, "Host");
|
||||
char *forwarded = http_get_header_field(&req->hdr, "Forwarded");
|
||||
const char *host = http_get_header_field(&req->hdr, "Host");
|
||||
const char *forwarded = http_get_header_field(&req->hdr, "Forwarded");
|
||||
int client_ipv6 = strchr(client_addr_str, ':') != NULL;
|
||||
int server_ipv6 = strchr(server_addr_str, ':') != NULL;
|
||||
int server_ipv6 = strchr(server_addr_str, ':') != NULL;
|
||||
|
||||
p_len = snprintf(buf1, sizeof(buf1), "by=%s%s%s;for=%s%s%s;host=%s;proto=%s",
|
||||
server_ipv6 ? "\"[" : "", server_addr_str, server_ipv6 ? "]\"" : "",
|
||||
@@ -62,6 +61,7 @@ int rev_proxy_request_header(http_req *req, int enc) {
|
||||
print(ERR_STR "Appended part of header field 'Forwarded' too long" CLR_STR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (forwarded == NULL) {
|
||||
http_add_header_field(&req->hdr, "Forwarded", buf1);
|
||||
} else {
|
||||
@@ -74,7 +74,7 @@ int rev_proxy_request_header(http_req *req, int enc) {
|
||||
http_add_header_field(&req->hdr, "Forwarded", buf2);
|
||||
}
|
||||
|
||||
char *xff = http_get_header_field(&req->hdr, "X-Forwarded-For");
|
||||
const char *xff = http_get_header_field(&req->hdr, "X-Forwarded-For");
|
||||
if (xff == NULL) {
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-For", client_addr_str);
|
||||
} else {
|
||||
@@ -83,7 +83,7 @@ int rev_proxy_request_header(http_req *req, int enc) {
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-For", buf1);
|
||||
}
|
||||
|
||||
char *xfh = http_get_header_field(&req->hdr, "X-Forwarded-Host");
|
||||
const char *xfh = http_get_header_field(&req->hdr, "X-Forwarded-Host");
|
||||
if (xfh == NULL) {
|
||||
if (forwarded == NULL) {
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-Host", host);
|
||||
@@ -104,7 +104,7 @@ int rev_proxy_request_header(http_req *req, int enc) {
|
||||
}
|
||||
}
|
||||
|
||||
char *xfp = http_get_header_field(&req->hdr, "X-Forwarded-Proto");
|
||||
const char *xfp = http_get_header_field(&req->hdr, "X-Forwarded-Proto");
|
||||
if (xfp == NULL) {
|
||||
if (forwarded == NULL) {
|
||||
http_add_header_field(&req->hdr, "X-Forwarded-Proto", enc ? "https" : "http");
|
||||
@@ -129,11 +129,10 @@ int rev_proxy_request_header(http_req *req, int enc) {
|
||||
}
|
||||
|
||||
int rev_proxy_response_header(http_req *req, http_res *res, host_config *conf) {
|
||||
char buf1[256];
|
||||
char buf2[256];
|
||||
char buf1[256], buf2[256];
|
||||
int p_len;
|
||||
|
||||
char *via = http_get_header_field(&res->hdr, "Via");
|
||||
const char *via = http_get_header_field(&res->hdr, "Via");
|
||||
p_len = snprintf(buf1, sizeof(buf1), "HTTP/%s %s", req->version, SERVER_NAME);
|
||||
if (p_len < 0 || p_len >= sizeof(buf1)) {
|
||||
print(ERR_STR "Appended part of header field 'Via' too long" CLR_STR);
|
||||
@@ -151,7 +150,7 @@ int rev_proxy_response_header(http_req *req, http_res *res, host_config *conf) {
|
||||
http_add_header_field(&res->hdr, "Via", buf2);
|
||||
}
|
||||
|
||||
char *location = http_get_header_field(&res->hdr, "Location");
|
||||
const char *location = http_get_header_field(&res->hdr, "Location");
|
||||
if (location != NULL) {
|
||||
char *hostnames[] = {conf->name, conf->rev_proxy.hostname};
|
||||
for (int i = 0; i < sizeof(hostnames) / sizeof(hostnames[0]); i++) {
|
||||
@@ -172,20 +171,19 @@ int rev_proxy_response_header(http_req *req, http_res *res, host_config *conf) {
|
||||
|
||||
if (0) {
|
||||
match:
|
||||
strcpy(buf1, location + p_len - 1);
|
||||
http_remove_header_field(&res->hdr, "Location", HTTP_REMOVE_ALL);
|
||||
http_add_header_field(&res->hdr, "Location", location + p_len - 1);
|
||||
http_add_header_field(&res->hdr, "Location", buf1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config *conf, sock *client,
|
||||
http_status *custom_status, char *err_msg) {
|
||||
int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config *conf, sock *client, http_status *custom_status, char *err_msg) {
|
||||
char buffer[CHUNK_SIZE];
|
||||
long ret;
|
||||
int tries = 0;
|
||||
int retry = 0;
|
||||
int tries = 0, retry = 0;
|
||||
|
||||
if (rev_proxy.socket != 0 && strcmp(rev_proxy_host, conf->name) == 0 && sock_check(&rev_proxy) == 0) {
|
||||
goto rev_proxy;
|
||||
@@ -307,7 +305,7 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf
|
||||
goto proxy_err;
|
||||
}
|
||||
|
||||
char *content_length = http_get_header_field(&req->hdr, "Content-Length");
|
||||
const char *content_length = http_get_header_field(&req->hdr, "Content-Length");
|
||||
if (content_length != NULL) {
|
||||
unsigned long content_len = strtoul(content_length, NULL, 10);
|
||||
if (client->buf_len - client->buf_off > 0) {
|
||||
@@ -455,13 +453,9 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf
|
||||
|
||||
int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
// TODO handle websockets
|
||||
long ret;
|
||||
char buffer[CHUNK_SIZE];
|
||||
char comp_out[CHUNK_SIZE];
|
||||
char buf[256];
|
||||
long len, snd_len;
|
||||
char buffer[CHUNK_SIZE], comp_out[CHUNK_SIZE], buf[256], *ptr;
|
||||
long ret = 0, len, snd_len;
|
||||
int finish_comp = 0;
|
||||
char *ptr;
|
||||
|
||||
compress_ctx comp_ctx;
|
||||
if (flags & REV_PROXY_COMPRESS_BR) {
|
||||
@@ -498,6 +492,7 @@ int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
if (len_to_send == 0 && (flags & REV_PROXY_COMPRESS)) {
|
||||
finish_comp = 1;
|
||||
len = 0;
|
||||
ptr = NULL;
|
||||
goto out;
|
||||
finish:
|
||||
compress_free(&comp_ctx);
|
||||
@@ -519,8 +514,7 @@ int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||
long buf_len = len;
|
||||
if (flags & REV_PROXY_COMPRESS) {
|
||||
avail_out = sizeof(comp_out);
|
||||
compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out,
|
||||
finish_comp);
|
||||
compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out, finish_comp);
|
||||
ptr = comp_out;
|
||||
buf_len = (int) (sizeof(comp_out) - avail_out);
|
||||
snd_len += (long) (len - avail_in);
|
||||
|
@@ -124,7 +124,6 @@ int mime_is_compressible(const char *type) {
|
||||
strcmp(type_parsed, "application/vnd.ms-fontobject") == 0 ||
|
||||
strcmp(type_parsed, "application/x-font-ttf") == 0 ||
|
||||
strcmp(type_parsed, "application/x-javascript") == 0 ||
|
||||
strcmp(type_parsed, "application/x-web-app-manifest+json") == 0 ||
|
||||
strcmp(type_parsed, "font/eot") == 0 ||
|
||||
strcmp(type_parsed, "font/opentype") == 0 ||
|
||||
strcmp(type_parsed, "image/bmp") == 0 ||
|
||||
@@ -135,11 +134,41 @@ int mime_is_compressible(const char *type) {
|
||||
}
|
||||
|
||||
int strcpy_rem_webroot(char *dst, const char *src, long len, const char *webroot) {
|
||||
strncpy(dst, src, len);
|
||||
if (webroot == NULL) return 0;
|
||||
memcpy(dst, src, len);
|
||||
dst[len] = 0;
|
||||
if (webroot == NULL)
|
||||
return 0;
|
||||
|
||||
char *pos;
|
||||
const unsigned long webroot_len = strlen(webroot);
|
||||
if (webroot_len == 0)
|
||||
return 0;
|
||||
|
||||
while ((pos = strstr(dst, webroot)) != NULL) {
|
||||
strcpy(pos, pos + strlen(webroot));
|
||||
strcpy(pos, pos + webroot_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int str_trim(char **start, char **end) {
|
||||
if (start == NULL || end == NULL || *start == NULL || *end == NULL)
|
||||
return -1;
|
||||
|
||||
(*end)--;
|
||||
while (*start[0] == ' ' || *start[0] == '\t' || *start[0] == '\r' || *start[0] == '\n') (*start)++;
|
||||
while (*end[0] == ' ' || *end[0] == '\t' || *end[0] == '\r' || *end[0] == '\n') (*end)--;
|
||||
(*end)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int str_trim_lws(char **start, char **end) {
|
||||
if (start == NULL || end == NULL || *start == NULL || *end == NULL)
|
||||
return -1;
|
||||
|
||||
(*end)--;
|
||||
while (*start[0] == ' ' || *start[0] == '\t') (*start)++;
|
||||
while (*end[0] == ' ' || *end[0] == '\t') (*end)--;
|
||||
(*end)++;
|
||||
return 0;
|
||||
}
|
||||
|
@@ -42,4 +42,8 @@ int mime_is_compressible(const char *type);
|
||||
|
||||
int strcpy_rem_webroot(char *dst, const char *str, long len, const char *webroot);
|
||||
|
||||
int str_trim(char **start, char **end);
|
||||
|
||||
int str_trim_lws(char **start, char **end);
|
||||
|
||||
#endif //NECRONDA_SERVER_UTILS_H
|
||||
|
@@ -8,14 +8,15 @@
|
||||
#define _POSIX_C_SOURCE 199309L
|
||||
|
||||
#include "necronda.h"
|
||||
#include "necronda-server.h"
|
||||
#include "client.c"
|
||||
#include "server.h"
|
||||
#include "client.h"
|
||||
|
||||
#include "lib/cache.h"
|
||||
#include "lib/config.h"
|
||||
#include "lib/sock.h"
|
||||
#include "lib/rev_proxy.h"
|
||||
#include "lib/geoip.h"
|
||||
#include "lib/utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -65,13 +66,11 @@ void destroy() {
|
||||
if (children[i] != 0) {
|
||||
ret = waitpid(children[i], &status, WNOHANG);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
|
||||
children[i], strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n", children[i], strerror(errno));
|
||||
} else if (ret == children[i]) {
|
||||
children[i] = 0;
|
||||
if (status != 0) {
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
|
||||
ret, status);
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n", ret, status);
|
||||
}
|
||||
} else {
|
||||
kill(children[i], SIGKILL);
|
||||
@@ -106,13 +105,11 @@ void terminate() {
|
||||
if (children[i] != 0) {
|
||||
ret = waitpid(children[i], &status, WNOHANG);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
|
||||
children[i], strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n", children[i], strerror(errno));
|
||||
} else if (ret == children[i]) {
|
||||
children[i] = 0;
|
||||
if (status != 0) {
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
|
||||
ret, status);
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n", ret, status);
|
||||
}
|
||||
} else {
|
||||
kill(children[i], SIGTERM);
|
||||
@@ -129,13 +126,11 @@ void terminate() {
|
||||
if (children[i] != 0) {
|
||||
ret = waitpid(children[i], &status, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
|
||||
children[i], strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n", children[i], strerror(errno));
|
||||
} else if (ret == children[i]) {
|
||||
children[i] = 0;
|
||||
if (status != 0) {
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
|
||||
ret, status);
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n", ret, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,15 +311,15 @@ int main(int argc, const char *argv[]) {
|
||||
SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
|
||||
|
||||
if (SSL_CTX_use_certificate_chain_file(ctx, conf->full_chain) != 1) {
|
||||
fprintf(stderr, ERR_STR "Unable to load certificate chain file: %s: %s" CLR_STR "\n",
|
||||
ERR_reason_error_string(ERR_get_error()), conf->full_chain);
|
||||
fprintf(stderr, ERR_STR "Unable to load certificate chain file: %s: %s" CLR_STR "\n", ERR_reason_error_string(ERR_get_error()), conf->full_chain);
|
||||
config_unload();
|
||||
cache_unload();
|
||||
return 1;
|
||||
}
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, conf->priv_key, SSL_FILETYPE_PEM) != 1) {
|
||||
fprintf(stderr, ERR_STR "Unable to load private key file: %s: %s" CLR_STR "\n",
|
||||
ERR_reason_error_string(ERR_get_error()), conf->priv_key);
|
||||
fprintf(stderr, ERR_STR "Unable to load private key file: %s: %s" CLR_STR "\n", ERR_reason_error_string(ERR_get_error()), conf->priv_key);
|
||||
config_unload();
|
||||
cache_unload();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -338,6 +333,7 @@ int main(int argc, const char *argv[]) {
|
||||
if (listen(sockets[i], LISTEN_BACKLOG) < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to listen on socket %i: %s" CLR_STR "\n", i, strerror(errno));
|
||||
config_unload();
|
||||
cache_unload();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -378,7 +374,7 @@ int main(int argc, const char *argv[]) {
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
|
||||
client.socket = client_fd;
|
||||
client.enc = i == 1;
|
||||
client.enc = (i == 1);
|
||||
return client_handler(&client, client_num, &client_addr);
|
||||
} else if (pid > 0) {
|
||||
// parent
|
||||
@@ -402,18 +398,18 @@ int main(int argc, const char *argv[]) {
|
||||
if (children[i] != 0) {
|
||||
ret = waitpid(children[i], &status, WNOHANG);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
|
||||
children[i], strerror(errno));
|
||||
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n", children[i], strerror(errno));
|
||||
} else if (ret == children[i]) {
|
||||
children[i] = 0;
|
||||
if (status != 0) {
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
|
||||
ret, status);
|
||||
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n", ret, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config_unload();
|
||||
cache_unload();
|
||||
return 0;
|
||||
}
|
@@ -5,8 +5,8 @@
|
||||
* Lorenz Stechauner, 2020-12-03
|
||||
*/
|
||||
|
||||
#ifndef NECRONDA_SERVER_NECRONDA_SERVER_H
|
||||
#define NECRONDA_SERVER_NECRONDA_SERVER_H
|
||||
#ifndef NECRONDA_SERVER_SERVER_H
|
||||
#define NECRONDA_SERVER_SERVER_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <maxminddb.h>
|
||||
@@ -31,4 +31,4 @@ extern char *log_client_prefix, *log_conn_prefix, *log_req_prefix, *client_geoip
|
||||
extern char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ptr, *client_host_str;
|
||||
extern struct timeval client_timeout;
|
||||
|
||||
#endif //NECRONDA_SERVER_NECRONDA_SERVER_H
|
||||
#endif //NECRONDA_SERVER_SERVER_H
|
Reference in New Issue
Block a user