#include <poll.h>
#include <sys/mman.h>
+#ifdef LWS_OPENSSL_SUPPORT
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+SSL_CTX *ssl_ctx;
+int use_ssl;
+#endif
+
+//#define DEBUG
+
#include "libwebsockets.h"
#ifdef DEBUG
#define LWS_ADDITIONAL_HDR_ALLOC 64
+
enum lws_connection_states {
WSI_STATE_HTTP,
WSI_STATE_HTTP_HEADERS,
enum lws_rx_parse_state lws_rx_parse_state;
size_t rx_packet_length;
+#ifdef LWS_OPENSSL_SUPPORT
+ char m_fOccupied;
+ struct sockaddr_in m_addr;
+ int m_addrlen;
+
+ SSL *ssl;
+
+ // these are valid if it is a POST
+
+ char m_fOngoingPost;
+ int m_nSessionID;
+
+ time_t m_timeStarted;
+ long long m_llTransferred;
+ long long m_llSizeIfKnown;
+
+ char m_szTitle[PATH_MAX];
+ char m_szStatus[PATH_MAX];
+#endif
+
/* last */
char user_space[0];
};
// fprintf(stderr, "closing fd=%d\n", wsi->sock);
- shutdown(wsi->sock, SHUT_RDWR);
- close(wsi->sock);
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ n = SSL_get_fd(wsi->ssl);
+ SSL_shutdown(wsi->ssl);
+ close(n);
+ SSL_free(wsi->ssl);
+ } else {
+#endif
+ shutdown(wsi->sock, SHUT_RDWR);
+ close(wsi->sock);
+#ifdef LWS_OPENSSL_SUPPORT
+ }
+#endif
free(wsi);
}
* which will be used by the user application to store
* per-session data. A pointer to this space is given
* when the user callback is called.
+ * @ssl_cert_filepath: If libwebsockets was compiled to use ssl, and you want
+ * to listen using SSL, set to the filepath to fetch the
+ * server cert from, otherwise NULL for unencrypted
+ * @ssl_private_key_filepath: filepath to private key if wanting SSL mode,
+ * else ignored
+ * @gid: group id to change to after setting listen socket, or -1.
+ * @uid: user id to change to after setting listen socket, or -1.
*
* This function forks to create the listening socket and takes care
* of all initialization in one step.
int (*callback)(struct libwebsocket *,
enum libwebsocket_callback_reasons,
void *, void *, size_t),
- int protocol, size_t user_area_size)
+ int protocol, size_t user_area_size,
+ const char * ssl_cert_filepath,
+ const char * ssl_private_key_filepath,
+ int gid, int uid)
{
int n;
int client;
struct libwebsocket *wsi[MAX_CLIENTS + 1];
struct pollfd fds[MAX_CLIENTS + 1];
int fds_count = 0;
- unsigned char buf[256];
+ unsigned char buf[1024];
int opt = 1;
+#ifdef LWS_OPENSSL_SUPPORT
+ const SSL_METHOD *method;
+ char ssl_err_buf[512];
+
+ use_ssl = ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL;
+ if (use_ssl)
+ fprintf(stderr, " Compiled with SSL support, using it\n");
+ else
+ fprintf(stderr, " Compiled with SSL support, but not using it\n");
+
+#else
+ if (ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL) {
+ fprintf(stderr, " Not compiled for OpenSSl support!\n");
+ return -1;
+ }
+ fprintf(stderr, " Compiled without SSL support, listening unencrypted\n");
+#endif
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ SSL_library_init();
+
+ OpenSSL_add_all_algorithms();
+ SSL_load_error_strings();
+
+ // Firefox insists on SSLv23 not SSLv3
+ // Konq disables SSLv2 by default now, SSLv23 works
+
+ method = SSLv23_server_method(); // create server instance
+ if (!method) {
+ fprintf(stderr, "problem creating ssl method: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return -1;
+ }
+ ssl_ctx = SSL_CTX_new(method); /* create context */
+ if (!ssl_ctx) {
+ printf("problem creating ssl context: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return -1;
+ }
+ /* set the local certificate from CertFile */
+ n = SSL_CTX_use_certificate_file(ssl_ctx,
+ ssl_cert_filepath, SSL_FILETYPE_PEM);
+ if (n != 1) {
+ fprintf(stderr, "problem getting cert '%s': %s\n",
+ ssl_cert_filepath,
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return -1;
+ }
+ /* set the private key from KeyFile */
+ if (SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_private_key_filepath,
+ SSL_FILETYPE_PEM) != 1) {
+ fprintf(stderr, "ssl problem getting key '%s': %s\n", ssl_private_key_filepath, ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return (-1);
+ }
+ /* verify private key */
+ if (!SSL_CTX_check_private_key(ssl_ctx)) {
+ fprintf(stderr, "Private SSL key does not match cert\n");
+ return (-1);
+ }
+
+ /* SSL is happy and has a cert it's content with */
+ }
+#endif
+
/* sanity check */
switch (protocol) {
if (n)
return sockfd;
+ // drop any root privs for this thread
+
+ if (gid != -1)
+ if (setgid(gid))
+ fprintf(stderr, "setgid: %s\n", strerror(errno));
+ if (uid != -1)
+ if (setuid(uid))
+ fprintf(stderr, "setuid: %s\n", strerror(errno));
+
/* we are running in a forked subprocess now */
listen(sockfd, 5);
if (fds[0].revents & POLLIN) {
- /* listen socket got a new connection... */
-
+ /* listen socket got an unencrypted connection... */
+
clilen = sizeof(cli_addr);
- fd = accept(sockfd, (struct sockaddr *)&cli_addr,
- &clilen);
+ fd = accept(sockfd,
+ (struct sockaddr *)&cli_addr,
+ &clilen);
if (fd < 0) {
fprintf(stderr, "ERROR on accept");
continue;
}
-
+
if (fds_count >= MAX_CLIENTS) {
fprintf(stderr, "too busy");
close(fd);
continue;
}
-
-// fprintf(stderr, "accepted new conn port %u on fd=%d\n",
-// ntohs(cli_addr.sin_port), fd);
-
- /* intialize the instance struct */
-
+
wsi[fds_count] = malloc(sizeof(struct libwebsocket) +
user_area_size);
if (!wsi[fds_count])
return -1;
-
+
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+
+ wsi[fds_count]->ssl = SSL_new(ssl_ctx); // get new SSL state with context
+ if (wsi[fds_count]->ssl == NULL) {
+ fprintf(stderr, "SSL_new failed: %s\n",
+ ERR_error_string(SSL_get_error(wsi[fds_count]->ssl, 0), NULL));
+ free(wsi[fds_count]);
+ continue;
+ }
+
+ SSL_set_fd(wsi[fds_count]->ssl, fd); // set SSL socket
+
+ n = SSL_accept(wsi[fds_count]->ssl);
+ if (n != 1) {
+ /* browsers seem to probe with various ssl params which fail then retry */
+ debug("SSL_accept failed for socket %u: %s\n",
+ fd,
+ ERR_error_string(SSL_get_error(wsi[fds_count]->ssl, n),
+ NULL));
+ SSL_free(wsi[fds_count]->ssl);
+ free(wsi[fds_count]);
+ continue;
+ }
+ debug("accepted new SSL conn port %u on fd=%d SSL ver %s\n",
+ ntohs(cli_addr.sin_port), fd, SSL_get_version(wsi[fds_count]->ssl));
+
+ } else {
+// fprintf(stderr, "accepted new conn port %u on fd=%d\n",
+// ntohs(cli_addr.sin_port), fd);
+ }
+#endif
+
+ /* intialize the instance struct */
+
wsi[fds_count]->sock = fd;
wsi[fds_count]->state = WSI_STATE_HTTP;
wsi[fds_count]->name_buffer_pos = 0;
continue;
// fprintf(stderr, "POLLIN\n");
-
- n = recv(fds[client].fd, buf, sizeof(buf), 0);
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl)
+ n = SSL_read(wsi[client]->ssl, buf, sizeof buf);
+ else
+#endif
+ n = recv(fds[client].fd, buf, sizeof(buf), 0);
+
+// fprintf(stderr, "read returned %d\n", n);
+
if (n < 0) {
fprintf(stderr, "Socket read returned %d\n", n);
continue;
}
fatal:
+ /* listening socket */
close(fds[0].fd);
for (client = 1; client < fds_count; client++)
libwebsocket_close_and_free_session(wsi[client]);
+#ifdef LWS_OPENSSL_SUPPORT
+ SSL_CTX_free(ssl_ctx);
+#endif
kill(0, SIGTERM);
return 0;
"a v76 close, sending ack\n");
buf[0] = 0xff;
buf[1] = 0;
- n = write(wsi->sock, buf, 2);
+ n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
return -1;
"Sec-WebSocket-Origin: ");
strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
- strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://");
- p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ strcpy(p, "\x0d\x0aSec-WebSocket-Location: wss://");
+ p += strlen("\x0d\x0aSec-WebSocket-Location: wss://");
+ } else {
+#endif
+ strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://");
+ p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
+#ifdef LWS_OPENSSL_SUPPORT
+ }
+#endif
strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token);
p += wsi->utf8_token[WSI_TOKEN_HOST].token_len;
strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token);
#ifdef DEBUG
fwrite(response, 1, p - response, stderr);
#endif
- n = write(wsi->sock, response, p - response);
+ n = libwebsocket_write(wsi, (unsigned char *)response, p - response,
+ LWS_WRITE_HTTP);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
goto bail;
#endif
send_raw:
-
- n = send(wsi->sock, buf - pre, len + pre + post, 0);
- if (n < 0) {
- fprintf(stderr, "ERROR writing to socket");
- return -1;
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ n = SSL_write(wsi->ssl, buf - pre, len + pre + post);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+ } else {
+#endif
+ n = send(wsi->sock, buf - pre, len + pre + post, 0);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+#ifdef LWS_OPENSSL_SUPPORT
}
-
+#endif
// fprintf(stderr, "written %d bytes to client\n", (int)len);
return 0;
#define LOCAL_RESOURCE_PATH "/usr/share/libwebsockets-test-server"
static int port = 7681;
static int ws_protocol = 76;
+static int use_ssl = 0;
struct per_session_data {
int number;
*/
case LWS_CALLBACK_SEND:
n = sprintf(p, "%d", pss->number++);
- n = libwebsocket_write(wsi, (unsigned char *)p, n, 0);
+ n = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
exit(1);
case LWS_CALLBACK_HTTP:
uri = libwebsocket_get_uri(wsi);
+
+ fprintf(stderr, "serving HTTP URI %s\n", uri);
+
if (uri && strcmp(uri, "/favicon.ico") == 0) {
if (libwebsockets_serve_http_file(wsi,
LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon"))
{ "help", no_argument, NULL, 'h' },
{ "port", required_argument, NULL, 'p' },
{ "protocol", required_argument, NULL, 'r' },
+ { "ssl", no_argument, NULL, 's' },
{ NULL, 0, 0, 0 }
};
int main(int argc, char **argv)
{
int n = 0;
+ const char * cert_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
+ const char * key_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
fprintf(stderr, "libwebsockets test server\n"
"Copyright 2010 Andy Green <andy@warmcat.com> "
if (n < 0)
continue;
switch (n) {
+ case 's':
+ use_ssl = 1;
+ break;
case 'p':
port = atoi(optarg);
break;
exit(1);
}
}
+
+ if (!use_ssl)
+ cert_path = key_path = NULL;
if (libwebsocket_create_server(port, websocket_callback, ws_protocol,
- sizeof(struct per_session_data)) < 0) {
+ sizeof(struct per_session_data),
+ cert_path, key_path, -1, -1) < 0) {
fprintf(stderr, "libwebsocket init failed\n");
return -1;
}