Add HTTPS support
This commit is contained in:
+202
-18
@@ -189,8 +189,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
||||
}
|
||||
|
||||
int socket_manager_listen_tls(SocketManager *sm,
|
||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
||||
HTTP_String key_file_name)
|
||||
HTTP_String addr, Port port, HTTP_String cert_file,
|
||||
HTTP_String key_file)
|
||||
{
|
||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
||||
return -1;
|
||||
@@ -201,7 +201,8 @@ int socket_manager_listen_tls(SocketManager *sm,
|
||||
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
||||
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);
|
||||
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
||||
return -1;
|
||||
@@ -227,7 +228,7 @@ int socket_manager_add_certificate(SocketManager *sm,
|
||||
static bool is_secure(Socket *s)
|
||||
{
|
||||
#ifdef HTTPS_ENABLED
|
||||
return (s->ssl != NULL);
|
||||
return (s->server_secure_context != NULL);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@@ -269,6 +270,24 @@ connect_failed_because_of_peer(void)
|
||||
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
|
||||
// to the next state until an I/O event would
|
||||
// be required to continue.
|
||||
@@ -297,6 +316,13 @@ static void socket_update(Socket *s)
|
||||
if (s->sock != NATIVE_SOCKET_INVALID) {
|
||||
// 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);
|
||||
|
||||
s->next_addr++;
|
||||
@@ -389,18 +415,76 @@ static void socket_update(Socket *s)
|
||||
|
||||
case SOCKET_STATE_CONNECTED:
|
||||
{
|
||||
// We managed to connect to the peer.
|
||||
// We can free the target array if it
|
||||
// was allocated dynamically.
|
||||
if (s->num_addr > 1)
|
||||
free(s->addrs);
|
||||
|
||||
if (!is_secure(s)) {
|
||||
|
||||
// We managed to connect to the peer.
|
||||
// We can free the target array if it
|
||||
// was allocated dynamically.
|
||||
if (s->num_addr > 1)
|
||||
free(s->addrs);
|
||||
|
||||
s->events = 0;
|
||||
s->state = SOCKET_STATE_ESTABLISHED_READY;
|
||||
} else {
|
||||
#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
|
||||
}
|
||||
}
|
||||
@@ -413,7 +497,45 @@ static void socket_update(Socket *s)
|
||||
s->events = 0;
|
||||
} else {
|
||||
#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
|
||||
}
|
||||
}
|
||||
@@ -431,7 +553,26 @@ static void socket_update(Socket *s)
|
||||
s->events = 0;
|
||||
} else {
|
||||
#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
|
||||
}
|
||||
}
|
||||
@@ -593,6 +734,9 @@ static int socket_manager_translate_events_nolock(
|
||||
s->sock = sock;
|
||||
s->events = 0;
|
||||
s->user = NULL;
|
||||
#ifdef HTTPS_ENABLED
|
||||
s->ssl = NULL;
|
||||
#endif
|
||||
|
||||
socket_update(s);
|
||||
if (s->state == SOCKET_STATE_DIED) {
|
||||
@@ -678,23 +822,35 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
||||
{
|
||||
char portstr[16];
|
||||
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
|
||||
if (len < 0 || len >= (int) sizeof(portstr))
|
||||
return -1;
|
||||
assert(len > 1 && len < (int) sizeof(portstr));
|
||||
|
||||
struct addrinfo hints = {0};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
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?
|
||||
if (targets[i].name.len >= (int) sizeof(hostname))
|
||||
return -1;
|
||||
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
||||
hostname[targets[i].name.len] = '\0';
|
||||
|
||||
#endif
|
||||
struct addrinfo *res = NULL;
|
||||
int ret = getaddrinfo(hostname, portstr, &hints, &res);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
if (ret != 0) {
|
||||
free_addr_list(resolved, num_resolved);
|
||||
return -1; // TODO: free all allocated registered names
|
||||
}
|
||||
|
||||
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
||||
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].ipv4 = ipv4;
|
||||
resolved[num_resolved].port = targets[i].port;
|
||||
#ifdef HTTPS_ENABLED
|
||||
resolved[num_resolved].name = name;
|
||||
name->refs++;
|
||||
#endif
|
||||
num_resolved++;
|
||||
}
|
||||
} 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].ipv6 = ipv6;
|
||||
resolved[num_resolved].port = targets[i].port;
|
||||
#ifdef HTTPS_ENABLED
|
||||
resolved[num_resolved].name = name;
|
||||
name->refs++;
|
||||
#endif
|
||||
num_resolved++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HTTPS_ENABLED
|
||||
if (name->refs == 0)
|
||||
free(name);
|
||||
#endif
|
||||
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
break;
|
||||
@@ -724,6 +893,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
||||
resolved[num_resolved].is_ipv4 = true;
|
||||
resolved[num_resolved].ipv4 = targets[i].ipv4;
|
||||
resolved[num_resolved].port = targets[i].port;
|
||||
#ifdef HTTPS_ENABLED
|
||||
resolved[num_resolved].name = NULL;
|
||||
#endif
|
||||
num_resolved++;
|
||||
}
|
||||
break;
|
||||
@@ -732,6 +904,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
||||
resolved[num_resolved].is_ipv4 = false;
|
||||
resolved[num_resolved].ipv6 = targets[i].ipv6;
|
||||
resolved[num_resolved].port = targets[i].port;
|
||||
#ifdef HTTPS_ENABLED
|
||||
resolved[num_resolved].name = NULL;
|
||||
#endif
|
||||
num_resolved++;
|
||||
}
|
||||
break;
|
||||
@@ -748,6 +923,11 @@ int socket_connect(SocketManager *sm, int num_targets,
|
||||
if (sm->num_used == sm->max_used)
|
||||
return -1;
|
||||
|
||||
#ifndef HTTPS_ENABLED
|
||||
if (secure)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
AddressAndPort resolved[MAX_CONNECT_TARGETS];
|
||||
int num_resolved = resolve_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->sock = NATIVE_SOCKET_INVALID;
|
||||
s->user = user;
|
||||
#ifdef HTTPS_ENABLED
|
||||
s->server_secure_context = &sm->server_secure_context;
|
||||
s->ssl = NULL;
|
||||
#endif
|
||||
sm->num_used++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user