Add HTTPS support
This commit is contained in:
@@ -6,7 +6,7 @@ chttp.c chttp.h: $(wildcard src/*.c src/*.h) misc/amalg.py Makefile
|
|||||||
python misc/amalg.py
|
python misc/amalg.py
|
||||||
|
|
||||||
example: main.c chttp.c chttp.h
|
example: main.c chttp.c chttp.h
|
||||||
gcc main.c chttp.c -o example -Wfatal-errors -lws2_32
|
gcc main.c chttp.c -o example -Wfatal-errors -DHTTPS_ENABLED -lcrypto -lssl
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm chttp.c chttp.h
|
rm chttp.c chttp.h
|
||||||
|
|||||||
@@ -1475,38 +1475,206 @@ int mutex_unlock(Mutex *mutex)
|
|||||||
|
|
||||||
int global_secure_context_init(void)
|
int global_secure_context_init(void)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_library_init();
|
||||||
|
SSL_load_error_strings();
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int global_secure_context_free(void)
|
int global_secure_context_free(void)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
EVP_cleanup();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int client_secure_context_init(ClientSecureContext *ctx)
|
int client_secure_context_init(ClientSecureContext *ctx)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX *p = SSL_CTX_new(TLS_client_method());
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
|
||||||
|
|
||||||
|
SSL_CTX_set_verify(p, SSL_VERIFY_PEER, NULL);
|
||||||
|
|
||||||
|
if (SSL_CTX_set_default_verify_paths(p) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->p = p;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int client_secure_context_free(ClientSecureContext *ctx)
|
void client_secure_context_free(ClientSecureContext *ctx)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX_free(ctx->p);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_secure_context_init(ServerSecureContext *ctx)
|
#ifdef HTTPS_ENABLED
|
||||||
|
static int servername_callback(SSL *ssl, int *ad, void *arg)
|
||||||
{
|
{
|
||||||
// TODO
|
ServerSecureContext *ctx = arg;
|
||||||
|
|
||||||
|
(void) ad; // TODO: use this?
|
||||||
|
|
||||||
|
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||||
|
if (servername == NULL)
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
|
||||||
|
for (int i = 0; i < ctx->num_certs; i++) {
|
||||||
|
ServerCertificate *cert = &ctx->certs[i];
|
||||||
|
if (!strcmp(cert->domain, servername)) {
|
||||||
|
SSL_set_SSL_CTX(ssl, cert->ctx);
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int server_secure_context_init(ServerSecureContext *ctx,
|
||||||
|
HTTP_String cert_file, HTTP_String key_file)
|
||||||
|
{
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
|
||||||
|
|
||||||
|
char cert_buffer[1024];
|
||||||
|
if (cert_file.len >= (int) sizeof(cert_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
|
||||||
|
cert_buffer[cert_file.len] = '\0';
|
||||||
|
|
||||||
|
// Copy private key file path to static buffer
|
||||||
|
char key_buffer[1024];
|
||||||
|
if (key_file.len >= (int) sizeof(key_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(key_buffer, key_file.ptr, key_file.len);
|
||||||
|
key_buffer[key_file.len] = '\0';
|
||||||
|
|
||||||
|
// Load certificate and private key
|
||||||
|
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the private key matches the certificate
|
||||||
|
if (SSL_CTX_check_private_key(p) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_tlsext_servername_callback(p, servername_callback);
|
||||||
|
SSL_CTX_set_tlsext_servername_arg(p, ctx);
|
||||||
|
|
||||||
|
ctx->p = p;
|
||||||
|
ctx->num_certs = 0;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
(void) cert_file;
|
||||||
|
(void) key_file;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_secure_context_free(ServerSecureContext *ctx)
|
void server_secure_context_free(ServerSecureContext *ctx)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX_free(ctx->p);
|
||||||
|
for (int i = 0; i < ctx->num_certs; i++)
|
||||||
|
SSL_CTX_free(ctx->certs[i].ctx);
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
|
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
if (ctx->num_certs == SERVER_CERTIFICATE_LIMIT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
|
||||||
|
|
||||||
|
char cert_buffer[1024];
|
||||||
|
if (cert_file.len >= (int) sizeof(cert_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
|
||||||
|
cert_buffer[cert_file.len] = '\0';
|
||||||
|
|
||||||
|
char key_buffer[1024];
|
||||||
|
if (key_file.len >= (int) sizeof(key_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(key_buffer, key_file.ptr, key_file.len);
|
||||||
|
key_buffer[key_file.len] = '\0';
|
||||||
|
|
||||||
|
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_check_private_key(p) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerCertificate *cert = &ctx->certs[ctx->num_certs];
|
||||||
|
if (domain.len >= (int) sizeof(cert->domain)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(cert->domain, domain.ptr, domain.len);
|
||||||
|
cert->domain[domain.len] = '\0';
|
||||||
|
cert->ctx = p;
|
||||||
|
ctx->num_certs++;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
(void) domain;
|
||||||
|
(void) cert_file;
|
||||||
|
(void) key_file;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1703,8 +1871,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
HTTP_String addr, Port port, HTTP_String cert_file,
|
||||||
HTTP_String key_file_name)
|
HTTP_String key_file)
|
||||||
{
|
{
|
||||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1715,7 +1883,8 @@ int socket_manager_listen_tls(SocketManager *sm,
|
|||||||
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (server_secure_context_init(&sm->server_secure_context) < 0) {
|
if (server_secure_context_init(&sm->server_secure_context,
|
||||||
|
cert_file, key_file) < 0) {
|
||||||
CLOSE_NATIVE_SOCKET(sm->secure_sock);
|
CLOSE_NATIVE_SOCKET(sm->secure_sock);
|
||||||
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1741,7 +1910,7 @@ int socket_manager_add_certificate(SocketManager *sm,
|
|||||||
static bool is_secure(Socket *s)
|
static bool is_secure(Socket *s)
|
||||||
{
|
{
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
return (s->ssl != NULL);
|
return (s->server_secure_context != NULL);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@@ -1783,6 +1952,24 @@ connect_failed_because_of_peer(void)
|
|||||||
return connect_failed_because_of_peer_2(err);
|
return connect_failed_because_of_peer_2(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void free_addr_list(AddressAndPort *addrs, int num_addr)
|
||||||
|
{
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
for (int i = 0; i < num_addr; i++) {
|
||||||
|
RegisteredName *name = addrs[i].name;
|
||||||
|
if (name) {
|
||||||
|
assert(name->refs > 0);
|
||||||
|
name->refs--;
|
||||||
|
if (name->refs == 0)
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) addrs;
|
||||||
|
(void) num_addr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// This function moves the socket state machine
|
// This function moves the socket state machine
|
||||||
// to the next state until an I/O event would
|
// to the next state until an I/O event would
|
||||||
// be required to continue.
|
// be required to continue.
|
||||||
@@ -1811,6 +1998,13 @@ static void socket_update(Socket *s)
|
|||||||
if (s->sock != NATIVE_SOCKET_INVALID) {
|
if (s->sock != NATIVE_SOCKET_INVALID) {
|
||||||
// This is not the first attempt
|
// This is not the first attempt
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
if (s->ssl) {
|
||||||
|
SSL_free(s->ssl);
|
||||||
|
s->ssl = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
CLOSE_NATIVE_SOCKET(s->sock);
|
CLOSE_NATIVE_SOCKET(s->sock);
|
||||||
|
|
||||||
s->next_addr++;
|
s->next_addr++;
|
||||||
@@ -1903,18 +2097,76 @@ static void socket_update(Socket *s)
|
|||||||
|
|
||||||
case SOCKET_STATE_CONNECTED:
|
case SOCKET_STATE_CONNECTED:
|
||||||
{
|
{
|
||||||
|
if (!is_secure(s)) {
|
||||||
|
|
||||||
// We managed to connect to the peer.
|
// We managed to connect to the peer.
|
||||||
// We can free the target array if it
|
// We can free the target array if it
|
||||||
// was allocated dynamically.
|
// was allocated dynamically.
|
||||||
if (s->num_addr > 1)
|
if (s->num_addr > 1)
|
||||||
free(s->addrs);
|
free(s->addrs);
|
||||||
|
|
||||||
if (!is_secure(s)) {
|
|
||||||
s->events = 0;
|
s->events = 0;
|
||||||
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
||||||
} else {
|
} else {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
assert(0); // TODO
|
if (s->ssl == NULL) {
|
||||||
|
s->ssl = SSL_new(s->server_secure_context->p);
|
||||||
|
if (s->ssl == NULL) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_set_fd(s->ssl, s->sock) != 1) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressAndPort addr;
|
||||||
|
if (s->num_addr > 1)
|
||||||
|
addr = s->addrs[s->next_addr];
|
||||||
|
else
|
||||||
|
addr = s->addr;
|
||||||
|
|
||||||
|
if (addr.name)
|
||||||
|
SSL_set_tlsext_host_name(s->ssl, addr.name->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = SSL_connect(s->ssl);
|
||||||
|
if (ret == 1) {
|
||||||
|
// Handshake done
|
||||||
|
|
||||||
|
// We managed to connect to the peer.
|
||||||
|
// We can free the target array if it
|
||||||
|
// was allocated dynamically.
|
||||||
|
if (s->num_addr == 1)
|
||||||
|
free_addr_list(&s->addr, 1);
|
||||||
|
else {
|
||||||
|
assert(s->num_addr > 1);
|
||||||
|
free_addr_list(s->addrs, s->num_addr);
|
||||||
|
free(s->addrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = SSL_get_error(s->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
|
s->events = POLLIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
s->events = POLLOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state = SOCKET_STATE_PENDING;
|
||||||
|
s->events = 0;
|
||||||
|
again = true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1927,7 +2179,45 @@ static void socket_update(Socket *s)
|
|||||||
s->events = 0;
|
s->events = 0;
|
||||||
} else {
|
} else {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
assert(0); // TODO
|
// Start server-side SSL handshake
|
||||||
|
if (!s->ssl) {
|
||||||
|
|
||||||
|
s->ssl = SSL_new(s->server_secure_context->p);
|
||||||
|
if (s->ssl == NULL) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_set_fd(s->ssl, s->sock) != 1) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = SSL_accept(s->ssl);
|
||||||
|
if (ret == 1) {
|
||||||
|
// Handshake done
|
||||||
|
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = SSL_get_error(s->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
|
s->events = POLLIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
s->events = POLLOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server socket error - close the connection
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1945,7 +2235,26 @@ static void socket_update(Socket *s)
|
|||||||
s->events = 0;
|
s->events = 0;
|
||||||
} else {
|
} else {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
assert(0); // TODO
|
int ret = SSL_shutdown(s->ssl);
|
||||||
|
if (ret == 1) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = SSL_get_error(s->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
|
s->events = POLLIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
s->events = POLLOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2107,6 +2416,9 @@ static int socket_manager_translate_events_nolock(
|
|||||||
s->sock = sock;
|
s->sock = sock;
|
||||||
s->events = 0;
|
s->events = 0;
|
||||||
s->user = NULL;
|
s->user = NULL;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
s->ssl = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
socket_update(s);
|
socket_update(s);
|
||||||
if (s->state == SOCKET_STATE_DIED) {
|
if (s->state == SOCKET_STATE_DIED) {
|
||||||
@@ -2192,23 +2504,35 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
{
|
{
|
||||||
char portstr[16];
|
char portstr[16];
|
||||||
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
|
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
|
||||||
if (len < 0 || len >= (int) sizeof(portstr))
|
assert(len > 1 && len < (int) sizeof(portstr));
|
||||||
return -1;
|
|
||||||
|
|
||||||
struct addrinfo hints = {0};
|
struct addrinfo hints = {0};
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
|
||||||
|
if (name == NULL) {
|
||||||
|
free_addr_list(resolved, num_resolved);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
name->refs = 0;
|
||||||
|
memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
|
||||||
|
name->data[targets[i].name.len] = '\0';
|
||||||
|
char *hostname = name->data;
|
||||||
|
#else
|
||||||
char hostname[1<<9]; // TODO: Is this a good length?
|
char hostname[1<<9]; // TODO: Is this a good length?
|
||||||
if (targets[i].name.len >= (int) sizeof(hostname))
|
if (targets[i].name.len >= (int) sizeof(hostname))
|
||||||
return -1;
|
return -1;
|
||||||
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
||||||
hostname[targets[i].name.len] = '\0';
|
hostname[targets[i].name.len] = '\0';
|
||||||
|
#endif
|
||||||
struct addrinfo *res = NULL;
|
struct addrinfo *res = NULL;
|
||||||
int ret = getaddrinfo(hostname, portstr, &hints, &res);
|
int ret = getaddrinfo(hostname, portstr, &hints, &res);
|
||||||
if (ret != 0)
|
if (ret != 0) {
|
||||||
return -1;
|
free_addr_list(resolved, num_resolved);
|
||||||
|
return -1; // TODO: free all allocated registered names
|
||||||
|
}
|
||||||
|
|
||||||
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
||||||
if (rp->ai_family == AF_INET) {
|
if (rp->ai_family == AF_INET) {
|
||||||
@@ -2217,6 +2541,10 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = true;
|
resolved[num_resolved].is_ipv4 = true;
|
||||||
resolved[num_resolved].ipv4 = ipv4;
|
resolved[num_resolved].ipv4 = ipv4;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = name;
|
||||||
|
name->refs++;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
} else if (rp->ai_family == AF_INET6) {
|
} else if (rp->ai_family == AF_INET6) {
|
||||||
@@ -2225,11 +2553,20 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = false;
|
resolved[num_resolved].is_ipv4 = false;
|
||||||
resolved[num_resolved].ipv6 = ipv6;
|
resolved[num_resolved].ipv6 = ipv6;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = name;
|
||||||
|
name->refs++;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
if (name->refs == 0)
|
||||||
|
free(name);
|
||||||
|
#endif
|
||||||
|
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2238,6 +2575,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = true;
|
resolved[num_resolved].is_ipv4 = true;
|
||||||
resolved[num_resolved].ipv4 = targets[i].ipv4;
|
resolved[num_resolved].ipv4 = targets[i].ipv4;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = NULL;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2246,6 +2586,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = false;
|
resolved[num_resolved].is_ipv4 = false;
|
||||||
resolved[num_resolved].ipv6 = targets[i].ipv6;
|
resolved[num_resolved].ipv6 = targets[i].ipv6;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = NULL;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2262,6 +2605,11 @@ int socket_connect(SocketManager *sm, int num_targets,
|
|||||||
if (sm->num_used == sm->max_used)
|
if (sm->num_used == sm->max_used)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
#ifndef HTTPS_ENABLED
|
||||||
|
if (secure)
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
AddressAndPort resolved[MAX_CONNECT_TARGETS];
|
AddressAndPort resolved[MAX_CONNECT_TARGETS];
|
||||||
int num_resolved = resolve_connect_targets(
|
int num_resolved = resolve_connect_targets(
|
||||||
targets, num_targets, resolved, MAX_CONNECT_TARGETS);
|
targets, num_targets, resolved, MAX_CONNECT_TARGETS);
|
||||||
@@ -2292,6 +2640,10 @@ int socket_connect(SocketManager *sm, int num_targets,
|
|||||||
s->state = SOCKET_STATE_PENDING;
|
s->state = SOCKET_STATE_PENDING;
|
||||||
s->sock = NATIVE_SOCKET_INVALID;
|
s->sock = NATIVE_SOCKET_INVALID;
|
||||||
s->user = user;
|
s->user = user;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
s->server_secure_context = &sm->server_secure_context;
|
||||||
|
s->ssl = NULL;
|
||||||
|
#endif
|
||||||
sm->num_used++;
|
sm->num_used++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// src/basic.h
|
// src/basic.h
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -204,32 +208,43 @@ int mutex_unlock(Mutex *mutex);
|
|||||||
// src/secure_context.h
|
// src/secure_context.h
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef SERVER_CERTIFICATE_LIMIT
|
||||||
|
// Maximum number of certificates that can be
|
||||||
|
// associated to a TLS server. This doesn't include
|
||||||
|
// the default certificate.
|
||||||
|
#define SERVER_CERTIFICATE_LIMIT 8
|
||||||
|
#endif
|
||||||
|
|
||||||
int global_secure_context_init(void);
|
int global_secure_context_init(void);
|
||||||
int global_secure_context_free(void);
|
int global_secure_context_free(void);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
// TODO
|
|
||||||
SSL_CTX *p;
|
SSL_CTX *p;
|
||||||
#endif
|
#endif
|
||||||
} ClientSecureContext;
|
} ClientSecureContext;
|
||||||
|
|
||||||
int client_secure_context_init(ClientSecureContext *ctx);
|
int client_secure_context_init(ClientSecureContext *ctx);
|
||||||
int client_secure_context_free(ClientSecureContext *ctx);
|
void client_secure_context_free(ClientSecureContext *ctx);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
} SecureDomain;
|
char domain[128];
|
||||||
|
SSL_CTX *ctx;
|
||||||
|
#endif
|
||||||
|
} ServerCertificate;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
// TODO
|
|
||||||
SSL_CTX *p;
|
SSL_CTX *p;
|
||||||
|
int num_certs;
|
||||||
|
ServerCertificate certs[SERVER_CERTIFICATE_LIMIT];
|
||||||
#endif
|
#endif
|
||||||
} ServerSecureContext;
|
} ServerSecureContext;
|
||||||
|
|
||||||
int server_secure_context_init(ServerSecureContext *ctx);
|
int server_secure_context_init(ServerSecureContext *ctx,
|
||||||
int server_secure_context_free(ServerSecureContext *ctx);
|
HTTP_String cert_file, HTTP_String key_file);
|
||||||
|
void server_secure_context_free(ServerSecureContext *ctx);
|
||||||
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
||||||
|
|
||||||
@@ -366,6 +381,11 @@ typedef enum {
|
|||||||
SOCKET_STATE_DIED,
|
SOCKET_STATE_DIED,
|
||||||
} SocketState;
|
} SocketState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int refs;
|
||||||
|
char data[];
|
||||||
|
} RegisteredName;
|
||||||
|
|
||||||
// Internal use only
|
// Internal use only
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
@@ -374,6 +394,15 @@ typedef struct {
|
|||||||
};
|
};
|
||||||
bool is_ipv4;
|
bool is_ipv4;
|
||||||
Port port;
|
Port port;
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
// When connecting to a peer using TLS, if the address
|
||||||
|
// was resolved from a registered name, that name is
|
||||||
|
// used to request the correct certificate once the TCP
|
||||||
|
// handshake is established, and therefore need to
|
||||||
|
// store it somewhere until that happens.
|
||||||
|
RegisteredName *name;
|
||||||
|
#endif
|
||||||
} AddressAndPort;
|
} AddressAndPort;
|
||||||
|
|
||||||
// Internal use only
|
// Internal use only
|
||||||
@@ -405,6 +434,12 @@ typedef struct {
|
|||||||
AddressAndPort addr; // When num_addr=1
|
AddressAndPort addr; // When num_addr=1
|
||||||
AddressAndPort *addrs; // Dynamically allocated when num_addr>1
|
AddressAndPort *addrs; // Dynamically allocated when num_addr>1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
ServerSecureContext *server_secure_context;
|
||||||
|
SSL *ssl;
|
||||||
|
#endif
|
||||||
|
|
||||||
} Socket;
|
} Socket;
|
||||||
|
|
||||||
// Glorified array of sockets. This structure
|
// Glorified array of sockets. This structure
|
||||||
@@ -476,8 +511,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
// and secure connections.
|
// and secure connections.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
HTTP_String addr, Port port, HTTP_String cert_file,
|
||||||
HTTP_String key_file_name);
|
HTTP_String key_file);
|
||||||
|
|
||||||
// If the socket manager was configures to accept
|
// If the socket manager was configures to accept
|
||||||
// TLS connections, this adds additional certificates
|
// TLS connections, this adds additional certificates
|
||||||
|
|||||||
@@ -23,3 +23,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|||||||
+178
-10
@@ -1,36 +1,204 @@
|
|||||||
|
|
||||||
int global_secure_context_init(void)
|
int global_secure_context_init(void)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_library_init();
|
||||||
|
SSL_load_error_strings();
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int global_secure_context_free(void)
|
int global_secure_context_free(void)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
EVP_cleanup();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int client_secure_context_init(ClientSecureContext *ctx)
|
int client_secure_context_init(ClientSecureContext *ctx)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX *p = SSL_CTX_new(TLS_client_method());
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
|
||||||
|
|
||||||
|
SSL_CTX_set_verify(p, SSL_VERIFY_PEER, NULL);
|
||||||
|
|
||||||
|
if (SSL_CTX_set_default_verify_paths(p) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->p = p;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int client_secure_context_free(ClientSecureContext *ctx)
|
void client_secure_context_free(ClientSecureContext *ctx)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX_free(ctx->p);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_secure_context_init(ServerSecureContext *ctx)
|
#ifdef HTTPS_ENABLED
|
||||||
|
static int servername_callback(SSL *ssl, int *ad, void *arg)
|
||||||
{
|
{
|
||||||
// TODO
|
ServerSecureContext *ctx = arg;
|
||||||
|
|
||||||
|
(void) ad; // TODO: use this?
|
||||||
|
|
||||||
|
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||||
|
if (servername == NULL)
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
|
||||||
|
for (int i = 0; i < ctx->num_certs; i++) {
|
||||||
|
ServerCertificate *cert = &ctx->certs[i];
|
||||||
|
if (!strcmp(cert->domain, servername)) {
|
||||||
|
SSL_set_SSL_CTX(ssl, cert->ctx);
|
||||||
|
return SSL_TLSEXT_ERR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSL_TLSEXT_ERR_NOACK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int server_secure_context_init(ServerSecureContext *ctx,
|
||||||
|
HTTP_String cert_file, HTTP_String key_file)
|
||||||
|
{
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
|
||||||
|
|
||||||
|
char cert_buffer[1024];
|
||||||
|
if (cert_file.len >= (int) sizeof(cert_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
|
||||||
|
cert_buffer[cert_file.len] = '\0';
|
||||||
|
|
||||||
|
// Copy private key file path to static buffer
|
||||||
|
char key_buffer[1024];
|
||||||
|
if (key_file.len >= (int) sizeof(key_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(key_buffer, key_file.ptr, key_file.len);
|
||||||
|
key_buffer[key_file.len] = '\0';
|
||||||
|
|
||||||
|
// Load certificate and private key
|
||||||
|
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the private key matches the certificate
|
||||||
|
if (SSL_CTX_check_private_key(p) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_tlsext_servername_callback(p, servername_callback);
|
||||||
|
SSL_CTX_set_tlsext_servername_arg(p, ctx);
|
||||||
|
|
||||||
|
ctx->p = p;
|
||||||
|
ctx->num_certs = 0;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
(void) cert_file;
|
||||||
|
(void) key_file;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_secure_context_free(ServerSecureContext *ctx)
|
void server_secure_context_free(ServerSecureContext *ctx)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
SSL_CTX_free(ctx->p);
|
||||||
|
for (int i = 0; i < ctx->num_certs; i++)
|
||||||
|
SSL_CTX_free(ctx->certs[i].ctx);
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
|
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef HTTPS_ENABLED
|
||||||
|
if (ctx->num_certs == SERVER_CERTIFICATE_LIMIT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
|
||||||
|
|
||||||
|
char cert_buffer[1024];
|
||||||
|
if (cert_file.len >= (int) sizeof(cert_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
|
||||||
|
cert_buffer[cert_file.len] = '\0';
|
||||||
|
|
||||||
|
char key_buffer[1024];
|
||||||
|
if (key_file.len >= (int) sizeof(key_buffer)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(key_buffer, key_file.ptr, key_file.len);
|
||||||
|
key_buffer[key_file.len] = '\0';
|
||||||
|
|
||||||
|
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_check_private_key(p) != 1) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerCertificate *cert = &ctx->certs[ctx->num_certs];
|
||||||
|
if (domain.len >= (int) sizeof(cert->domain)) {
|
||||||
|
SSL_CTX_free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(cert->domain, domain.ptr, domain.len);
|
||||||
|
cert->domain[domain.len] = '\0';
|
||||||
|
cert->ctx = p;
|
||||||
|
ctx->num_certs++;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
|
(void) domain;
|
||||||
|
(void) cert_file;
|
||||||
|
(void) key_file;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-9
@@ -1,29 +1,40 @@
|
|||||||
|
|
||||||
|
#ifndef SERVER_CERTIFICATE_LIMIT
|
||||||
|
// Maximum number of certificates that can be
|
||||||
|
// associated to a TLS server. This doesn't include
|
||||||
|
// the default certificate.
|
||||||
|
#define SERVER_CERTIFICATE_LIMIT 8
|
||||||
|
#endif
|
||||||
|
|
||||||
int global_secure_context_init(void);
|
int global_secure_context_init(void);
|
||||||
int global_secure_context_free(void);
|
int global_secure_context_free(void);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
// TODO
|
|
||||||
SSL_CTX *p;
|
SSL_CTX *p;
|
||||||
#endif
|
#endif
|
||||||
} ClientSecureContext;
|
} ClientSecureContext;
|
||||||
|
|
||||||
int client_secure_context_init(ClientSecureContext *ctx);
|
int client_secure_context_init(ClientSecureContext *ctx);
|
||||||
int client_secure_context_free(ClientSecureContext *ctx);
|
void client_secure_context_free(ClientSecureContext *ctx);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
} SecureDomain;
|
char domain[128];
|
||||||
|
SSL_CTX *ctx;
|
||||||
|
#endif
|
||||||
|
} ServerCertificate;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
// TODO
|
|
||||||
SSL_CTX *p;
|
SSL_CTX *p;
|
||||||
|
int num_certs;
|
||||||
|
ServerCertificate certs[SERVER_CERTIFICATE_LIMIT];
|
||||||
#endif
|
#endif
|
||||||
} ServerSecureContext;
|
} ServerSecureContext;
|
||||||
|
|
||||||
int server_secure_context_init(ServerSecureContext *ctx);
|
int server_secure_context_init(ServerSecureContext *ctx,
|
||||||
int server_secure_context_free(ServerSecureContext *ctx);
|
HTTP_String cert_file, HTTP_String key_file);
|
||||||
|
void server_secure_context_free(ServerSecureContext *ctx);
|
||||||
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
||||||
|
|||||||
+197
-13
@@ -189,8 +189,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
HTTP_String addr, Port port, HTTP_String cert_file,
|
||||||
HTTP_String key_file_name)
|
HTTP_String key_file)
|
||||||
{
|
{
|
||||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -201,7 +201,8 @@ int socket_manager_listen_tls(SocketManager *sm,
|
|||||||
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (server_secure_context_init(&sm->server_secure_context) < 0) {
|
if (server_secure_context_init(&sm->server_secure_context,
|
||||||
|
cert_file, key_file) < 0) {
|
||||||
CLOSE_NATIVE_SOCKET(sm->secure_sock);
|
CLOSE_NATIVE_SOCKET(sm->secure_sock);
|
||||||
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -227,7 +228,7 @@ int socket_manager_add_certificate(SocketManager *sm,
|
|||||||
static bool is_secure(Socket *s)
|
static bool is_secure(Socket *s)
|
||||||
{
|
{
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
return (s->ssl != NULL);
|
return (s->server_secure_context != NULL);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@@ -269,6 +270,24 @@ connect_failed_because_of_peer(void)
|
|||||||
return connect_failed_because_of_peer_2(err);
|
return connect_failed_because_of_peer_2(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void free_addr_list(AddressAndPort *addrs, int num_addr)
|
||||||
|
{
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
for (int i = 0; i < num_addr; i++) {
|
||||||
|
RegisteredName *name = addrs[i].name;
|
||||||
|
if (name) {
|
||||||
|
assert(name->refs > 0);
|
||||||
|
name->refs--;
|
||||||
|
if (name->refs == 0)
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) addrs;
|
||||||
|
(void) num_addr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// This function moves the socket state machine
|
// This function moves the socket state machine
|
||||||
// to the next state until an I/O event would
|
// to the next state until an I/O event would
|
||||||
// be required to continue.
|
// be required to continue.
|
||||||
@@ -297,6 +316,13 @@ static void socket_update(Socket *s)
|
|||||||
if (s->sock != NATIVE_SOCKET_INVALID) {
|
if (s->sock != NATIVE_SOCKET_INVALID) {
|
||||||
// This is not the first attempt
|
// This is not the first attempt
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
if (s->ssl) {
|
||||||
|
SSL_free(s->ssl);
|
||||||
|
s->ssl = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
CLOSE_NATIVE_SOCKET(s->sock);
|
CLOSE_NATIVE_SOCKET(s->sock);
|
||||||
|
|
||||||
s->next_addr++;
|
s->next_addr++;
|
||||||
@@ -389,18 +415,76 @@ static void socket_update(Socket *s)
|
|||||||
|
|
||||||
case SOCKET_STATE_CONNECTED:
|
case SOCKET_STATE_CONNECTED:
|
||||||
{
|
{
|
||||||
|
if (!is_secure(s)) {
|
||||||
|
|
||||||
// We managed to connect to the peer.
|
// We managed to connect to the peer.
|
||||||
// We can free the target array if it
|
// We can free the target array if it
|
||||||
// was allocated dynamically.
|
// was allocated dynamically.
|
||||||
if (s->num_addr > 1)
|
if (s->num_addr > 1)
|
||||||
free(s->addrs);
|
free(s->addrs);
|
||||||
|
|
||||||
if (!is_secure(s)) {
|
|
||||||
s->events = 0;
|
s->events = 0;
|
||||||
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
||||||
} else {
|
} else {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
assert(0); // TODO
|
if (s->ssl == NULL) {
|
||||||
|
s->ssl = SSL_new(s->server_secure_context->p);
|
||||||
|
if (s->ssl == NULL) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_set_fd(s->ssl, s->sock) != 1) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressAndPort addr;
|
||||||
|
if (s->num_addr > 1)
|
||||||
|
addr = s->addrs[s->next_addr];
|
||||||
|
else
|
||||||
|
addr = s->addr;
|
||||||
|
|
||||||
|
if (addr.name)
|
||||||
|
SSL_set_tlsext_host_name(s->ssl, addr.name->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = SSL_connect(s->ssl);
|
||||||
|
if (ret == 1) {
|
||||||
|
// Handshake done
|
||||||
|
|
||||||
|
// We managed to connect to the peer.
|
||||||
|
// We can free the target array if it
|
||||||
|
// was allocated dynamically.
|
||||||
|
if (s->num_addr == 1)
|
||||||
|
free_addr_list(&s->addr, 1);
|
||||||
|
else {
|
||||||
|
assert(s->num_addr > 1);
|
||||||
|
free_addr_list(s->addrs, s->num_addr);
|
||||||
|
free(s->addrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = SSL_get_error(s->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
|
s->events = POLLIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
s->events = POLLOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state = SOCKET_STATE_PENDING;
|
||||||
|
s->events = 0;
|
||||||
|
again = true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,7 +497,45 @@ static void socket_update(Socket *s)
|
|||||||
s->events = 0;
|
s->events = 0;
|
||||||
} else {
|
} else {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
assert(0); // TODO
|
// Start server-side SSL handshake
|
||||||
|
if (!s->ssl) {
|
||||||
|
|
||||||
|
s->ssl = SSL_new(s->server_secure_context->p);
|
||||||
|
if (s->ssl == NULL) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_set_fd(s->ssl, s->sock) != 1) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = SSL_accept(s->ssl);
|
||||||
|
if (ret == 1) {
|
||||||
|
// Handshake done
|
||||||
|
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = SSL_get_error(s->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
|
s->events = POLLIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
s->events = POLLOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server socket error - close the connection
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,7 +553,26 @@ static void socket_update(Socket *s)
|
|||||||
s->events = 0;
|
s->events = 0;
|
||||||
} else {
|
} else {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
assert(0); // TODO
|
int ret = SSL_shutdown(s->ssl);
|
||||||
|
if (ret == 1) {
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = SSL_get_error(s->ssl, ret);
|
||||||
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
|
s->events = POLLIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
s->events = POLLOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state = SOCKET_STATE_DIED;
|
||||||
|
s->events = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -593,6 +734,9 @@ static int socket_manager_translate_events_nolock(
|
|||||||
s->sock = sock;
|
s->sock = sock;
|
||||||
s->events = 0;
|
s->events = 0;
|
||||||
s->user = NULL;
|
s->user = NULL;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
s->ssl = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
socket_update(s);
|
socket_update(s);
|
||||||
if (s->state == SOCKET_STATE_DIED) {
|
if (s->state == SOCKET_STATE_DIED) {
|
||||||
@@ -678,23 +822,35 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
{
|
{
|
||||||
char portstr[16];
|
char portstr[16];
|
||||||
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
|
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
|
||||||
if (len < 0 || len >= (int) sizeof(portstr))
|
assert(len > 1 && len < (int) sizeof(portstr));
|
||||||
return -1;
|
|
||||||
|
|
||||||
struct addrinfo hints = {0};
|
struct addrinfo hints = {0};
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
|
||||||
|
if (name == NULL) {
|
||||||
|
free_addr_list(resolved, num_resolved);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
name->refs = 0;
|
||||||
|
memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
|
||||||
|
name->data[targets[i].name.len] = '\0';
|
||||||
|
char *hostname = name->data;
|
||||||
|
#else
|
||||||
char hostname[1<<9]; // TODO: Is this a good length?
|
char hostname[1<<9]; // TODO: Is this a good length?
|
||||||
if (targets[i].name.len >= (int) sizeof(hostname))
|
if (targets[i].name.len >= (int) sizeof(hostname))
|
||||||
return -1;
|
return -1;
|
||||||
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
||||||
hostname[targets[i].name.len] = '\0';
|
hostname[targets[i].name.len] = '\0';
|
||||||
|
#endif
|
||||||
struct addrinfo *res = NULL;
|
struct addrinfo *res = NULL;
|
||||||
int ret = getaddrinfo(hostname, portstr, &hints, &res);
|
int ret = getaddrinfo(hostname, portstr, &hints, &res);
|
||||||
if (ret != 0)
|
if (ret != 0) {
|
||||||
return -1;
|
free_addr_list(resolved, num_resolved);
|
||||||
|
return -1; // TODO: free all allocated registered names
|
||||||
|
}
|
||||||
|
|
||||||
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
||||||
if (rp->ai_family == AF_INET) {
|
if (rp->ai_family == AF_INET) {
|
||||||
@@ -703,6 +859,10 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = true;
|
resolved[num_resolved].is_ipv4 = true;
|
||||||
resolved[num_resolved].ipv4 = ipv4;
|
resolved[num_resolved].ipv4 = ipv4;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = name;
|
||||||
|
name->refs++;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
} else if (rp->ai_family == AF_INET6) {
|
} else if (rp->ai_family == AF_INET6) {
|
||||||
@@ -711,11 +871,20 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = false;
|
resolved[num_resolved].is_ipv4 = false;
|
||||||
resolved[num_resolved].ipv6 = ipv6;
|
resolved[num_resolved].ipv6 = ipv6;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = name;
|
||||||
|
name->refs++;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
if (name->refs == 0)
|
||||||
|
free(name);
|
||||||
|
#endif
|
||||||
|
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -724,6 +893,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = true;
|
resolved[num_resolved].is_ipv4 = true;
|
||||||
resolved[num_resolved].ipv4 = targets[i].ipv4;
|
resolved[num_resolved].ipv4 = targets[i].ipv4;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = NULL;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -732,6 +904,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
resolved[num_resolved].is_ipv4 = false;
|
resolved[num_resolved].is_ipv4 = false;
|
||||||
resolved[num_resolved].ipv6 = targets[i].ipv6;
|
resolved[num_resolved].ipv6 = targets[i].ipv6;
|
||||||
resolved[num_resolved].port = targets[i].port;
|
resolved[num_resolved].port = targets[i].port;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
resolved[num_resolved].name = NULL;
|
||||||
|
#endif
|
||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -748,6 +923,11 @@ int socket_connect(SocketManager *sm, int num_targets,
|
|||||||
if (sm->num_used == sm->max_used)
|
if (sm->num_used == sm->max_used)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
#ifndef HTTPS_ENABLED
|
||||||
|
if (secure)
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
AddressAndPort resolved[MAX_CONNECT_TARGETS];
|
AddressAndPort resolved[MAX_CONNECT_TARGETS];
|
||||||
int num_resolved = resolve_connect_targets(
|
int num_resolved = resolve_connect_targets(
|
||||||
targets, num_targets, resolved, MAX_CONNECT_TARGETS);
|
targets, num_targets, resolved, MAX_CONNECT_TARGETS);
|
||||||
@@ -778,6 +958,10 @@ int socket_connect(SocketManager *sm, int num_targets,
|
|||||||
s->state = SOCKET_STATE_PENDING;
|
s->state = SOCKET_STATE_PENDING;
|
||||||
s->sock = NATIVE_SOCKET_INVALID;
|
s->sock = NATIVE_SOCKET_INVALID;
|
||||||
s->user = user;
|
s->user = user;
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
s->server_secure_context = &sm->server_secure_context;
|
||||||
|
s->ssl = NULL;
|
||||||
|
#endif
|
||||||
sm->num_used++;
|
sm->num_used++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-2
@@ -128,6 +128,11 @@ typedef enum {
|
|||||||
SOCKET_STATE_DIED,
|
SOCKET_STATE_DIED,
|
||||||
} SocketState;
|
} SocketState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int refs;
|
||||||
|
char data[];
|
||||||
|
} RegisteredName;
|
||||||
|
|
||||||
// Internal use only
|
// Internal use only
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
@@ -136,6 +141,15 @@ typedef struct {
|
|||||||
};
|
};
|
||||||
bool is_ipv4;
|
bool is_ipv4;
|
||||||
Port port;
|
Port port;
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
// When connecting to a peer using TLS, if the address
|
||||||
|
// was resolved from a registered name, that name is
|
||||||
|
// used to request the correct certificate once the TCP
|
||||||
|
// handshake is established, and therefore need to
|
||||||
|
// store it somewhere until that happens.
|
||||||
|
RegisteredName *name;
|
||||||
|
#endif
|
||||||
} AddressAndPort;
|
} AddressAndPort;
|
||||||
|
|
||||||
// Internal use only
|
// Internal use only
|
||||||
@@ -167,6 +181,12 @@ typedef struct {
|
|||||||
AddressAndPort addr; // When num_addr=1
|
AddressAndPort addr; // When num_addr=1
|
||||||
AddressAndPort *addrs; // Dynamically allocated when num_addr>1
|
AddressAndPort *addrs; // Dynamically allocated when num_addr>1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
ServerSecureContext *server_secure_context;
|
||||||
|
SSL *ssl;
|
||||||
|
#endif
|
||||||
|
|
||||||
} Socket;
|
} Socket;
|
||||||
|
|
||||||
// Glorified array of sockets. This structure
|
// Glorified array of sockets. This structure
|
||||||
@@ -238,8 +258,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
// and secure connections.
|
// and secure connections.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
HTTP_String addr, Port port, HTTP_String cert_file,
|
||||||
HTTP_String key_file_name);
|
HTTP_String key_file);
|
||||||
|
|
||||||
// If the socket manager was configures to accept
|
// If the socket manager was configures to accept
|
||||||
// TLS connections, this adds additional certificates
|
// TLS connections, this adds additional certificates
|
||||||
|
|||||||
Reference in New Issue
Block a user