Greenbone Vulnerability Management Libraries  22.8.0
mqtt.c
Go to the documentation of this file.
1 /* SPDX-FileCopyrightText: 2021-2023 Greenbone AG
2  *
3  * SPDX-License-Identifier: GPL-2.0-or-later
4  */
5 
27 #include "mqtt.h"
28 
29 #include "uuidutils.h" /* gvm_uuid_make */
30 
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #undef G_LOG_DOMAIN
35 #define G_LOG_DOMAIN "libgvm util"
36 
37 #define QOS 1
38 #define TIMEOUT 10000L
39 
40 typedef struct
41 {
42  void *client;
43  char *client_id;
44 } mqtt_t;
45 
46 static const char *global_server_uri = NULL;
47 static const char *global_username = NULL;
48 static const char *global_password = NULL;
49 static mqtt_t *global_mqtt_client = NULL;
50 static gboolean mqtt_initialized = FALSE;
51 
57 static void
58 mqtt_set_initialized_status (gboolean status)
59 {
60  mqtt_initialized = status;
61 }
62 
68 gboolean
70 {
71  return mqtt_initialized;
72 }
73 
79 static void
80 mqtt_set_global_server_uri (const char *server_uri_in)
81 {
82  global_server_uri = server_uri_in;
83 }
84 
90 static const char *
92 {
93  return global_server_uri;
94 }
95 
101 static void
102 mqtt_set_global_username (const char *username)
103 {
104  global_username = username;
105 }
106 
110 static const char *
112 {
113  return global_username;
114 }
115 
121 static void
122 mqtt_set_global_password (const char *password)
123 {
124  global_password = password;
125 }
126 
130 static const char *
132 {
133  return global_password;
134 }
135 
141 static mqtt_t *
143 {
144  return global_mqtt_client;
145 }
146 
150 static void
152 {
153  global_mqtt_client = mqtt;
154 }
155 
163 static int
165 {
166  int rc;
167 
168  rc = MQTTClient_disconnect5 (mqtt->client, 200,
169  MQTTREASONCODE_NORMAL_DISCONNECTION, NULL);
170  if (rc != MQTTCLIENT_SUCCESS)
171  {
172  g_warning ("Failed to disconnect: %s", MQTTClient_strerror (rc));
173  return -1;
174  }
175 
176  return 0;
177 }
178 
185 static void
187 {
188  if (mqtt == NULL)
189  return;
190 
191  MQTTClient client;
192  client = (MQTTClient) mqtt->client;
193 
194  if (client != NULL)
195  {
196  MQTTClient_destroy (&client);
197  client = NULL;
198  }
199 
200  return;
201 }
202 
208 static void
210 {
211  g_free ((*mqtt)->client_id);
212  g_free (*mqtt);
213  *mqtt = NULL;
214 }
215 
219 void
221 {
222  g_debug ("%s: start", __func__);
223  mqtt_t *mqtt = mqtt_get_global_client ();
224 
225  if (mqtt == NULL)
226  return;
227 
228  mqtt_client_destroy (mqtt);
229  mqtt_client_data_destroy (&mqtt);
230 
231  mqtt_set_global_client (NULL);
232 
233  g_debug ("%s: end", __func__);
234  return;
235 }
236 
245 static MQTTClient
246 mqtt_create (mqtt_t *mqtt, const char *address)
247 {
248  MQTTClient client;
249  MQTTClient_createOptions create_opts = MQTTClient_createOptions_initializer;
250  create_opts.MQTTVersion = MQTTVERSION_5;
251 
252  if (mqtt == NULL || mqtt->client_id == NULL)
253  return NULL;
254 
255  int rc = MQTTClient_createWithOptions (&client, address, mqtt->client_id,
256  MQTTCLIENT_PERSISTENCE_NONE, NULL,
257  &create_opts);
258 
259  if (rc != MQTTCLIENT_SUCCESS)
260  {
261  g_warning ("%s: Error creating MQTTClient: %s", __func__,
262  MQTTClient_strerror (rc));
263  MQTTClient_destroy (&client);
264  return NULL;
265  }
266  return client;
267 }
268 
276 static char *
278 {
279  if (mqtt == NULL)
280  return NULL;
281 
282  char *uuid;
283 
284  uuid = gvm_uuid_make ();
285  mqtt->client_id = uuid;
286 
287  return uuid;
288 }
289 
295 static int
296 mqtt_set_client (mqtt_t *mqtt, MQTTClient client)
297 {
298  if (mqtt == NULL)
299  {
300  return -1;
301  }
302  mqtt->client = client;
303  return 0;
304 }
305 
316 static int
317 mqtt_connect (mqtt_t *mqtt, const char *server_uri, const char *username,
318  const char *password)
319 {
320  int rc;
321  MQTTClient client;
322  MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer5;
323  MQTTProperties connect_properties = MQTTProperties_initializer;
324  MQTTResponse resp;
325 
326  if (mqtt == NULL)
327  return -1;
328 
329  client = mqtt_create (mqtt, server_uri);
330  if (!client)
331  return -2;
332 
333  conn_opts.keepAliveInterval = 0;
334  conn_opts.cleanstart = 1;
335  conn_opts.MQTTVersion = MQTTVERSION_5;
336 
337  if (username != NULL && password != NULL)
338  {
339  conn_opts.username = username;
340  conn_opts.password = password;
341  }
342 
343  resp = MQTTClient_connect5 (client, &conn_opts, &connect_properties, NULL);
344  rc = resp.reasonCode;
345  MQTTProperties_free (&connect_properties);
346  if (rc != MQTTCLIENT_SUCCESS)
347  {
348  g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
349  "%s: mqtt connection error to %s: %s", __func__, server_uri,
350  MQTTClient_strerror (rc));
351  MQTTResponse_free (resp);
352  return -3;
353  }
354 
355  mqtt_set_client (mqtt, client);
356 
357  return 0;
358 }
359 
367 int
368 mqtt_init (const char *server_uri)
369 {
370  return mqtt_init_auth (server_uri, NULL, NULL);
371 }
381 int
382 mqtt_init_auth (const char *server_uri, const char *username,
383  const char *password)
384 {
385  mqtt_t *mqtt = NULL;
386  const char *g_server_uri;
387  const char *g_username;
388  const char *g_password;
389 
390  g_debug ("%s: start", __func__);
391 
392  mqtt = g_malloc0 (sizeof (mqtt_t));
393  // Set random uuid as client id
394  if (mqtt_set_client_id (mqtt) == NULL)
395  {
396  g_warning ("%s: Could not set client id.", __func__);
397  g_free (mqtt);
398  mqtt = NULL;
399  return -1;
400  }
401  g_debug ("%s: client id set: %s", __func__, mqtt->client_id);
402  g_server_uri = mqtt_get_global_server_uri ();
403  if (g_server_uri == NULL)
404  mqtt_set_global_server_uri (server_uri);
405 
406  g_username = mqtt_get_global_username ();
407  if (g_username == NULL)
408  mqtt_set_global_username (username);
409 
410  g_password = mqtt_get_global_password ();
411  if (g_password == NULL)
412  mqtt_set_global_password (password);
413 
414  if (mqtt_connect (mqtt, server_uri, username, password))
415  {
416  g_warning ("%s: Unable to connect to MQTT broker.", __func__);
417  g_free (mqtt);
418  mqtt = NULL;
419  return -1;
420  }
421 
422  mqtt_set_global_client (mqtt);
424 
425  g_debug ("%s: end", __func__);
426  return 0;
427 }
428 
433 static void
435 {
436  const char *server_uri;
437  const char *username;
438  const char *password;
439 
440  server_uri = mqtt_get_global_server_uri ();
441  if (server_uri == NULL)
442  {
443  g_warning ("%s: mqtt_init() has to be called once at program start "
444  "else the server URI is not set. ",
445  __func__);
446  }
447  username = mqtt_get_global_username ();
448  password = mqtt_get_global_password ();
449  mqtt_init_auth (server_uri, username, password);
450 }
451 
461 static int
462 mqtt_client_publish (mqtt_t *mqtt, const char *topic, const char *msg)
463 {
464  MQTTClient client;
465  MQTTClient_message pubmsg = MQTTClient_message_initializer;
466  MQTTClient_deliveryToken token;
467  MQTTResponse resp;
468  int rc;
469 
470  client = mqtt->client;
471  if (client == NULL)
472  {
473  return -1;
474  }
475 
476  pubmsg.payload = (char *) msg;
477  pubmsg.payloadlen = (int) strlen (msg);
478  pubmsg.qos = QOS;
479  pubmsg.retained = 0;
480 
481  resp = MQTTClient_publishMessage5 (client, topic, &pubmsg, &token);
482  rc = resp.reasonCode;
483  if (rc != MQTTCLIENT_SUCCESS)
484  {
485  g_warning ("Failed to connect: %s", MQTTClient_strerror (rc));
486  MQTTResponse_free (resp);
487  return -2;
488  }
489 
490  if ((rc = MQTTClient_waitForCompletion (client, token, TIMEOUT))
491  != MQTTCLIENT_SUCCESS)
492  {
493  g_debug ("Message '%s' with delivery token %d could not be "
494  "published on topic %s",
495  msg, token, topic);
496  }
497 
498  return rc;
499 }
500 
509 int
510 mqtt_publish (const char *topic, const char *msg)
511 {
512  mqtt_t *mqtt = NULL;
513  int rc = 0;
514 
515  if ((mqtt_get_global_client ()) == NULL)
516  mqtt_reinit ();
517  mqtt = mqtt_get_global_client ();
518 
519  rc = mqtt_client_publish (mqtt, topic, msg);
520 
521  return rc;
522 }
523 
538 int
539 mqtt_publish_single_message (const char *server_uri_in, const char *topic,
540  const char *msg)
541 {
542  return mqtt_publish_single_message_auth (server_uri_in, NULL, NULL, topic,
543  msg);
544 }
561 int
562 mqtt_publish_single_message_auth (const char *server_uri_in,
563  const char *username_in,
564  const char *passwd_in, const char *topic,
565  const char *msg)
566 {
567  const char *server_uri;
568  const char *username = NULL;
569  const char *password = NULL;
570  mqtt_t *mqtt = NULL;
571  int ret = 0;
572 
573  // If server_uri is NULL try to get global
574  if (server_uri_in == NULL)
575  {
576  server_uri = mqtt_get_global_server_uri ();
577  if (server_uri == NULL)
578  {
579  g_warning (
580  "%s: No server URI provided and no global server URI available.",
581  __func__);
582  return -1;
583  }
584  }
585  else
586  {
587  server_uri = server_uri_in;
588  }
589 
590  if (username_in == NULL || passwd_in == NULL)
591  {
592  username = mqtt_get_global_username ();
593  password = mqtt_get_global_password ();
594  }
595  else
596  {
597  username = username_in;
598  password = passwd_in;
599  }
600 
601  mqtt = g_malloc0 (sizeof (mqtt_t));
602  // Set random uuid as client id
603  if (mqtt_set_client_id (mqtt) == NULL)
604  {
605  g_warning ("%s: Could not set client id.", __func__);
606  g_free (mqtt);
607  return -2;
608  }
609 
610  mqtt_connect (mqtt, server_uri, username, password);
611  mqtt_client_publish (mqtt, topic, msg);
612 
613  mqtt_disconnect (mqtt);
614  mqtt_client_destroy (mqtt);
615  mqtt_client_data_destroy (&mqtt);
616 
617  return ret;
618 }
619 
636 static int
637 mqtt_subscribe_r (mqtt_t *mqtt, int qos, const char *topic)
638 {
639  if (mqtt == NULL || mqtt->client == NULL)
640  {
641  return -1;
642  }
643  MQTTSubscribe_options opts = MQTTSubscribe_options_initializer;
644  MQTTProperties props = MQTTProperties_initializer;
645  MQTTResponse resp =
646  MQTTClient_subscribe5 (mqtt->client, topic, qos, &opts, &props);
647  if (resp.reasonCode != MQTTREASONCODE_GRANTED_QOS_1)
648  {
649  return -2;
650  }
651  return 0;
652 }
653 
669 int
670 mqtt_subscribe (const char *topic)
671 {
672  if ((mqtt_get_global_client ()) == NULL)
673  mqtt_reinit ();
674  return mqtt_subscribe_r (mqtt_get_global_client (), QOS, topic);
675 }
676 
688 static int
689 mqtt_unsubscribe_r (mqtt_t *mqtt, const char *topic)
690 {
691  if (mqtt == NULL || mqtt->client == NULL)
692  {
693  return -1;
694  }
695 
696  if (MQTTClient_unsubscribe (mqtt->client, topic) != MQTTCLIENT_SUCCESS)
697  {
698  return -2;
699  }
700 
701  return 0;
702 }
703 
714 int
715 mqtt_unsubscribe (const char *topic)
716 {
717  return mqtt_unsubscribe_r (mqtt_get_global_client (), topic);
718 }
719 
744 static int
745 mqtt_retrieve_message_r (mqtt_t *mqtt, char **topic, int *topic_len,
746  char **payload, int *payload_len,
747  const unsigned int timeout)
748 {
749  int rc = -1;
750  char *tmp = NULL;
751  MQTTClient_message *message = NULL;
752  if (mqtt == NULL || mqtt->client == NULL)
753  {
754  g_warning ("mqtt is not initialized.");
755  goto exit;
756  }
757  // copy from tmp into topic to make free work as usual and don't force the
758  // user to double check topic_len and topic
759  rc = MQTTClient_receive (mqtt->client, &tmp, topic_len, &message, timeout);
760  if (rc == MQTTCLIENT_SUCCESS || rc == MQTTCLIENT_TOPICNAME_TRUNCATED)
761  {
762  if (message)
763  {
764  g_debug ("%s: got message %s (%d) on topic %s (%d) \n", __func__,
765  (char *) message->payload, message->payloadlen, tmp,
766  *topic_len);
767 
768  if ((*topic = calloc (1, *topic_len)) == NULL)
769  {
770  goto exit;
771  }
772  rc = 0;
773  if ((memcpy (*topic, tmp, *topic_len)) == NULL)
774  {
775  g_warning ("unable to copy topic");
776  rc = -1;
777  goto exit;
778  }
779 
780  *payload_len = message->payloadlen;
781  *payload = calloc (1, message->payloadlen);
782  if ((memcpy (*payload, (char *) message->payload,
783  message->payloadlen))
784  == NULL)
785  {
786  g_warning ("unable to copy payload");
787  rc = -1;
788  goto exit;
789  }
790  }
791  else
792  {
793  rc = 1;
794  *payload = NULL;
795  *payload_len = 0;
796  *topic = NULL;
797  *topic_len = 0;
798  }
799  }
800  else
801  {
802  rc = -1;
803  }
804 
805 exit:
806  if (message != NULL)
807  MQTTClient_freeMessage (&message);
808  if (tmp != NULL)
809  MQTTClient_free (tmp);
810 
811  return rc;
812 }
813 
837 int
838 mqtt_retrieve_message (char **topic, int *topic_len, char **payload,
839  int *payload_len, const unsigned int timeout)
840 {
841  return mqtt_retrieve_message_r (mqtt_get_global_client (), topic, topic_len,
842  payload, payload_len, timeout);
843 }
mqtt_initialized
static gboolean mqtt_initialized
Definition: mqtt.c:50
mqtt_set_client_id
static char * mqtt_set_client_id(mqtt_t *mqtt)
Set a random client ID.
Definition: mqtt.c:277
mqtt_get_global_username
static const char * mqtt_get_global_username()
Get global username.
Definition: mqtt.c:111
mqtt_t::client
void * client
Definition: mqtt.c:42
global_server_uri
static const char * global_server_uri
Definition: mqtt.c:46
mqtt_client_destroy
static void mqtt_client_destroy(mqtt_t *mqtt)
Destroy the MQTTClient client of the mqtt_t.
Definition: mqtt.c:186
mqtt_client_publish
static int mqtt_client_publish(mqtt_t *mqtt, const char *topic, const char *msg)
Use the provided client to publish message on a topic.
Definition: mqtt.c:462
mqtt_set_global_client
static void mqtt_set_global_client(mqtt_t *mqtt)
Set global client.
Definition: mqtt.c:151
uuidutils.h
UUID creation.
G_LOG_DOMAIN
#define G_LOG_DOMAIN
Definition: mqtt.c:35
mqtt_client_data_destroy
static void mqtt_client_data_destroy(mqtt_t **mqtt)
Destroy the mqtt_t data.
Definition: mqtt.c:209
mqtt_connect
static int mqtt_connect(mqtt_t *mqtt, const char *server_uri, const char *username, const char *password)
Make new client and connect to mqtt broker.
Definition: mqtt.c:317
mqtt_init_auth
int mqtt_init_auth(const char *server_uri, const char *username, const char *password)
Init MQTT communication.
Definition: mqtt.c:382
gvm_uuid_make
char * gvm_uuid_make(void)
Make a new universal identifier.
Definition: uuidutils.c:30
mqtt_set_global_server_uri
static void mqtt_set_global_server_uri(const char *server_uri_in)
Set the global mqtt server URI.
Definition: mqtt.c:80
mqtt_is_initialized
gboolean mqtt_is_initialized()
Get the global init status.
Definition: mqtt.c:69
mqtt_disconnect
static int mqtt_disconnect(mqtt_t *mqtt)
Disconnect from the Broker.
Definition: mqtt.c:164
mqtt_set_initialized_status
static void mqtt_set_initialized_status(gboolean status)
Set the global init status.
Definition: mqtt.c:58
mqtt_set_global_username
static void mqtt_set_global_username(const char *username)
Set the global mqtt username.
Definition: mqtt.c:102
mqtt_init
int mqtt_init(const char *server_uri)
Init MQTT communication.
Definition: mqtt.c:368
TIMEOUT
#define TIMEOUT
Definition: mqtt.c:38
global_mqtt_client
static mqtt_t * global_mqtt_client
Definition: mqtt.c:49
mqtt_reinit
static void mqtt_reinit()
Reinitializes communication after mqtt_reset was used.
Definition: mqtt.c:434
global_password
static const char * global_password
Definition: mqtt.c:48
mqtt_retrieve_message_r
static int mqtt_retrieve_message_r(mqtt_t *mqtt, char **topic, int *topic_len, char **payload, int *payload_len, const unsigned int timeout)
wait for a given timeout in ms to retrieve any message of subscribed topics
Definition: mqtt.c:745
mqtt_unsubscribe_r
static int mqtt_unsubscribe_r(mqtt_t *mqtt, const char *topic)
unsubscribe a single topic.
Definition: mqtt.c:689
global_username
static const char * global_username
Definition: mqtt.c:47
mqtt_set_global_password
static void mqtt_set_global_password(const char *password)
Set the global mqtt password.
Definition: mqtt.c:122
mqtt_reset
void mqtt_reset()
Destroy MQTTClient handle and free mqtt_t.
Definition: mqtt.c:220
mqtt_create
static MQTTClient mqtt_create(mqtt_t *mqtt, const char *address)
Create a new mqtt client.
Definition: mqtt.c:246
mqtt_get_global_client
static mqtt_t * mqtt_get_global_client()
Definition: mqtt.c:142
mqtt_subscribe
int mqtt_subscribe(const char *topic)
subscribes to a single topic.
Definition: mqtt.c:670
mqtt_unsubscribe
int mqtt_unsubscribe(const char *topic)
unsubscribe a single topic.
Definition: mqtt.c:715
mqtt_publish_single_message
int mqtt_publish_single_message(const char *server_uri_in, const char *topic, const char *msg)
Send a single message.
Definition: mqtt.c:539
mqtt_get_global_password
static const char * mqtt_get_global_password()
Get global password.
Definition: mqtt.c:131
mqtt_set_client
static int mqtt_set_client(mqtt_t *mqtt, MQTTClient client)
Set MQTTClient of mqtt_t.
Definition: mqtt.c:296
mqtt_t
Definition: mqtt.c:41
mqtt_subscribe_r
static int mqtt_subscribe_r(mqtt_t *mqtt, int qos, const char *topic)
subscribes to a single topic.
Definition: mqtt.c:637
mqtt_get_global_server_uri
static const char * mqtt_get_global_server_uri()
Get global server URI.
Definition: mqtt.c:91
mqtt_retrieve_message
int mqtt_retrieve_message(char **topic, int *topic_len, char **payload, int *payload_len, const unsigned int timeout)
wait for a given timeout in ms to retrieve any message of subscribed topics
Definition: mqtt.c:838
mqtt.h
Protos for MQTT handling.
QOS
#define QOS
Definition: mqtt.c:37
mqtt_t::client_id
char * client_id
Definition: mqtt.c:43
mqtt_publish
int mqtt_publish(const char *topic, const char *msg)
Publish a message on topic using the global client.
Definition: mqtt.c:510
mqtt_publish_single_message_auth
int mqtt_publish_single_message_auth(const char *server_uri_in, const char *username_in, const char *passwd_in, const char *topic, const char *msg)
Send a single message with credentials.
Definition: mqtt.c:562