diff --git a/ChangeLog.txt b/ChangeLog.txt
index fd86cb4d..329145ca 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -45,6 +45,8 @@ Broker:
of `per_listener_settings`.
- Protocol version numbers reported in the log when a client connects now
match the MQTT protocol version numbers, not internal Mosquitto values.
+- Add the `websockets_origin` option to allow optional enforcement of origin
+ when a connection attempts an upgrade to WebSockets.
Client library:
- Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair
diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml
index 9d36300c..5734b485 100644
--- a/man/mosquitto.conf.5.xml
+++ b/man/mosquitto.conf.5.xml
@@ -1285,6 +1285,19 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
Not reloaded on reload signal.
+
+ size
+
+ Change the websockets headers size. This is a
+ global option, it is not possible to set per
+ listener. This option sets the size of the buffer
+ used in the libwebsockets library when reading HTTP
+ headers. If you are passing large header data such
+ as cookies then you may need to increase this
+ value. If left unset, or set to 0, then the default
+ of 1024 bytes will be used.
+
+ level
@@ -1300,16 +1313,23 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
- size
+ level
- Change the websockets headers size. This is a
- global option, it is not possible to set per
- listener. This option sets the size of the buffer
- used in the libwebsockets library when reading HTTP
- headers. If you are passing large header data such
- as cookies then you may need to increase this
- value. If left unset, or set to 0, then the default
- of 1024 bytes will be used.
+
+ If set, this will be compared to the http origin
+ header when a connection attempts to upgrade to
+ WebSockets. Only matching origins will be allowed.
+ Use the exact string provided by the browser in the
+ origin header, e.g.
+ http://example.com:8080.
+ Can be specified multiple times per listener.
+
+
+ If not set, connections from any origin will be allowed.
+
+
+ Requires libwebsockets 3.1.0 or later.
+
diff --git a/mosquitto.conf b/mosquitto.conf
index 36bcb1c4..99282299 100644
--- a/mosquitto.conf
+++ b/mosquitto.conf
@@ -280,6 +280,14 @@
# unset, or set to 0, then the default of 1024 bytes will be used.
#websockets_headers_size
+# Enforce origin checking for websockets connections. This should be set to the
+# string of the host that you wish to allow connections from, as set as the
+# Origin header in the http request.
+# For example: https://example.com:8080
+# If left unset will allow connections from any origin.
+# May be set multiple times.
+#websockets_origin
+
# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
diff --git a/src/conf.c b/src/conf.c
index f7c08100..307922ff 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -253,7 +253,7 @@ void config__init(struct mosquitto__config *config)
void config__cleanup(struct mosquitto__config *config)
{
- int i;
+ int i, j;
mosquitto__free(config->clientid_prefixes);
mosquitto__free(config->persistence_location);
@@ -298,6 +298,10 @@ void config__cleanup(struct mosquitto__config *config)
#endif
#ifdef WITH_WEBSOCKETS
mosquitto__free(config->listeners[i].http_dir);
+ for(j=0; jlisteners[i].ws_origin_count; j++){
+ mosquitto__free(config->listeners[i].ws_origins[j]);
+ }
+ mosquitto__free(config->listeners[i].ws_origins);
#endif
#ifdef WITH_UNIX_SOCKETS
mosquitto__free(config->listeners[i].unix_socket_path);
@@ -799,6 +803,9 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload,
char *kpass_sha = NULL, *kpass_sha_bin = NULL;
char *keyform ;
#endif
+#ifdef WITH_WEBSOCKETS
+ char **ws_origins = NULL;
+#endif
*lineno = 0;
@@ -2307,6 +2314,24 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload,
return MOSQ_ERR_INVAL;
}
config->websockets_headers_size = (uint16_t)tmp_int;
+#else
+ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Websockets support not available.");
+#endif
+ }else if(!strcmp(token, "websockets_origin")){
+#ifdef WITH_WEBSOCKETS
+# if LWS_LIBRARY_VERSION_NUMBER >= 3001000
+ ws_origins = mosquitto__realloc(cur_listener->ws_origins, sizeof(char *)*(size_t)(cur_listener->ws_origin_count+1));
+ if(ws_origins == NULL){
+ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
+ return MOSQ_ERR_NOMEM;
+ }
+ ws_origins[cur_listener->ws_origin_count] = NULL;
+ if(conf__parse_string(&token, "websockets_origin", &ws_origins[cur_listener->ws_origin_count], &saveptr)) return MOSQ_ERR_INVAL;
+ cur_listener->ws_origins = ws_origins;
+ cur_listener->ws_origin_count++;
+# else
+ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: websockets_origin support not available, libwebsockets version is too old.");
+# endif
#else
log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Websockets support not available.");
#endif
diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h
index 86388b9e..3194a104 100644
--- a/src/mosquitto_broker_internal.h
+++ b/src/mosquitto_broker_internal.h
@@ -232,6 +232,8 @@ struct mosquitto__listener {
bool ws_in_init;
char *http_dir;
struct lws_protocols *ws_protocol;
+ char **ws_origins;
+ int ws_origin_count;
#endif
struct mosquitto__security_options security_options;
#ifdef WITH_UNIX_SOCKETS
diff --git a/src/websockets.c b/src/websockets.c
index 46688a2e..9b173ef4 100644
--- a/src/websockets.c
+++ b/src/websockets.c
@@ -447,6 +447,8 @@ static int callback_http(
struct stat filestat;
struct mosquitto *mosq;
struct lws_pollargs *pollargs = (struct lws_pollargs *)in;
+ int hlen;
+ int i;
/* FIXME - ssl cert verification is done here. */
@@ -538,6 +540,26 @@ static int callback_http(
/* Access control here */
return 0;
+#if LWS_LIBRARY_VERSION_NUMBER >= 3001000
+ case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE:
+ hack = lws_context_user(lws_get_context(wsi));
+ if(hack->listener->ws_origin_count){
+ hlen = lws_hdr_total_length(wsi, WSI_TOKEN_ORIGIN);
+ if(hlen <= 0 || hlen >= (int)sizeof(buf)){
+ return -1;
+ }
+ for(i=0; ilistener->ws_origin_count; i++){
+ lws_hdr_copy(wsi, (char *)buf, sizeof(buf), WSI_TOKEN_ORIGIN);
+ if((!strcmp(hack->listener->ws_origins[i], (char *)buf))){
+ return 0;
+ }
+ }
+ return -1;
+ }else{
+ return 0;
+ }
+#endif
+
case LWS_CALLBACK_HTTP_WRITEABLE:
/* Send our data here */
if(u && u->fptr){