0.1.0
This commit is contained in:
commit
b295d9cc64
|
@ -0,0 +1,4 @@
|
|||
[submodule "src/hashmap"]
|
||||
path = src/hashmap
|
||||
url = https://git.tcp.direct/aiden/hashmap
|
||||
branch = main
|
|
@ -0,0 +1,15 @@
|
|||
ISC License
|
||||
|
||||
Copyright (c) 2021, interparse (aiden)
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,3 @@
|
|||
# bidirectiond-experimental
|
||||
|
||||
experimental modular "reverse" proxy written in c
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python3
|
||||
semver = "0.1.0"
|
||||
import sys, json, os, subprocess
|
||||
if sys.argv[0] != "./build.py":
|
||||
print("shit")
|
||||
exit(1)
|
||||
gcc_args = ["gcc", "-o", "bidirectiond"]
|
||||
services_c = "#include <bddc/api.h>\n"
|
||||
services = "struct bdd_internal_service internal_services[] = {"
|
||||
names = [
|
||||
["bool %s(struct bdd_connections *connections, void *buf, size_t buf_size);", "serve"],
|
||||
["bool %s(struct bdd_connections *connections, void *service_info, bdd_io_id client_id, struct sockaddr client_sockaddr);", "connections_init"],
|
||||
["void %s(void *service_info);", "service_info_destructor"]]
|
||||
np = 0
|
||||
for dir, _, files in os.walk("src"):
|
||||
for file in files:
|
||||
if file[-2:] == ".c" or file[-2:] == ".o":
|
||||
gcc_args.append(os.path.join(dir, file))
|
||||
elif file == "service.json":
|
||||
np += 1
|
||||
path = os.path.join(dir, file)
|
||||
fd = open(path)
|
||||
p = json.loads(fd.read())
|
||||
services += "{ NULL, "
|
||||
for fn, member in names:
|
||||
services += f".{member}="
|
||||
if member in p:
|
||||
name = p.get(member)
|
||||
services_c += fn % name + "\n"
|
||||
services += f"&({p.get(member)})"
|
||||
else:
|
||||
services += "NULL"
|
||||
services += ","
|
||||
services_c += f"""bool {p["service_init"]}(struct locked_hashmap *name_descriptions, struct bdd_internal_service *service, size_t n_arguments, char **arguments);\n"""
|
||||
services += """
|
||||
.service_init = &(%s),
|
||||
.supported_arguments = (char *[]){ %s, NULL, },
|
||||
.arguments_help = (char *)%s,
|
||||
.n_max_io = %i,
|
||||
}""" % (p["service_init"], json.dumps(p["supported_arguments"])[1:-1], json.dumps(p["arguments_help"]), p["n_max_io"])
|
||||
fd.close()
|
||||
services += "};"
|
||||
services_c += services
|
||||
fd = open("src/_services.c", "w")
|
||||
fd.write(services_c)
|
||||
fd.close()
|
||||
gcc_args.append("src/_services.c")
|
||||
sysname = os.uname().sysname
|
||||
gcc_args.extend(["-lpthread", "-lssl", "-lcrypto", "-Iinc", "-DN_INTERNAL_SERVICES=" + str(np), "-DPROG_SEMVER=" + json.dumps(semver), "-funsigned-char"] + sys.argv[1:])
|
||||
subprocess.call(gcc_args)
|
||||
os.unlink("src/_services.c")
|
|
@ -0,0 +1 @@
|
|||
../src/hashmap/
|
|
@ -0,0 +1,177 @@
|
|||
#include "internal.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
int bdd_use_correct_ctx(SSL *client_ssl, int *_, struct bdd_accept_ctx *ctx) {
|
||||
int r = SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
|
||||
char *name = (char *)SSL_get_servername(client_ssl, TLSEXT_NAMETYPE_host_name);
|
||||
if (unlikely(name == NULL)) {
|
||||
BDD_DEBUG_LOG("no dns name\n");
|
||||
goto ucc__err;
|
||||
}
|
||||
|
||||
// to-do: strlen is slow af, maybe openssl can give us the length instead
|
||||
size_t name_len = strlen(name);
|
||||
if (name_len == 0 || (name_len == 254 && name[253] != '.') || name_len > 254) {
|
||||
goto ucc__err;
|
||||
}
|
||||
if (name[name_len - 1] == '.') {
|
||||
if ((name_len -= 1) == 0) {
|
||||
goto ucc__err;
|
||||
}
|
||||
}
|
||||
|
||||
// i'm doing great, okay?
|
||||
// thabks
|
||||
uint8_t found_req = 0;
|
||||
for (size_t idx = 0;;) {
|
||||
struct bdd_name_description *name_description = locked_hashmap_get_wl(ctx->locked_name_descriptions, &(name[idx]), name_len);
|
||||
if (name_description != NULL) {
|
||||
if (!(found_req & 0b01) && name_description->ssl_ctx != NULL) {
|
||||
found_req |= 0b01;
|
||||
SSL_set_SSL_CTX(client_ssl, name_description->ssl_ctx);
|
||||
}
|
||||
if (!(found_req & 0b10) && name_description->service_type != bdd_service_type_none) {
|
||||
found_req |= 0b10;
|
||||
ctx->service_name_description = name_description;
|
||||
}
|
||||
if (found_req == 0b11) {
|
||||
r = SSL_TLSEXT_ERR_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool fwc = name[idx] == '*';
|
||||
do {
|
||||
idx += 1;
|
||||
name_len -= 1;
|
||||
if (name_len == 0) {
|
||||
if (fwc) {
|
||||
goto ucc__err;
|
||||
}
|
||||
goto ucc__place_wc;
|
||||
}
|
||||
if (name[idx] == '.') {
|
||||
if (!fwc) {
|
||||
ucc__place_wc:;
|
||||
name_len += 1;
|
||||
name[--idx] = '*';
|
||||
break;
|
||||
}
|
||||
fwc = false;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
ucc__err:;
|
||||
BDD_DEBUG_LOG("r: %i\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
void *bdd_accept(struct bdd_instance *instance) {
|
||||
struct bdd_accept_ctx *ctx = &(instance->accept.accept_ctx);
|
||||
bdd_accept_thread__poll:;
|
||||
while (poll(instance->accept.pollfds, 2, -1) < 0) {
|
||||
if (errno != EINTR) {
|
||||
bdd_stop(instance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unlikely(atomic_load(&(instance->exiting)))) {
|
||||
bdd_thread_exit(instance);
|
||||
}
|
||||
|
||||
struct bdd_connections *connections = NULL;
|
||||
SSL *client_ssl = NULL;
|
||||
instance->accept.accept_ctx.service_name_description = NULL;
|
||||
int cl_socket = -1;
|
||||
|
||||
#ifdef BIDIRECTIOND_ACCEPT_OCBCNS
|
||||
if ((connections = bdd_connections_obtain(instance)) == NULL) {
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
#endif
|
||||
if ((client_ssl = SSL_new(instance->accept.ssl_ctx)) == NULL) {
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
|
||||
// accept
|
||||
BDD_DEBUG_LOG("accepting tcp connection\n");
|
||||
struct sockaddr cl_sockaddr;
|
||||
socklen_t sockaddr_sz = sizeof(struct sockaddr);
|
||||
do {
|
||||
cl_socket = accept(instance->sv_socket, &(cl_sockaddr), &(sockaddr_sz));
|
||||
} while (cl_socket < 0 && errno == EINTR);
|
||||
if (cl_socket < 0) {
|
||||
BDD_DEBUG_LOG("rejected tcp connection\n");
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
BDD_DEBUG_LOG("accepted tcp connection\n");
|
||||
|
||||
fcntl(cl_socket, F_SETFL, fcntl(cl_socket, F_GETFL, 0) & ~(O_NONBLOCK));
|
||||
setsockopt(cl_socket, SOL_SOCKET, SO_SNDTIMEO, &(instance->client_timeout), sizeof(instance->client_timeout));
|
||||
setsockopt(cl_socket, SOL_SOCKET, SO_RCVTIMEO, &(instance->client_timeout), sizeof(instance->client_timeout));
|
||||
|
||||
if (!SSL_set_fd(client_ssl, cl_socket)) {
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
|
||||
if ((ctx->locked_name_descriptions = hashmap_lock(instance->name_descriptions)) == NULL) {
|
||||
BDD_DEBUG_LOG("failed to obtain name_descriptions\n");
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
if (SSL_accept(client_ssl) < 0) {
|
||||
BDD_DEBUG_LOG("rejected tls setup\n");
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
|
||||
switch (ctx->service_name_description->service_type) {
|
||||
case (bdd_service_type_internal): {
|
||||
#ifndef BIDIRECTIOND_ACCEPT_OCBCNS
|
||||
if ((connections = bdd_connections_obtain(instance)) == NULL) {
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
#endif
|
||||
switch (bdd_connections_init(connections, &(client_ssl), cl_sockaddr, ctx->service_name_description->service.internal.service, ctx->service_name_description->service.internal.service_info)) {
|
||||
case (bdd_connections_init_failed): {
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
case (bdd_connections_init_success): {
|
||||
bdd_connections_link(instance, &(connections));
|
||||
break;
|
||||
}
|
||||
case (bdd_connections_init_failed_wants_deinit): {
|
||||
bdd_connections_deinit(connections);
|
||||
goto bdd_accept__err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
locked_hashmap_unlock(&(ctx->locked_name_descriptions));
|
||||
goto bdd_accept_thread__poll;
|
||||
|
||||
bdd_accept__err:;
|
||||
BDD_DEBUG_LOG("failed to accept connection\n");
|
||||
|
||||
if (ctx->locked_name_descriptions != NULL) {
|
||||
locked_hashmap_unlock(&(ctx->locked_name_descriptions));
|
||||
}
|
||||
if (client_ssl != NULL) {
|
||||
SSL_free(client_ssl);
|
||||
}
|
||||
if (cl_socket >= 0) {
|
||||
close(cl_socket);
|
||||
}
|
||||
if (connections != NULL) {
|
||||
bdd_connections_release(instance, &(connections));
|
||||
}
|
||||
goto bdd_accept_thread__poll;
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
#ifndef bidirectiond_core__api__h
|
||||
#define bidirectiond_core__api__h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <hashmap/hashmap.h>
|
||||
#include <poll.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef POLLRDHUP
|
||||
#define POLLRDHUP 0x400
|
||||
#endif
|
||||
|
||||
enum bdd_name_description_service_type { bdd_name_description_service_type_none, bdd_name_description_service_type_internal, } __attribute__((packed));
|
||||
typedef unsigned short int bdd_io_id;
|
||||
|
||||
struct bdd_instance;
|
||||
struct bdd_io {
|
||||
int fd;
|
||||
SSL *ssl;
|
||||
};
|
||||
|
||||
struct bdd_connections_associated {
|
||||
void *data;
|
||||
void (*destructor)(void *data);
|
||||
};
|
||||
|
||||
struct bdd_connections {
|
||||
struct bdd_connections *next;
|
||||
|
||||
bool working : 1, broken : 1;
|
||||
pthread_mutex_t working_mutex;
|
||||
|
||||
const struct bdd_internal_service *service;
|
||||
|
||||
struct bdd_io *io;
|
||||
|
||||
struct bdd_connections_associated associated;
|
||||
};
|
||||
|
||||
struct bdd_internal_service {
|
||||
char *name;
|
||||
|
||||
bool (*serve)(struct bdd_connections *connections, void *buf, size_t buf_size);
|
||||
|
||||
bool (*connections_init)(struct bdd_connections *connections, void *service_info, bdd_io_id client_id, struct sockaddr client_sockaddr);
|
||||
|
||||
void (*service_info_destructor)(void *service_info);
|
||||
bool (*service_init)(struct locked_hashmap *name_descriptions, struct bdd_internal_service *service, size_t n_arguments, char **arguments);
|
||||
char **supported_arguments;
|
||||
char *arguments_help;
|
||||
|
||||
bdd_io_id n_max_io;
|
||||
};
|
||||
|
||||
struct bdd_settings {
|
||||
int sv_socket;
|
||||
|
||||
struct hashmap *name_descriptions;
|
||||
|
||||
uint32_t client_timeout;
|
||||
|
||||
uint32_t buf_sz;
|
||||
bool use_stack_buf : 1, use_work_queues : 1;
|
||||
|
||||
int n_connections;
|
||||
int n_epoll_oevents;
|
||||
unsigned short int n_worker_threads;
|
||||
|
||||
sigset_t sigmask;
|
||||
};
|
||||
|
||||
enum bdd_service_type { bdd_service_type_none, bdd_service_type_internal, } __attribute__((packed));
|
||||
struct bdd_name_description {
|
||||
SSL_CTX *ssl_ctx;
|
||||
|
||||
enum bdd_service_type service_type;
|
||||
union {
|
||||
struct {
|
||||
struct bdd_internal_service *service;
|
||||
void *service_info;
|
||||
} internal;
|
||||
} service;
|
||||
};
|
||||
|
||||
__attribute__((warn_unused_result)) int bdd_poll(struct bdd_connections *connections, bdd_io_id io_id);
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_read(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz);
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_read_whole(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz);
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_write(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz);
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_write_whole(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz);
|
||||
|
||||
bool bdd_create_io(struct bdd_connections *connections, bdd_io_id *io_id, int *fd, char *ssl_name);
|
||||
void bdd_remove_io(struct bdd_connections *connections, bdd_io_id io_id);
|
||||
void bdd_set_associated(struct bdd_connections *connections, void *data, void (*destructor)(void *));
|
||||
void bdd_name_description_destroy(struct bdd_name_description *name_description);
|
||||
#define bdd_get_associated(connections) (connections->associated.data)
|
||||
|
||||
struct bdd_instance *bdd_go(struct bdd_settings settings);
|
||||
bool bdd_running(struct bdd_instance *instance);
|
||||
void bdd_wait(struct bdd_instance *instance);
|
||||
void bdd_stop(struct bdd_instance *instance);
|
||||
void bdd_destroy(struct bdd_instance *instance);
|
||||
|
||||
bool bdd_name_descriptions_set_internal_service(struct locked_hashmap *name_descriptions, char *name, size_t name_len, struct bdd_internal_service *service, void *service_info);
|
||||
bool bdd_name_descriptions_set_ssl_ctx(struct locked_hashmap *name_descriptions, char *name, size_t name_len, SSL_CTX *ssl_ctx);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,242 @@
|
|||
#include "internal.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((warn_unused_result)) int bdd_poll(struct bdd_connections *connections, bdd_io_id io_id) {
|
||||
assert(connections != NULL && io_id >= 0 && io_id < bdd_connections_n_max_io(connections));
|
||||
struct bdd_io *io = &(connections->io[io_id]);
|
||||
assert(io->fd != -1);
|
||||
struct pollfd pollfd = {
|
||||
.fd = io->fd,
|
||||
.events = POLLIN | POLLOUT | POLLRDHUP,
|
||||
.revents = 0,
|
||||
};
|
||||
poll(&(pollfd), 1, 0);
|
||||
if (io->ssl != NULL && SSL_has_pending(io->ssl)) {
|
||||
pollfd.revents |= POLLIN;
|
||||
}
|
||||
return pollfd.revents;
|
||||
}
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_read_internal(struct bdd_io *io, void *buf, ssize_t sz) {
|
||||
if (sz <= 0) {
|
||||
return 0;
|
||||
}
|
||||
ssize_t r = 0;
|
||||
do {
|
||||
if (io->ssl != NULL) {
|
||||
r = SSL_read(io->ssl, buf, sz);
|
||||
} else {
|
||||
r = recv(io->fd, buf, sz, 0);
|
||||
}
|
||||
} while (r < 0 && errno == EINTR);
|
||||
return r;
|
||||
}
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_read(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz) {
|
||||
assert(connections != NULL && io_id >= 0 && io_id < bdd_connections_n_max_io(connections));
|
||||
struct bdd_io *io = &(connections->io[io_id]);
|
||||
assert(io->fd != -1);
|
||||
return bdd_read_internal(io, buf, sz);
|
||||
}
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_read_whole(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz) {
|
||||
assert(connections != NULL && io_id >= 0 && io_id < bdd_connections_n_max_io(connections));
|
||||
struct bdd_io *io = &(connections->io[io_id]);
|
||||
assert(io->fd != -1);
|
||||
ssize_t n = 0;
|
||||
while (n < sz) {
|
||||
ssize_t r = bdd_read_internal(io, buf + n, sz - n);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
} else if (r == 0) {
|
||||
return n;
|
||||
}
|
||||
n += r;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_write_internal(struct bdd_io *io, void *buf, ssize_t sz) {
|
||||
if (sz <= 0) {
|
||||
return 0;
|
||||
}
|
||||
ssize_t r = 0;
|
||||
do {
|
||||
if (io->ssl != NULL) {
|
||||
r = SSL_write(io->ssl, buf, sz);
|
||||
} else {
|
||||
r = send(io->fd, buf, sz, 0);
|
||||
}
|
||||
} while (r < 0 && errno == EINTR);
|
||||
return r;
|
||||
}
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_write(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz) {
|
||||
assert(connections != NULL && io_id >= 0 && io_id < bdd_connections_n_max_io(connections));
|
||||
struct bdd_io *io = &(connections->io[io_id]);
|
||||
assert(io->fd != -1);
|
||||
return bdd_write_internal(io, buf, sz);
|
||||
}
|
||||
__attribute__((warn_unused_result)) ssize_t bdd_write_whole(struct bdd_connections *connections, bdd_io_id io_id, void *buf, ssize_t sz) {
|
||||
assert(connections != NULL && io_id >= 0 && io_id < bdd_connections_n_max_io(connections));
|
||||
struct bdd_io *io = &(connections->io[io_id]);
|
||||
assert(io->fd != -1);
|
||||
ssize_t n = 0;
|
||||
while (n < sz) {
|
||||
ssize_t r = bdd_write_internal(io, buf + n, sz - n);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
} else if (r == 0) {
|
||||
return n;
|
||||
}
|
||||
n += r;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void bdd_set_associated(struct bdd_connections *connections, void *data, void (*destructor)(void *)) {
|
||||
assert(connections != NULL);
|
||||
if (connections->associated.destructor != NULL) {
|
||||
connections->associated.destructor(connections->associated.data);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
if (data != NULL || destructor != NULL) {
|
||||
assert(data != NULL && destructor != NULL);
|
||||
}
|
||||
#endif
|
||||
connections->associated.data = data;
|
||||
connections->associated.destructor = destructor;
|
||||
return;
|
||||
}
|
||||
bool bdd_create_io(struct bdd_connections *connections, bdd_io_id *io_id, int *fd, char *ssl_name) {
|
||||
assert(connections != NULL && io_id != NULL);
|
||||
struct bdd_io *io = NULL;
|
||||
bdd_io_id idx = 0;
|
||||
for (; idx < bdd_connections_n_max_io(connections); ++idx) {
|
||||
if (connections->io[idx].fd == -1) {
|
||||
io = &(connections->io[idx]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(io != NULL);
|
||||
if (unlikely(io == NULL)) {
|
||||
return false;
|
||||
}
|
||||
SSL *ssl = NULL;
|
||||
if (ssl_name != NULL) {
|
||||
// i think it's not finna write to the ctx, so a global mutex lock is not required here
|
||||
// also, BDD_GLOBAL_CL_SSL_CTX is guaranteed to be valid here
|
||||
if ((ssl = SSL_new(BDD_GLOBAL_CL_SSL_CTX)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (unlikely(!SSL_set_fd(ssl, (*fd)))) {
|
||||
SSL_free(ssl);
|
||||
return false;
|
||||
}
|
||||
SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
if (unlikely(!SSL_set_tlsext_host_name(ssl, ssl_name) || !SSL_set1_host(ssl, ssl_name))) {
|
||||
SSL_free(ssl);
|
||||
return false;
|
||||
}
|
||||
SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
||||
if (SSL_connect(ssl) != 1) {
|
||||
SSL_free(ssl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
io->fd = (*fd);
|
||||
io->ssl = ssl;
|
||||
(*fd) = -1;
|
||||
(*io_id) = idx;
|
||||
return true;
|
||||
}
|
||||
void bdd_remove_io(struct bdd_connections *connections, bdd_io_id io_id) {
|
||||
assert(connections != NULL && io_id >= 0 && io_id < bdd_connections_n_max_io(connections));
|
||||
struct bdd_io *io = &(connections->io[io_id]);
|
||||
assert(io->fd >= 0);
|
||||
if (io->ssl != NULL) {
|
||||
SSL_shutdown(io->ssl);
|
||||
SSL_free(io->ssl);
|
||||
} else {
|
||||
shutdown(io->fd, SHUT_RDWR);
|
||||
}
|
||||
close(io->fd);
|
||||
io->fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
enum bdd_connections_init_status bdd_connections_init(struct bdd_connections *connections, SSL **client_ssl, struct sockaddr client_sockaddr, const struct bdd_internal_service *service, void *service_info) {
|
||||
assert(service->n_max_io > 0);
|
||||
if ((connections->io = malloc(sizeof(struct bdd_io) * service->n_max_io)) == NULL) {
|
||||
return bdd_connections_init_failed;
|
||||
}
|
||||
connections->service = service;
|
||||
connections->io[0].fd = SSL_get_fd((*client_ssl));
|
||||
connections->io[0].ssl = (*client_ssl);
|
||||
(*client_ssl) = NULL;
|
||||
for (bdd_io_id idx = 1; idx < service->n_max_io; ++idx) {
|
||||
connections->io[idx].fd = -1;
|
||||
connections->io[idx].ssl = NULL;
|
||||
}
|
||||
if (service->connections_init != NULL && !service->connections_init(connections, service_info, 0, client_sockaddr)) {
|
||||
return bdd_connections_init_failed_wants_deinit;
|
||||
}
|
||||
return bdd_connections_init_success;
|
||||
}
|
||||
void bdd_connections_deinit(struct bdd_connections *connections) {
|
||||
if (connections->io != NULL) {
|
||||
for (bdd_io_id io_id = 0; io_id < bdd_connections_n_max_io(connections); ++io_id) {
|
||||
struct bdd_io *io = &(connections->io[io_id]);
|
||||
if (io->fd < 0) {
|
||||
continue;
|
||||
}
|
||||
bdd_remove_io(connections, io_id);
|
||||
}
|
||||
free(connections->io);
|
||||
connections->io = NULL;
|
||||
}
|
||||
bdd_set_associated(connections, NULL, NULL);
|
||||
connections->working = false;
|
||||
connections->broken = false;
|
||||
return;
|
||||
}
|
||||
void bdd_connections_link(struct bdd_instance *instance, struct bdd_connections **_connections) {
|
||||
assert(_connections != NULL);
|
||||
struct bdd_connections *connections = (*_connections);
|
||||
assert(connections != NULL);
|
||||
pthread_mutex_lock(&(instance->linked_connections.mutex));
|
||||
connections->next = instance->linked_connections.head;
|
||||
instance->linked_connections.head = connections;
|
||||
bdd_signal(instance);
|
||||
pthread_mutex_unlock(&(instance->linked_connections.mutex));
|
||||
(*_connections) = NULL;
|
||||
return;
|
||||
}
|
||||
struct bdd_connections *bdd_connections_obtain(struct bdd_instance *instance) {
|
||||
if (instance->connections.n_connections <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
struct bdd_connections *connections = NULL;
|
||||
pthread_mutex_lock(&(instance->connections.available_mutex));
|
||||
while (!atomic_load(&(instance->exiting)) && instance->connections.available_idx == instance->connections.n_connections) {
|
||||
pthread_cond_wait(&(instance->connections.available_cond), &(instance->connections.available_mutex));
|
||||
}
|
||||
if (!atomic_load(&(instance->exiting))) {
|
||||
connections = &(instance->connections.connections[instance->connections.available[instance->connections.available_idx++]]);
|
||||
}
|
||||
pthread_mutex_unlock(&(instance->connections.available_mutex));
|
||||
return connections;
|
||||
}
|
||||
void bdd_connections_release(struct bdd_instance *instance, struct bdd_connections **_connections) {
|
||||
assert(_connections != NULL);
|
||||
struct bdd_connections *connections = (*_connections);
|
||||
assert(connections != NULL);
|
||||
pthread_mutex_lock(&(instance->connections.available_mutex));
|
||||
assert(instance->connections.available_idx != 0);
|
||||
int id = bdd_connections_id(instance, connections);
|
||||
assert(id >= 0 && id < instance->connections.n_connections);
|
||||
instance->connections.available[--(instance->connections.available_idx)] = id;
|
||||
pthread_cond_signal(&(instance->connections.available_cond));
|
||||
pthread_mutex_unlock(&(instance->connections.available_mutex));
|
||||
(*_connections) = NULL;
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#if !defined(NDEBUG) && defined(BIDIRECTIOND_VERBOSE_DEBUG_LOG)
|
||||
#include <stdio.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int bdd_vdl_SSL_write(void *x, char *data, size_t len) {
|
||||
if (len == 0) {
|
||||
puts("SSL_write len is 0?");
|
||||
}
|
||||
for (size_t idx = 0; idx < len; ++idx) {
|
||||
if (data[idx] >= 0x20 && data[idx] <= 0x7e) {
|
||||
putc(data[idx], stdout);
|
||||
} else {
|
||||
printf("\\x%02x", data[idx]);
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
return SSL_write(x, data, len);
|
||||
}
|
||||
int bdd_vdl_send(int a, char *b, size_t c, int _) {
|
||||
if (c == 0) {
|
||||
puts("send len is 0?");
|
||||
}
|
||||
for (size_t idx = 0; idx < c; ++idx) {
|
||||
if (b[idx] >= 0x20 && b[idx] <= 0x7e) {
|
||||
putc(b[idx], stdout);
|
||||
} else {
|
||||
printf("\\x%02x", b[idx]);
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
return send(a, b, c, _);
|
||||
}
|
||||
int bdd_vdl_pthread_mutex_lock(void *_, char *name, int ln) {
|
||||
printf("%p (%s) lock attempt @ %i!\n", _, name, ln);
|
||||
int x = pthread_mutex_lock(_);
|
||||
printf("%p (%s) lock @ %i!\n", _, name, ln);
|
||||
return x;
|
||||
}
|
||||
int bdd_vdl_pthread_mutex_trylock(void *_, char *name, int ln) {
|
||||
printf("%p (%s) lock attempt @ %i!\n", _, name, ln);
|
||||
int x = pthread_mutex_trylock(_);
|
||||
if (x == 0) {
|
||||
printf("%p (%s) lock @ %i!\n", _, name, ln);
|
||||
} else {
|
||||
printf("%p (%s) trylock failed @ %i!\n", _, name, ln);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
int bdd_vdl_pthread_mutex_unlock(void *_, char *name, int ln) {
|
||||
int x = pthread_mutex_unlock(_);
|
||||
printf("%p (%s) unlock @ %i!\n", _, name, ln);
|
||||
return x;
|
||||
}
|
||||
int bdd_vdl_pthread_cond_wait(void *_, void *__, char *name, int ln) {
|
||||
printf("%p (%s) unlock @ %i!\n", _, name, ln);
|
||||
int x = pthread_cond_wait(_, __);
|
||||
printf("%p (%s) lock @ %i!\n", _, name, ln);
|
||||
return x;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,351 @@
|
|||
#include "internal.h"
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// fuck this whole file
|
||||
|
||||
struct bdd_instance *bdd_instance_alloc(void) {
|
||||
struct bdd_instance *instance = malloc(sizeof(struct bdd_instance));
|
||||
if (instance == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// exiting
|
||||
atomic_store(&(instance->exiting), false);
|
||||
// running threads
|
||||
instance->n_running_threads = 0;
|
||||
bdd_mutex_preinit(&(instance->n_running_threads_mutex));
|
||||
bdd_cond_preinit(&(instance->n_running_threads_cond));
|
||||
// epoll
|
||||
instance->epoll_fd = -1;
|
||||
instance->n_epoll_oevents = 0;
|
||||
instance->epoll_oevents = NULL;
|
||||
// name_descriptions
|
||||
instance->name_descriptions = NULL;
|
||||
// client timeout
|
||||
instance->client_timeout.tv_sec = 0;
|
||||
instance->client_timeout.tv_usec = 0;
|
||||
// server socket
|
||||
instance->sv_socket = -1;
|
||||
// connections
|
||||
instance->connections.n_connections = 0;
|
||||
instance->connections.connections = NULL;
|
||||
instance->connections.connections_idx = 0;
|
||||
instance->connections.available = NULL;
|
||||
instance->connections.available_idx = 0;
|
||||
bdd_mutex_preinit(&(instance->connections.available_mutex));
|
||||
bdd_cond_preinit(&(instance->connections.available_cond));
|
||||
// linked connections
|
||||
bdd_mutex_preinit(&(instance->linked_connections.mutex));
|
||||
// accept thread stuff
|
||||
instance->accept.eventfd = -1;
|
||||
for (uint8_t idx = 0; idx < 2; ++idx) {
|
||||
instance->accept.pollfds[idx].fd = -1;
|
||||
instance->accept.pollfds[idx].events = 0;
|
||||
instance->accept.pollfds[idx].revents = 0;
|
||||
}
|
||||
instance->accept.ssl_ctx = NULL;
|
||||
instance->accept.accept_ctx.service_name_description = NULL;
|
||||
instance->accept.accept_ctx.locked_name_descriptions = NULL;
|
||||
instance->linked_connections.head = NULL;
|
||||
// serve_eventfd
|
||||
instance->serve_eventfd = -1;
|
||||
// workers
|
||||
bdd_mutex_preinit(&(instance->workers.available_stack.mutex));
|
||||
bdd_cond_preinit(&(instance->workers.available_stack.cond));
|
||||
instance->workers.available_stack.ids = NULL;
|
||||
instance->workers.available_stack.idx = 0;
|
||||
instance->workers.n_workers = 0;
|
||||
instance->workers.info = NULL;
|
||||
instance->workers.info_idx = 0;
|
||||
instance->workers.buf = NULL;
|
||||
instance->workers.buf_sz_per_worker = 0;
|
||||
return instance;
|
||||
}
|
||||
|
||||
struct bdd_instance *bdd_go(struct bdd_settings settings) {
|
||||
if (settings.sv_socket < 0 || settings.buf_sz == 0 || settings.n_connections < 0 || settings.n_epoll_oevents < 0 || settings.name_descriptions == NULL ||
|
||||
(
|
||||
(settings.n_connections == 0 || settings.n_worker_threads == 0 || settings.n_epoll_oevents == 0) &&
|
||||
(settings.n_connections != 0 || settings.n_worker_threads != 0 || settings.n_epoll_oevents != 0)
|
||||
)) {
|
||||
return NULL;
|
||||
}
|
||||
bool uses_internal_services = settings.n_connections != 0;
|
||||
|
||||
struct bdd_instance *instance = bdd_instance_alloc();
|
||||
if (instance == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&(instance->n_running_threads_mutex), NULL) != 0) {
|
||||
free(instance);
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_cond_init(&(instance->n_running_threads_cond), NULL) != 0) {
|
||||
pthread_mutex_destroy(&(instance->n_running_threads_mutex));
|
||||
free(instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct bdd_instance *ret = (struct bdd_instance *)instance;
|
||||
{
|
||||
while (atomic_flag_test_and_set(&(BDD_GLOBAL_MUTEX)));
|
||||
if (BDD_GLOBAL_RC == 0) {
|
||||
if ((BDD_GLOBAL_CL_SSL_CTX = SSL_CTX_new(TLS_client_method())) == NULL) {
|
||||
atomic_flag_clear(&(BDD_GLOBAL_MUTEX));
|
||||
free(instance);
|
||||
return NULL;
|
||||
}
|
||||
SSL_CTX_set_mode(BDD_GLOBAL_CL_SSL_CTX, SSL_MODE_RELEASE_BUFFERS);
|
||||
SSL_CTX_set_options(BDD_GLOBAL_CL_SSL_CTX, SSL_OP_NO_COMPRESSION);
|
||||
}
|
||||
if ((BDD_GLOBAL_RC += 1) <= 0) {
|
||||
atomic_flag_clear(&(BDD_GLOBAL_MUTEX));
|
||||
goto bdd_go__err;
|
||||
}
|
||||
atomic_flag_clear(&(BDD_GLOBAL_MUTEX));
|
||||
}
|
||||
|
||||
// sigmask
|
||||
instance->sigmask = settings.sigmask;
|
||||
if (uses_internal_services) {
|
||||
// epoll
|
||||
if ((instance->epoll_fd = epoll_create1(0)) < 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
instance->n_epoll_oevents = settings.n_epoll_oevents;
|
||||
if ((instance->epoll_oevents = malloc(sizeof(struct epoll_event) * settings.n_epoll_oevents)) == NULL) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
}
|
||||
// name_descriptions
|
||||
instance->name_descriptions = settings.name_descriptions;
|
||||
// client timeout
|
||||
instance->client_timeout.tv_sec = (settings.client_timeout / 1000);
|
||||
instance->client_timeout.tv_usec = (settings.client_timeout % 1000) * 1000;
|
||||
// server socket
|
||||
instance->sv_socket = settings.sv_socket;
|
||||
if (uses_internal_services) {
|
||||
// connections
|
||||
instance->connections.n_connections = settings.n_connections;
|
||||
if ((instance->connections.connections = malloc(
|
||||
(settings.n_connections * sizeof(struct bdd_connections)) +
|
||||
(settings.n_connections * sizeof(int))
|
||||
)) == NULL) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
// available stack
|
||||
instance->connections.available = (void *)&(instance->connections.connections[settings.n_connections]);
|
||||
instance->connections.available_idx = 0;
|
||||
if (pthread_mutex_init(&(instance->connections.available_mutex), NULL) != 0 || pthread_cond_init(&(instance->connections.available_cond), NULL) != 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
// init connections, and the available stack
|
||||
for (int *idx = &(instance->connections.connections_idx); (*idx) < settings.n_connections; ++(*idx)) {
|
||||
if (pthread_mutex_init(&(instance->connections.connections[(*idx)].working_mutex), NULL) != 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
instance->connections.connections[(*idx)].associated.data = NULL;
|
||||
instance->connections.connections[(*idx)].associated.destructor = NULL;
|
||||
instance->connections.connections[(*idx)].io = NULL;
|
||||
instance->connections.connections[(*idx)].working = false;
|
||||
instance->connections.connections[(*idx)].broken = false;
|
||||
instance->connections.available[(*idx)] = (*idx);
|
||||
}
|
||||
// linked connections
|
||||
if (pthread_mutex_init(&(instance->linked_connections.mutex), NULL) != 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
instance->linked_connections.head = NULL;
|
||||
}
|
||||
// accept
|
||||
if ((instance->accept.eventfd = eventfd(0, EFD_NONBLOCK)) < 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
instance->accept.pollfds[0].fd = settings.sv_socket;
|
||||
instance->accept.pollfds[0].events = POLLIN;
|
||||
instance->accept.pollfds[1].fd = instance->accept.eventfd;
|
||||
instance->accept.pollfds[1].events = POLLIN;
|
||||
if ((instance->accept.ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL || SSL_CTX_set_cipher_list(instance->accept.ssl_ctx, "AES256-SHA256") == 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
SSL_CTX_set_tlsext_servername_callback(instance->accept.ssl_ctx, &(bdd_use_correct_ctx));
|
||||
SSL_CTX_set_tlsext_servername_arg(instance->accept.ssl_ctx, &(instance->accept.accept_ctx));
|
||||
if (uses_internal_services) {
|
||||
// serve
|
||||
if ((instance->serve_eventfd = eventfd(0, EFD_NONBLOCK)) < 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
struct epoll_event event = {
|
||||
.events = EPOLLIN,
|
||||
.data = {
|
||||
.ptr = NULL,
|
||||
},
|
||||
};
|
||||
if (epoll_ctl(instance->epoll_fd, EPOLL_CTL_ADD, instance->serve_eventfd, &(event)) != 0) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
// workers
|
||||
if (!settings.use_work_queues) {
|
||||
if ((instance->workers.available_stack.ids = malloc(settings.n_worker_threads * sizeof(unsigned short int))) == NULL) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
instance->workers.available_stack.idx = settings.n_worker_threads;
|
||||
}
|
||||
if ((instance->workers.buf = malloc(settings.buf_sz * settings.n_worker_threads)) == NULL) {
|
||||
goto bdd_go__err;
|
||||
}
|
||||
instance->workers.buf_sz_per_worker = settings.buf_sz;
|
||||
}
|
||||
{
|
||||
pthread_mutex_lock(&(instance->n_running_threads_mutex));
|
||||
bool e = false;
|
||||
pthread_t pthid;
|
||||
if (pthread_create(&(pthid), NULL, (void *(*)(void *))(&(bdd_accept)), instance) != 0) {
|
||||
e = true;
|
||||
} else {
|
||||
instance->n_running_threads += 1;
|
||||
}
|
||||
if (uses_internal_services) {
|
||||
if (!e) {
|
||||
if (pthread_create(&(pthid), NULL, (void *(*)(void *))(&(bdd_serve)), instance) != 0) {
|
||||
e = true;
|
||||
} else {
|
||||
instance->n_running_threads += 1;
|
||||
}
|
||||
}
|
||||
struct bdd_worker *workers;
|
||||
if (!e && (workers = malloc(sizeof(struct bdd_worker) * settings.n_worker_threads)) == NULL) {
|
||||
e = true;
|
||||
}
|
||||
instance->workers.info = workers;
|
||||
for (unsigned short int *idx = &(instance->workers.n_workers); !e && (*idx) < settings.n_worker_threads; ++(*idx)) {
|
||||
(*((struct bdd_instance **)&(workers[(*idx)].instance))) = instance;
|
||||
if (pthread_mutex_init(&(workers[(*idx)].work_mutex), NULL) != 0) {
|
||||
e = true;
|
||||
}
|
||||
if (pthread_cond_init(&(workers[(*idx)].work_cond), NULL) != 0) {
|
||||
pthread_mutex_destroy(&(workers[(*idx)].work_mutex));
|
||||
e = true;
|
||||
}
|
||||
workers[(*idx)].id = (*idx);
|
||||
workers[(*idx)].connections = NULL;
|
||||
workers[(*idx)].connections_appender = NULL;
|
||||
if (pthread_create(&(pthid), NULL, (void *(*)(void *))(&(bdd_worker)), &(workers[(*idx)])) != 0) {
|
||||
e = true;
|
||||
} else {
|
||||
instance->n_running_threads += 1;
|
||||
}
|
||||
}
|
||||
if (e) {
|
||||
pthread_mutex_unlock(&(instance->n_running_threads_mutex));
|
||||
goto bdd_go__err;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&(instance->n_running_threads_mutex));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
bdd_go__err:;
|
||||
bdd_stop(ret);
|
||||
bdd_wait(ret);
|
||||
bdd_destroy(ret);
|
||||
return NULL;
|
||||
}
|
||||
void bdd_stop(struct bdd_instance *instance) {
|
||||
atomic_store(&(instance->exiting), true);
|
||||
if (instance->accept.eventfd != -1) {
|
||||
bdd_stop_accept(instance);
|
||||
}
|
||||
if (instance->serve_eventfd != -1) {
|
||||
bdd_signal(instance);
|
||||
}
|
||||
for (unsigned short int idx = 0; idx < instance->workers.n_workers; ++idx) {
|
||||
pthread_mutex_lock(&(instance->workers.info[idx].work_mutex));
|
||||
pthread_cond_signal(&(instance->workers.info[idx].work_cond));
|
||||
pthread_mutex_unlock(&(instance->workers.info[idx].work_mutex));
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool bdd_running(struct bdd_instance *instance) {
|
||||
pthread_mutex_lock(&(instance->n_running_threads_mutex));
|
||||
bool running = instance->n_running_threads != 0;
|
||||
pthread_mutex_unlock(&(instance->n_running_threads_mutex));
|
||||
return running;
|
||||
}
|
||||
void bdd_wait(struct bdd_instance *instance) {
|
||||
if (pthread_mutex_lock(&(instance->n_running_threads_mutex)) != 0) {
|
||||
return;
|
||||
}
|
||||
while (instance->n_running_threads != 0) {
|
||||
pthread_cond_wait(&(instance->n_running_threads_cond), &(instance->n_running_threads_mutex));
|
||||
}
|
||||
pthread_mutex_unlock(&(instance->n_running_threads_mutex));
|
||||
return;
|
||||
}
|
||||
void bdd_destroy(struct bdd_instance *instance) {
|
||||
pthread_mutex_destroy(&(instance->n_running_threads_mutex));
|
||||
pthread_cond_destroy(&(instance->n_running_threads_cond));
|
||||
|
||||
if (instance->epoll_fd >= 0) {
|
||||
close(instance->epoll_fd);
|
||||
}
|
||||
if (instance->epoll_oevents != NULL) {
|
||||
free(instance->epoll_oevents);
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&(instance->connections.available_mutex));
|
||||
pthread_cond_destroy(&(instance->connections.available_cond));
|
||||
|
||||
for (int idx = 0; idx < instance->connections.n_connections; ++idx) {
|
||||
bdd_connections_deinit(&(instance->connections.connections[idx]));
|
||||
pthread_mutex_destroy(&(instance->connections.connections[idx].working_mutex));
|
||||
}
|
||||
|
||||
if (instance->connections.connections != NULL) {
|
||||
free(instance->connections.connections);
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&(instance->linked_connections.mutex));
|
||||
|
||||
if (instance->accept.eventfd >= 0) {
|
||||
close(instance->accept.eventfd);
|
||||
}
|
||||
if (instance->accept.ssl_ctx != NULL) {
|
||||
SSL_CTX_free(instance->accept.ssl_ctx);
|
||||
}
|
||||
|
||||
if (instance->serve_eventfd >= 0) {
|
||||
close(instance->serve_eventfd);
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&(instance->workers.available_stack.mutex));
|
||||
pthread_cond_destroy(&(instance->workers.available_stack.cond));
|
||||
if (instance->workers.available_stack.ids != NULL) {
|
||||
free(instance->workers.available_stack.ids);
|
||||
}
|
||||
|
||||
for (unsigned short int idx = 0; idx < instance->workers.n_workers; ++idx) {
|
||||
pthread_mutex_destroy(&(instance->workers.info[idx].work_mutex));
|
||||
pthread_cond_destroy(&(instance->workers.info[idx].work_cond));
|
||||
}
|
||||
|
||||
if (instance->workers.buf != NULL) {
|
||||
free(instance->workers.buf);
|
||||
}
|
||||
if (instance->workers.info != NULL) {
|
||||
free(instance->workers.info);
|
||||
}
|
||||
|
||||
free(instance);
|
||||
|
||||
while (atomic_flag_test_and_set(&(BDD_GLOBAL_MUTEX)));
|
||||
if (--BDD_GLOBAL_RC == 0) {
|
||||
SSL_CTX_free(BDD_GLOBAL_CL_SSL_CTX);
|
||||
}
|
||||
atomic_flag_clear(&(BDD_GLOBAL_MUTEX));
|
||||
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
#ifndef bidirectiond_core__internal__h
|
||||
#define bidirectiond_core__internal__h
|
||||
|
||||
#include "api.h"
|
||||
|
||||
extern atomic_flag BDD_GLOBAL_MUTEX;
|
||||
extern size_t BDD_GLOBAL_RC;
|
||||
extern SSL_CTX *BDD_GLOBAL_CL_SSL_CTX;
|
||||
|
||||
void bdd_mutex_preinit(pthread_mutex_t *dest);
|
||||
void bdd_cond_preinit(pthread_cond_t *dest);
|
||||
|
||||
// my justification for the following shit is: my ram usage is already kinda pushing it, so i'd like to save some heap space in exchange for a few extra instructions
|
||||
#define bdd_connections_n_max_io(c) (c->service->n_max_io)
|
||||
#define bdd_connections_id(instance, c) (((char *)c - (char *)(instance->connections.connections)) / sizeof(struct bdd_connections))
|
||||
|
||||
struct bdd_worker {
|
||||
struct bdd_instance *const instance;
|
||||
unsigned short int id;
|
||||
pthread_mutex_t work_mutex;
|
||||
pthread_cond_t work_cond;
|
||||
struct bdd_connections *connections;
|
||||
struct bdd_connections **connections_appender;
|
||||
};
|
||||
struct bdd_workers {
|
||||
struct {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
unsigned short int *ids;
|
||||
unsigned short int idx;
|
||||
} available_stack;
|
||||
unsigned short int n_workers;
|
||||
struct bdd_worker *info;
|
||||
unsigned short int info_idx;
|
||||
void *buf;
|
||||
size_t buf_sz_per_worker;
|
||||
};
|
||||
|
||||
struct bdd_accept_ctx {
|
||||
struct bdd_name_description *service_name_description;
|
||||
struct locked_hashmap *locked_name_descriptions;
|
||||
};
|
||||
|
||||
struct bdd_instance {
|
||||
sigset_t sigmask;
|
||||
|
||||
atomic_bool exiting;
|
||||
|
||||
int n_running_threads;
|
||||
pthread_mutex_t n_running_threads_mutex;
|
||||
pthread_cond_t n_running_threads_cond;
|
||||
|
||||
int epoll_fd;
|
||||
int n_epoll_oevents;
|
||||
struct epoll_event *epoll_oevents;
|
||||
|
||||
void *name_descriptions;
|
||||
|
||||
struct timeval client_timeout;
|
||||
|
||||
int sv_socket;
|
||||
|
||||
struct {
|
||||
int n_connections;
|
||||
|
||||
struct bdd_connections *connections;
|
||||
int connections_idx;
|
||||
|
||||
// stack
|
||||
int *available;
|
||||
int available_idx;
|
||||
pthread_mutex_t available_mutex;
|
||||
pthread_cond_t available_cond;
|
||||
} connections;
|
||||
struct {
|
||||
pthread_mutex_t mutex;
|
||||
struct bdd_connections *head;
|
||||
} linked_connections;
|
||||
|
||||
struct {
|
||||
int eventfd;
|
||||
struct pollfd pollfds[2];
|
||||
SSL_CTX *ssl_ctx;
|
||||
struct bdd_accept_ctx accept_ctx;
|
||||
} accept;
|
||||
|
||||
int serve_eventfd;
|
||||
|
||||
struct bdd_workers workers;
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define BDD_DEBUG_LOG(x...) (0)
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#ifdef BIDIRECTIOND_VERBOSE_DEBUG_LOG
|
||||
int bdd_vdl_SSL_write(void *x, char *data, size_t len);
|
||||
int bdd_vdl_send(int a, char *b, size_t c, int _);
|
||||
#define SSL_write bdd_vdl_SSL_write
|
||||
#define send bdd_vdl_send
|
||||
int bdd_vdl_pthread_mutex_lock(void *_, char *name, int ln);
|
||||
int bdd_vdl_pthread_mutex_unlock(void *_, char *name, int ln);
|
||||
int bdd_vdl_pthread_cond_wait(void *_, void *__, char *name, int ln);
|
||||
int bdd_vdl_pthread_mutex_trylock(void *_, char *name, int ln);
|
||||
#define pthread_mutex_lock(x) bdd_vdl_pthread_mutex_lock(x, #x, __LINE__)
|
||||
#define pthread_mutex_unlock(x) bdd_vdl_pthread_mutex_unlock(x, #x, __LINE__)
|
||||
#define pthread_cond_wait(_, __) bdd_vdl_pthread_cond_wait(_, __, #__, __LINE__)
|
||||
#define pthread_mutex_trylock(x) bdd_vdl_pthread_mutex_trylock(x, #x, __LINE__)
|
||||
#endif
|
||||
#define BDD_DEBUG_LOG(string, args...) (printf("[DEBUG (%p)] "string, (void *)pthread_self(), ##args), fflush(stdout))
|
||||
#endif
|
||||
|
||||
enum bdd_connections_init_status { bdd_connections_init_success, bdd_connections_init_failed_wants_deinit, bdd_connections_init_failed, } __attribute__((packed));
|
||||
enum bdd_connections_init_status bdd_connections_init(struct bdd_connections *connections, SSL **client_ssl, struct sockaddr client_sockaddr, const struct bdd_internal_service *service, void *service_info);
|
||||
struct bdd_connections *bdd_connections_obtain(struct bdd_instance *instance);
|
||||
void bdd_connections_release(struct bdd_instance *instance, struct bdd_connections **connections);
|
||||
void bdd_connections_deinit(struct bdd_connections *connections);
|
||||
void bdd_connections_link(struct bdd_instance *instance, struct bdd_connections **connections);
|
||||
void bdd_signal(struct bdd_instance *instance);
|
||||
|
||||
int bdd_use_correct_ctx(SSL *client_ssl, int *_, struct bdd_accept_ctx *ctx);
|
||||
|
||||
void *bdd_serve(struct bdd_instance *instance);
|
||||
void *bdd_accept(struct bdd_instance *instance);
|
||||
void *bdd_worker(struct bdd_worker *worker);
|
||||
|
||||
void bdd_stop_accept(struct bdd_instance *instance);
|
||||
|
||||
void bdd_thread_exit(struct bdd_instance *instance);
|
||||
|
||||
#ifndef unlikely
|
||||
#define unlikely(x) __builtin_expect((x), 0)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
#include <openssl/ssl.h>
|
||||
atomic_flag BDD_GLOBAL_MUTEX = ATOMIC_FLAG_INIT;
|
||||
size_t BDD_GLOBAL_RC = 0;
|
||||
SSL_CTX *BDD_GLOBAL_CL_SSL_CTX = NULL;
|
|
@ -0,0 +1,79 @@
|
|||
#include "internal.h"
|
||||
|
||||
// struct bdd_name_description
|
||||
struct bdd_name_description *bdd_name_description_alloc(void) {
|
||||
struct bdd_name_description *name_description = malloc(sizeof(struct bdd_name_description));
|
||||
if (name_description == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
name_description->ssl_ctx = NULL;
|
||||
name_description->service_type = bdd_service_type_none;
|
||||
return name_description;
|
||||
}
|
||||
void bdd_name_description_clean_ssl_ctx(struct bdd_name_description *name_description) {
|
||||
if (name_description->ssl_ctx != NULL) {
|
||||
SSL_CTX_free(name_description->ssl_ctx); // misleading function name; it actually decs the ref count and frees the ssl_ctx if the rc hits 0
|
||||
name_description->ssl_ctx = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
void bdd_name_description_clean_service(struct bdd_name_description *name_description) {
|
||||
if (name_description->service_type == bdd_service_type_internal && name_description->service.internal.service->service_info_destructor != NULL) {
|
||||
name_description->service.internal.service->service_info_destructor(name_description->service.internal.service_info);
|
||||
}
|
||||
name_description->service_type = bdd_service_type_none;
|
||||
return;
|
||||
}
|
||||
void bdd_name_description_set_internal_service(struct bdd_name_description *name_description, struct bdd_internal_service *service, void *service_info) {
|
||||
bdd_name_description_clean_service(name_description);
|
||||
name_description->service_type = bdd_service_type_internal;
|
||||
name_description->service.internal.service = service;
|
||||
name_description->service.internal.service_info = service_info;
|
||||
return;
|
||||
}
|
||||
void bdd_name_description_set_ssl_ctx(struct bdd_name_description *name_description, SSL_CTX *ssl_ctx) {
|
||||
bdd_name_description_clean_ssl_ctx(name_description);
|
||||
name_description->ssl_ctx = ssl_ctx;
|
||||
return;
|
||||
}
|
||||
void bdd_name_description_destroy(struct bdd_name_description *name_description) {
|
||||
bdd_name_description_clean_service(name_description);
|
||||
bdd_name_description_clean_ssl_ctx(name_description);
|
||||
free(name_description);
|
||||
return;
|
||||
}
|
||||
|
||||
// name_descriptions hashmap
|
||||
#define bdd_name_descriptions() \
|
||||
if (name_len == 0 || name_len > 254 || (name_len == 254 && name[253] != '.')) { \
|
||||
return false; \
|
||||
} \
|
||||
if (name[name_len - 1] == '.') { \
|
||||
if ((name_len -= 1) == 0) { \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
struct bdd_name_description *name_description = locked_hashmap_get_wl(name_descriptions, name, name_len); \
|
||||
if (name_description == NULL) { \
|
||||
if ((name_description = bdd_name_description_alloc()) == NULL) { \
|
||||
return false; \
|
||||
} \
|
||||
if (!locked_hashmap_set_wl(name_descriptions, name, name_len, name_description, 1)) { \
|
||||
bdd_name_description_destroy(name_description); \
|
||||
return false; \
|
||||
} \
|
||||
}
|
||||
bool bdd_name_descriptions_set_internal_service(struct locked_hashmap *name_descriptions, char *name, size_t name_len, struct bdd_internal_service *service, void *service_info) {
|
||||
bdd_name_descriptions();
|
||||
|
||||
bdd_name_description_set_internal_service(name_description, service, service_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
bool bdd_name_descriptions_set_ssl_ctx(struct locked_hashmap *name_descriptions, char *name, size_t name_len, SSL_CTX *ssl_ctx) {
|
||||
bdd_name_descriptions();
|
||||
|
||||
bdd_name_description_set_ssl_ctx(name_description, ssl_ctx);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <string.h>
|
||||
|
||||
void bdd_mutex_preinit(pthread_mutex_t *dest) {
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
memcpy(dest, &(mutex), sizeof(pthread_mutex_t));
|
||||
}
|
||||
|
||||
void bdd_cond_preinit(pthread_cond_t *dest) {
|
||||
static pthread_cond_t mutex = PTHREAD_COND_INITIALIZER;
|
||||
memcpy(dest, &(mutex), sizeof(pthread_cond_t));
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
#include "internal.h"
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <signal.h>
|
||||
|
||||
void *bdd_serve(struct bdd_instance *instance) {
|
||||
pthread_sigmask(SIG_BLOCK, &(instance->sigmask), NULL);
|
||||
unsigned short int next_worker_id = 0;
|
||||
struct bdd_workers *workers = &(instance->workers);
|
||||
bdd_serve__find_connections:;
|
||||
|
||||
BDD_DEBUG_LOG("linked_connections.head is %p\n", instance->linked_connections.head);
|
||||
BDD_DEBUG_LOG("polling\n");
|
||||
|
||||
int n_events;
|
||||
do {
|
||||
n_events = epoll_wait(instance->epoll_fd, instance->epoll_oevents, instance->n_epoll_oevents, -1);
|
||||
} while (n_events < 0 && errno == EINTR);
|
||||
if (unlikely(n_events < 0)) {
|
||||
fprintf(stderr, "bidirectiond epoll error: %i - try increasing your rlimits for open files\n", errno);
|
||||
bdd_stop(instance);
|
||||
bdd_thread_exit(instance);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&(instance->linked_connections.mutex));
|
||||
{
|
||||
char g[8];
|
||||
int r = read(instance->serve_eventfd, &(g), 8);
|
||||
assert(r == 8 || r < 0);
|
||||
}
|
||||
for (struct bdd_connections **connections = &(instance->linked_connections.head); (*connections) != NULL;) {
|
||||
struct bdd_connections *next = (*connections)->next;
|
||||
(*connections)->working = false;
|
||||
bool broken = true;
|
||||
if (!(*connections)->broken) for (bdd_io_id idx = 0; idx < bdd_connections_n_max_io((*connections)); ++idx) {
|
||||
int fd = (*connections)->io[idx].fd;
|
||||
if (fd < 0) {
|
||||
continue;
|
||||
}
|
||||
struct epoll_event event = {
|
||||
.events = EPOLLIN,
|
||||
.data = {
|
||||
.ptr = (*connections),
|
||||
},
|
||||
};
|
||||
if (epoll_ctl(instance->epoll_fd, EPOLL_CTL_ADD, fd, &(event)) != 0) {
|
||||
for (bdd_io_id idx2 = 0; idx2 < idx; ++idx2) {
|
||||
fd = (*connections)->io[idx2].fd;
|
||||
if (fd >= 0) {
|
||||
int r = epoll_ctl(instance->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
|
||||
assert(r == 0);
|
||||
}
|
||||
}
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
broken = false;
|
||||
}
|
||||
if (broken) {
|
||||
bdd_connections_deinit((*connections));
|
||||
bdd_connections_release(instance, connections);
|
||||
}
|
||||
(*connections) = next;
|
||||
}
|
||||
pthread_mutex_unlock(&(instance->linked_connections.mutex));
|
||||
|
||||
if (unlikely(atomic_load(&(instance->exiting)))) {
|
||||
bdd_thread_exit(instance);
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < n_events; ++idx) {
|
||||
struct epoll_event *event = &(instance->epoll_oevents[idx]);
|
||||
struct bdd_connections *connections = event->data.ptr;
|
||||
|
||||
if (connections == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (pthread_mutex_trylock(&(connections->working_mutex)) != 0) {
|
||||
continue;
|
||||
}
|
||||
bool already_working = connections->working;
|
||||
connections->working = true;
|
||||
pthread_mutex_unlock(&(connections->working_mutex));
|
||||
if (already_working) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool broken = false;
|
||||
for (bdd_io_id io_id = 0; io_id < bdd_connections_n_max_io(connections); ++io_id) {
|
||||
if (connections->io[io_id].fd < 0) {
|
||||
continue;
|
||||
}
|
||||
if (!broken) {
|
||||
short revents = bdd_poll(connections, io_id);
|
||||
if ((revents & (POLLERR | POLLHUP | POLLRDHUP)) && !(revents & POLLIN)) {
|
||||
broken = true;
|
||||
}
|
||||
assert(!(revents & POLLNVAL));
|
||||
}
|
||||
int r = epoll_ctl(instance->epoll_fd, EPOLL_CTL_DEL, connections->io[io_id].fd, NULL);
|
||||
assert(r == 0);
|
||||
}
|
||||
|
||||
if (broken) {
|
||||
BDD_DEBUG_LOG("found broken connections struct\n");
|
||||
bdd_connections_deinit(connections);
|
||||
bdd_connections_release(instance, &(connections));
|
||||
} else {
|
||||
BDD_DEBUG_LOG("found working connections struct\n");
|
||||
|
||||
struct bdd_worker *worker;
|
||||
if (workers->available_stack.ids == NULL) {
|
||||
worker = &(workers->info[next_worker_id]);
|
||||
next_worker_id = (next_worker_id + 1) % workers->n_workers;
|
||||
} else {
|
||||
pthread_mutex_lock(&(workers->available_stack.mutex));
|
||||
if (workers->available_stack.idx == workers->n_workers) {
|
||||
BDD_DEBUG_LOG("no available worker threads; waiting...\n");
|
||||
do {
|
||||
pthread_cond_wait(&(workers->available_stack.cond), &(workers->available_stack.mutex));
|
||||
} while (workers->available_stack.idx == workers->n_workers);
|
||||
}
|
||||
worker = &(workers->info[workers->available_stack.ids[(workers->available_stack.idx)++]]);
|
||||
pthread_mutex_unlock(&(workers->available_stack.mutex));
|
||||
}
|
||||
|
||||
BDD_DEBUG_LOG("worker thread %i chosen!\n", (int)worker->id);
|
||||
|
||||
pthread_mutex_lock(&(worker->work_mutex));
|
||||
if (worker->connections == NULL) {
|
||||
worker->connections_appender = &(worker->connections);
|
||||
}
|
||||
connections->next = NULL;
|
||||
(*(worker->connections_appender)) = connections;
|
||||
worker->connections_appender = &(connections->next);
|
||||
pthread_cond_signal(&(worker->work_cond));
|
||||
pthread_mutex_unlock(&(worker->work_mutex));
|
||||
}
|
||||
}
|
||||
|
||||
goto bdd_serve__find_connections;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#include "internal.h"
|
||||
#include <unistd.h>
|
||||
|
||||
void bdd_signal(struct bdd_instance *instance) {
|
||||
char buf[8] = { ~0, 0, 0, 0, 0, 0, 0, ~0, };
|
||||
int r = write(instance->serve_eventfd, (void *)buf, 8);
|
||||
assert(r == 8 || r < 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void bdd_stop_accept(struct bdd_instance *instance) {
|
||||
char buf[8] = { ~0, 0, 0, 0, 0, 0, 0, ~0, };
|
||||
int r = write(instance->accept.eventfd, (void *)buf, 8);
|
||||
assert(r == 8 || r < 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void bdd_thread_exit(struct bdd_instance *instance) {
|
||||
pthread_mutex_lock(&(instance->n_running_threads_mutex));
|
||||
if ((instance->n_running_threads -= 1) == 0) {
|
||||
pthread_cond_signal(&(instance->n_running_threads_cond));
|
||||
}
|
||||
pthread_mutex_unlock(&(instance->n_running_threads_mutex));
|
||||
pthread_exit(NULL);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#include "internal.h"
|
||||
#include <signal.h>
|
||||
|
||||
void *bdd_worker(struct bdd_worker *worker) {
|
||||
struct bdd_instance *instance = worker->instance;
|
||||
struct bdd_workers *workers = &(instance->workers);
|
||||
|
||||
pthread_sigmask(SIG_BLOCK, &(instance->sigmask), NULL);
|
||||
|
||||
unsigned char *buf;
|
||||
size_t buf_sz = workers->buf_sz_per_worker;
|
||||
if (workers->buf == NULL) {
|
||||
buf = alloca(buf_sz);
|
||||
} else {
|
||||
buf = workers->buf + (worker->id * buf_sz);
|
||||
}
|
||||
|
||||
bdd_worker__work:;
|
||||
if (workers->available_stack.ids != NULL) {
|
||||
pthread_mutex_lock(&(workers->available_stack.mutex));
|
||||
workers->available_stack.ids[--(workers->available_stack.idx)] = worker->id;
|
||||
pthread_cond_signal(&(workers->available_stack.cond));
|
||||
pthread_mutex_unlock(&(workers->available_stack.mutex));
|
||||
}
|
||||
BDD_DEBUG_LOG("thread accepting work\n");
|
||||
|
||||
// await work
|
||||
pthread_mutex_lock(&(worker->work_mutex));
|
||||
while (worker->connections == NULL && !atomic_load(&(instance->exiting))) {
|
||||
pthread_cond_wait(&(worker->work_cond), &(worker->work_mutex));
|
||||
}
|
||||
BDD_DEBUG_LOG("thread received work!\n");
|
||||
|
||||
if (unlikely(atomic_load(&(instance->exiting)))) {
|
||||
bdd_thread_exit(instance);
|
||||
}
|
||||
|
||||
struct bdd_connections *connections = worker->connections;
|
||||
worker->connections = connections->next;
|
||||
|
||||
pthread_mutex_unlock(&(worker->work_mutex));
|
||||
|
||||
assert(connections->service->serve != NULL);
|
||||
if (!connections->service->serve(connections, buf, buf_sz)) {
|
||||
connections->broken = true;
|
||||
}
|
||||
|
||||
bdd_connections_link(instance, &(connections));
|
||||
|
||||
goto bdd_worker__work;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef bidirectiond__core_settings__h
|
||||
#define bidirectiond__core_settings__h
|
||||
#include <bddc/api.h>
|
||||
extern struct bdd_internal_service internal_services[];
|
||||
extern struct bdd_settings settings;
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "cp_pwd.h"
|
||||
|
||||
int bdd_cp_pwd(char *dest, int dest_sz, int rwflag, void *_ctx) {
|
||||
assert(rwflag == 0);
|
||||
struct bdd_cp_ctx *ctx = _ctx;
|
||||
ctx->success = false;
|
||||
char *src = ctx->password;
|
||||
if (src == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int src_len = (int)strlen(src);
|
||||
if (dest_sz <= 0 || src_len <= 0 || src_len >= dest_sz) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(dest, src, dest_sz);
|
||||
ctx->success = true;
|
||||
return src_len;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef bidirectiond__cp_pwd__h
|
||||
#define bidirectiond__cp_pwd__h
|
||||
#include <stdbool.h>
|
||||
struct bdd_cp_ctx {
|
||||
bool success;
|
||||
char *password;
|
||||
};
|
||||
int bdd_cp_pwd(char *dest, int dest_sz, int rwflag, void *_ctx);
|
||||
#endif
|
|
@ -0,0 +1 @@
|
|||
Subproject commit f610c4618f586e59abb4c46895c6287f1848707b
|
|
@ -0,0 +1,232 @@
|
|||
#include "input_processor.h"
|
||||
#include "cp_pwd.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include "tls_put.h"
|
||||
#include "core_settings.h"
|
||||
|
||||
#ifndef BIDIRECTIOND_BUFFERED_READ_BUF_SIZE
|
||||
#define BIDIRECTIOND_BUFFERED_READ_BUF_SIZE 0x200
|
||||
#endif
|
||||
|
||||
struct buffered_read_ctx {
|
||||
unsigned int idx;
|
||||
unsigned int len;
|
||||
char byte;
|
||||
};
|
||||
#define BUFFERED_READ_CTX_INITALISER { .idx = 0, .len = 0, .byte = 0, }
|
||||
static bool buffered_read(int fd, char *buf, int buf_sz, struct buffered_read_ctx *ctx) {
|
||||
if (buf_sz < 0) {
|
||||
return false;
|
||||
}
|
||||
if (ctx->idx == ctx->len) {
|
||||
for (;;) {
|
||||
int r = read(fd, buf, buf_sz);
|
||||
if (r < 0 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (r <= 0) {
|
||||
return false;
|
||||
}
|
||||
ctx->len = r;
|
||||
ctx->idx = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->byte = buf[ctx->idx++];
|
||||
return true;
|
||||
}
|
||||
|
||||
#define buffered_read() buffered_read(fd, br_buf, br_buf_sz, &(br_ctx))
|
||||
void input_processor(int sfd, char *br_buf, int br_buf_sz) {
|
||||
int fd = accept(sfd, NULL, 0);
|
||||
struct buffered_read_ctx br_ctx = BUFFERED_READ_CTX_INITALISER;
|
||||
static struct {
|
||||
char *str;
|
||||
uint8_t str_sz;
|
||||
} match_list[] = {
|
||||
{
|
||||
.str = "TLS_PEM_LOAD",
|
||||
.str_sz = 12,
|
||||
},
|
||||
{
|
||||
.str = "PING",
|
||||
.str_sz = 4,
|
||||
},
|
||||
};
|
||||
|
||||
input_processor__process:;
|
||||
uint8_t n_matches = sizeof(match_list) / sizeof(match_list[0]);
|
||||
uint8_t match;
|
||||
bool matches[sizeof(match_list) / sizeof(match_list[0])];
|
||||
for (typeof(match) idx = 0; idx < n_matches; ++idx) {
|
||||
matches[idx] = true;
|
||||
}
|
||||
for (uint8_t it = 0;; ++it) {
|
||||
match = 0;
|
||||
if (it == 0xFF) {
|
||||
goto input_processor__process;
|
||||
}
|
||||
if (!buffered_read()) {
|
||||
goto input_processor__err;
|
||||
}
|
||||
if (br_ctx.byte == 1) {
|
||||
goto input_processor__process;
|
||||
}
|
||||
if (br_ctx.byte == 0) {
|
||||
if (it == 0) {
|
||||
goto input_processor__wait;
|
||||
}
|
||||
for (typeof(match) rmatch = 0; rmatch < n_matches; ++rmatch) {
|
||||
match = n_matches - 1 - rmatch;
|
||||
if (matches[match]) {
|
||||
goto input_processor__matched;
|
||||
}
|
||||
}
|
||||
goto input_processor__wait;
|
||||
}
|
||||
for (; match < n_matches; ++match) {
|
||||
if (it >= match_list[match].str_sz) {
|
||||
if ((n_matches = match) == 0) {
|
||||
goto input_processor__process;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (match_list[match].str[it] != br_ctx.byte) {
|
||||
matches[match] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input_processor__matched:;
|
||||
if (match == 0 /* TLS_PEM_LOAD */) {
|
||||
SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
|
||||
BIO *bio = NULL;
|
||||
X509 *cert = NULL;
|
||||
EVP_PKEY *key = NULL;
|
||||
uint8_t e = 0;
|
||||
if (ctx == NULL) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
if (SSL_CTX_set_cipher_list(ctx, "AES256-SHA256") == 0) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
if ((bio = BIO_new(BIO_s_mem())) == NULL) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
for (;;) {
|
||||
if (!buffered_read()) {
|
||||
e |= 0b1;
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
if (br_ctx.byte == 0) {
|
||||
break;
|
||||
}
|
||||
if (br_ctx.byte == 1 || BIO_write(bio, &(br_ctx.byte), 1) != 1) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
}
|
||||
if ((cert = PEM_read_bio_X509(bio, NULL, NULL, "")) == NULL) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
BIO_free(bio);
|
||||
if ((bio = BIO_new(BIO_s_mem())) == NULL) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
for (;;) {
|
||||
if (!buffered_read()) {
|
||||
e |= 0b1;
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
if (br_ctx.byte == 0) {
|
||||
break;
|
||||
}
|
||||
if (br_ctx.byte == 1 || BIO_write(bio, &(br_ctx.byte), 1) != 1) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
}
|
||||
if (!buffered_read()) {
|
||||
br_ctx.byte = 1;
|
||||
}
|
||||
struct bdd_cp_ctx cp_ctx = {
|
||||
.success = false,
|
||||
.password = NULL,
|
||||
};
|
||||
if (br_ctx.byte != 1) {
|
||||
char env_variable_name[0x100];
|
||||
for (size_t idx = 0;; ++idx) {
|
||||
if (idx == sizeof(env_variable_name)) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
env_variable_name[idx] = br_ctx.byte;
|
||||
if (br_ctx.byte == 0) {
|
||||
cp_ctx.password = getenv(env_variable_name);
|
||||
break;
|
||||
}
|
||||
if (br_ctx.byte == 1) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
if (!buffered_read()) {
|
||||
e |= 0b1;
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((key = PEM_read_bio_PrivateKey(bio, NULL, bdd_cp_pwd, &(cp_ctx))) == NULL) {
|
||||
goto input_processor__tls_pem_load_err;
|
||||
}
|
||||
if (SSL_CTX_use_certificate(ctx, cert) == 1 &&
|
||||
SSL_CTX_use_PrivateKey(ctx, key) == 1) {
|
||||
struct locked_hashmap *lh = hashmap_lock(settings.name_descriptions);
|
||||
if (tls_put(lh, &(ctx))) {
|
||||
e |= 0b10;
|
||||
}
|
||||
locked_hashmap_unlock(&(lh));
|
||||
}
|
||||
input_processor__tls_pem_load_err:;
|
||||
if (ctx != NULL) {
|
||||
SSL_CTX_free(ctx);
|
||||
}
|
||||
if (bio != NULL) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
if (cert != NULL) {
|
||||
X509_free(cert);
|
||||
}
|
||||
if (key != NULL) {
|
||||
EVP_PKEY_free(key);
|
||||
}
|
||||
if (e & 0b10) {
|
||||
puts("added SSL_CTX");
|
||||
} else {
|
||||
puts("failed to add SSL_CTX");
|
||||
}
|
||||
if (e & 0b1) {
|
||||
goto input_processor__err;
|
||||
}
|
||||
} else if (match == 1) {
|
||||
puts("input_processor got PING!");
|
||||
}
|
||||
|
||||
input_processor__wait:;
|
||||
while (br_ctx.byte != 1) {
|
||||
if (!buffered_read()) {
|
||||
goto input_processor__err;
|
||||
}
|
||||
if (br_ctx.byte == 1) {
|
||||
break;
|
||||
}
|
||||
puts("extraneous byte sent to input_processor");
|
||||
}
|
||||
|
||||
goto input_processor__process;
|
||||
|
||||
input_processor__err:;
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef bidirectiond__fifo_processor__h
|
||||
#define bidirectiond__fifo_processor__h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void input_processor(int fd, char *br_buf, int br_buf_sz);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,459 @@
|
|||
#include <bddc/api.h>
|
||||
#include <sys/resource.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "strtoint.h"
|
||||
#include "core_settings.h"
|
||||
#include "input_processor.h"
|
||||
#include "tls_put.h"
|
||||
#include "cp_pwd.h"
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <signal.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <openssl/engine.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#ifndef BIDIRECTIOND_USERNAME
|
||||
#define BIDIRECTIOND_USERNAME "nobody"
|
||||
#endif
|
||||
|
||||
#if CHAR_BIT != 8
|
||||
#error The target architecture is unsupported
|
||||
#endif
|
||||
|
||||
struct bdd_settings settings = {
|
||||
.name_descriptions = NULL,
|
||||
.n_connections = 0x100,
|
||||
.n_epoll_oevents = 0x100,
|
||||
.buf_sz = 0x800,
|
||||
.n_worker_threads = 16,
|
||||
.client_timeout = 12000,
|
||||
.use_stack_buf = false,
|
||||
.sv_socket = -1,
|
||||
.use_work_queues = false,
|
||||
// .sigmask = x,
|
||||
};
|
||||
|
||||
#define PASTE(x, y) x##y
|
||||
#define sto(w, t) \
|
||||
void PASTE(sto, w)(t *dest, char *str) { \
|
||||
signed long long int v; \
|
||||
if (!bdd_strtosll(str, strlen(str), &(v))) { \
|
||||
return; \
|
||||
} \
|
||||
if (v == (t)v) { \
|
||||
*dest = (t)v; \
|
||||
} \
|
||||
return; \
|
||||
}
|
||||
sto(i, int);
|
||||
sto(ui, unsigned int);
|
||||
sto(usi, unsigned short int);
|
||||
sto(uid, uid_t);
|
||||
sto(gid, gid_t);
|
||||
sto(sz, size_t);
|
||||
void storlim(rlim_t *dest, char *str) {
|
||||
unsigned long long int v;
|
||||
if (!bdd_strtoull(str, strlen(str), &(v))) {
|
||||
return;
|
||||
}
|
||||
if (v == (rlim_t)v) {
|
||||
*dest = (rlim_t)v;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// main
|
||||
#ifndef HASHMAP_MAIN
|
||||
int main(int argc, char *argv[], char *env[]) {
|
||||
puts("bidirectiond version "PROG_SEMVER);
|
||||
// set up tls
|
||||
if (!OPENSSL_init_ssl(0, NULL)) {
|
||||
return 1; // nothing to clean up
|
||||
}
|
||||
|
||||
struct bdd_instance *bdd_instance = NULL;
|
||||
int input_fd = -1;
|
||||
struct sockaddr_un input_addr = { 0, .sun_family = AF_UNIX, };
|
||||
int sig_fd = -1;
|
||||
|
||||
// name_descriptions
|
||||
if ((settings.name_descriptions = hashmap_create((void (*)(void *))&(bdd_name_description_destroy))) == NULL) {
|
||||
fputs("failed to allocate settings.name_descriptions\n", stderr);
|
||||
goto main__clean_up;
|
||||
}
|
||||
// args
|
||||
char **arg = &(argv[1]);
|
||||
if (argc < 1 /* linux */) {
|
||||
arg = argv;
|
||||
}
|
||||
bool use_ipv4 = false;
|
||||
int backlog = 0;
|
||||
unsigned short int port = 443;
|
||||
|
||||
// the uid of the current user
|
||||
/* for example:
|
||||
nuid will contain the uid that the program was started by,
|
||||
even if bidirectiond is setuid.
|
||||
nuid will be used later to restore a safe uid, if the process
|
||||
becomes (or already is) root. */
|
||||
uid_t nuid = getuid();
|
||||
gid_t ngid = getgid();
|
||||
|
||||
setpwent();
|
||||
for (struct passwd *pw = getpwent(); pw != NULL; pw = getpwent()) {
|
||||
if (strcmp(pw->pw_name, BIDIRECTIOND_USERNAME) == 0) {
|
||||
#ifdef BIDIRECTIOND_SU
|
||||
nuid = pw->pw_uid;
|
||||
ngid = pw->pw_gid;
|
||||
#else
|
||||
if (nuid == 0) {
|
||||
nuid = pw->pw_uid;
|
||||
}
|
||||
if (ngid == 0) {
|
||||
ngid = pw->pw_gid;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
endpwent();
|
||||
|
||||
struct locked_hashmap *lh = hashmap_lock(settings.name_descriptions);
|
||||
size_t big_alloc_sz = 0;
|
||||
|
||||
#define EXPECT_ARGS(n) for (size_t idx = 1; idx <= n; ++idx) { \
|
||||
if (arg[idx] == NULL || arg[idx][0] == '-') { \
|
||||
goto main__arg_fuck; \
|
||||
} \
|
||||
}
|
||||
main__arg_iter:;
|
||||
while ((*arg) != NULL) {
|
||||
if (strcmp((*arg), "--n-connection-threads") == 0 || strcmp((*arg), "-t") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stousi(&(settings.n_worker_threads), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--client-timeout") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stoui(&(settings.client_timeout), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "-l") == 0) {
|
||||
EXPECT_ARGS(2);
|
||||
struct rlimit rlimit;
|
||||
storlim(&(rlimit.rlim_cur), arg[1]);
|
||||
storlim(&(rlimit.rlim_max), arg[2]);
|
||||
if (setrlimit(RLIMIT_NOFILE, &(rlimit)) != 0) {
|
||||
fputs("setrlimit failed\n", stderr);
|
||||
}
|
||||
arg += 3;
|
||||
} else if (strcmp((*arg), "--buffer-size") == 0 || strcmp((*arg), "-b") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stoui(&(settings.buf_sz), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--backlog") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stoi(&(backlog), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--server-tcp-port") == 0 || strcmp((*arg), "-p") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stousi(&(port), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--max-connections") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stoi(&(settings.n_connections), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--listen-ipv4") == 0) {
|
||||
use_ipv4 = true;
|
||||
arg += 1;
|
||||
} else if (strcmp((*arg), "--use-work-queue") == 0) {
|
||||
settings.use_work_queues = true;
|
||||
arg += 1;
|
||||
} else if (strcmp((*arg), "--nohup") == 0) {
|
||||
struct sigaction action = {
|
||||
.sa_handler = SIG_IGN,
|
||||
.sa_flags = SA_RESTART,
|
||||
};
|
||||
sigaction(SIGHUP, &(action), 0);
|
||||
arg += 1;
|
||||
} else if (strcmp((*arg), "--tls-credentials") == 0 || strcmp((*arg), "-c") == 0) {
|
||||
EXPECT_ARGS(3);
|
||||
SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
|
||||
if (SSL_CTX_set_cipher_list(ctx, "AES256-SHA256") == 0) {
|
||||
goto main__arg_creds_err;
|
||||
}
|
||||
if (SSL_CTX_use_certificate_file(ctx, arg[1], SSL_FILETYPE_PEM) != 1) {
|
||||
fputs("invalid certificate file\n", stderr);
|
||||
goto main__arg_creds_err;
|
||||
}
|
||||
struct bdd_cp_ctx cp_ctx = {
|
||||
.success = false,
|
||||
.password = getenv(arg[3]),
|
||||
};
|
||||
SSL_CTX_set_default_passwd_cb(ctx, bdd_cp_pwd);
|
||||
SSL_CTX_set_default_passwd_cb_userdata(ctx, &(cp_ctx));
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, arg[2], SSL_FILETYPE_PEM) != 1) {
|
||||
fputs("invalid private key file\n", stderr);
|
||||
goto main__arg_creds_err;
|
||||
}
|
||||
if (!cp_ctx.success) {
|
||||
fputs("the private key file must be encrypted\n", stderr);
|
||||
goto main__arg_creds_err;
|
||||
}
|
||||
if (!tls_put(lh, &(ctx))) {
|
||||
fputs("seemingly invalid certificate file\n", stderr);
|
||||
goto main__arg_creds_err;
|
||||
}
|
||||
main__arg_creds_err:;
|
||||
if (ctx != NULL) {
|
||||
SSL_CTX_free(ctx);
|
||||
}
|
||||
arg += 4;
|
||||
} else if (strcmp((*arg), "--UNSAFE allocate buffer on stack") == 0) {
|
||||
fputs("'--UNSAFE allocate buffer on stack' is unsafe and shouldn't be used\n", stderr);
|
||||
settings.use_stack_buf = true;
|
||||
arg += 1;
|
||||
} else if (getuid() == 0 && strcmp((*arg), "--uid") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stouid(&(nuid), arg[1]);
|
||||
arg += 2;
|
||||
} else if (getgid() == 0 && strcmp((*arg), "--gid") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stogid(&(ngid), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--n-epoll-oevents") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stoi(&(settings.n_epoll_oevents), arg[1]);
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--input") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
size_t str_len = strlen(arg[1]);
|
||||
if (str_len == 0 || str_len >= sizeof(input_addr.sun_path)) {
|
||||
fputs("--input path is of invalid length\n", stderr);
|
||||
} else if (input_addr.sun_path[0] != 0) {
|
||||
fputs("cannot specify --input twice\n", stderr);
|
||||
} else {
|
||||
strcpy(input_addr.sun_path, arg[1]);
|
||||
}
|
||||
arg += 2;
|
||||
} else if (strcmp((*arg), "--big-alloc") == 0) {
|
||||
EXPECT_ARGS(1);
|
||||
stosz(&(big_alloc_sz), arg[1]);
|
||||
arg += 2;
|
||||
} else {
|
||||
for (size_t idx = 0; idx < N_INTERNAL_SERVICES; ++idx) {
|
||||
if (internal_services[idx].supported_arguments != NULL) for (size_t pidx = 0; internal_services[idx].supported_arguments[pidx]; ++pidx) {
|
||||
if (strcmp((*arg), internal_services[idx].supported_arguments[pidx]) == 0) {
|
||||
size_t n = 1;
|
||||
while (arg[n] != NULL && (arg[n][0] != '-' || (arg[n][1] >= '0' && arg[n][1] <= '9'))) {
|
||||
n += 1;
|
||||
}
|
||||
if (!internal_services[idx].service_init(lh, &(internal_services[idx]), n, arg)) {
|
||||
goto main__arg_fuck;
|
||||
}
|
||||
arg = &(arg[n]);
|
||||
goto main__arg_iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
main__arg_fuck:;
|
||||
puts(
|
||||
"argument parsing failed\n"
|
||||
"-t: set the amount of worker threads\n"
|
||||
"--client-timeout: set the timeout (in ms) for client socket i/o\n"
|
||||
"-l: set the rlimits for open files (soft limit, hard limit)\n"
|
||||
"-b: set the size of the large worker buffers\n"
|
||||
"--backlog: set the tcp backlog for sv_socket\n"
|
||||
"-p: set the tcp port to bind sv_socket to\n"
|
||||
"--max-connections: the max amount of bdd_connections structs\n"
|
||||
"--listen-ipv4: sv_socket should not use ipv6\n"
|
||||
"--use-work-queue: do not wait for worker threads before giving them work\n"
|
||||
"--nohup: SIG_IGN SIGHUP\n"
|
||||
"-c: load pem-encoded tls credentials (e.g. `-c cert.pem encrypted-key.pem name-of-password-environment-variable`)\n"
|
||||
"--input: set the path for a udp unix socket, so that some bidirectiond settings can be modified without restarting\n"
|
||||
"--n-epoll-oevents: epoll_wait maxevents\n"
|
||||
"--big-alloc: reserve some ram"
|
||||
);
|
||||
for (size_t idx = 0; idx < N_INTERNAL_SERVICES; ++idx) {
|
||||
if (internal_services[idx].arguments_help != NULL) {
|
||||
fputs(internal_services[idx].arguments_help, stdout);
|
||||
}
|
||||
}
|
||||
locked_hashmap_unlock(&(lh));
|
||||
goto main__clean_up;
|
||||
}
|
||||
}
|
||||
locked_hashmap_unlock(&(lh));
|
||||
|
||||
// potentially a setgid program
|
||||
if (getgid() != 0 && getegid() == 0) {
|
||||
setgid(0);
|
||||
}
|
||||
// potentially a setuid program
|
||||
if (getuid() != 0 && geteuid() == 0) {
|
||||
setuid(0);
|
||||
}
|
||||
|
||||
if (port < 1024 && getuid() != 0) {
|
||||
// root required to obtain port 443
|
||||
fputs("must be run as root\n", stderr);
|
||||
goto main__clean_up;
|
||||
}
|
||||
|
||||
// set up socket
|
||||
union {
|
||||
struct sockaddr_in inet4;
|
||||
struct sockaddr_in6 inet6;
|
||||
} sv_addr = { 0, };
|
||||
size_t sv_addr_sz = 0;
|
||||
if (use_ipv4) {
|
||||
settings.sv_socket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
sv_addr_sz = sizeof(struct sockaddr_in);
|
||||
sv_addr.inet4.sin_family = AF_INET;
|
||||
sv_addr.inet4.sin_addr.s_addr = INADDR_ANY;
|
||||
sv_addr.inet4.sin_port = htons(port);
|
||||
} else {
|
||||
settings.sv_socket = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
sv_addr_sz = sizeof(struct sockaddr_in6);
|
||||
sv_addr.inet6.sin6_family = AF_INET6;
|
||||
sv_addr.inet6.sin6_addr = in6addr_any;
|
||||
sv_addr.inet6.sin6_port = htons(port);
|
||||
}
|
||||
// try to bind to port
|
||||
int opt = 1;
|
||||
setsockopt(settings.sv_socket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &(opt), sizeof(opt));
|
||||
if (bind(settings.sv_socket, (struct sockaddr *)&(sv_addr), sv_addr_sz) < 0 ||
|
||||
listen(settings.sv_socket, backlog) < 0) {
|
||||
fputs("failed to bind sv_socket! is the port already use? is the internet protocol enabled?\n", stderr);
|
||||
goto main__clean_up;
|
||||
}
|
||||
|
||||
if (getgid() == 0) {
|
||||
setgid(ngid);
|
||||
}
|
||||
if (getuid() == 0) {
|
||||
setuid(nuid);
|
||||
}
|
||||
|
||||
//#ifndef NDEBUG
|
||||
// check should never pass
|
||||
if (getuid() == 0 || getgid() == 0 || geteuid() == 0 || getegid() == 0) {
|
||||
fputs("unsafe\n", stderr);
|
||||
assert(false);
|
||||
return 1;
|
||||
}
|
||||
//#endif
|
||||
|
||||
if (input_addr.sun_path[0] != 0 && (input_fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
|
||||
if (bind(input_fd, (struct sockaddr *)&(input_addr), sizeof(struct sockaddr_un)) != 0) {
|
||||
fputs("failed to bind input socket\n", stderr);
|
||||
close(input_fd);
|
||||
input_fd = -1;
|
||||
} else if (listen(input_fd, 0) != 0) {
|
||||
close(input_fd);
|
||||
input_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// signals
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
sigset_t sigset;
|
||||
sigemptyset(&(sigset));
|
||||
sigaddset(&(sigset), SIGINT);
|
||||
sigaddset(&(sigset), SIGTERM);
|
||||
settings.sigmask = sigset;
|
||||
pthread_sigmask(SIG_BLOCK, &(sigset), NULL);
|
||||
if ((sig_fd = signalfd(-1, &(sigset), 0)) < 0) {
|
||||
goto main__clean_up;
|
||||
}
|
||||
struct pollfd pollfds[2] = {
|
||||
{
|
||||
.fd = sig_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
{
|
||||
.fd = input_fd,
|
||||
.events = POLLIN,
|
||||
.revents = 0,
|
||||
},
|
||||
};
|
||||
|
||||
// serve
|
||||
bdd_instance = bdd_go(settings);
|
||||
if (bdd_instance == NULL) {
|
||||
goto main__clean_up;
|
||||
}
|
||||
if (big_alloc_sz > 0) {
|
||||
void *big_alloc = malloc(big_alloc_sz);
|
||||
if (big_alloc == NULL) {
|
||||
goto main__clean_up;
|
||||
}
|
||||
free(big_alloc);
|
||||
}
|
||||
|
||||
struct signalfd_siginfo sig;
|
||||
for (;;) {
|
||||
while (poll((struct pollfd *)&(pollfds), input_fd < 0 ? 1 : 2, -1) < 0) {
|
||||
if (errno != EINTR /* e.g., SIGUSR1 could be sent to bidirectiond, and then handled by this thread */) {
|
||||
goto main__clean_up;
|
||||
}
|
||||
}
|
||||
if (pollfds[0].revents & POLLIN) {
|
||||
if (read(sig_fd, &(sig), sizeof(struct signalfd_siginfo)) != sizeof(struct signalfd_siginfo)) {
|
||||
goto main__clean_up;
|
||||
}
|
||||
switch (sig.ssi_signo) {
|
||||
case (SIGINT): case (SIGTERM): {
|
||||
goto main__clean_up;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pollfds[1].revents & POLLIN) {
|
||||
char buf[0x100];
|
||||
input_processor(input_fd, (char *)&(buf), sizeof(buf));
|
||||
}
|
||||
}
|
||||
|
||||
main__clean_up:;
|
||||
if (bdd_instance != NULL) {
|
||||
bdd_stop(bdd_instance);
|
||||
bdd_wait(bdd_instance);
|
||||
bdd_destroy(bdd_instance);
|
||||
}
|
||||
if (sig_fd != -1) {
|
||||
close(sig_fd);
|
||||
}
|
||||
if (input_fd != -1) {
|
||||
unlink(input_addr.sun_path);
|
||||
close(input_fd);
|
||||
}
|
||||
if (settings.sv_socket != -1) {
|
||||
shutdown(settings.sv_socket, SHUT_RDWR);
|
||||
close(settings.sv_socket);
|
||||
}
|
||||
// aight
|
||||
if (settings.name_descriptions != NULL) {
|
||||
hashmap_destroy(settings.name_descriptions);
|
||||
}
|
||||
// https://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
|
||||
//FIPS_mode_set(0);
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
CRYPTO_set_id_callback(NULL);
|
||||
//ERR_remove_state(0);
|
||||
SSL_COMP_free_compression_methods();
|
||||
ENGINE_cleanup();
|
||||
CONF_modules_free();
|
||||
CONF_modules_unload(1);
|
||||
COMP_zlib_cleanup();
|
||||
ERR_free_strings(); // TODO: is that needed?
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,125 @@
|
|||
#include <bddc/api.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
struct general_service__associated {
|
||||
bdd_io_id client;
|
||||
bdd_io_id service;
|
||||
};
|
||||
static bool serve(struct bdd_connections *connections, void *buf, size_t buf_size, bdd_io_id from, bdd_io_id to) {
|
||||
do {
|
||||
int n;
|
||||
if ((n = bdd_read(connections, from, buf, buf_size)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (bdd_write(connections, to, buf, n) <= 0) {
|
||||
return false;
|
||||
}
|
||||
} while ((bdd_poll(connections, from) & POLLIN));
|
||||
return true;
|
||||
}
|
||||
bool general_service__serve(struct bdd_connections *connections, void *buf, size_t buf_size) {
|
||||
struct general_service__associated *associated = bdd_get_associated(connections);
|
||||
if ((bdd_poll(connections, associated->client) & POLLIN)) {
|
||||
if (!serve(connections, buf, buf_size, associated->client, associated->service)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((bdd_poll(connections, associated->service)) & POLLIN) {
|
||||
if (!serve(connections, buf, buf_size, associated->service, associated->client)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
struct general_service__info {
|
||||
struct addrinfo *addrinfo;
|
||||
char *tls_name;
|
||||
};
|
||||
bool general_service__connections_init(struct bdd_connections *connections, void *service_info, bdd_io_id client_id, struct sockaddr client_sockaddr) {
|
||||
struct general_service__info *info = service_info;
|
||||
struct addrinfo *addrinfo = info->addrinfo;
|
||||
int sock = -1;
|
||||
for (; addrinfo != NULL; addrinfo = addrinfo->ai_next) {
|
||||
if ((sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
sock = -1;
|
||||
}
|
||||
if (sock < 0) {
|
||||
return false;
|
||||
}
|
||||
bdd_io_id service;
|
||||
struct general_service__associated *associated;
|
||||
if (!bdd_create_io(connections, &(service), &(sock), info->tls_name)) {
|
||||
close(sock);
|
||||
return false;
|
||||
}
|
||||
if ((associated = malloc(sizeof(struct general_service__associated))) == NULL) {
|
||||
// bdd-core will destroy the io
|
||||
return false;
|
||||
}
|
||||
associated->client = client_id;
|
||||
associated->service = service;
|
||||
bdd_set_associated(connections, associated, free);
|
||||
return true;
|
||||
}
|
||||
void general_service__service_info_destructor(void *hint) {
|
||||
struct general_service__info *info = hint;
|
||||
freeaddrinfo(info->addrinfo);
|
||||
free(info);
|
||||
}
|
||||
static bool handle_s(struct locked_hashmap *name_descriptions, struct bdd_internal_service *service, char *scope, char *addr, char *port, bool use_tls) {
|
||||
struct general_service__info *info = malloc(sizeof(struct general_service__info));
|
||||
struct addrinfo hints = { 0, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, };
|
||||
struct addrinfo *res = NULL;
|
||||
if (info == NULL) {
|
||||
goto handle_s__err;
|
||||
}
|
||||
if (use_tls) {
|
||||
info->tls_name = addr;
|
||||
} else {
|
||||
info->tls_name = NULL;
|
||||
}
|
||||
if (getaddrinfo(addr, port, &(hints), &(res)) != 0) {
|
||||
goto handle_s__err;
|
||||
}
|
||||
info->addrinfo = res;
|
||||
if (!bdd_name_descriptions_set_internal_service(name_descriptions, scope, strlen(scope), service, info)) {
|
||||
goto handle_s__err;
|
||||
}
|
||||
return true;
|
||||
handle_s__err:;
|
||||
if (info != NULL) {
|
||||
free(info);
|
||||
}
|
||||
if (res != NULL) {
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool general_service__service_init(struct locked_hashmap *name_descriptions, struct bdd_internal_service *service, size_t argc, char **argv) {
|
||||
if (strcmp(argv[0], "-s") == 0) {
|
||||
if (argc != 5) {
|
||||
return false;
|
||||
}
|
||||
bool use_tls;
|
||||
if (strcmp(argv[4], "true") == 0) {
|
||||
use_tls = true;
|
||||
} else if (strcmp(argv[4], "false") == 0) {
|
||||
use_tls = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return handle_s(name_descriptions, service, argv[1], argv[2], argv[3], use_tls);
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "general_service",
|
||||
"serve": "general_service__serve",
|
||||
"service_init": "general_service__service_init",
|
||||
"connections_init": "general_service__connections_init",
|
||||
"service_info_destructor": "general_service__service_info_destructor",
|
||||
"supported_arguments": [ "-s" ],
|
||||
"arguments_help": "-s: creates a barebones proxy (e.g. `-s [service name] [target address/name] [target tcp port] [use ssl (true/false)]`)\n",
|
||||
"n_max_io": 2
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
#include "strtoint.h"
|
||||
|
||||
bool bdd_strtosll(char *str, size_t len, signed long long int *sll) {
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
bool negative = false;
|
||||
size_t idx = 0;
|
||||
if (str[idx] == '-') {
|
||||
negative = true;
|
||||
if ((idx += 1) <= len) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
long long int r = 0;
|
||||
long long int t;
|
||||
char d;
|
||||
for (; idx < len; ++idx) {
|
||||
t = r;
|
||||
r *= 10;
|
||||
d = str[idx];
|
||||
if (d < '0' || d > '9') {
|
||||
return false;
|
||||
}
|
||||
d -= '0';
|
||||
if (negative) {
|
||||
if ((r -= d) > t) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((r += d) < t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
*sll = r;
|
||||
return true;
|
||||
}
|
||||
bool bdd_strtoull(char *str, size_t len, unsigned long long int *llu) {
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
bool negative = false;
|
||||
long long int r = 0;
|
||||
long long int t;
|
||||
char d;
|
||||
for (size_t idx = 0; idx < len; ++idx) {
|
||||
t = r;
|
||||
r *= 10;
|
||||
d = str[idx];
|
||||
if (d < '0' || d > '9') {
|
||||
return false;
|
||||
}
|
||||
d -= '0';
|
||||
if ((r += d) < t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*llu = r;
|
||||
return true;
|
||||
}
|
||||
size_t bdd_strlene(char *str, char e) {
|
||||
size_t len = 0;
|
||||
while (str[len] != e) {
|
||||
len += 1;
|
||||
}
|
||||
return len;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef bidirectiond__strtolli__h
|
||||
#define bidirectiond__strtolli__h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool bdd_strtosll(char *str, size_t len, signed long long int *sll);
|
||||
bool bdd_strtoull(char *str, size_t len, unsigned long long int *ull);
|
||||
size_t bdd_strlene(char *str, char e);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,70 @@
|
|||
#include <string.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include "tls_put.h"
|
||||
#include "core_settings.h"
|
||||
|
||||
// hashmap_lock call makes it thread-safe
|
||||
bool tls_put(struct locked_hashmap *ns, SSL_CTX **ctx_ref) {
|
||||
// take the ref
|
||||
SSL_CTX *ctx = *ctx_ref;
|
||||
*ctx_ref = NULL;
|
||||
|
||||
bool should_up_rc = false;
|
||||
GENERAL_NAMES *dns_alt_names = X509_get_ext_d2i(SSL_CTX_get0_certificate(ctx), NID_subject_alt_name, 0, 0);
|
||||
if (dns_alt_names != NULL) {
|
||||
int n_dns_alt_names = sk_GENERAL_NAME_num(dns_alt_names);
|
||||
if (n_dns_alt_names < 0) {
|
||||
n_dns_alt_names = 0;
|
||||
}
|
||||
for (int idx = 0; idx < n_dns_alt_names; ++idx) {
|
||||
GENERAL_NAME *entry = sk_GENERAL_NAME_value(dns_alt_names, idx);
|
||||
if (entry->type != GEN_DNS) {
|
||||
continue;
|
||||
}
|
||||
ASN1_IA5STRING *asn1_str = entry->d.dNSName;
|
||||
int data_length = asn1_str->length;
|
||||
if (bdd_name_descriptions_set_ssl_ctx(ns, (char *)asn1_str->data, data_length, ctx)) {
|
||||
if (should_up_rc) {
|
||||
SSL_CTX_up_ref(ctx);
|
||||
} else {
|
||||
should_up_rc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
GENERAL_NAMES_free(dns_alt_names);
|
||||
} else { // rfc6125
|
||||
X509_NAME *dns_subject_names = X509_get_subject_name(SSL_CTX_get0_certificate(ctx));
|
||||
if (dns_subject_names != NULL) {
|
||||
int n_dns_subject_names = X509_NAME_entry_count(dns_subject_names);
|
||||
if (n_dns_subject_names < 0) {
|
||||
n_dns_subject_names = 0;
|
||||
}
|
||||
for (int idx = 0; idx < n_dns_subject_names; ++idx) {
|
||||
X509_NAME_ENTRY *entry = X509_NAME_get_entry(dns_subject_names, idx);
|
||||
ASN1_OBJECT *asn1_obj = X509_NAME_ENTRY_get_object(entry);
|
||||
ASN1_STRING *asn1_str = X509_NAME_ENTRY_get_data(entry);
|
||||
if (asn1_obj == NULL || asn1_str == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (OBJ_obj2nid(asn1_obj) != NID_commonName) {
|
||||
continue;
|
||||
}
|
||||
int data_length = asn1_str->length;
|
||||
if (bdd_name_descriptions_set_ssl_ctx(ns, (char *)asn1_str->data, data_length, ctx)) {
|
||||
if (should_up_rc) {
|
||||
SSL_CTX_up_ref(ctx);
|
||||
} else {
|
||||
should_up_rc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_up_rc) /* if `ctx` is not referenced by any hashmap values, then free `ctx` */ {
|
||||
SSL_CTX_free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef bidirectiond__tls_put__h
|
||||
#define bidirectiond__tls_put__h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <hashmap/hashmap.h>
|
||||
|
||||
bool tls_put(struct locked_hashmap *ns, SSL_CTX **ctx);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue