1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 #include <stdio.h>
12 #include <setjmp.h>
13 #include <cmocka.h>  /* macros: https://api.cmocka.org/group__cmocka__asserts.html */
14 
15 #include "nx_api.h"
16 #include "nx_azure_iot_hub_client.h"
17 #include "nx_azure_iot_cert.h"
18 #include "nx_azure_iot_ciphersuites.h"
19 
20 
21 #define DEMO_DHCP_DISABLE
22 #define DEMO_IPV4_ADDRESS         IP_ADDRESS(192, 168, 100, 33)
23 #define DEMO_IPV4_MASK            0xFFFFFF00UL
24 #define DEMO_GATEWAY_ADDRESS      IP_ADDRESS(192, 168, 100, 1)
25 #define DEMO_DNS_SERVER_ADDRESS   IP_ADDRESS(192, 168, 100, 1)
26 #define NETWORK_DRIVER            _nx_ram_network_driver
27 
28 /* Include main.c in the test case since we need to disable DHCP in this test. */
29 #include "main.c"
30 
31 
32 #define STRING_UNSIGNED_ARGS(s) (UCHAR *)s, strlen(s)
33 
34 #ifndef DEMO_CLOUD_STACK_SIZE
35 #define DEMO_CLOUD_STACK_SIZE   2048
36 #endif /* DEMO_CLOUD_STACK_SIZE */
37 
38 #ifndef DEMO_CLOUD_THREAD_PRIORITY
39 #define DEMO_CLOUD_THREAD_PRIORITY  (4)
40 #endif /* DEMO_CLOUD_THREAD_PRIORITY */
41 
42 static UINT deplete_packets(NX_PACKET_POOL *pool_ptr, UINT remaining_packets);
43 static VOID release_packets(NX_PACKET_POOL *pool_ptr, UINT count);
44 
45 static NX_AZURE_IOT iot;
46 static NX_AZURE_IOT_HUB_CLIENT iot_client;
47 static NX_SECURE_X509_CERT root_ca_cert;
48 static UCHAR metadata_buffer[NX_AZURE_IOT_TLS_METADATA_BUFFER_SIZE];
49 static ULONG demo_cloud_thread_stack[DEMO_CLOUD_STACK_SIZE / sizeof(ULONG)];
50 static NX_PACKET *allocated_packets[256];
51 static UCHAR large_property_name[2048];
52 static ULONG small_pool_stack[1024 >> 2];
53 static NX_PACKET_POOL small_pool;
54 extern int g_argc;
55 extern char **g_argv;
56 
57 CHAR *expected_topic = "";
58 CHAR *expected_message = "";
59 
demo_entry(NX_IP * ip_ptr,NX_PACKET_POOL * pool_ptr,NX_DNS * dns_ptr,UINT (* unix_time_callback)(ULONG * unix_time))60 VOID demo_entry(NX_IP* ip_ptr, NX_PACKET_POOL* pool_ptr, NX_DNS* dns_ptr, UINT (*unix_time_callback)(ULONG *unix_time))
61 {
62 CHAR *host_name = "host_name";
63 CHAR *device_id = "device_id";
64 CHAR *module_id = "module_id";
65 CHAR *symmetric_key = "symmetric_key";
66 CHAR property_name[] = "property_name";
67 CHAR property_value[] = "property_value";
68 NX_PACKET *packet_ptr;
69 UINT count;
70 ULONG pool_ptr_available_packet;
71 ULONG small_pool_available_packet;
72 
73     assert_int_equal(nx_packet_pool_create(&small_pool, "Small Packet Pool", 4,
74                                            (UCHAR *)small_pool_stack , sizeof(small_pool_stack)),
75                      NX_AZURE_IOT_SUCCESS);
76 
77     /* Initialize root certificate.  */
78     assert_int_equal(nx_secure_x509_certificate_initialize(&root_ca_cert, (UCHAR *)_nx_azure_iot_root_cert, (USHORT)_nx_azure_iot_root_cert_size,
79                                                            NX_NULL, 0, NULL, 0, NX_SECURE_X509_KEY_TYPE_NONE),
80                      NX_AZURE_IOT_SUCCESS);
81 
82     assert_int_equal(nx_azure_iot_create(&iot, (UCHAR *)"Azure IoT", ip_ptr, pool_ptr, dns_ptr, (UCHAR *)demo_cloud_thread_stack,
83                                          sizeof(demo_cloud_thread_stack), DEMO_CLOUD_THREAD_PRIORITY, unix_time_callback),
84                      NX_AZURE_IOT_SUCCESS);
85 
86     /* Record number of available packet before test */
87     pool_ptr_available_packet = pool_ptr -> nx_packet_pool_available;
88     small_pool_available_packet = small_pool.nx_packet_pool_available;
89 
90     assert_int_equal(nx_azure_iot_hub_client_initialize(&iot_client, &iot,
91                                                         STRING_UNSIGNED_ARGS(host_name),
92                                                         STRING_UNSIGNED_ARGS(device_id),
93                                                         STRING_UNSIGNED_ARGS(""),
94                                                         _nx_azure_iot_tls_supported_crypto,
95                                                         _nx_azure_iot_tls_supported_crypto_size,
96                                                         _nx_azure_iot_tls_ciphersuite_map,
97                                                         _nx_azure_iot_tls_ciphersuite_map_size,
98                                                         metadata_buffer, sizeof(metadata_buffer),
99                                                         &root_ca_cert),
100                      NX_AZURE_IOT_SUCCESS);
101 
102     /* FAIL: allocate packet before connect. */
103     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_use_tls = NX_TRUE;
104     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
105                          NX_AZURE_IOT_SUCCESS);
106     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_use_tls = NX_FALSE;
107 
108     assert_int_equal(nx_azure_iot_hub_client_connect(&iot_client, NX_TRUE, NX_IP_PERIODIC_RATE),
109                      NX_AZURE_IOT_SUCCESS);
110 
111     expected_topic = "devices/device_id/messages/events/";
112     expected_message = "{\"Message\": \"Empty\"}";
113 
114     will_return(__wrap__nxd_mqtt_client_publish_packet_send, NXD_MQTT_NOT_CONNECTED);
115     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
116                      NX_AZURE_IOT_SUCCESS);
117     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_send(&iot_client, packet_ptr, expected_message,
118                                                                 strlen(expected_message), NX_IP_PERIODIC_RATE),
119                          NX_AZURE_IOT_SUCCESS);
120     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_delete(packet_ptr), NX_AZURE_IOT_SUCCESS);
121 
122     will_return_always(__wrap__nxd_mqtt_client_publish_packet_send, NXD_MQTT_SUCCESS);
123     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
124                      NX_AZURE_IOT_SUCCESS);
125 
126     /* FAIL: hub_client_ptr is NULL. */
127     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_send(NX_NULL, packet_ptr, expected_message,
128                                                                 strlen(expected_message), NX_IP_PERIODIC_RATE),
129                          NX_AZURE_IOT_SUCCESS);
130 
131     /* FAIL: packet_ptr is NULL. */
132     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_send(&iot_client, NX_NULL, expected_message,
133                                                                 strlen(expected_message), NX_IP_PERIODIC_RATE),
134                          NX_AZURE_IOT_SUCCESS);
135 
136     /* SUCCESS: Send telemetry as a device. */
137     assert_int_equal(nx_azure_iot_hub_client_telemetry_send(&iot_client, packet_ptr, expected_message,
138                                                             strlen(expected_message), NX_IP_PERIODIC_RATE),
139                      NX_AZURE_IOT_SUCCESS);
140 
141     /* FAIL: hub_client_ptr is NULL. */
142     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_message_create(NX_NULL, &packet_ptr, NX_IP_PERIODIC_RATE),
143                          NX_AZURE_IOT_SUCCESS);
144 
145     /* FAIL: packet_pptr is NULL. */
146     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, NX_NULL, NX_IP_PERIODIC_RATE),
147                          NX_AZURE_IOT_SUCCESS);
148 
149     /* FAIL: All packets are depleted. */
150     count = deplete_packets(pool_ptr, 0);
151     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
152                          NX_AZURE_IOT_SUCCESS);
153     release_packets(pool_ptr, count);
154 
155     /* FAIL: Packet pool is too small. */
156     iot.nx_azure_iot_pool_ptr = &small_pool;
157     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_use_tls = NX_TRUE;
158     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_packet_pool_ptr = iot.nx_azure_iot_pool_ptr;
159     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
160                          NX_AZURE_IOT_SUCCESS);
161     iot.nx_azure_iot_pool_ptr = pool_ptr;
162     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_use_tls = NX_FALSE;
163     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_packet_pool_ptr = iot.nx_azure_iot_pool_ptr;
164 
165     /* SUCCESS: All parameters are valid. */
166     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
167                      NX_AZURE_IOT_SUCCESS);
168 
169     /* FAIL: packet_ptr is NULL. */
170     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_property_add(NX_NULL,
171                                                                         STRING_UNSIGNED_ARGS(property_name),
172                                                                         STRING_UNSIGNED_ARGS(property_value),
173                                                                         NX_IP_PERIODIC_RATE),
174                          NX_AZURE_IOT_SUCCESS);
175 
176     /* FAIL: property_name is NULL. */
177     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_property_add(packet_ptr,
178                                                                         NX_NULL, 0,
179                                                                         STRING_UNSIGNED_ARGS(property_value),
180                                                                         NX_IP_PERIODIC_RATE),
181                          NX_AZURE_IOT_SUCCESS);
182 
183     /* FAIL: property_value is NULL. */
184     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_property_add(packet_ptr,
185                                                                         STRING_UNSIGNED_ARGS(property_name),
186                                                                         NX_NULL, 0,
187                                                                         NX_IP_PERIODIC_RATE),
188                          NX_AZURE_IOT_SUCCESS);
189 
190     /* FAIL: All packets are depleted and current packet is too small to hold property. */
191     count = deplete_packets(pool_ptr, 0);
192     memset(large_property_name, 'A', sizeof(large_property_name) - 1);
193     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_property_add(packet_ptr,
194                                                                         STRING_UNSIGNED_ARGS(large_property_name),
195                                                                         STRING_UNSIGNED_ARGS(property_value),
196                                                                         NX_IP_PERIODIC_RATE),
197                          NX_AZURE_IOT_SUCCESS);
198     nx_packet_release(packet_ptr);
199     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
200                      NX_AZURE_IOT_SUCCESS);
201 
202     /* SUCCESS: All parameters are valid. */
203     assert_int_equal(nx_azure_iot_hub_client_telemetry_property_add(packet_ptr,
204                                                                     STRING_UNSIGNED_ARGS(property_name),
205                                                                     STRING_UNSIGNED_ARGS(property_value),
206                                                                     NX_IP_PERIODIC_RATE),
207                      NX_AZURE_IOT_SUCCESS);
208 
209     /* FAIL: Adjust the current packet to full. No room to append '&' */
210     packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_data_end;
211     packet_ptr -> nx_packet_length = (ULONG)(packet_ptr -> nx_packet_append_ptr - packet_ptr -> nx_packet_prepend_ptr);
212     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_property_add(packet_ptr,
213                                                                         STRING_UNSIGNED_ARGS(property_name),
214                                                                         STRING_UNSIGNED_ARGS(property_value),
215                                                                         NX_IP_PERIODIC_RATE),
216                          NX_AZURE_IOT_SUCCESS);
217     nx_packet_release(packet_ptr);
218     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
219                      NX_AZURE_IOT_SUCCESS);
220 
221     /* FAIL: Adjust the current packet to full. Left two bytes for '&' and property name 'n' */
222     packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_data_end - 2;
223     packet_ptr -> nx_packet_length = (ULONG)(packet_ptr -> nx_packet_append_ptr - packet_ptr -> nx_packet_prepend_ptr) - 2;
224     assert_int_not_equal(nx_azure_iot_hub_client_telemetry_property_add(packet_ptr,
225                                                                         "n", 1,
226                                                                         STRING_UNSIGNED_ARGS(property_value),
227                                                                         NX_IP_PERIODIC_RATE),
228                          NX_AZURE_IOT_SUCCESS);
229     nx_packet_release(packet_ptr);
230     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
231                      NX_AZURE_IOT_SUCCESS);
232     release_packets(pool_ptr, count);
233 
234     /* SUCCESS: All parameters are valid. */
235     assert_int_equal(nx_azure_iot_hub_client_telemetry_property_add(packet_ptr,
236                                                                     STRING_UNSIGNED_ARGS(property_name),
237                                                                     STRING_UNSIGNED_ARGS(property_value),
238                                                                     NX_IP_PERIODIC_RATE),
239                      NX_AZURE_IOT_SUCCESS);
240     nx_packet_release(packet_ptr);
241 
242     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_socket.nx_tcp_socket_state = NX_TCP_CLOSED;
243     assert_int_equal(nx_azure_iot_hub_client_deinitialize(&iot_client),
244                      NX_AZURE_IOT_SUCCESS);
245 
246     assert_int_equal(nx_azure_iot_hub_client_initialize(&iot_client, &iot,
247                                                         STRING_UNSIGNED_ARGS(host_name),
248                                                         STRING_UNSIGNED_ARGS(device_id),
249                                                         STRING_UNSIGNED_ARGS(module_id),
250                                                         _nx_azure_iot_tls_supported_crypto,
251                                                         _nx_azure_iot_tls_supported_crypto_size,
252                                                         _nx_azure_iot_tls_ciphersuite_map,
253                                                         _nx_azure_iot_tls_ciphersuite_map_size,
254                                                         metadata_buffer, sizeof(metadata_buffer),
255                                                         &root_ca_cert),
256                      NX_AZURE_IOT_SUCCESS);
257 
258     assert_int_equal(nx_azure_iot_hub_client_connect(&iot_client, NX_TRUE, NX_IP_PERIODIC_RATE),
259                      NX_AZURE_IOT_SUCCESS);
260 
261     /* SUCCESS: Send telemetry as a module. */
262     expected_topic = "devices/device_id/modules/module_id/messages/events/";
263     expected_message = "{\"Message\": \"Empty\"}";
264     assert_int_equal(nx_azure_iot_hub_client_telemetry_message_create(&iot_client, &packet_ptr, NX_IP_PERIODIC_RATE),
265                      NX_AZURE_IOT_SUCCESS);
266     assert_int_equal(nx_azure_iot_hub_client_telemetry_send(&iot_client, packet_ptr, expected_message,
267                                                             strlen(expected_message), NX_IP_PERIODIC_RATE),
268                      NX_AZURE_IOT_SUCCESS);
269 
270     iot_client.nx_azure_iot_hub_client_resource.resource_mqtt.nxd_mqtt_client_socket.nx_tcp_socket_state = NX_TCP_CLOSED;
271     assert_int_equal(nx_azure_iot_hub_client_deinitialize(&iot_client),
272                      NX_AZURE_IOT_SUCCESS);
273 
274     /* Check if all the packet are released */
275     assert_int_equal(pool_ptr -> nx_packet_pool_available, pool_ptr_available_packet);
276     assert_int_equal(small_pool.nx_packet_pool_available, small_pool_available_packet);
277 
278     /* SUCCESS: iot is created. */
279     assert_int_equal(nx_azure_iot_delete(&iot), NX_AZURE_IOT_SUCCESS);
280 }
281 
deplete_packets(NX_PACKET_POOL * pool_ptr,UINT remaining_packets)282 static UINT deplete_packets(NX_PACKET_POOL *pool_ptr, UINT remaining_packets)
283 {
284 UINT count = 0;
285 
286     while (pool_ptr -> nx_packet_pool_available > remaining_packets)
287     {
288         nx_packet_allocate(pool_ptr, &allocated_packets[count++], 0, NX_WAIT_FOREVER);
289     }
290     return(count);
291 }
292 
release_packets(NX_PACKET_POOL * pool_ptr,UINT count)293 static VOID release_packets(NX_PACKET_POOL *pool_ptr, UINT count)
294 {
295     while (count != 0)
296     {
297         nx_packet_release(allocated_packets[--count]);
298     }
299 }
300 
__wrap__nxde_mqtt_client_secure_connect(NXD_MQTT_CLIENT * client_ptr,NXD_ADDRESS * server_ip,UINT server_port,UINT (* tls_setup)(NXD_MQTT_CLIENT * client_ptr,NX_SECURE_TLS_SESSION *,NX_SECURE_X509_CERT *,NX_SECURE_X509_CERT *),UINT keepalive,UINT clean_session,ULONG timeout)301 UINT __wrap__nxde_mqtt_client_secure_connect(NXD_MQTT_CLIENT *client_ptr, NXD_ADDRESS *server_ip, UINT server_port,
302                                              UINT (*tls_setup)(NXD_MQTT_CLIENT *client_ptr, NX_SECURE_TLS_SESSION *,
303                                                                NX_SECURE_X509_CERT *, NX_SECURE_X509_CERT *),
304                                              UINT keepalive, UINT clean_session, ULONG timeout)
305 {
306     printf("HIJACKED: %s\n", __func__);
307     tx_thread_suspend(&(iot.nx_azure_iot_ip_ptr -> nx_ip_thread));
308     client_ptr -> nxd_mqtt_client_state = NXD_MQTT_CLIENT_STATE_CONNECTED;
309     client_ptr -> nxd_mqtt_client_packet_identifier = 1;
310     client_ptr -> nxd_mqtt_tls_session.nx_secure_tls_id = NX_SECURE_TLS_ID;
311     client_ptr -> nxd_mqtt_tls_session.nx_secure_tls_local_session_active = NX_FALSE;
312     client_ptr -> nxd_mqtt_tls_session.nx_secure_tls_tcp_socket = &client_ptr -> nxd_mqtt_client_socket;
313     client_ptr -> nxd_mqtt_client_socket.nx_tcp_socket_connect_ip.nxd_ip_version = NX_IP_VERSION_V4;
314     client_ptr -> nxd_mqtt_client_socket.nx_tcp_socket_state = NX_TCP_ESTABLISHED;
315     return(NXD_MQTT_SUCCESS);
316 }
317 
__wrap__nxde_dns_host_by_name_get(NX_DNS * dns_ptr,UCHAR * host_name,NXD_ADDRESS * host_address_ptr,ULONG wait_option,UINT lookup_type)318 UINT __wrap__nxde_dns_host_by_name_get(NX_DNS *dns_ptr, UCHAR *host_name, NXD_ADDRESS *host_address_ptr,
319                                        ULONG wait_option, UINT lookup_type)
320 {
321     printf("HIJACKED: %s\n", __func__);
322     host_address_ptr -> nxd_ip_address.v4 = IP_ADDRESS(127, 0, 0, 1);
323     return(NX_DNS_SUCCESS);
324 }
325 
__wrap__nxd_mqtt_client_publish_packet_send(NXD_MQTT_CLIENT * client_ptr,NX_PACKET * packet_ptr,USHORT packet_id,UINT QoS,ULONG wait_option)326 UINT __wrap__nxd_mqtt_client_publish_packet_send(NXD_MQTT_CLIENT *client_ptr, NX_PACKET *packet_ptr,
327                                                  USHORT packet_id, UINT QoS, ULONG wait_option)
328 {
329 UINT topic_name_length;
330 UINT message_length;
331 UCHAR *buffer_ptr;
332 UINT status = (UINT)mock();
333 
334     printf("HIJACKED: %s\n", __func__);
335     tx_mutex_put(client_ptr -> nxd_mqtt_client_mutex_ptr);
336 
337     if (status)
338     {
339         return(status);
340     }
341 
342     buffer_ptr = packet_ptr -> nx_packet_prepend_ptr;
343     topic_name_length = (buffer_ptr[5] << 8) | (buffer_ptr[6]);
344     message_length = packet_ptr -> nx_packet_length - (9 + topic_name_length);
345     assert_int_equal(topic_name_length, strlen(expected_topic));
346     assert_memory_equal(&buffer_ptr[7], expected_topic, topic_name_length);
347     assert_int_equal(message_length, strlen(expected_message));
348     assert_memory_equal(&buffer_ptr[9 + topic_name_length], expected_message, message_length);
349     assert_int_equal(QoS, 1);
350 
351     /* packet ownership taken and released */
352     nx_packet_release(packet_ptr);
353 
354     return(status);
355 }
356 
__wrap_nx_azure_iot_security_module_enable(NX_AZURE_IOT * nx_azure_iot_ptr)357 UINT __wrap_nx_azure_iot_security_module_enable(NX_AZURE_IOT *nx_azure_iot_ptr)
358 {
359     printf("HIJACKED: %s\n", __func__);
360     return(NX_AZURE_IOT_SUCCESS);
361 }
362 
__wrap_nx_azure_iot_security_module_disable(NX_AZURE_IOT * nx_azure_iot_ptr)363 UINT __wrap_nx_azure_iot_security_module_disable(NX_AZURE_IOT *nx_azure_iot_ptr)
364 {
365     printf("HIJACKED: %s\n", __func__);
366     return(NX_AZURE_IOT_SUCCESS);
367 }
368