diff --git a/Makefile b/Makefile
index dc91f36..301780f 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,8 @@ LIB_WS = libws.a
INCLUDE = include
CFLAGS += -Wall -Wextra -O2
CFLAGS += -I $(INCLUDE) -std=c99 -pedantic
-LDLIBS = $(LIB_WS) -pthread
+#LDLIBS = $(LIB_WS) -pthread -lssl -lcrypto
+LDLIBS = $(LIB_WS) -pthread -lssl -lcrypto
ARFLAGS = cru
MCSS_DIR ?= /usr/bin/
MANPAGES = doc/man/man3
diff --git a/certs/localhost.crt b/certs/localhost.crt
new file mode 100644
index 0000000..020a18c
--- /dev/null
+++ b/certs/localhost.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDzCCAfegAwIBAgIUOBsdLqSiHICAenBcLw1Gd4GildkwDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTExNTA1MDg1OFoXDTI0MTIx
+NTA1MDg1OFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAinMabl7EyoJLQkebJJaLfDv+23T1jQvHrHppHM6tGqhh
+Fdx+RH568KJepc/cxiuhQEAUp3uYJqPCZm7LXTvnk4zk8zIASoIaMsyDJ4SdEsMe
+h4jdALmaXJA89i6diiyhb46FDKE4pfj9xP1Zm6I3/o7b/gU30Do5g7ZVI2z6K+50
+tCCv4eovZ7ES18SDj6WpcutviQKaInJfUVy0+SH68hcf0P0x599LeplTFLNJBXIJ
+WEd4JvpLQ02ENrjJQa3oQrD00FthiacEjOgEqHMMoruUdT8x8C2PmBn+JpzcDwC/
+99od5lOHPykokCcFo8eTrZarkSJrBuJ12qzJuDA5OQIDAQABo1kwVzAUBgNVHREE
+DTALgglsb2NhbGhvc3QwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB
+MB0GA1UdDgQWBBRDZOrwgxsoDn8gmf1AZm7bEv0RDjANBgkqhkiG9w0BAQsFAAOC
+AQEANMBeZjgqgFoZnAXoKJYSQgvjhATMUIOw/NnvqTlKKKBp5R7R2kOmb/XDgF7Z
+0uQc7LRRy9diTVo3uUjlgFb2AfZMozqAqk0oIg2R6KdpMOWmce6j6BQEgtGEsHUx
+WmvaSgrOimEMO8MG2Hf+sHWLifhFVsd5eZHEBVRkk6a8ydVTBtntNke2Mt1/99WC
+qenTmGHe+WZ0YJouxeqyIpiTauKF1/YV2urQfSI3Dcu/fdUeoitiykp+7XEl8FZY
+MUOQxgEMnHYOGhpSbyD6YeonUGse/OZswZNPTWRPhpRRjOE6S5oJDjyZtgI8vzIY
+pm0FL5EJ4kRadxcCUx5WPN+KAA==
+-----END CERTIFICATE-----
diff --git a/certs/localhost.key b/certs/localhost.key
new file mode 100644
index 0000000..75b6720
--- /dev/null
+++ b/certs/localhost.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCKcxpuXsTKgktC
+R5sklot8O/7bdPWNC8esemkczq0aqGEV3H5Efnrwol6lz9zGK6FAQBSne5gmo8Jm
+bstdO+eTjOTzMgBKghoyzIMnhJ0Swx6HiN0AuZpckDz2Lp2KLKFvjoUMoTil+P3E
+/Vmbojf+jtv+BTfQOjmDtlUjbPor7nS0IK/h6i9nsRLXxIOPpaly62+JApoicl9R
+XLT5IfryFx/Q/THn30t6mVMUs0kFcglYR3gm+ktDTYQ2uMlBrehCsPTQW2GJpwSM
+6ASocwyiu5R1PzHwLY+YGf4mnNwPAL/32h3mU4c/KSiQJwWjx5OtlquRImsG4nXa
+rMm4MDk5AgMBAAECggEAAT3LN8BgiBbPPTDGbLdvWH2yuS3FKl63y318/gnB3lZy
+aWdk1/Eg4dG44sh4eKvpOhng/YfziywmePPnHabE+qsVnfMdss1UFg7Eocjz/hgz
+CMDSx3gCkPAnpcbw+W1RRL5sxbpX11AiWV7yq90yXpVSFmbeU18ER0CuJn9KhnSN
+ZaK3CuyS1vwdUbk/vtE1cV/RkbNlHTLAwvSUrxUz/pKCjxSTluJf6OrvzJYmaYeV
+g2Fcw5YBZwmXWic0TtZRc0jwuv1PgjqoLWkt/KcvGOJi9XlLl1BmSWWxTc9WRNN3
+8SbOZfbJTODb8RgZvwbbEZJfpjy2Ls+SGe18KiK98QKBgQC/78V9eliDJgKOkZvP
+dgB+DdHBQFZmWw5L5SgoD6lYuHAKalJtdxoMHJvzsVerTkBTDH4MOaZALacGbSEi
+TxVn7FHwBz3I5c8IUHeLn6Z+Sdvs0oFGCtVflR104sBIX5xR7uKl5Mp7p0lJ5C3T
+ojEBg1ZvTRArgZBLiRe5jKjcsQKBgQC4qRRFdUu+oy3CCzUUIfFLmNQ4DOUisYC8
+zO8qp9iNaWi6a1iEvlUMRLv8GpJYbFFxCvu7IRuho1lR89N2WrdOrr9KhT604rkm
+MUiZx/nPLNNOmw/LuApvKStj2jWGRbKfGjrrLDQITv8m2NO+jrHrxw5rmERiCVJr
+4o/1YRmnCQKBgF7B3itDkp00z8BZDKZQqI+S/QI+ZClmPNmlSbz1dnKxo8bQlN8s
+FLT0Dt4xqImPOVyG51fbwkMfBr26dMg+aOAEoLMysAMVya9eIpbQ+96PUj9J8b6F
+rK/iq0dAhxNz+HXOzSB/oqOHhDwxpZo9EZqgn7SVRC1marmP9iGJ2vgRAoGAbAU0
+P4FjAqyv+r/Vl+ZXIZLQKc2ZSFcvaMI08e3npqlPwyVCTbyNQbT/hnUAwMr0RT1Y
+jlnKsR4+BMuuGqDsdBGTAwO479Vk22ue/Z5WipO9NFFOxyvspqQdprah8t7Yo2uT
+pbhg469aYJ4cF2+fsr3puJUFA5hSZ5vL/I2FlQkCgYANv8fNaei0EYfuP92lb+re
+rpFjfpjP6ZNJjeWRoUHV7hP2IuSSscE7VFjGtzcebyhqAsJnEltAqaKU/iG+Z6tw
+si+GI73cief/jVU7jPNmwSkSL2djptRG87W6mSRc0u+Bbni08mx2ocdtJYwkcDcr
+Oe3L9UbIguutuWZ4d0rnqg==
+-----END PRIVATE KEY-----
diff --git a/examples/echo/echo.c b/examples/echo/echo.c
index 058c3cc..f176803 100644
--- a/examples/echo/echo.c
+++ b/examples/echo/echo.c
@@ -125,6 +125,8 @@ int main(void)
*/
.host = "0.0.0.0",
.port = 8080,
+ //.cert = "certs/localhost.crt",
+ //.cert_key = "certs/localhost.key",
.thread_loop = 0,
.timeout_ms = 1000,
.evs.onopen = &onopen,
diff --git a/examples/echo/echo.html b/examples/echo/echo.html
index dcac1cc..7db9196 100644
--- a/examples/echo/echo.html
+++ b/examples/echo/echo.html
@@ -121,9 +121,9 @@
wsServer
onclick="document.getElementById('txtServer').value='ws://localhost:8080'">
-
-
+
+
diff --git a/include/ws.h b/include/ws.h
index 900d03b..6d50312 100644
--- a/include/ws.h
+++ b/include/ws.h
@@ -280,6 +280,16 @@ extern "C" {
* @brief Listening port
*/
uint16_t port;
+#ifdef ENABLE_SSL
+ /**
+ * @brief SSL certificate path (if any) (MUST to enable "wss")
+ */
+ const char *cert;
+ /**
+ * @brief SSL private key path (if any) (MUST to enable "wss")
+ */
+ const char *cert_key;
+#endif
/**
* @brief Whether if the ws_socket() should create a new thread
* and be non-blocking (1) or not (0).
@@ -305,6 +315,10 @@ extern "C" {
/* Internal usage. */
extern int get_handshake_accept(char *wsKey, unsigned char **dest);
extern int get_handshake_response(char *hsrequest, char **hsresponse);
+#ifdef ENABLE_SSL
+ extern int ssl_init(const char *cert, const char *cert_key);
+ extern void ssl_end(void);
+#endif
/* External usage. */
extern char *ws_getaddress(ws_cli_conn_t client);
diff --git a/src/ws.c b/src/ws.c
index 7ee76a5..355051e 100644
--- a/src/ws.c
+++ b/src/ws.c
@@ -14,6 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
*/
+//#define ENABLE_SSL
#define _POSIX_C_SOURCE 200809L
#include
#include
@@ -27,6 +28,11 @@
#include
#include
+#ifdef ENABLE_SSL
+#include
+#include
+#endif
+
/* clang-format off */
#ifndef _WIN32
#include
@@ -59,6 +65,13 @@ typedef int socklen_t;
* @brief wsServer main routines.
*/
+#ifdef ENABLE_SSL
+/**
+ * @brief SSL context
+ */
+ SSL_CTX *ssl_ctx;
+#endif
+
/**
* @brief Client socks.
*/
@@ -88,6 +101,11 @@ struct ws_connection
int32_t current_ping_id;
pthread_mutex_t mtx_ping;
+#ifdef ENABLE_SSL
+ /* SSL */
+ SSL *ssl;
+#endif
+
/* Connection context */
void *connection_context;
@@ -341,7 +359,11 @@ static ssize_t send_all(
pthread_mutex_lock(&client->mtx_snd);
while (len)
{
+#ifdef ENABLE_SSL
+ r = client->ssl ? SSL_write(client->ssl, p, len) : send(client->client_sock, p, len, flags);
+#else
r = send(client->client_sock, p, len, flags);
+#endif
if (r == -1)
{
pthread_mutex_unlock(&client->mtx_snd);
@@ -373,6 +395,15 @@ static void close_client(struct ws_connection *client, int lock)
set_client_state(client, WS_STATE_CLOSED);
+#ifdef ENABLE_SSL
+ /* Close the client SSL instance */
+ if (client->ssl)
+ {
+ SSL_shutdown(client->ssl);
+ SSL_free(client->ssl);
+ }
+#endif
+
close_socket(client->client_sock);
/* Destroy client mutexes and clear fd 'slot'. */
@@ -656,7 +687,11 @@ static int ws_sendframe_internal(struct ws_connection *client, const char *msg,
output = 0;
if (client && port == 0)
{
+#ifdef ENABLE_SSL
+ output = client->ssl ? SSL_write(client->ssl, response, idx_response) : SEND(client, response, idx_response);
+#else
output = SEND(client, response, idx_response);
+#endif
goto skip_broadcast;
}
@@ -671,7 +706,11 @@ static int ws_sendframe_internal(struct ws_connection *client, const char *msg,
get_client_state(cli) == WS_STATE_OPEN &&
(cli->ws_srv.port == port))
{
+#ifdef ENABLE_SSL
+ if ((send_ret = cli->ssl ? SSL_write(cli->ssl, response, idx_response) : SEND(cli, response, idx_response)) != -1)
+#else
if ((send_ret = SEND(cli, response, idx_response)) != -1)
+#endif
output += send_ret;
else
{
@@ -1027,7 +1066,11 @@ static int do_handshake(struct ws_frame_data *wfd)
ssize_t n; /* Read/Write bytes. */
/* Read the very first client message. */
+#ifdef ENABLE_SSL
+ if ((n = (wfd->client)->ssl ? SSL_read((wfd->client)->ssl, wfd->frm, sizeof(wfd->frm) - 1) : RECV(wfd->client, wfd->frm, sizeof(wfd->frm) - 1)) < 0)
+#else
if ((n = RECV(wfd->client, wfd->frm, sizeof(wfd->frm) - 1)) < 0)
+#endif
return (-1);
/* Advance our pointers before the first next_byte(). */
@@ -1055,7 +1098,11 @@ static int do_handshake(struct ws_frame_data *wfd)
response);
/* Send handshake. */
- if (SEND(wfd->client, response, strlen(response)) < 0)
+#ifdef ENABLE_SSL
+ if (((wfd->client)->ssl ? SSL_write((wfd->client)->ssl, response, strlen(response)) : SEND(wfd->client, response, strlen(response))) < 0)
+#else
+ if ((SEND(wfd->client, response, strlen(response))) < 0)
+#endif
{
free(response);
DEBUG("As error has occurred while handshaking!\n");
@@ -1182,7 +1229,11 @@ static inline int next_byte(struct ws_frame_data *wfd)
/* If empty or full. */
if (wfd->cur_pos == 0 || wfd->cur_pos == wfd->amt_read)
{
+#ifdef ENABLE_SSL
+ if ((n = (wfd->client)->ssl ? SSL_read((wfd->client)->ssl, wfd->frm, sizeof(wfd->frm)) : RECV(wfd->client, wfd->frm, sizeof(wfd->frm))) <= 0)
+#else
if ((n = RECV(wfd->client, wfd->frm, sizeof(wfd->frm))) <= 0)
+#endif
{
wfd->error = 1;
DEBUG("An error has occurred while trying to read next byte\n");
@@ -1884,6 +1935,22 @@ static void *ws_accept(void *data)
client_socks[i].client_id = get_next_cid();
set_client_address(&client_socks[i]);
+#ifdef ENABLE_SSL
+ /* Accept the SSL instance */
+ client_socks[i].ssl = 0;
+ if (ssl_ctx)
+ {
+ SSL *cli_ssl = SSL_new(ssl_ctx);
+ if (SSL_set_fd(cli_ssl, new_sock))
+ {
+ if (SSL_accept(cli_ssl))
+ {
+ client_socks[i].ssl = cli_ssl;
+ }
+ }
+ }
+#endif
+
if (pthread_mutex_init(&client_socks[i].mtx_state, NULL))
panic("Error on allocating close mutex");
if (pthread_cond_init(&client_socks[i].cnd_state_close, NULL))
@@ -1910,6 +1977,13 @@ static void *ws_accept(void *data)
close_socket(new_sock);
}
+#ifdef ENABLE_SSL
+ if (ssl_ctx)
+ {
+ SSL_CTX_free(ssl_ctx);
+ }
+#endif
+
free(data);
return (data);
}
@@ -2022,6 +2096,14 @@ int ws_socket(struct ws_server *ws_srv)
setvbuf(stdout, NULL, _IONBF, 0);
#endif
+#ifdef ENABLE_SSL
+ /* Initialize SSL context */
+ if (ssl_init(ws_srv->cert, ws_srv->cert_key))
+ {
+ panic("Unable to initialize SSL context!");
+ }
+#endif
+
/* Create socket and bind. */
sock = do_bind_socket(ws_srv);
@@ -2099,3 +2181,65 @@ int ws_file(struct ws_events *evs, const char *file)
return (0);
}
#endif
+
+#ifdef ENABLE_SSL
+/**
+ * @brief Initialization of the SSL functions
+ *
+ * @param cert path to the certificate (.pem).
+ *
+ * @param cert_key path to the certificate key (.pem).
+ *
+ * @return nothing
+ *
+ */
+int ssl_init(const char *cert, const char *cert_key)
+{
+ ssl_ctx = 0;
+
+ if (!cert || !cert_key)
+ {
+ printf("SSL DISABLED\n");
+ return 0;
+ }
+
+ if (!*cert || !*cert_key)
+ {
+ return -1;
+ }
+
+ const SSL_METHOD *method;
+
+ method = TLS_server_method();
+
+ ssl_ctx = SSL_CTX_new(method);
+ if (!ssl_ctx)
+ {
+ return -1;
+ }
+
+ /* Configure the context of SSL session */
+ if (SSL_CTX_use_certificate_file(ssl_ctx, cert, SSL_FILETYPE_PEM) <= 0 ||
+ SSL_CTX_use_PrivateKey_file(ssl_ctx, cert_key, SSL_FILETYPE_PEM) <= 0)
+ {
+ return -1;
+ }
+
+ atexit(ssl_end);
+
+ printf("SSL ENABLED\n");
+
+ return 0;
+}
+
+/**
+ * @brief Finalize the SSL session
+ *
+ * @return nothing
+ *
+ */
+void ssl_end(void)
+{
+ SSL_CTX_free(ssl_ctx);
+}
+#endif
\ No newline at end of file