From 969885d96791f04a13c375a70e89604b66f84002 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 10 Apr 2019 22:44:34 +0100 Subject: [PATCH] Crude random client testing This needs a lot of improvement, but is a reasonable start. --- test/random/Makefile | 26 ++++++ test/random/auth_plugin.c | 56 +++++++++++++ test/random/pwfile | 1 + test/random/random.conf | 92 +++++++++++++++++++++ test/random/random_client.py | 150 +++++++++++++++++++++++++++++++++++ test/random/test.py | 46 +++++++++++ 6 files changed, 371 insertions(+) create mode 100644 test/random/Makefile create mode 100644 test/random/auth_plugin.c create mode 100644 test/random/pwfile create mode 100644 test/random/random.conf create mode 100755 test/random/random_client.py create mode 100755 test/random/test.py diff --git a/test/random/Makefile b/test/random/Makefile new file mode 100644 index 00000000..87477d46 --- /dev/null +++ b/test/random/Makefile @@ -0,0 +1,26 @@ +include ../../config.mk + +.PHONY: all test + +ifeq ($(WITH_SHARED_LIBRARIES),yes) +LIB_DEP:=../../lib/libmosquitto.so.${SOVERSION} +else +LIB_DEP:=../../lib/libmosquitto.a +endif + +all : auth_plugin.so + +auth_plugin.so : auth_plugin.c + $(CC) ${CFLAGS} -fPIC -shared $< -o $@ -I../../lib -I../../src + +../lib/libmosquitto.so.${SOVERSION} : + $(MAKE) -C ../../lib + +../lib/libmosquitto.a : + $(MAKE) -C ../../lib libmosquitto.a + +clean : + -rm -f *.o random_client *.gcda *.gcno + +test : all + ./test.py diff --git a/test/random/auth_plugin.c b/test/random/auth_plugin.c new file mode 100644 index 00000000..0df05e6c --- /dev/null +++ b/test/random/auth_plugin.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include + +int mosquitto_auth_plugin_version(void) +{ + return MOSQ_AUTH_PLUGIN_VERSION; +} + +int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) +{ + srandom(time(NULL)); + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) +{ + if(random() % 2 == 0){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_ACL_DENIED; + } +} + +int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) +{ + if(random() % 2 == 0){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_AUTH; + } +} + +int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) +{ + return MOSQ_ERR_AUTH; +} + diff --git a/test/random/pwfile b/test/random/pwfile new file mode 100644 index 00000000..9556df90 --- /dev/null +++ b/test/random/pwfile @@ -0,0 +1 @@ +test:$6$cBP7e6sUriMSh8yf$+Z3E9P1g+Hui8zDJA+XJpTHl6+0eym0MtWokmOY4j1svAR5RtjZoXB4OQuHYzrGrdp1e8gXoqcKlcP+1lmepmg== diff --git a/test/random/random.conf b/test/random/random.conf new file mode 100644 index 00000000..171923b3 --- /dev/null +++ b/test/random/random.conf @@ -0,0 +1,92 @@ +per_listener_settings true + +# Unencrypted MQTT over TCP +listener 1883 + +listener 1884 +password_file pwfile + +listener 1885 +auth_plugin ./auth_plugin.so + +listener 1886 +password_file pwfile +auth_plugin ./auth_plugin.so + + +# Encrypted MQTT over TCP +listener 8883 +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key + +listener 8884 +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key +password_file pwfile + +listener 8885 +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key +auth_plugin ./auth_plugin.so + +listener 8886 +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key +password_file pwfile +auth_plugin ./auth_plugin.so + + +# Unencrypted MQTT over WebSockets +listener 8000 +protocol websockets + +listener 8001 +protocol websockets +password_file pwfile + +listener 8002 +protocol websockets +auth_plugin ./auth_plugin.so + +listener 8003 +protocol websockets +password_file pwfile +auth_plugin ./auth_plugin.so + + +# Encrypted MQTT over WebSockets +listener 4430 +protocol websockets +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key + +listener 4431 +protocol websockets +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key +password_file pwfile + +listener 4432 +protocol websockets +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key +auth_plugin ./auth_plugin.so + +listener 4433 +protocol websockets +cafile ../ssl/all-ca.crt +certfile ../ssl/server.crt +keyfile ../ssl/server.key +password_file pwfile +auth_plugin ./auth_plugin.so + + +#log_dest file mosquitto.log +#log_type all diff --git a/test/random/random_client.py b/test/random/random_client.py new file mode 100755 index 00000000..6206485a --- /dev/null +++ b/test/random/random_client.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +import paho.mqtt.client as paho +import random +import sys +import time + +# This is a client that carries out randomised behaviour. It is intended for +# use with the local config file. This file has multiple listeners configured: +# * 1883 - unencrypted MQTT over TCP with no authentication +# * 1884 - unencrypted MQTT over TCP with password authentication +# * 1885 - unencrypted MQTT over TCP with plugin authentication +# * 1886 - unencrypted MQTT over TCP with password and plugin authentication +# +# * 8883 - encrypted MQTT over TCP with no authentication +# * 8884 - encrypted MQTT over TCP with password authentication +# * 8885 - encrypted MQTT over TCP with plugin authentication +# * 8886 - encrypted MQTT over TCP with password and plugin authentication +# +# * 8000 - unencrypted MQTT over WebSockets with no authentication +# * 8001 - unencrypted MQTT over WebSockets with password authentication +# * 8002 - unencrypted MQTT over WebSockets with plugin authentication +# * 8003 - unencrypted MQTT over WebSockets with password and plugin authentication +# +# * 4430 - encrypted MQTT over WebSockets with no authentication +# * 4431 - encrypted MQTT over WebSockets with password authentication +# * 4432 - encrypted MQTT over WebSockets with plugin authentication +# * 4433 - encrypted MQTT over WebSockets with password and plugin authentication +# +# The client randomly picks: +# * A port out of the list +# * Whether to use encryption +# * Whether to use WebSockets +# * Clean start or not +# * Session expiry interval or not +# * QoS to use when subscribing - topics "outgoing/[client id]/message" and "response/#" +# * Lifetime of connection +# On a per publish message basis it chooses: +# * QoS of message +# * Topic of message "outgoing/[0-max client]/message" +# * Retain +# * Interval until next outgoing message + +ports = [ + {"port":1883, "tls":False, "transport":"tcp", "auth":False}, + {"port":1884, "tls":False, "transport":"tcp", "auth":True}, + {"port":1885, "tls":False, "transport":"tcp", "auth":True}, + {"port":1886, "tls":False, "transport":"tcp", "auth":True}, + + {"port":8883, "tls":True, "transport":"tcp", "auth":False}, + {"port":8884, "tls":True, "transport":"tcp", "auth":True}, + {"port":8885, "tls":True, "transport":"tcp", "auth":True}, + {"port":8886, "tls":True, "transport":"tcp", "auth":True}, + + {"port":8000, "tls":False, "transport":"websockets", "auth":False}, + {"port":8001, "tls":False, "transport":"websockets", "auth":True}, + {"port":8002, "tls":False, "transport":"websockets", "auth":True}, + {"port":8003, "tls":False, "transport":"websockets", "auth":True}, + + {"port":4430, "tls":True, "transport":"websockets", "auth":False}, + {"port":4431, "tls":True, "transport":"websockets", "auth":True}, + {"port":4432, "tls":True, "transport":"websockets", "auth":True}, + {"port":4433, "tls":True, "transport":"websockets", "auth":True}, + ] + +booleans = [True, False] +qos_values = [0, 1, 2] + + +def on_connect(client, userdata, flags, rc): + global running + if rc == 0: + client.subscribe("response/#", subscribe_qos) + client.subscribe("outgoing/%s/message" % (client_id), subscribe_qos) + else: + running = False + + +def on_message(client, userdata, msg): + pass + + +def on_publish(client, userdata, mid): + pass + + +def on_disconnect(client, userdata, rc): + running = False + + +def do_publish(client): + retain = random.choice(booleans) + qos = random.choice(qos_values) + topic = "outgoing/%d/message" % (random.uniform(1, 1000)) + payload = "message" + + client.publish(topic, payload, qos, retain) + + next_publish_time = time.time() + random.uniform(0.1, 2.0) + + +def main(): + global running + global lifetime + + mqttc = paho.Client(client_id, clean_session=clean_start, protocol=protocol, transport=transport) + mqttc.on_message = on_message + mqttc.on_publish = on_publish + mqttc.on_connect = on_connect + mqttc.on_disconnect = on_disconnect + if auth and random.choice(booleans): + if random.choice(booleans): + mqttc.username_pw_set("test", "password") + else: + mqttc.username_pw_set("bad", "bad") + + if use_tls: + mqttc.tls_set(ca_certs="../ssl/all-ca.crt") + + mqttc.connect("localhost", port) + mqttc.loop_start() + + while running: + time.sleep(0.1) + now = time.time() + if now > next_publish_time: + do_publish(mqttc) + if now > lifetime: + if random.choice(booleans): + mqttc.disconnect() + lifetime += 5.0 + else: + running = False + + +p = random.choice(ports) +port = p["port"] +use_tls = p["tls"] +transport = p["transport"] +auth = p["auth"] + +client_id = "cid"+sys.argv[1] +clean_start = random.choice(booleans) +subscribe_qos = random.choice(qos_values) +protocol = paho.MQTTv311 +next_publish_time = time.time() + random.uniform(0.1, 2.0) +lifetime = time.time() + random.uniform(5.0, 10.0) +running = True + +main() diff --git a/test/random/test.py b/test/random/test.py new file mode 100755 index 00000000..6d058182 --- /dev/null +++ b/test/random/test.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +import subprocess +import time +import sys + +def next_client(clients): + if len(clients) == 0: + return + + c = clients.pop() + args = ["./random_client.py", str(c)] + + proc = subprocess.Popen(args, stderr=subprocess.DEVNULL) + proc.cid = c + return proc + + +def run_clients(max_clients): + clients = list(range(1, max_clients)) + start_time = time.time() + + running_clients = [] + while True: + print(len(running_clients)) + if len(running_clients) < max_clients: + c = next_client(clients) + if c is not None: + running_clients.append(c) + else: + time.sleep(0.1) + + for c in running_clients: + c.poll() + if c.returncode is not None: + running_clients.remove(c) + clients.append(c.cid) + c.terminate() + c.wait() + + +env = {} +env["LD_LIBRARY_PATH"] = "../../lib" + +#broker = subprocess.Popen(["../../src/mosquitto", "-c", "random.conf"], env=env) +run_clients(1000)