Coverage Report

Created: 2026-08-12 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wolfmqtt/src/mqtt_client.c
Line
Count
Source
1
/* mqtt_client.c
2
 *
3
 * Copyright (C) 2006-2026 wolfSSL Inc.
4
 *
5
 * This file is part of wolfMQTT.
6
 *
7
 * wolfMQTT is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * wolfMQTT is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20
 */
21
22
/* Include the autoconf generated config.h */
23
#ifdef HAVE_CONFIG_H
24
    #include <config.h>
25
#endif
26
27
#include "wolfmqtt/mqtt_client.h"
28
29
/* Secure memory zeroing - uses volatile pointer to prevent the compiler
30
 * from optimizing away the stores (dead-store elimination).
31
 * Declared WOLFMQTT_LOCAL in mqtt_client.h so the MQTT-SN client can reuse it
32
 * (see SN_WillMessage) via the shared CLIENT_FORCE_ZERO macro. */
33
WOLFMQTT_LOCAL void MqttClient_ForceZero(void* mem, word32 len)
34
8.22k
{
35
8.22k
    volatile byte* p = (volatile byte*)mem;
36
8.22k
    word32 i;
37
15.8M
    for (i = 0; i < len; i++) {
38
15.8M
        p[i] = 0;
39
15.8M
    }
40
8.22k
}
41
42
/* DOCUMENTED BUILD OPTIONS:
43
 *
44
 * WOLFMQTT_MULTITHREAD: Enables multi-thread support with mutex protection on
45
 *  client struct, write and read. When a pending response is needed its added
46
 *  to a linked list and if another thread reads the expected response it is
47
 *  flagged, so the other thread knows it completed.
48
 *
49
 * WOLFMQTT_NONBLOCK: Enabled transport support for returning WANT READ/WRITE,
50
 *  which becomes WOLFMQTT_CODE_CONTINUE. This prevents blocking if the
51
 *  transport (socket) has no data.
52
 *
53
 * WOLFMQTT_V5: Enables MQTT v5.0 support
54
 *
55
 * WOLFMQTT_ALLOW_NODATA_UNLOCK: Used with multi-threading and non-blocking to
56
 *   allow unlock if no data was sent/received. Note the TLS stack typically
57
 *   requires an attempt to write to continue with same write, not different.
58
 *   By default if we attempt a write we keep the mutex locked and return
59
 *   MQTT_CODE_CONTINUE
60
 *
61
 * WOLFMQTT_USER_THREADING: Allows custom mutex functions to be defined by the
62
 *  user. Example: wm_SemInit
63
 *
64
 * WOLFMQTT_DEBUG_CLIENT: Enables verbose PRINTF for the client code.
65
 */
66
67
68
/* Private functions */
69
70
/* forward declarations */
71
static int MqttClient_Publish_ReadPayload(MqttClient* client,
72
    MqttPublish* publish, int timeout_ms);
73
#if !defined(WOLFMQTT_MULTITHREAD) && !defined(WOLFMQTT_NONBLOCK)
74
static int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg);
75
#endif
76
77
78
#ifdef WOLFMQTT_MULTITHREAD
79
80
#ifdef WOLFMQTT_USER_THREADING
81
82
    /* User will supply their own semaphore functions.
83
     * int wm_SemInit(wm_Sem *s)
84
     * int wm_SemFree(wm_Sem *s)
85
     * int wm_SemLock(wm_Sem *s)
86
     * int wm_SemUnlock(wm_Sem *s)
87
     */
88
89
#elif defined(__MACH__)
90
91
    /* Apple style dispatch semaphore */
92
    int wm_SemInit(wm_Sem *s) {
93
        /* dispatch_release() fails hard, with Trace/BPT trap signal, if the
94
         * sem's internal count is less than the value passed in with
95
         * dispatch_semaphore_create().  work around this by initializing
96
         * with 0, then incrementing it afterwards.
97
         */
98
        s->sem = dispatch_semaphore_create(0);
99
        if (s->sem == NULL)
100
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MEMORY);
101
        if (dispatch_semaphore_signal(s->sem) < 0) {
102
            dispatch_release(s->sem);
103
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_SYSTEM);
104
        }
105
106
        return 0;
107
    }
108
    int wm_SemFree(wm_Sem *s) {
109
        if ((s == NULL) ||
110
            (s->sem == NULL))
111
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
112
        dispatch_release(s->sem);
113
        s->sem = NULL;
114
        return 0;
115
    }
116
    int wm_SemLock(wm_Sem *s) {
117
        dispatch_semaphore_wait(s->sem, DISPATCH_TIME_FOREVER);
118
        return 0;
119
    }
120
    int wm_SemUnlock(wm_Sem *s){
121
        dispatch_semaphore_signal(s->sem);
122
        return 0;
123
    }
124
#elif defined(WOLFMQTT_POSIX_SEMAPHORES)
125
    /* Posix style semaphore */
126
    int wm_SemInit(wm_Sem *s) {
127
    #ifndef WOLFMQTT_NO_COND_SIGNAL
128
        s->lockCount = 0;
129
        pthread_cond_init(&s->cond, NULL);
130
    #endif
131
        pthread_mutex_init(&s->mutex, NULL);
132
        return 0;
133
    }
134
    int wm_SemFree(wm_Sem *s) {
135
        pthread_mutex_destroy(&s->mutex);
136
    #ifndef WOLFMQTT_NO_COND_SIGNAL
137
        pthread_cond_destroy(&s->cond);
138
    #endif
139
        return 0;
140
    }
141
    int wm_SemLock(wm_Sem *s) {
142
        pthread_mutex_lock(&s->mutex);
143
    #ifndef WOLFMQTT_NO_COND_SIGNAL
144
        while (s->lockCount > 0)
145
            pthread_cond_wait(&s->cond, &s->mutex);
146
        s->lockCount++;
147
        pthread_mutex_unlock(&s->mutex);
148
    #endif
149
        return 0;
150
    }
151
    int wm_SemUnlock(wm_Sem *s) {
152
    #ifndef WOLFMQTT_NO_COND_SIGNAL
153
        pthread_mutex_lock(&s->mutex);
154
        if (s->lockCount > 0) {
155
            s->lockCount--;
156
            pthread_cond_signal(&s->cond);
157
        }
158
    #endif
159
        pthread_mutex_unlock(&s->mutex);
160
        return 0;
161
    }
162
#elif defined(FREERTOS)
163
    /* FreeRTOS binary semaphore */
164
    int wm_SemInit(wm_Sem *s) {
165
        *s = xSemaphoreCreateBinary();
166
        xSemaphoreGive(*s);
167
        return 0;
168
    }
169
    int wm_SemFree(wm_Sem *s) {
170
        vSemaphoreDelete(*s);
171
        *s = NULL;
172
        return 0;
173
    }
174
    int wm_SemLock(wm_Sem *s) {
175
        xSemaphoreTake(*s, portMAX_DELAY);
176
        return 0;
177
    }
178
    int wm_SemUnlock(wm_Sem *s) {
179
        xSemaphoreGive(*s);
180
        return 0;
181
    }
182
#elif defined(USE_WINDOWS_API)
183
    /* Windows semaphore object */
184
    int wm_SemInit(wm_Sem *s) {
185
        *s = CreateSemaphoreW( NULL, 1, 1, NULL);
186
        return 0;
187
    }
188
    int wm_SemFree(wm_Sem *s) {
189
        CloseHandle(*s);
190
        *s = NULL;
191
        return 0;
192
    }
193
    int wm_SemLock(wm_Sem *s) {
194
        WaitForSingleObject(*s, INFINITE);
195
        return 0;
196
    }
197
    int wm_SemUnlock(wm_Sem *s) {
198
        ReleaseSemaphore(*s, 1, NULL);
199
        return 0;
200
    }
201
202
#elif defined(THREADX)
203
    /* ThreadX semaphore */
204
    int wm_SemInit(wm_Sem *s) {
205
        if (tx_semaphore_create(s, NULL, 1) != TX_SUCCESS) {
206
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_SYSTEM);
207
        }
208
        return 0;
209
    }
210
    int wm_SemFree(wm_Sem *s) {
211
        if (tx_semaphore_delete(s) != TX_SUCCESS) {
212
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_SYSTEM);
213
        }
214
        return 0;
215
    }
216
217
    int wm_SemLock(wm_Sem *s) {
218
        UINT semstatus = tx_semaphore_get(s, TX_WAIT_FOREVER);
219
        if (semstatus != TX_SUCCESS) {
220
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_SYSTEM);
221
        }
222
        return 0;
223
    }
224
    int wm_SemUnlock(wm_Sem *s) {
225
        if (tx_semaphore_put(s) != TX_SUCCESS) {
226
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_SYSTEM);
227
        }
228
        return 0;
229
    }
230
#endif /* MUTEX */
231
#endif /* WOLFMQTT_MULTITHREAD */
232
233
static int MqttWriteStart(MqttClient* client, MqttMsgStat* stat)
234
11.6k
{
235
11.6k
    int rc = MQTT_CODE_SUCCESS;
236
237
11.6k
#if defined(WOLFMQTT_DEBUG_CLIENT) || !defined(WOLFMQTT_ALLOW_NODATA_UNLOCK)
238
  #ifdef WOLFMQTT_DEBUG_CLIENT
239
    if (stat->isWriteActive) {
240
        MQTT_TRACE_MSG("Warning, send already locked!");
241
        rc = MQTT_CODE_ERROR_SYSTEM;
242
    }
243
  #endif
244
11.6k
  #ifndef WOLFMQTT_ALLOW_NODATA_UNLOCK
245
    /* detect if a write is already in progress */
246
    #ifdef WOLFMQTT_MULTITHREAD
247
    if (wm_SemLock(&client->lockClient) == 0)
248
    #endif
249
11.6k
    {
250
11.6k
        if (client->write.isActive) {
251
0
            MQTT_TRACE_MSG("Partial write in progress!");
252
0
            rc = MQTT_CODE_CONTINUE; /* can't write yet */
253
0
        }
254
    #ifdef WOLFMQTT_MULTITHREAD
255
        wm_SemUnlock(&client->lockClient);
256
    #endif
257
11.6k
    }
258
11.6k
  #endif /* WOLFMQTT_ALLOW_NODATA_UNLOCK */
259
11.6k
    if (rc != MQTT_CODE_SUCCESS) {
260
0
        return rc;
261
0
    }
262
11.6k
#endif
263
264
#ifdef WOLFMQTT_MULTITHREAD
265
    rc = wm_SemLock(&client->lockSend);
266
#endif
267
11.6k
    if (rc == MQTT_CODE_SUCCESS) {
268
11.6k
        stat->isWriteActive = 1;
269
270
    #ifdef WOLFMQTT_MULTITHREAD
271
        if (wm_SemLock(&client->lockClient) == 0)
272
    #endif
273
11.6k
        {
274
11.6k
            client->write.isActive = 1;
275
        #ifdef WOLFMQTT_MULTITHREAD
276
            wm_SemUnlock(&client->lockClient);
277
        #endif
278
11.6k
        }
279
280
11.6k
        MQTT_TRACE_MSG("lockSend");
281
11.6k
    }
282
283
11.6k
    return rc;
284
11.6k
}
285
static void MqttWriteStop(MqttClient* client, MqttMsgStat* stat)
286
11.7k
{
287
#ifdef WOLFMQTT_DEBUG_CLIENT
288
    if (!stat->isWriteActive) {
289
        MQTT_TRACE_MSG("Warning, send not locked!");
290
        return;
291
    }
292
#endif
293
294
#ifdef WOLFMQTT_MULTITHREAD
295
    if (wm_SemLock(&client->lockClient) == 0)
296
#endif
297
11.7k
    {
298
        /* reset write */
299
11.7k
        XMEMSET(&client->write, 0, sizeof(client->write));
300
    #ifdef WOLFMQTT_MULTITHREAD
301
        wm_SemUnlock(&client->lockClient);
302
    #endif
303
11.7k
    }
304
305
11.7k
    if (stat->isWriteActive) {
306
11.6k
        MQTT_TRACE_MSG("unlockSend");
307
11.6k
        stat->isWriteActive = 0;
308
    #ifdef WOLFMQTT_MULTITHREAD
309
        wm_SemUnlock(&client->lockSend);
310
    #endif
311
11.6k
    }
312
11.7k
}
313
314
WOLFMQTT_LOCAL int MqttReadStart(MqttClient* client, MqttMsgStat* stat)
315
13.4k
{
316
13.4k
    int rc = MQTT_CODE_SUCCESS;
317
318
13.4k
#if defined(WOLFMQTT_DEBUG_CLIENT) || !defined(WOLFMQTT_ALLOW_NODATA_UNLOCK)
319
  #ifdef WOLFMQTT_DEBUG_CLIENT
320
    if (stat->isReadActive) {
321
        MQTT_TRACE_MSG("Warning, recv already locked!");
322
        rc = MQTT_CODE_ERROR_SYSTEM;
323
    }
324
  #endif /* WOLFMQTT_DEBUG_CLIENT */
325
13.4k
  #ifndef WOLFMQTT_ALLOW_NODATA_UNLOCK
326
    /* detect if a read is already in progress */
327
    #ifdef WOLFMQTT_MULTITHREAD
328
    if (wm_SemLock(&client->lockClient) == 0)
329
    #endif
330
13.4k
    {
331
13.4k
        if (client->read.isActive) {
332
0
            MQTT_TRACE_MSG("Partial read in progress!");
333
0
            rc = MQTT_CODE_CONTINUE; /* can't read yet */
334
0
        }
335
    #ifdef WOLFMQTT_MULTITHREAD
336
        wm_SemUnlock(&client->lockClient);
337
    #endif
338
13.4k
    }
339
13.4k
  #endif /* WOLFMQTT_ALLOW_NODATA_UNLOCK */
340
13.4k
    if (rc != MQTT_CODE_SUCCESS) {
341
0
        return rc;
342
0
    }
343
13.4k
#endif /* WOLFMQTT_DEBUG_CLIENT || !WOLFMQTT_ALLOW_NODATA_UNLOCK */
344
345
#ifdef WOLFMQTT_MULTITHREAD
346
    rc = wm_SemLock(&client->lockRecv);
347
#endif
348
13.4k
    if (rc == MQTT_CODE_SUCCESS) {
349
13.4k
        stat->isReadActive = 1;
350
351
    #ifdef WOLFMQTT_MULTITHREAD
352
        if (wm_SemLock(&client->lockClient) == 0)
353
    #endif
354
13.4k
        {
355
            /* mark read active */
356
13.4k
            client->read.isActive = 1;
357
358
            /* reset the packet state used by MqttPacket_Read */
359
13.4k
            client->packet.stat = MQTT_PK_BEGIN;
360
361
        #ifdef WOLFMQTT_MULTITHREAD
362
            wm_SemUnlock(&client->lockClient);
363
        #endif
364
13.4k
        }
365
366
13.4k
        MQTT_TRACE_MSG("lockRecv");
367
13.4k
    }
368
369
13.4k
    return rc;
370
13.4k
}
371
WOLFMQTT_LOCAL void MqttReadStop(MqttClient* client, MqttMsgStat* stat)
372
28.0k
{
373
#ifdef WOLFMQTT_DEBUG_CLIENT
374
    if (!stat->isReadActive) {
375
        MQTT_TRACE_MSG("Warning, recv not locked!");
376
        return;
377
    }
378
#endif
379
380
#ifdef WOLFMQTT_MULTITHREAD
381
    if (wm_SemLock(&client->lockClient) == 0)
382
#endif
383
28.0k
    {
384
        /* reset read */
385
28.0k
        XMEMSET(&client->read, 0, sizeof(client->read));
386
    #ifdef WOLFMQTT_MULTITHREAD
387
        wm_SemUnlock(&client->lockClient);
388
    #endif
389
28.0k
    }
390
391
28.0k
    if (stat->isReadActive) {
392
13.4k
        MQTT_TRACE_MSG("unlockRecv");
393
13.4k
        stat->isReadActive = 0;
394
    #ifdef WOLFMQTT_MULTITHREAD
395
        wm_SemUnlock(&client->lockRecv);
396
    #endif
397
13.4k
    }
398
28.0k
}
399
400
#ifdef WOLFMQTT_MULTITHREAD
401
402
/* These RespList functions assume caller has locked client->lockClient mutex */
403
int MqttClient_RespList_Add(MqttClient *client,
404
    MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp,
405
    void *packet_obj)
406
{
407
    MqttPendResp *tmpResp;
408
409
    if (client == NULL)
410
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
411
412
#ifdef WOLFMQTT_DEBUG_CLIENT
413
    PRINTF("PendResp Add: %p, Type %s (%d), ID %d",
414
        newResp, MqttPacket_TypeDesc(packet_type), packet_type, packet_id);
415
#endif
416
417
    /* Verify newResp is not already in the list, and enforce MQTT Packet
418
     * Identifier in-use uniqueness: the spec (3.1.1 section 2.3.1, 5.0 section 2.2.1)
419
     * requires a new QoS-related Control Packet to use a Packet Identifier
420
     * that is not currently in use. The identifier becomes reusable only
421
     * after the corresponding acknowledgement flow completes and the entry
422
     * is removed from this list. A packet_id of 0 is used for packet types
423
     * that do not carry a Packet Identifier (CONNECT_ACK, PING_RESP, AUTH)
424
     * and is excluded from the collision check. */
425
    for (tmpResp = client->firstPendResp;
426
         tmpResp != NULL;
427
         tmpResp = tmpResp->next)
428
    {
429
        if (tmpResp == newResp) {
430
        #ifdef WOLFMQTT_DEBUG_CLIENT
431
            PRINTF("Pending Response already in list!");
432
        #endif
433
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
434
        }
435
        if (packet_id != 0 && tmpResp->packet_id == packet_id) {
436
        #ifdef WOLFMQTT_DEBUG_CLIENT
437
            PRINTF("Pending Response packet_id %d already in use "
438
                   "(existing type %s (%d), new type %s (%d))",
439
                packet_id,
440
                MqttPacket_TypeDesc(tmpResp->packet_type),
441
                tmpResp->packet_type,
442
                MqttPacket_TypeDesc(packet_type), packet_type);
443
        #endif
444
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_ID);
445
        }
446
    }
447
448
    /* Initialize new response */
449
    XMEMSET(newResp, 0, sizeof(MqttPendResp));
450
    newResp->packet_id = packet_id;
451
    newResp->packet_type = packet_type;
452
    /* opaque pointer to struct based on type */
453
    newResp->packet_obj = packet_obj;
454
455
    if (client->lastPendResp == NULL) {
456
        /* This is the only list item */
457
        client->firstPendResp = newResp;
458
        client->lastPendResp = newResp;
459
    }
460
    else {
461
        /* Append to end of list */
462
        newResp->prev = client->lastPendResp;
463
        client->lastPendResp->next = newResp;
464
        client->lastPendResp = newResp;
465
    }
466
    return MQTT_CODE_SUCCESS;
467
}
468
469
void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp)
470
{
471
    MqttPendResp *tmpResp;
472
473
    if (client == NULL)
474
        return;
475
476
#ifdef WOLFMQTT_DEBUG_CLIENT
477
    PRINTF("PendResp Remove: %p", rmResp);
478
#endif
479
480
    /* Find the response entry */
481
    for (tmpResp = client->firstPendResp;
482
         tmpResp != NULL;
483
         tmpResp = tmpResp->next)
484
    {
485
        if (tmpResp == rmResp) {
486
            break;
487
        }
488
    }
489
    if (tmpResp) {
490
        /* Fix up the first and last pointers */
491
        if (client->firstPendResp == tmpResp) {
492
            client->firstPendResp = tmpResp->next;
493
        }
494
        if (client->lastPendResp == tmpResp) {
495
            client->lastPendResp = tmpResp->prev;
496
        }
497
498
        /* Remove the entry from the list */
499
        if (tmpResp->next != NULL) {
500
            tmpResp->next->prev = tmpResp->prev;
501
        }
502
        if (tmpResp->prev != NULL) {
503
            tmpResp->prev->next = tmpResp->next;
504
        }
505
    }
506
#ifdef WOLFMQTT_DEBUG_CLIENT
507
    else {
508
        PRINTF("\tPendResp not found");
509
    }
510
#endif
511
}
512
513
/* return codes: 0=not found, 1=found */
514
int MqttClient_RespList_Find(MqttClient *client,
515
    MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp)
516
{
517
    int rc = 0;
518
    MqttPendResp *tmpResp;
519
520
    if (client == NULL)
521
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
522
523
#ifdef WOLFMQTT_DEBUG_CLIENT
524
    #ifdef WOLFMQTT_NONBLOCK
525
    if (client->lastRc != MQTT_CODE_CONTINUE)
526
    #endif
527
    {
528
        PRINTF("PendResp Find: Type %s (%d), ID %d",
529
            MqttPacket_TypeDesc(packet_type), packet_type, packet_id);
530
    }
531
#endif
532
533
    if (retResp)
534
        *retResp = NULL; /* clear */
535
536
    /* Find pending response entry */
537
    for (tmpResp = client->firstPendResp;
538
         tmpResp != NULL;
539
         tmpResp = tmpResp->next)
540
    {
541
        if (packet_type == tmpResp->packet_type &&
542
           (packet_id == tmpResp->packet_id))
543
        {
544
        #ifdef WOLFMQTT_DEBUG_CLIENT
545
            #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_DEBUG_CLIENT)
546
            if (client->lastRc != MQTT_CODE_CONTINUE)
547
            #endif
548
            {
549
            PRINTF("PendResp Found: %p, Type %s (%d), ID %d, InProc %d, Done %d",
550
                tmpResp, MqttPacket_TypeDesc(tmpResp->packet_type),
551
                tmpResp->packet_type, tmpResp->packet_id,
552
                tmpResp->packetProcessing, tmpResp->packetDone);
553
            }
554
        #endif
555
556
            if (retResp)
557
                *retResp = tmpResp;
558
            rc = 1;
559
            break;
560
        }
561
    }
562
    return rc;
563
}
564
#endif /* WOLFMQTT_MULTITHREAD */
565
566
#ifdef WOLFMQTT_V5
567
/* Populate client fields from CONNACK server properties so that the
568
 * publish/packet-size guards are effective without requiring the
569
 * application to register a property callback. */
570
static void Handle_ConnectAck_Props(MqttClient* client, MqttProp* props)
571
319
{
572
319
    MqttProp* prop;
573
574
319
    for (prop = props; prop != NULL; prop = prop->next) {
575
0
        if (prop->type == MQTT_PROP_MAX_QOS) {
576
            /* MQTT v5 [3.1.2.11.6]: only 0 or 1 are legal. Clamp a
577
             * non-conforming broker value, then narrow against this
578
             * build's WOLFMQTT_MAX_QOS so client-side publish guards
579
             * remain meaningful. */
580
0
            byte adv = (prop->data_byte <= MQTT_QOS_1) ?
581
0
                    prop->data_byte : MQTT_QOS_1;
582
0
            if (adv > WOLFMQTT_MAX_QOS) {
583
0
                adv = (byte)WOLFMQTT_MAX_QOS;
584
0
            }
585
0
            client->max_qos = adv;
586
0
        }
587
0
        else if (prop->type == MQTT_PROP_RETAIN_AVAIL) {
588
            /* MQTT v5 [3.1.2.11.5]: only 0 or 1 are legal. */
589
0
            client->retain_avail = (prop->data_byte <= 1) ?
590
0
                    prop->data_byte : 1;
591
0
        }
592
0
        else if (prop->type == MQTT_PROP_MAX_PACKET_SZ) {
593
0
            if ((prop->data_int > 0) &&
594
0
                (prop->data_int <= MQTT_PACKET_SZ_MAX)) {
595
                /* Honor the smaller of the client's existing cap
596
                 * (0 means unset) and the server's limit. */
597
0
                if ((client->packet_sz_max == 0) ||
598
0
                    (prop->data_int < client->packet_sz_max)) {
599
0
                    client->packet_sz_max = prop->data_int;
600
0
                }
601
0
            }
602
0
        }
603
0
    #ifndef WOLFMQTT_NO_TIME
604
0
        else if (prop->type == MQTT_PROP_SERVER_KEEP_ALIVE) {
605
            /* MQTT v5 [3.1.2.11.2]: when the broker returns a Server Keep
606
             * Alive, the client MUST use it in place of the value it sent, and
607
             * a value of 0 disables keep-alive. The flag lets the arming logic
608
             * tell a server-provided 0 from an absent property. */
609
0
            client->keep_alive_sec = prop->data_short;
610
0
            client->keep_alive_from_server = 1;
611
0
        }
612
0
    #endif
613
0
    }
614
319
}
615
616
static int Handle_Props(MqttClient* client, MqttProp* props, byte use_cb,
617
                        byte free_props)
618
18.5k
{
619
18.5k
    int rc = MQTT_CODE_SUCCESS;
620
621
    /* If no properties, just return */
622
18.5k
    if (props != NULL) {
623
1.60k
    #ifdef WOLFMQTT_PROPERTY_CB
624
        /* Check for properties set by the server */
625
1.60k
        if ((use_cb == 1) && (client->property_cb != NULL)) {
626
            /* capture error if returned */
627
0
            int rc_err = client->property_cb(client, props,
628
0
                    client->property_ctx);
629
0
            if (rc_err < 0) {
630
0
                rc = rc_err;
631
0
            }
632
0
        }
633
    #else
634
        (void)client;
635
        (void)use_cb;
636
    #endif
637
1.60k
        if (free_props) {
638
            /* Free the properties */
639
1.60k
            MqttProps_Free(props);
640
1.60k
        }
641
1.60k
    }
642
18.5k
    return rc;
643
18.5k
}
644
#endif
645
646
647
/* Returns length decoded or error (as negative) */
648
/*! \brief      Take a received MQTT packet and try and decode it
649
 *  \param      client       MQTT client context
650
 *  \param      rx_buf       Incoming buffer data
651
 *  \param      rx_len       Incoming buffer length
652
 *  \param      p_decode     Opaque pointer to packet structure based on type
653
 *  \param      ppacket_type Decoded packet type
654
 *  \param      ppacket_qos  Decoded QoS level
655
 *  \param      ppacket_id   Decoded packet id
656
 *  \param      doProps      True: Call Handle_Props to free prop struct
657
658
 *  \return     Returns length decoded or error (as negative) MQTT_CODE_ERROR_*
659
                (see enum MqttPacketResponseCodes)
660
 */
661
static int MqttClient_DecodePacket(MqttClient* client, byte* rx_buf,
662
    word32 rx_len, void *packet_obj, MqttPacketType* ppacket_type,
663
    MqttQoS* ppacket_qos, word16* ppacket_id, int doProps)
664
25.0k
{
665
25.0k
    int rc = MQTT_CODE_SUCCESS;
666
25.0k
    MqttPacket* header;
667
25.0k
    MqttPacketType packet_type;
668
25.0k
    MqttQoS packet_qos;
669
25.0k
    word16 packet_id = 0;
670
671
    /* must have rx buffer with at least 2 byes for header */
672
25.0k
    if (rx_buf == NULL || rx_len < MQTT_PACKET_HEADER_MIN_SIZE) {
673
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
674
0
    }
675
676
    /* Decode header */
677
25.0k
    header = (MqttPacket*)rx_buf;
678
25.0k
    packet_type = (MqttPacketType)MQTT_PACKET_TYPE_GET(header->type_flags);
679
25.0k
    if (ppacket_type) {
680
25.0k
        *ppacket_type = packet_type;
681
25.0k
    }
682
25.0k
    packet_qos = (MqttQoS)MQTT_PACKET_FLAGS_GET_QOS(header->type_flags);
683
25.0k
    if (ppacket_qos) {
684
25.0k
        *ppacket_qos = packet_qos;
685
25.0k
    }
686
687
    /* Decode packet specific data (if requested) */
688
25.0k
    if (ppacket_id || packet_obj) {
689
25.0k
        switch (packet_type) {
690
1.36k
        case MQTT_PACKET_TYPE_CONNECT_ACK:
691
1.36k
        {
692
1.36k
            MqttConnectAck connect_ack, *p_connect_ack = &connect_ack;
693
1.36k
            if (packet_obj) {
694
777
                p_connect_ack = (MqttConnectAck*)packet_obj;
695
777
            }
696
588
            else {
697
588
                XMEMSET(p_connect_ack, 0, sizeof(MqttConnectAck));
698
588
            }
699
1.36k
        #ifdef WOLFMQTT_V5
700
1.36k
            p_connect_ack->protocol_level = client->protocol_level;
701
1.36k
        #endif
702
1.36k
            rc = MqttDecode_ConnectAck(rx_buf, rx_len, p_connect_ack);
703
1.36k
        #ifdef WOLFMQTT_V5
704
1.36k
            if (rc >= 0 && doProps) {
705
1.00k
                int tmp;
706
                /* Only latch server-supplied session limits when the broker
707
                 * accepted the connection. A refused CONNACK must not
708
                 * mutate long-lived MqttClient state. */
709
1.00k
                if (p_connect_ack->return_code ==
710
1.00k
                        MQTT_CONNECT_ACK_CODE_ACCEPTED) {
711
319
                    Handle_ConnectAck_Props(client, p_connect_ack->props);
712
319
                }
713
1.00k
                tmp = Handle_Props(client, p_connect_ack->props,
714
1.00k
                                   (packet_obj != NULL), 1);
715
1.00k
                p_connect_ack->props = NULL;
716
1.00k
                if (tmp != MQTT_CODE_SUCCESS) {
717
0
                    rc = tmp;
718
0
                }
719
1.00k
            }
720
1.36k
        #endif
721
1.36k
            break;
722
0
        }
723
3.39k
        case MQTT_PACKET_TYPE_PUBLISH:
724
3.39k
        {
725
3.39k
            MqttPublish publish, *p_publish;
726
3.39k
            if (packet_obj) {
727
2.08k
                p_publish = (MqttPublish*)packet_obj;
728
2.08k
            #ifdef WOLFMQTT_V5
729
                /* setting the protocol level will enable parsing of the
730
                 * properties. The properties are allocated from a list,
731
                 * so only parse if we are using a return packet object */
732
2.08k
                p_publish->protocol_level = client->protocol_level;
733
2.08k
            #endif
734
2.08k
            }
735
1.30k
            else {
736
1.30k
                p_publish = &publish;
737
1.30k
                XMEMSET(p_publish, 0, sizeof(MqttPublish));
738
1.30k
            }
739
3.39k
            rc = MqttDecode_Publish(rx_buf, rx_len, p_publish);
740
3.39k
            if (rc >= 0) {
741
2.21k
                packet_id = p_publish->packet_id;
742
2.21k
            #ifdef WOLFMQTT_V5
743
2.21k
                if (doProps) {
744
                    /* Do not free property list here. It will be freed
745
                       after the message callback. */
746
2.21k
                    int tmp = Handle_Props(client, p_publish->props,
747
2.21k
                                           (packet_obj != NULL), 0);
748
2.21k
                    if (tmp != MQTT_CODE_SUCCESS) {
749
0
                        rc = tmp;
750
0
                    }
751
2.21k
                }
752
2.21k
            #endif
753
2.21k
            }
754
3.39k
            break;
755
0
        }
756
931
        case MQTT_PACKET_TYPE_PUBLISH_ACK:
757
2.32k
        case MQTT_PACKET_TYPE_PUBLISH_REC:
758
3.77k
        case MQTT_PACKET_TYPE_PUBLISH_REL:
759
5.38k
        case MQTT_PACKET_TYPE_PUBLISH_COMP:
760
5.38k
        {
761
5.38k
            MqttPublishResp publish_resp, *p_publish_resp = &publish_resp;
762
5.38k
            if (packet_obj) {
763
2.75k
                p_publish_resp = (MqttPublishResp*)packet_obj;
764
2.75k
            }
765
2.63k
            else {
766
2.63k
                XMEMSET(p_publish_resp, 0, sizeof(MqttPublishResp));
767
2.63k
            }
768
769
5.38k
        #ifdef WOLFMQTT_V5
770
5.38k
            p_publish_resp->protocol_level = client->protocol_level;
771
5.38k
        #endif
772
5.38k
            rc = MqttDecode_PublishResp(rx_buf, rx_len, packet_type,
773
5.38k
                p_publish_resp);
774
5.38k
            if (rc >= 0) {
775
5.15k
                packet_id = p_publish_resp->packet_id;
776
5.15k
            #ifdef WOLFMQTT_V5
777
5.15k
                if (doProps) {
778
5.15k
                    int tmp = Handle_Props(client, p_publish_resp->props,
779
5.15k
                                           (packet_obj != NULL), 1);
780
5.15k
                    p_publish_resp->props = NULL;
781
5.15k
                    if (tmp != MQTT_CODE_SUCCESS) {
782
0
                        rc = tmp;
783
0
                    }
784
5.15k
                }
785
5.15k
            #endif
786
5.15k
            }
787
5.38k
            break;
788
3.77k
        }
789
1.86k
        case MQTT_PACKET_TYPE_SUBSCRIBE_ACK:
790
1.86k
        {
791
1.86k
            MqttSubscribeAck subscribe_ack, *p_subscribe_ack = &subscribe_ack;
792
1.86k
            if (packet_obj) {
793
1.02k
                p_subscribe_ack = (MqttSubscribeAck*)packet_obj;
794
1.02k
            }
795
839
            else {
796
839
                XMEMSET(p_subscribe_ack, 0, sizeof(MqttSubscribeAck));
797
839
            }
798
1.86k
        #ifdef WOLFMQTT_V5
799
1.86k
            p_subscribe_ack->protocol_level = client->protocol_level;
800
1.86k
        #endif
801
1.86k
            rc = MqttDecode_SubscribeAck(rx_buf, rx_len, p_subscribe_ack);
802
1.86k
            if (rc >= 0) {
803
1.43k
                packet_id = p_subscribe_ack->packet_id;
804
1.43k
            #ifdef WOLFMQTT_V5
805
1.43k
                if (doProps) {
806
1.43k
                    int tmp = Handle_Props(client, p_subscribe_ack->props,
807
1.43k
                                           (packet_obj != NULL), 1);
808
1.43k
                    p_subscribe_ack->props = NULL;
809
1.43k
                    if (tmp != MQTT_CODE_SUCCESS) {
810
0
                        rc = tmp;
811
0
                    }
812
1.43k
                }
813
1.43k
            #endif
814
1.43k
            }
815
1.86k
            break;
816
3.77k
        }
817
1.30k
        case MQTT_PACKET_TYPE_UNSUBSCRIBE_ACK:
818
1.30k
        {
819
1.30k
            MqttUnsubscribeAck unsubscribe_ack,
820
1.30k
                               *p_unsubscribe_ack = &unsubscribe_ack;
821
1.30k
            if (packet_obj) {
822
687
                p_unsubscribe_ack = (MqttUnsubscribeAck*)packet_obj;
823
687
            }
824
620
            else {
825
620
                XMEMSET(p_unsubscribe_ack, 0, sizeof(MqttUnsubscribeAck));
826
620
            }
827
1.30k
        #ifdef WOLFMQTT_V5
828
1.30k
            p_unsubscribe_ack->protocol_level = client->protocol_level;
829
1.30k
        #endif
830
1.30k
            rc = MqttDecode_UnsubscribeAck(rx_buf, rx_len, p_unsubscribe_ack);
831
1.30k
            if (rc >= 0) {
832
1.14k
                packet_id = p_unsubscribe_ack->packet_id;
833
1.14k
            #ifdef WOLFMQTT_V5
834
1.14k
                if (doProps) {
835
1.14k
                    int tmp = Handle_Props(client, p_unsubscribe_ack->props,
836
1.14k
                                           (packet_obj != NULL), 1);
837
1.14k
                    p_unsubscribe_ack->props = NULL;
838
1.14k
                    if (tmp != MQTT_CODE_SUCCESS) {
839
0
                        rc = tmp;
840
0
                    }
841
1.14k
                }
842
1.14k
            #endif
843
1.14k
            }
844
1.30k
            break;
845
3.77k
        }
846
1.35k
        case MQTT_PACKET_TYPE_PING_RESP:
847
1.35k
        {
848
1.35k
            MqttPing ping, *p_ping = &ping;
849
1.35k
            if (packet_obj) {
850
717
                p_ping = (MqttPing*)packet_obj;
851
717
            }
852
640
            else {
853
640
                XMEMSET(p_ping, 0, sizeof(MqttPing));
854
640
            }
855
1.35k
            rc = MqttDecode_Ping(rx_buf, rx_len, p_ping);
856
1.35k
            break;
857
3.77k
        }
858
6.01k
        case MQTT_PACKET_TYPE_AUTH:
859
6.01k
        {
860
6.01k
        #ifdef WOLFMQTT_V5
861
6.01k
            MqttAuth auth, *p_auth = &auth;
862
6.01k
            if (packet_obj) {
863
3.27k
                p_auth = (MqttAuth*)packet_obj;
864
3.27k
            }
865
2.73k
            else {
866
2.73k
                XMEMSET(p_auth, 0, sizeof(MqttAuth));
867
2.73k
            }
868
6.01k
            rc = MqttDecode_Auth(rx_buf, rx_len, p_auth);
869
6.01k
            if (rc >= 0 && doProps) {
870
4.46k
                int tmp = Handle_Props(client, p_auth->props,
871
4.46k
                                       (packet_obj != NULL), 1);
872
4.46k
                p_auth->props = NULL;
873
4.46k
                if (tmp != MQTT_CODE_SUCCESS) {
874
0
                    rc = tmp;
875
0
                }
876
4.46k
            }
877
        #else
878
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE);
879
        #endif /* WOLFMQTT_V5 */
880
6.01k
            break;
881
3.77k
        }
882
4.17k
        case MQTT_PACKET_TYPE_DISCONNECT:
883
4.17k
        {
884
4.17k
        #ifdef WOLFMQTT_V5
885
4.17k
            MqttDisconnect disc, *p_disc = &disc;
886
4.17k
            if (packet_obj) {
887
2.24k
                p_disc = (MqttDisconnect*)packet_obj;
888
2.24k
            }
889
1.93k
            else {
890
1.93k
                XMEMSET(p_disc, 0, sizeof(MqttDisconnect));
891
1.93k
            }
892
4.17k
            rc = MqttDecode_Disconnect(rx_buf, rx_len, p_disc);
893
4.17k
            if (rc >= 0 && doProps) {
894
3.12k
                int tmp = Handle_Props(client, p_disc->props,
895
3.12k
                                       (packet_obj != NULL), 1);
896
3.12k
                p_disc->props = NULL;
897
3.12k
                if (tmp != MQTT_CODE_SUCCESS) {
898
0
                    rc = tmp;
899
0
                }
900
3.12k
            }
901
4.17k
            #ifdef WOLFMQTT_DISCONNECT_CB
902
            /* Call disconnect callback with reason code */
903
4.17k
            if ((packet_obj != NULL) && client->disconnect_cb) {
904
0
                client->disconnect_cb(client, p_disc->reason_code,
905
0
                    client->disconnect_ctx);
906
0
            }
907
4.17k
            #endif
908
        #else
909
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE);
910
        #endif /* WOLFMQTT_V5 */
911
4.17k
            break;
912
3.77k
        }
913
8
        case MQTT_PACKET_TYPE_CONNECT:
914
8
        case MQTT_PACKET_TYPE_SUBSCRIBE:
915
8
        case MQTT_PACKET_TYPE_UNSUBSCRIBE:
916
8
        case MQTT_PACKET_TYPE_PING_REQ:
917
8
        case MQTT_PACKET_TYPE_ANY:
918
144
        case MQTT_PACKET_TYPE_RESERVED:
919
144
        default:
920
            /* these type are only encoded by client */
921
144
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE);
922
144
            break;
923
25.0k
        } /* switch (packet_type) */
924
25.0k
    }
925
926
25.0k
    if (ppacket_id) {
927
25.0k
        *ppacket_id = packet_id;
928
25.0k
    }
929
930
25.0k
    (void)client;
931
25.0k
    (void)doProps;
932
933
#ifdef WOLFMQTT_DEBUG_CLIENT
934
    PRINTF("MqttClient_DecodePacket: Rc %d, Len %d, Type %s (%d), ID %d,"
935
            " QoS %d, doProps %d",
936
        rc, rx_len, MqttPacket_TypeDesc(packet_type), packet_type, packet_id,
937
        packet_qos, doProps);
938
#endif
939
940
25.0k
    return rc;
941
25.0k
}
942
943
static int MqttClient_HandlePacket(MqttClient* client,
944
    MqttPacketType packet_type, void *packet_obj, MqttPublishResp* resp,
945
    int timeout_ms)
946
13.6k
{
947
13.6k
    int rc = MQTT_CODE_SUCCESS;
948
13.6k
    MqttQoS packet_qos = MQTT_QOS_0;
949
13.6k
    word16 packet_id = 0;
950
951
13.6k
    if (client == NULL || packet_obj == NULL) {
952
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
953
0
    }
954
955
    /* make sure the response defaults to no ACK */
956
13.6k
    resp->packet_type = MQTT_PACKET_TYPE_RESERVED;
957
958
13.6k
    switch (packet_type)
959
13.6k
    {
960
493
        case MQTT_PACKET_TYPE_CONNECT_ACK:
961
493
        {
962
493
            rc = MqttClient_DecodePacket(client, client->rx_buf,
963
493
                client->packet.buf_len, packet_obj, &packet_type, &packet_qos,
964
493
                &packet_id, 1);
965
493
            break;
966
0
        }
967
4.99k
        case MQTT_PACKET_TYPE_PUBLISH:
968
4.99k
        {
969
4.99k
            MqttPublish* publish = (MqttPublish*)packet_obj;
970
4.99k
            if (publish->stat.read != MQTT_MSG_PAYLOAD2) {
971
4.99k
                rc = MqttClient_DecodePacket(client, client->rx_buf,
972
4.99k
                    client->packet.buf_len, packet_obj, &packet_type,
973
4.99k
                    &packet_qos, &packet_id, 1);
974
4.99k
                if (rc <= 0) {
975
3.59k
                    return rc;
976
3.59k
                }
977
4.99k
            }
978
0
            else {
979
                /* packet ID and QoS were already established */
980
0
                packet_id =  publish->packet_id;
981
0
                packet_qos = publish->qos;
982
0
            }
983
984
1.39k
            rc = MqttClient_Publish_ReadPayload(client, publish, timeout_ms);
985
986
            /* MQTT_CODE_CONTINUE means the payload is not fully read yet. Return
987
             * to the caller and keep publish->props and the read state intact so
988
             * the non-blocking re-entry can resume. */
989
1.39k
            if (rc == MQTT_CODE_CONTINUE) {
990
0
                break;
991
0
            }
992
993
            /* The publish read is terminal here, whether it succeeded or failed
994
             * to deliver (e.g. no msg_cb, or the callback returned an error).
995
             * Reset the read state and free the retained V5 property list for
996
             * every terminal result: the properties are intentionally kept
997
             * through decode/callback and were previously freed only on the
998
             * success path, so an error return would leak the property pool
999
             * (or heap, under WOLFMQTT_DYN_PROP). Also resetting the read state
1000
             * keeps a caller that logs the error and retries from re-entering on
1001
             * stale MQTT_MSG_PAYLOAD2 state. */
1002
1.39k
            publish->stat.read = MQTT_MSG_BEGIN; /* reset state */
1003
1.39k
        #ifdef WOLFMQTT_V5
1004
            /* Free the properties */
1005
1.39k
            MqttProps_Free(publish->props);
1006
1.39k
            publish->props = NULL;
1007
1.39k
        #endif
1008
1009
1.39k
            if (rc < 0) {
1010
197
                break;
1011
197
            }
1012
1013
            /* Handle QoS */
1014
1.20k
            if (packet_qos == MQTT_QOS_0) {
1015
                /* we are done, no QoS response */
1016
490
                break;
1017
490
            }
1018
1019
710
        #ifdef WOLFMQTT_V5
1020
            /* Copy response code in case changed by callback */
1021
710
            resp->reason_code = publish->resp.reason_code;
1022
710
        #endif
1023
            /* Populate information needed for ack */
1024
710
            resp->packet_type = (packet_qos == MQTT_QOS_1) ?
1025
479
                MQTT_PACKET_TYPE_PUBLISH_ACK :
1026
710
                MQTT_PACKET_TYPE_PUBLISH_REC;
1027
710
            resp->packet_id = packet_id;
1028
710
            break;
1029
1.20k
        }
1030
443
        case MQTT_PACKET_TYPE_PUBLISH_ACK:
1031
1.10k
        case MQTT_PACKET_TYPE_PUBLISH_REC:
1032
1.80k
        case MQTT_PACKET_TYPE_PUBLISH_REL:
1033
2.57k
        case MQTT_PACKET_TYPE_PUBLISH_COMP:
1034
2.57k
        {
1035
        #if defined(WOLFMQTT_V5) && defined(WOLFMQTT_DEBUG_CLIENT)
1036
            MqttPublishResp* publish_resp = (MqttPublishResp*)packet_obj;
1037
        #endif
1038
2.57k
            rc = MqttClient_DecodePacket(client, client->rx_buf,
1039
2.57k
                client->packet.buf_len, packet_obj, &packet_type,
1040
2.57k
                &packet_qos, &packet_id, 1);
1041
2.57k
            if (rc <= 0) {
1042
0
                return rc;
1043
0
            }
1044
1045
        #if defined(WOLFMQTT_V5) && defined(WOLFMQTT_DEBUG_CLIENT)
1046
            PRINTF("\tPublish response: reason code %d, Type %s (%d),"
1047
                    " ID %d, QoS %d",
1048
                    publish_resp->reason_code,
1049
                    MqttPacket_TypeDesc(packet_type),
1050
                    packet_type, packet_id, packet_qos);
1051
        #endif
1052
1053
            /* Only ACK publish Received or Release QoS levels */
1054
2.57k
            if (packet_type != MQTT_PACKET_TYPE_PUBLISH_REC &&
1055
1.90k
                packet_type != MQTT_PACKET_TYPE_PUBLISH_REL) {
1056
1.21k
                break;
1057
1.21k
            }
1058
1059
1.36k
        #ifdef WOLFMQTT_V5
1060
            /* A v5 broker rejects a QoS 2 PUBLISH at the PUBREC stage with a
1061
             * reason code >= 0x80 (e.g. not authorized, quota exceeded, topic
1062
             * name invalid, payload format invalid). Per [MQTT-4.3.3] the
1063
             * exchange is then complete and the sender MUST NOT send a PUBREL.
1064
             * Surface the rejection instead of advancing the handshake, which
1065
             * would emit an illegal PUBREL and then block waiting for a PUBCOMP
1066
             * the broker will never send. The QoS 1 PUBACK and the QoS 2
1067
             * PUBCOMP reason codes are checked by the caller after the wait.
1068
             * Note (WOLFMQTT_MULTITHREAD): when a separate thread drives reads
1069
             * and processes this PUBREC, it receives this error directly and
1070
             * the publishing thread's PUBCOMP pending response is not marked
1071
             * done, so that publish blocks until cmd_timeout_ms. This matches
1072
             * the pre-existing behavior (which left the publisher waiting on a
1073
             * PUBCOMP after an illegal PUBREL) and is not made worse here. */
1074
1.36k
            if (packet_type == MQTT_PACKET_TYPE_PUBLISH_REC &&
1075
666
                client->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5 &&
1076
0
                (((MqttPublishResp*)packet_obj)->reason_code & 0x80)) {
1077
0
                return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PUBLISH_REJECTED);
1078
0
            }
1079
1.36k
        #endif
1080
1081
            /* Populate information needed for ack */
1082
1.36k
            resp->packet_type = packet_type+1; /* next ack */
1083
1.36k
            resp->packet_id = packet_id;
1084
1.36k
            break;
1085
1.36k
        }
1086
706
        case MQTT_PACKET_TYPE_SUBSCRIBE_ACK:
1087
706
        {
1088
706
            rc = MqttClient_DecodePacket(client, client->rx_buf,
1089
706
                client->packet.buf_len, packet_obj, &packet_type, &packet_qos,
1090
706
                &packet_id, 1);
1091
706
            break;
1092
1.36k
        }
1093
562
        case MQTT_PACKET_TYPE_UNSUBSCRIBE_ACK:
1094
562
        {
1095
562
            rc = MqttClient_DecodePacket(client, client->rx_buf,
1096
562
                client->packet.buf_len, packet_obj, &packet_type, &packet_qos,
1097
562
                &packet_id, 1);
1098
562
            break;
1099
1.36k
        }
1100
599
        case MQTT_PACKET_TYPE_PING_RESP:
1101
599
        {
1102
599
            rc = MqttClient_DecodePacket(client, client->rx_buf,
1103
599
                client->packet.buf_len, packet_obj, &packet_type, &packet_qos,
1104
599
                &packet_id, 1);
1105
599
            break;
1106
1.36k
        }
1107
2.22k
        case MQTT_PACKET_TYPE_AUTH:
1108
2.22k
        {
1109
2.22k
        #ifdef WOLFMQTT_V5
1110
2.22k
            rc = MqttClient_DecodePacket(client, client->rx_buf,
1111
2.22k
                client->packet.buf_len, packet_obj, &packet_type, &packet_qos,
1112
2.22k
                &packet_id, 1);
1113
            /* The decoded AUTH properties (including AUTH_DATA) were pointers
1114
             * into rx_buf and have now been delivered to the callback and
1115
             * freed. Scrub rx_buf so the enhanced-authentication material does
1116
             * not linger until the next read, matching MqttClient_Auth and the
1117
             * v5 CONNACK path. This runs under lockRecv (held by MqttReadStart
1118
             * in MqttClient_WaitType), so no additional locking is needed. */
1119
2.22k
            CLIENT_FORCE_ZERO(client->rx_buf, client->rx_buf_len);
1120
        #else
1121
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE);
1122
        #endif
1123
2.22k
            break;
1124
1.36k
        }
1125
1126
1.51k
        case MQTT_PACKET_TYPE_DISCONNECT:
1127
1.51k
        {
1128
1.51k
        #ifdef WOLFMQTT_V5
1129
1.51k
            rc = MqttClient_DecodePacket(client, client->rx_buf,
1130
1.51k
                client->packet.buf_len, packet_obj, &packet_type, &packet_qos,
1131
1.51k
                &packet_id, 1);
1132
        #else
1133
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE);
1134
        #endif
1135
1.51k
            break;
1136
1.36k
        }
1137
0
        case MQTT_PACKET_TYPE_CONNECT:
1138
0
        case MQTT_PACKET_TYPE_SUBSCRIBE:
1139
0
        case MQTT_PACKET_TYPE_UNSUBSCRIBE:
1140
0
        case MQTT_PACKET_TYPE_PING_REQ:
1141
0
        case MQTT_PACKET_TYPE_ANY:
1142
0
        case MQTT_PACKET_TYPE_RESERVED:
1143
0
        default:
1144
            /* these types are only sent from client and should not be sent
1145
             * by broker */
1146
0
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE);
1147
0
            break;
1148
13.6k
    } /* switch (packet_type) */
1149
1150
#ifdef WOLFMQTT_DEBUG_CLIENT
1151
    if (rc < 0) {
1152
        PRINTF("MqttClient_HandlePacket: Rc %d, Type %s (%d), QoS %d, ID %d",
1153
            rc, MqttPacket_TypeDesc(packet_type), packet_type, packet_qos,
1154
            packet_id);
1155
    }
1156
#endif
1157
1158
10.0k
    return rc;
1159
13.6k
}
1160
1161
static inline int MqttIsPubRespPacket(int packet_type)
1162
19.8k
{
1163
19.8k
    return (packet_type == MQTT_PACKET_TYPE_PUBLISH_ACK /* Acknowledgment */ ||
1164
18.7k
            packet_type == MQTT_PACKET_TYPE_PUBLISH_REC /* Received */ ||
1165
17.8k
            packet_type == MQTT_PACKET_TYPE_PUBLISH_REL /* Release */ ||
1166
16.5k
            packet_type == MQTT_PACKET_TYPE_PUBLISH_COMP /* Complete */);
1167
19.8k
}
1168
1169
#ifdef WOLFMQTT_MULTITHREAD
1170
/* this function will return:
1171
 * MQTT_CODE_CONTINUE indicating found, but not marked done
1172
 * MQTT_CODE_ERROR_NOT_FOUND: Not found
1173
 * Any other response is from the the packet_ret
1174
 */
1175
WOLFMQTT_LOCAL int MqttClient_CheckPendResp(MqttClient *client, byte wait_type,
1176
    word16 wait_packet_id)
1177
{
1178
    int rc;
1179
    MqttPendResp *pendResp = NULL;
1180
1181
    /* Check to see if packet type and id have already completed */
1182
    rc = wm_SemLock(&client->lockClient);
1183
    if (rc == 0) {
1184
        if (MqttClient_RespList_Find(client, (MqttPacketType)wait_type,
1185
            wait_packet_id, &pendResp))
1186
        {
1187
            if ((pendResp != NULL) && (pendResp->packetDone)) {
1188
                /* pending response is already done, so return */
1189
                rc = pendResp->packet_ret;
1190
            #ifdef WOLFMQTT_DEBUG_CLIENT
1191
                PRINTF("PendResp Check Done %p: Rc %d", pendResp, rc);
1192
            #endif
1193
                MqttClient_RespList_Remove(client, pendResp);
1194
            }
1195
            else {
1196
                /* item not done */
1197
                rc = MQTT_CODE_CONTINUE;
1198
            }
1199
        }
1200
        else {
1201
            /* item not found */
1202
            rc = MQTT_CODE_ERROR_NOT_FOUND;
1203
        }
1204
        wm_SemUnlock(&client->lockClient);
1205
    }
1206
    return rc;
1207
}
1208
#endif /* WOLFMQTT_MULTITHREAD */
1209
1210
/* Helper for clearing the contents of an object buffer based on packet type */
1211
static void MqttClient_PacketReset(MqttPacketType packet_type, void* packet_obj)
1212
9.67k
{
1213
9.67k
    size_t objSz = 0;
1214
9.67k
    size_t offset = sizeof(MqttMsgStat);
1215
    /* The MqttPendResp offset is added only for the types whose struct embeds
1216
     * a pendResp member (right after MqttMsgStat). Ack-only types (CONNECT_ACK,
1217
     * SUBSCRIBE_ACK, UNSUBSCRIBE_ACK, DISCONNECT) have no pendResp, so adding
1218
     * it there would skip live fields - matching the per-type handling in
1219
     * MqttSNClient_PacketReset. */
1220
9.67k
    switch (packet_type) {
1221
0
        case MQTT_PACKET_TYPE_CONNECT:
1222
0
            objSz = sizeof(MqttConnect);
1223
        #ifdef WOLFMQTT_MULTITHREAD
1224
            offset += sizeof(MqttPendResp);
1225
        #endif
1226
0
            break;
1227
493
        case MQTT_PACKET_TYPE_CONNECT_ACK:
1228
493
            objSz = sizeof(MqttConnectAck);
1229
493
            break;
1230
1.01k
        case MQTT_PACKET_TYPE_PUBLISH:
1231
1.01k
            objSz = sizeof(MqttPublish);
1232
        #ifdef WOLFMQTT_MULTITHREAD
1233
            offset += sizeof(MqttPendResp);
1234
        #endif
1235
1.01k
            break;
1236
443
        case MQTT_PACKET_TYPE_PUBLISH_ACK:
1237
1.10k
        case MQTT_PACKET_TYPE_PUBLISH_REC:
1238
1.80k
        case MQTT_PACKET_TYPE_PUBLISH_REL:
1239
2.57k
        case MQTT_PACKET_TYPE_PUBLISH_COMP:
1240
2.57k
            objSz = sizeof(MqttPublishResp);
1241
        #ifdef WOLFMQTT_MULTITHREAD
1242
            offset += sizeof(MqttPendResp);
1243
        #endif
1244
2.57k
            break;
1245
0
        case MQTT_PACKET_TYPE_SUBSCRIBE:
1246
0
            objSz = sizeof(MqttSubscribe);
1247
        #ifdef WOLFMQTT_MULTITHREAD
1248
            offset += sizeof(MqttPendResp);
1249
        #endif
1250
0
            break;
1251
706
        case MQTT_PACKET_TYPE_SUBSCRIBE_ACK:
1252
706
            objSz = sizeof(MqttSubscribeAck);
1253
706
            break;
1254
0
        case MQTT_PACKET_TYPE_UNSUBSCRIBE:
1255
0
            objSz = sizeof(MqttUnsubscribe);
1256
        #ifdef WOLFMQTT_MULTITHREAD
1257
            offset += sizeof(MqttPendResp);
1258
        #endif
1259
0
            break;
1260
562
        case MQTT_PACKET_TYPE_UNSUBSCRIBE_ACK:
1261
562
            objSz = sizeof(MqttUnsubscribeAck);
1262
562
            break;
1263
0
        case MQTT_PACKET_TYPE_PING_REQ:
1264
599
        case MQTT_PACKET_TYPE_PING_RESP:
1265
599
            objSz = sizeof(MqttPing);
1266
        #ifdef WOLFMQTT_MULTITHREAD
1267
            offset += sizeof(MqttPendResp);
1268
        #endif
1269
599
            break;
1270
2.22k
        case MQTT_PACKET_TYPE_AUTH:
1271
2.22k
        #ifdef WOLFMQTT_V5
1272
2.22k
            objSz = sizeof(MqttAuth);
1273
        #ifdef WOLFMQTT_MULTITHREAD
1274
            offset += sizeof(MqttPendResp);
1275
        #endif
1276
2.22k
        #endif
1277
2.22k
            break;
1278
1.51k
        case MQTT_PACKET_TYPE_DISCONNECT:
1279
1.51k
        #ifdef WOLFMQTT_V5
1280
1.51k
            objSz = sizeof(MqttDisconnect);
1281
1.51k
        #endif
1282
1.51k
            break;
1283
0
        case MQTT_PACKET_TYPE_ANY:
1284
0
        case MQTT_PACKET_TYPE_RESERVED:
1285
0
        default:
1286
0
            break;
1287
9.67k
    } /* switch (packet_type) */
1288
9.67k
    if (objSz > offset) {
1289
9.07k
        XMEMSET((byte*)packet_obj + offset, 0, objSz - offset);
1290
9.07k
    }
1291
9.67k
}
1292
1293
static int MqttClient_WaitType(MqttClient *client, void *packet_obj,
1294
    byte wait_type, word16 wait_packet_id, int timeout_ms)
1295
9.62k
{
1296
9.62k
    int rc = MQTT_CODE_SUCCESS;
1297
9.62k
    word16         packet_id;
1298
9.62k
    MqttPacketType packet_type;
1299
9.62k
    MqttQoS        packet_qos = MQTT_QOS_0;
1300
#ifdef WOLFMQTT_MULTITHREAD
1301
    MqttPendResp *pendResp;
1302
#endif
1303
9.62k
    MqttMsgStat* mms_stat;
1304
9.62k
    int waitMatchFound;
1305
9.62k
    void* use_packet_obj = NULL;
1306
1307
9.62k
    if (client == NULL || packet_obj == NULL) {
1308
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
1309
0
    }
1310
1311
    /* all packet type structures must have MqttMsgStat at top */
1312
9.62k
    mms_stat = (MqttMsgStat*)packet_obj;
1313
1314
18.2k
wait_again:
1315
1316
    /* initialize variables */
1317
18.2k
    packet_id = 0;
1318
18.2k
    packet_type = MQTT_PACKET_TYPE_RESERVED;
1319
#ifdef WOLFMQTT_MULTITHREAD
1320
    pendResp = NULL;
1321
#endif
1322
18.2k
    waitMatchFound = 0;
1323
1324
#ifdef WOLFMQTT_DEBUG_CLIENT
1325
    #ifdef WOLFMQTT_NONBLOCK
1326
    if (client->lastRc != MQTT_CODE_CONTINUE)
1327
    #endif
1328
    {
1329
        PRINTF("MqttClient_WaitType: Type %s (%d), ID %d, State %d-%d",
1330
            MqttPacket_TypeDesc((MqttPacketType)wait_type),
1331
                wait_type, wait_packet_id, mms_stat->read, mms_stat->write);
1332
    }
1333
#endif
1334
1335
18.2k
    switch (mms_stat->read)
1336
18.2k
    {
1337
13.4k
        case MQTT_MSG_BEGIN:
1338
13.4k
        {
1339
        #ifdef WOLFMQTT_MULTITHREAD
1340
            /* Check to see if packet type and id have already completed */
1341
            rc = MqttClient_CheckPendResp(client, wait_type, wait_packet_id);
1342
            if (rc != MQTT_CODE_ERROR_NOT_FOUND && rc != MQTT_CODE_CONTINUE) {
1343
                return rc;
1344
            }
1345
        #endif
1346
1347
13.4k
            if ((rc = MqttReadStart(client, mms_stat)) != 0) {
1348
0
                return rc;
1349
0
            }
1350
1351
13.4k
            mms_stat->read = MQTT_MSG_WAIT;
1352
13.4k
        }
1353
13.4k
        FALL_THROUGH;
1354
1355
13.4k
        case MQTT_MSG_WAIT:
1356
14.0k
        case MQTT_MSG_HEADER:
1357
14.0k
        {
1358
            /* Wait for packet */
1359
14.0k
            rc = MqttPacket_Read(client, client->rx_buf, client->rx_buf_len,
1360
14.0k
                    timeout_ms);
1361
            /* handle failure */
1362
14.0k
            if (rc <= 0) {
1363
            #ifdef WOLFMQTT_NONBLOCK
1364
                if (rc == MQTT_CODE_CONTINUE &&
1365
                    (client->packet.stat > MQTT_PK_BEGIN ||
1366
                     client->read.total > 0)
1367
                ) {
1368
                    /* advance state, since we received some data */
1369
                    mms_stat->read = MQTT_MSG_HEADER;
1370
                }
1371
            #endif
1372
2.72k
                break;
1373
2.72k
            }
1374
1375
            /* advance state, since we received some data */
1376
11.3k
            mms_stat->read = MQTT_MSG_HEADER;
1377
1378
            /* capture length read */
1379
11.3k
            client->packet.buf_len = rc;
1380
1381
            /* Decode Packet - get type, qos and id */
1382
11.3k
            rc = MqttClient_DecodePacket(client, client->rx_buf,
1383
11.3k
                client->packet.buf_len, NULL, &packet_type, &packet_qos,
1384
11.3k
                &packet_id, 1);
1385
11.3k
            if (rc < 0) {
1386
1.68k
                break;
1387
1.68k
            }
1388
1389
9.67k
            MqttClient_PacketReset(packet_type, &client->msg);
1390
1391
        #ifdef WOLFMQTT_DEBUG_CLIENT
1392
            PRINTF("Read Packet: Len %d, Type %d, ID %d",
1393
                client->packet.buf_len, packet_type, packet_id);
1394
        #endif
1395
1396
            /* Ping response is special case, no payload */
1397
9.67k
            if (packet_type != MQTT_PACKET_TYPE_PING_RESP) {
1398
9.07k
                mms_stat->read = MQTT_MSG_PAYLOAD;
1399
9.07k
            }
1400
599
            else {
1401
599
                mms_stat->read = MQTT_MSG_WAIT;
1402
599
            }
1403
9.67k
        }
1404
9.67k
        FALL_THROUGH;
1405
1406
13.6k
        case MQTT_MSG_PAYLOAD:
1407
13.6k
        case MQTT_MSG_PAYLOAD2:
1408
13.6k
        {
1409
13.6k
            MqttPublishResp resp;
1410
13.6k
            MqttPacketType use_packet_type;
1411
1412
            /* Determine if we received data for this request */
1413
13.6k
            if ((wait_type == MQTT_PACKET_TYPE_ANY ||
1414
8.38k
                 wait_type == packet_type ||
1415
7.82k
                 (MqttIsPubRespPacket(packet_type) &&
1416
2.20k
                  MqttIsPubRespPacket(wait_type))) &&
1417
6.21k
                (wait_packet_id == 0 || wait_packet_id == packet_id))
1418
5.80k
            {
1419
5.80k
                use_packet_obj = packet_obj;
1420
            #ifdef WOLFMQTT_DEBUG_CLIENT
1421
                PRINTF("Using INCOMING packet_obj %p", use_packet_obj);
1422
            #endif
1423
5.80k
                if (packet_type == wait_type ||
1424
5.60k
                        wait_type == MQTT_PACKET_TYPE_ANY) {
1425
                    /* Only stop waiting when matched or waiting for "any" */
1426
5.60k
                    waitMatchFound = 1;
1427
5.60k
                }
1428
5.80k
            }
1429
7.84k
            else {
1430
            #ifdef WOLFMQTT_MULTITHREAD
1431
                rc = wm_SemLock(&client->lockClient);
1432
                if (rc != 0) {
1433
                    break; /* error */
1434
                }
1435
            #endif
1436
1437
                /* use generic packet object */
1438
7.84k
                use_packet_obj = &client->msg;
1439
            #ifdef WOLFMQTT_DEBUG_CLIENT
1440
                PRINTF("Using SHARED packet_obj %p", use_packet_obj);
1441
            #endif
1442
1443
            #ifdef WOLFMQTT_MULTITHREAD
1444
                wm_SemUnlock(&client->lockClient);
1445
            #endif
1446
7.84k
            }
1447
13.6k
            use_packet_type = packet_type;
1448
1449
        #ifdef WOLFMQTT_MULTITHREAD
1450
            /* Check to see if we have a pending response for this packet */
1451
            pendResp = NULL;
1452
            rc = wm_SemLock(&client->lockClient);
1453
            if (rc == 0) {
1454
                if (MqttClient_RespList_Find(client, packet_type, packet_id,
1455
                                                               &pendResp)) {
1456
                    /* we found packet match this incoming read packet */
1457
                    pendResp->packetProcessing = 1;
1458
                    if (pendResp->packet_obj != packet_obj) {
1459
                        use_packet_obj = pendResp->packet_obj;
1460
                        use_packet_type = pendResp->packet_type;
1461
                        /* req from another thread... not a match */
1462
                        waitMatchFound = 0;
1463
                    }
1464
                }
1465
                wm_SemUnlock(&client->lockClient);
1466
            }
1467
            else {
1468
                break; /* error */
1469
            }
1470
        #endif /* WOLFMQTT_MULTITHREAD */
1471
1472
            /* for payload state packet type is always publish */
1473
13.6k
            if (use_packet_type == MQTT_PACKET_TYPE_RESERVED &&
1474
3.97k
                    (mms_stat->read == MQTT_MSG_PAYLOAD ||
1475
0
                     mms_stat->read == MQTT_MSG_PAYLOAD2))
1476
3.97k
            {
1477
3.97k
                use_packet_type = MQTT_PACKET_TYPE_PUBLISH;
1478
3.97k
            }
1479
            /* cache publish packet id and qos for MqttClient_HandlePacket payload */
1480
13.6k
            if (use_packet_type == MQTT_PACKET_TYPE_PUBLISH &&
1481
4.99k
                  mms_stat->read == MQTT_MSG_PAYLOAD && use_packet_obj != NULL)
1482
4.99k
            {
1483
4.99k
                MqttObject* obj = (MqttObject*)use_packet_obj;
1484
4.99k
                obj->publish.qos = packet_qos;
1485
4.99k
                obj->publish.packet_id = packet_id;
1486
4.99k
            }
1487
1488
            /* Perform packet handling for publish callback and QoS */
1489
13.6k
            XMEMSET(&resp, 0, sizeof(resp));
1490
13.6k
            rc = MqttClient_HandlePacket(client, use_packet_type,
1491
13.6k
                use_packet_obj, &resp, timeout_ms);
1492
1493
            /* if using the shared packet object, make sure the original
1494
             * state is correct for publish payload 2 (continued) */
1495
13.6k
            if (use_packet_obj != NULL && use_packet_obj != mms_stat &&
1496
7.84k
                    ((MqttMsgStat*)use_packet_obj)->read == MQTT_MSG_PAYLOAD2) {
1497
0
                mms_stat->read = MQTT_MSG_PAYLOAD2;
1498
0
            }
1499
1500
        #ifdef WOLFMQTT_NONBLOCK
1501
            if (rc == MQTT_CODE_CONTINUE) {
1502
                break;
1503
            }
1504
        #endif
1505
1506
            /* handle success case */
1507
13.6k
            if (rc >= 0) {
1508
9.86k
                rc = MQTT_CODE_SUCCESS;
1509
9.86k
            }
1510
3.79k
            else {
1511
                /* error, break */
1512
3.79k
                break;
1513
3.79k
            }
1514
1515
        #ifdef WOLFMQTT_MULTITHREAD
1516
            if (pendResp) {
1517
                /* Mark pending response entry done */
1518
                if (wm_SemLock(&client->lockClient) == 0) {
1519
                    pendResp->packetDone = 1;
1520
                    pendResp->packet_ret = rc;
1521
                #ifdef WOLFMQTT_DEBUG_CLIENT
1522
                    PRINTF("PendResp Done %p", pendResp);
1523
                #endif
1524
                    pendResp = NULL;
1525
                    wm_SemUnlock(&client->lockClient);
1526
                }
1527
            }
1528
        #endif /* WOLFMQTT_MULTITHREAD */
1529
1530
            /* Determine if we are sending ACK or done */
1531
9.86k
            if (MqttIsPubRespPacket(resp.packet_type)) {
1532
                /* if we get here, then we are sending an ACK */
1533
2.07k
                mms_stat->read = MQTT_MSG_ACK;
1534
2.07k
                mms_stat->ack = MQTT_MSG_WAIT;
1535
1536
                /* setup ACK in shared context */
1537
2.07k
                XMEMCPY(&client->packetAck, &resp, sizeof(MqttPublishResp));
1538
2.07k
            #ifdef WOLFMQTT_V5
1539
2.07k
                client->packetAck.protocol_level = client->protocol_level;
1540
2.07k
            #endif
1541
2.07k
            }
1542
1543
            /* done reading */
1544
9.86k
            MqttReadStop(client, mms_stat);
1545
9.86k
            break;
1546
13.6k
        }
1547
1548
164
        case MQTT_MSG_ACK:
1549
            /* go to write section below */
1550
164
            break;
1551
1552
0
        case MQTT_MSG_AUTH:
1553
0
        default:
1554
0
        {
1555
        #ifdef WOLFMQTT_DEBUG_CLIENT
1556
            PRINTF("MqttClient_WaitType: Invalid read state %d!",
1557
                mms_stat->read);
1558
        #endif
1559
0
            rc = MQTT_CODE_ERROR_STAT;
1560
0
            break;
1561
0
        }
1562
18.2k
    } /* switch (mms_stat->read) */
1563
1564
18.2k
    switch (mms_stat->ack)
1565
18.2k
    {
1566
16.0k
        case MQTT_MSG_BEGIN:
1567
            /* wait for read to set ack */
1568
16.0k
            break;
1569
1570
2.07k
        case MQTT_MSG_WAIT:
1571
2.07k
        {
1572
            /* Flag write active / lock mutex */
1573
2.07k
            if ((rc = MqttWriteStart(client, mms_stat)) != 0) {
1574
0
                break;
1575
0
            }
1576
2.07k
            mms_stat->ack = MQTT_MSG_ACK;
1577
2.07k
        }
1578
2.07k
        FALL_THROUGH;
1579
1580
2.14k
        case MQTT_MSG_ACK:
1581
2.14k
        {
1582
            /* send ack */
1583
2.14k
            rc = MqttEncode_PublishResp(client->tx_buf, client->tx_buf_len,
1584
2.14k
                client->packetAck.packet_type, &client->packetAck);
1585
        #ifdef WOLFMQTT_DEBUG_CLIENT
1586
            PRINTF("MqttEncode_PublishResp: Len %d, Type %s (%d), ID %d",
1587
                rc, MqttPacket_TypeDesc(client->packetAck.packet_type),
1588
                    client->packetAck.packet_type, client->packetAck.packet_id);
1589
        #endif
1590
2.14k
            if (rc < 0) {
1591
125
                MqttWriteStop(client, mms_stat);
1592
125
                break;
1593
125
            }
1594
1595
2.02k
            client->write.len = rc;
1596
            /* Note: static analyzer complains about set, but not used here.
1597
             * Keeping it to ensure no future issues with rc > 0 */
1598
2.02k
            rc = MQTT_CODE_SUCCESS;
1599
2.02k
            (void)rc; /* inhibit clang-analyzer-deadcode.DeadStores */
1600
1601
2.02k
            mms_stat->ack = MQTT_MSG_HEADER;
1602
2.02k
        }
1603
2.02k
        FALL_THROUGH;
1604
1605
2.02k
        case MQTT_MSG_HEADER:
1606
2.02k
        {
1607
2.02k
            int xfer = client->write.len;
1608
1609
            /* Send publish response packet */
1610
2.02k
            rc = MqttPacket_Write(client, client->tx_buf, xfer);
1611
        #ifdef WOLFMQTT_NONBLOCK
1612
            if (rc == MQTT_CODE_CONTINUE) {
1613
                /* keep send mutex locked and return to caller */
1614
                /* must keep send locked */
1615
                return rc;
1616
            }
1617
        #endif
1618
2.02k
            MqttWriteStop(client, mms_stat);
1619
2.02k
            if (rc == xfer) {
1620
1.48k
                rc = MQTT_CODE_SUCCESS; /* success */
1621
1.48k
            }
1622
1623
2.02k
            mms_stat->ack = MQTT_MSG_BEGIN; /* reset write state */
1624
2.02k
            break;
1625
2.02k
        }
1626
1627
0
        case MQTT_MSG_AUTH:
1628
0
        case MQTT_MSG_PAYLOAD:
1629
0
        case MQTT_MSG_PAYLOAD2:
1630
0
        default:
1631
        #ifdef WOLFMQTT_DEBUG_CLIENT
1632
            PRINTF("MqttClient_WaitType: Invalid ack state %d!",
1633
                mms_stat->ack);
1634
        #endif
1635
0
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT);
1636
0
            break;
1637
18.2k
    } /* switch (mms_stat->ack) */
1638
1639
    /* no data read or ack done, then reset state */
1640
18.2k
    if (mms_stat->read == MQTT_MSG_WAIT) {
1641
2.97k
        mms_stat->read = MQTT_MSG_BEGIN;
1642
2.97k
    }
1643
1644
#ifdef WOLFMQTT_NONBLOCK
1645
    /* if nonblocking and some data has been read, do not release read lock */
1646
    if (rc == MQTT_CODE_CONTINUE && mms_stat->read > MQTT_MSG_WAIT) {
1647
        return rc;
1648
    }
1649
#endif
1650
1651
18.2k
    MqttReadStop(client, mms_stat);
1652
1653
#ifdef WOLFMQTT_NONBLOCK
1654
    #ifdef WOLFMQTT_DEBUG_CLIENT
1655
    #ifdef WOLFMQTT_MULTITHREAD
1656
    if (wm_SemLock(&client->lockClient) == 0)
1657
    #endif
1658
    {
1659
        client->lastRc = rc;
1660
    #ifdef WOLFMQTT_MULTITHREAD
1661
        wm_SemUnlock(&client->lockClient);
1662
    #endif
1663
    }
1664
    #endif /* WOLFMQTT_DEBUG_CLIENT */
1665
    if (rc == MQTT_CODE_CONTINUE) {
1666
        return rc;
1667
    }
1668
#endif
1669
1670
18.2k
    if (rc < 0) {
1671
    #ifdef WOLFMQTT_DEBUG_CLIENT
1672
        if (rc != MQTT_CODE_CONTINUE) {
1673
            PRINTF("MqttClient_WaitType: Failure: %s (%d)",
1674
                MqttClient_ReturnCodeToString(rc), rc);
1675
        }
1676
    #endif
1677
7.93k
        return rc;
1678
7.93k
    }
1679
1680
10.2k
    if (!waitMatchFound) {
1681
        /* if we get here, then the we are still waiting for a packet */
1682
8.59k
        mms_stat->read = MQTT_MSG_BEGIN;
1683
    #ifdef WOLFMQTT_NONBLOCK
1684
        /* for non-blocking return with code continue instead of waiting again
1685
         * if called with packet type and id of 'any' */
1686
        if (wait_type == MQTT_PACKET_TYPE_ANY && wait_packet_id == 0) {
1687
            return MQTT_CODE_CONTINUE;
1688
        }
1689
    #endif
1690
8.59k
        MQTT_TRACE_MSG("Wait Again");
1691
8.59k
        goto wait_again;
1692
8.59k
    }
1693
#ifdef WOLFMQTT_DEBUG_CLIENT
1694
    if (rc != MQTT_CODE_CONTINUE) {
1695
        PRINTF("MqttClient_WaitType: rc %d, state %d-%d-%d",
1696
            rc, mms_stat->read, mms_stat->write, mms_stat->ack);
1697
    }
1698
#endif
1699
1700
1701
1.68k
    return rc;
1702
10.2k
}
1703
1704
1705
/* Public Functions */
1706
int MqttClient_Init(MqttClient *client, MqttNet* net,
1707
    MqttMsgCb msg_cb,
1708
    byte* tx_buf, int tx_buf_len,
1709
    byte* rx_buf, int rx_buf_len,
1710
    int cmd_timeout_ms)
1711
0
{
1712
0
    int rc = MQTT_CODE_SUCCESS;
1713
1714
    /* Check arguments */
1715
0
    if (client == NULL ||
1716
0
        tx_buf == NULL || tx_buf_len <= 0 ||
1717
0
        rx_buf == NULL || rx_buf_len <= 0 ||
1718
0
        cmd_timeout_ms < 0) {
1719
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
1720
0
    }
1721
1722
    /* Initialize the client structure to zero */
1723
0
    XMEMSET(client, 0, sizeof(MqttClient));
1724
1725
    /* Setup client structure */
1726
0
    client->msg_cb = msg_cb;
1727
0
    client->tx_buf = tx_buf;
1728
0
    client->tx_buf_len = tx_buf_len;
1729
0
    client->rx_buf = rx_buf;
1730
0
    client->rx_buf_len = rx_buf_len;
1731
0
    client->cmd_timeout_ms = cmd_timeout_ms;
1732
0
#ifdef WOLFMQTT_V5
1733
    /* Initialize to this build's Maximum QoS. Handle_Props will narrow
1734
     * this if the server advertises a lower MQTT_PROP_MAX_QOS. */
1735
0
    client->max_qos = (MqttQoS)WOLFMQTT_MAX_QOS;
1736
0
    client->retain_avail = 1;
1737
0
    client->protocol_level = MQTT_CONNECT_PROTOCOL_LEVEL;
1738
0
    rc = MqttProps_Init();
1739
0
#endif
1740
1741
#ifdef WOLFMQTT_MULTITHREAD
1742
    if (rc == 0) {
1743
        rc = wm_SemInit(&client->lockSend);
1744
    }
1745
    if (rc == 0) {
1746
        rc = wm_SemInit(&client->lockRecv);
1747
    }
1748
    if (rc == 0) {
1749
        rc = wm_SemInit(&client->lockClient);
1750
    }
1751
    #ifdef ENABLE_MQTT_CURL
1752
    if (rc == 0) {
1753
        rc = wm_SemInit(&client->lockCURL);
1754
    }
1755
    #endif
1756
#endif
1757
1758
0
    if (rc == 0) {
1759
        /* Init socket */
1760
0
        rc = MqttSocket_Init(client, net);
1761
0
    }
1762
1763
0
    if (rc != 0) {
1764
        /* Cleanup if init failed */
1765
0
        MqttClient_DeInit(client);
1766
0
    }
1767
1768
0
    return rc;
1769
0
}
1770
1771
void MqttClient_DeInit(MqttClient *client)
1772
0
{
1773
0
    if (client != NULL) {
1774
#ifdef WOLFMQTT_MULTITHREAD
1775
        (void)wm_SemFree(&client->lockSend);
1776
        (void)wm_SemFree(&client->lockRecv);
1777
        (void)wm_SemFree(&client->lockClient);
1778
    #ifdef ENABLE_MQTT_CURL
1779
        (void)wm_SemFree(&client->lockCURL);
1780
    #endif
1781
#endif
1782
0
#ifdef WOLFMQTT_V5
1783
0
        (void)MqttProps_ShutDown();
1784
0
#endif
1785
0
    }
1786
0
}
1787
1788
#ifdef WOLFMQTT_DISCONNECT_CB
1789
int MqttClient_SetDisconnectCallback(MqttClient *client,
1790
        MqttDisconnectCb discCb, void* ctx)
1791
0
{
1792
0
    if (client == NULL)
1793
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
1794
1795
0
    client->disconnect_cb = discCb;
1796
0
    client->disconnect_ctx = ctx;
1797
1798
0
    return MQTT_CODE_SUCCESS;
1799
0
}
1800
#endif
1801
1802
#ifdef WOLFMQTT_PROPERTY_CB
1803
int MqttClient_SetPropertyCallback(MqttClient *client, MqttPropertyCb propCb,
1804
    void* ctx)
1805
0
{
1806
0
    if (client == NULL)
1807
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
1808
1809
0
    client->property_cb = propCb;
1810
0
    client->property_ctx = ctx;
1811
1812
0
    return MQTT_CODE_SUCCESS;
1813
0
}
1814
#endif
1815
1816
int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect)
1817
4.61k
{
1818
4.61k
    int rc;
1819
1820
    /* Validate required arguments */
1821
4.61k
    if (client == NULL || mc_connect == NULL) {
1822
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
1823
0
    }
1824
1825
4.61k
    if (mc_connect->stat.write == MQTT_MSG_BEGIN) {
1826
        /* Warn if credentials are being sent without TLS */
1827
    #ifdef WOLFMQTT_DEBUG_CLIENT
1828
        if ((mc_connect->username != NULL || mc_connect->password != NULL) &&
1829
            !(MqttClient_Flags(client, 0, 0) & MQTT_CLIENT_FLAG_IS_TLS)) {
1830
            PRINTF("Warning: MQTT credentials are being sent without TLS");
1831
        }
1832
    #endif
1833
1834
4.61k
    #ifndef WOLFMQTT_NO_TIME
1835
        /* Disarm auto keep-alive for the handshake; it is armed only after
1836
         * CONNACK is accepted at the end of this function. Fully cancel any
1837
         * ping left mid-exchange by a prior connection so its held locks and
1838
         * pending response are released before the new write starts, rather
1839
         * than leaving the ping state machine to stall a later wait. Must run
1840
         * before MqttWriteStart so the send lock is free when it is taken.
1841
         * Propagate a cancel failure - only reachable if wm_SemLock itself
1842
         * errors (e.g. on ThreadX), since it otherwise blocks until acquired -
1843
         * rather than starting the write with locks the abandoned ping may
1844
         * still hold. */
1845
4.61k
        client->keep_alive_sec = 0;
1846
4.61k
        client->keep_alive_from_server = 0;
1847
4.61k
        rc = MqttClient_CancelMessage(client,
1848
4.61k
            (MqttObject*)&client->keep_alive_ping);
1849
4.61k
        if (rc != MQTT_CODE_SUCCESS) {
1850
0
            return rc;
1851
0
        }
1852
4.61k
    #endif
1853
1854
        /* Flag write active / lock mutex */
1855
4.61k
        if ((rc = MqttWriteStart(client, &mc_connect->stat)) != 0) {
1856
0
            return rc;
1857
0
        }
1858
1859
4.61k
    #ifdef WOLFMQTT_V5
1860
        /* Use specified protocol version if set */
1861
4.61k
        mc_connect->protocol_level = client->protocol_level;
1862
1863
        /* Reset server-supplied session limits so stale values from a
1864
         * prior broker do not leak across reconnects. An accepted CONNACK
1865
         * will repopulate these in Handle_ConnectAck_Props. Initialize to
1866
         * this build's Maximum QoS so the runtime guard in MqttPublishMsg
1867
         * caps publishes even before CONNACK is processed. */
1868
4.61k
        client->max_qos = (MqttQoS)WOLFMQTT_MAX_QOS;
1869
4.61k
        client->retain_avail = 1;
1870
4.61k
        client->packet_sz_max = 0;
1871
4.61k
    #endif
1872
1873
        /* Encode the connect packet */
1874
4.61k
        rc = MqttEncode_Connect(client->tx_buf, client->tx_buf_len, mc_connect);
1875
    #ifdef WOLFMQTT_DEBUG_CLIENT
1876
        PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d",
1877
            rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_CONNECT),
1878
            MQTT_PACKET_TYPE_CONNECT, 0, 0);
1879
    #endif
1880
4.61k
        if (rc <= 0) {
1881
            /* Encode failed: tx_buf may hold partial plaintext credentials.
1882
             * Zero the full buffer before MqttWriteStop releases lockSend
1883
             * so no other thread can see residual data. */
1884
203
            CLIENT_FORCE_ZERO(client->tx_buf, client->tx_buf_len);
1885
203
            MqttWriteStop(client, &mc_connect->stat);
1886
203
            return rc;
1887
203
        }
1888
4.40k
        client->write.len = rc;
1889
1890
    #ifdef WOLFMQTT_MULTITHREAD
1891
        rc = wm_SemLock(&client->lockClient);
1892
        if (rc == 0) {
1893
            /* inform other threads of expected response */
1894
            rc = MqttClient_RespList_Add(client, MQTT_PACKET_TYPE_CONNECT_ACK,
1895
                    0, &mc_connect->pendResp, &mc_connect->ack);
1896
            wm_SemUnlock(&client->lockClient);
1897
        }
1898
        if (rc != 0) {
1899
            /* Save write.len before MqttWriteStop zeroes client->write */
1900
            int xfer = client->write.len;
1901
            /* Clear tx_buf to remove plaintext credentials BEFORE
1902
             * MqttWriteStop releases lockSend, so another thread cannot
1903
             * race in and repopulate tx_buf before it is scrubbed. */
1904
            CLIENT_FORCE_ZERO(client->tx_buf, xfer);
1905
            MqttWriteStop(client, &mc_connect->stat);
1906
            return rc; /* Error locking client */
1907
        }
1908
    #endif
1909
1910
4.40k
        mc_connect->stat.write = MQTT_MSG_HEADER;
1911
4.40k
    }
1912
4.40k
    if (mc_connect->stat.write == MQTT_MSG_HEADER) {
1913
4.40k
        int xfer = client->write.len;
1914
1915
        /* Send connect packet */
1916
4.40k
        rc = MqttPacket_Write(client, client->tx_buf, xfer);
1917
    #ifdef WOLFMQTT_NONBLOCK
1918
        if (rc == MQTT_CODE_CONTINUE
1919
        #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK
1920
            && client->write.total > 0
1921
        #endif
1922
        ) {
1923
            /* keep send locked and return early.
1924
             * Note: tx_buf still contains credentials until write completes */
1925
            return rc;
1926
        }
1927
    #endif
1928
        /* Clear tx_buf to remove any plaintext credentials from memory
1929
         * BEFORE MqttWriteStop releases lockSend, so another thread cannot
1930
         * race in and populate tx_buf before it is scrubbed.
1931
         * Use xfer (saved before MqttWriteStop zeroes client->write). */
1932
4.40k
        CLIENT_FORCE_ZERO(client->tx_buf, xfer);
1933
4.40k
        MqttWriteStop(client, &mc_connect->stat);
1934
1935
4.40k
        if (rc != xfer) {
1936
3.01k
            MqttClient_CancelMessage(client, (MqttObject*)mc_connect);
1937
3.01k
            return rc;
1938
3.01k
        }
1939
1940
1.39k
    #ifdef WOLFMQTT_V5
1941
        /* Enhanced authentication */
1942
1.39k
        if (client->enable_eauth == 1) {
1943
0
            mc_connect->stat.write = MQTT_MSG_AUTH;
1944
0
        }
1945
1.39k
        else
1946
1.39k
    #endif
1947
1.39k
        {
1948
1.39k
            mc_connect->stat.write = MQTT_MSG_WAIT;
1949
1.39k
        }
1950
1.39k
    }
1951
1952
1.39k
#ifdef WOLFMQTT_V5
1953
    /* Enhanced authentication */
1954
1.39k
    if (mc_connect->protocol_level > MQTT_CONNECT_PROTOCOL_LEVEL_4 &&
1955
1.39k
            mc_connect->stat.write == MQTT_MSG_AUTH)
1956
0
    {
1957
0
        MqttAuth auth, *p_auth = &auth;
1958
0
        MqttProp* prop, *conn_prop;
1959
1960
        /* Find the AUTH property in the connect structure */
1961
0
        for (conn_prop = mc_connect->props;
1962
0
             (conn_prop != NULL) && (conn_prop->type != MQTT_PROP_AUTH_METHOD);
1963
0
             conn_prop = conn_prop->next) {
1964
0
        }
1965
0
        if (conn_prop == NULL) {
1966
        #ifdef WOLFMQTT_MULTITHREAD
1967
            if (wm_SemLock(&client->lockClient) == 0) {
1968
                MqttClient_RespList_Remove(client, &mc_connect->pendResp);
1969
                wm_SemUnlock(&client->lockClient);
1970
            }
1971
        #endif
1972
            /* AUTH property was not set in connect structure */
1973
0
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
1974
0
        }
1975
1976
0
        XMEMSET((void*)p_auth, 0, sizeof(MqttAuth));
1977
1978
        /* Set the authentication reason */
1979
0
        p_auth->reason_code = MQTT_REASON_CONT_AUTH;
1980
1981
        /* Use the same authentication method property from connect */
1982
0
        prop = MqttProps_Add(&p_auth->props);
1983
0
        if (prop == NULL) {
1984
        #ifdef WOLFMQTT_MULTITHREAD
1985
            if (wm_SemLock(&client->lockClient) == 0) {
1986
                MqttClient_RespList_Remove(client, &mc_connect->pendResp);
1987
                wm_SemUnlock(&client->lockClient);
1988
            }
1989
        #endif
1990
0
            return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MEMORY);
1991
0
        }
1992
0
        prop->type = MQTT_PROP_AUTH_METHOD;
1993
0
        prop->data_str.str = conn_prop->data_str.str;
1994
0
        prop->data_str.len = conn_prop->data_str.len;
1995
1996
        /* Send the AUTH packet */
1997
0
        rc = MqttClient_Auth(client, p_auth);
1998
0
        MqttClient_PropsFree(p_auth->props);
1999
    #ifdef WOLFMQTT_NONBLOCK
2000
        if (rc == MQTT_CODE_CONTINUE)
2001
            return rc;
2002
    #endif
2003
0
        if (rc < 0) {
2004
        #ifdef WOLFMQTT_MULTITHREAD
2005
            if (wm_SemLock(&client->lockClient) == 0) {
2006
                MqttClient_RespList_Remove(client, &mc_connect->pendResp);
2007
                wm_SemUnlock(&client->lockClient);
2008
            }
2009
        #endif
2010
0
            return rc;
2011
0
        }
2012
0
        mc_connect->stat.write = MQTT_MSG_WAIT;
2013
0
    }
2014
1.39k
#endif /* WOLFMQTT_V5 */
2015
2016
    /* Wait for connect ack packet */
2017
1.39k
    rc = MqttClient_WaitType(client, &mc_connect->ack,
2018
1.39k
        MQTT_PACKET_TYPE_CONNECT_ACK, 0, client->cmd_timeout_ms);
2019
#if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD)
2020
    if (rc == MQTT_CODE_CONTINUE)
2021
        return rc;
2022
#endif
2023
2024
#ifdef WOLFMQTT_MULTITHREAD
2025
    if (wm_SemLock(&client->lockClient) == 0) {
2026
        MqttClient_RespList_Remove(client, &mc_connect->pendResp);
2027
        wm_SemUnlock(&client->lockClient);
2028
    }
2029
#endif
2030
2031
1.39k
#ifdef WOLFMQTT_V5
2032
    /* Scrub the decoded v5 CONNACK from rx_buf. Its properties (e.g. the
2033
     * MQTT_PROP_AUTH_DATA SASL server-final blob used by enhanced
2034
     * authentication) decode as pointers into rx_buf and would otherwise
2035
     * linger until the next read overwrites them - for an idle or QoS-0-only
2036
     * client, potentially the process lifetime. MqttClient_WaitType already
2037
     * delivered and freed the property list above, so the bytes are consumed
2038
     * and no live pointer into rx_buf remains. Same hardening as
2039
     * MqttClient_Auth; v3.1.1 CONNACK carries no properties so gate on v5. */
2040
1.39k
    if (mc_connect->protocol_level > MQTT_CONNECT_PROTOCOL_LEVEL_4) {
2041
    #ifdef WOLFMQTT_MULTITHREAD
2042
        /* Hold lockRecv so the scrub cannot race a concurrent rx_buf read. If
2043
         * the lock cannot be taken, still scrub: leaving the AUTH_DATA
2044
         * plaintext behind is worse than an unsynchronized wipe. */
2045
        if (wm_SemLock(&client->lockRecv) == 0) {
2046
            CLIENT_FORCE_ZERO(client->rx_buf, client->rx_buf_len);
2047
            wm_SemUnlock(&client->lockRecv);
2048
        }
2049
        else {
2050
            CLIENT_FORCE_ZERO(client->rx_buf, client->rx_buf_len);
2051
        }
2052
    #else
2053
1.39k
        CLIENT_FORCE_ZERO(client->rx_buf, client->rx_buf_len);
2054
1.39k
    #endif
2055
1.39k
    }
2056
1.39k
#endif
2057
2058
    /* reset state */
2059
1.39k
    mc_connect->stat.write = MQTT_MSG_BEGIN;
2060
2061
    /* CONNACK was received and decoded, but the broker refused the
2062
     * connection. The specific reason is in mc_connect->ack.return_code
2063
     * (MqttConnectAckReturnCodes for v3.1.1, MqttReasonCodes for v5). */
2064
1.39k
    if (rc == MQTT_CODE_SUCCESS &&
2065
59
            mc_connect->ack.return_code != MQTT_CONNECT_ACK_CODE_ACCEPTED) {
2066
19
        rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_CONNECT_REFUSED);
2067
19
    }
2068
2069
1.39k
#ifndef WOLFMQTT_NO_TIME
2070
1.39k
    if (rc == MQTT_CODE_SUCCESS) {
2071
        /* Connection accepted: arm auto keep-alive. A v5 Server Keep Alive
2072
         * (applied while processing CONNACK) takes precedence, including a
2073
         * value of 0 which disables keep-alive per [MQTT-3.1.2.11.2];
2074
         * otherwise use the client-requested value. */
2075
40
        if (!client->keep_alive_from_server) {
2076
40
            client->keep_alive_sec = mc_connect->keep_alive_sec;
2077
40
        }
2078
        /* Baseline the idle timer from the completed handshake so the first
2079
         * ping is scheduled a full interval out, not immediately. */
2080
40
        client->last_tx_time = WOLFMQTT_GET_TIME_S();
2081
40
    }
2082
1.35k
    else {
2083
        /* Connect failed or was refused: leave the scheduler disarmed. */
2084
1.35k
        client->keep_alive_sec = 0;
2085
1.35k
    }
2086
1.39k
    client->keep_alive_from_server = 0;
2087
1.39k
#endif
2088
2089
1.39k
    return rc;
2090
1.39k
}
2091
2092
static int MqttClient_Publish_ReadPayload(MqttClient* client,
2093
    MqttPublish* publish, int timeout_ms)
2094
1.39k
{
2095
1.39k
    int rc = MQTT_CODE_SUCCESS;
2096
1.39k
    byte msg_done;
2097
2098
    /* Handle packet callback and read remaining payload */
2099
3.75k
    do {
2100
        /* Determine if message is done */
2101
3.75k
        msg_done = ((publish->buffer_pos + publish->buffer_len) >=
2102
3.75k
                    publish->total_len) ? 1 : 0;
2103
2104
3.75k
        if (publish->buffer_new) {
2105
            /* Issue callback for new message (first time only) */
2106
1.19k
            if (client->msg_cb) {
2107
                /* if using the temp publish message buffer,
2108
                   then populate message context with client context */
2109
1.19k
                if (publish->ctx == NULL && &client->msg.publish == publish) {
2110
1.19k
                    publish->ctx = client->ctx;
2111
1.19k
                }
2112
1.19k
                rc = client->msg_cb(client, publish, publish->buffer_new,
2113
1.19k
                                    msg_done);
2114
1.19k
                if (rc != MQTT_CODE_SUCCESS) {
2115
0
                    return rc;
2116
1.19k
                };
2117
1.19k
            }
2118
2119
            /* Reset topic name since valid on new message only */
2120
1.19k
            publish->topic_name = NULL;
2121
1.19k
            publish->topic_name_len = 0;
2122
2123
1.19k
            publish->buffer_new = 0;
2124
1.19k
        }
2125
2126
        /* Read payload */
2127
3.75k
        if (!msg_done) {
2128
2.56k
            int msg_len;
2129
2130
            /* add last length to position and reset len */
2131
2.56k
            publish->buffer_pos += publish->buffer_len;
2132
2.56k
            publish->buffer_len = 0;
2133
2134
            /* set state to reading payload */
2135
2.56k
            publish->stat.read = MQTT_MSG_PAYLOAD2;
2136
2137
2.56k
            msg_len = (publish->total_len - publish->buffer_pos);
2138
2.56k
            if (msg_len > client->rx_buf_len) {
2139
1.36k
                msg_len = client->rx_buf_len;
2140
1.36k
            }
2141
2142
            /* make sure there is something to read */
2143
2.56k
            if (msg_len > 0) {
2144
2.56k
                rc = MqttSocket_Read(client, client->rx_buf, msg_len,
2145
2.56k
                        timeout_ms);
2146
2.56k
                if (rc < 0) {
2147
197
                    break;
2148
197
                }
2149
2150
                /* Update message */
2151
2.36k
                publish->buffer = client->rx_buf;
2152
2.36k
                publish->buffer_len = rc;
2153
2.36k
                rc = MQTT_CODE_SUCCESS; /* mark success */
2154
2155
2.36k
                msg_done = ((publish->buffer_pos + publish->buffer_len) >=
2156
2.36k
                    publish->total_len) ? 1 : 0;
2157
2158
                /* Issue callback for additional publish payload */
2159
2.36k
                if (client->msg_cb) {
2160
2.36k
                    rc = client->msg_cb(client, publish, publish->buffer_new,
2161
2.36k
                                        msg_done);
2162
2.36k
                    if (rc != MQTT_CODE_SUCCESS) {
2163
0
                        return rc;
2164
2.36k
                    };
2165
2.36k
                }
2166
2.36k
            }
2167
2.56k
        }
2168
3.75k
    } while (!msg_done);
2169
2170
    /* No message callback registered to deliver this incoming PUBLISH. The
2171
     * payload was drained above to keep the stream in sync, but the application
2172
     * never saw it. Return a distinct error instead of success so the caller is
2173
     * notified and, for QoS 1/2, MqttClient_HandlePacket does not falsely ACK
2174
     * the message as delivered. */
2175
1.39k
    if (rc == MQTT_CODE_SUCCESS && client->msg_cb == NULL) {
2176
0
        rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_CALLBACK);
2177
0
    }
2178
2179
1.39k
    return rc;
2180
1.39k
}
2181
2182
static int MqttClient_Publish_WritePayload(MqttClient *client,
2183
    MqttPublish *publish, MqttPublishCb pubCb)
2184
333
{
2185
333
    int rc = MQTT_CODE_SUCCESS;
2186
2187
333
    if (client == NULL || publish == NULL)
2188
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
2189
2190
333
    if (pubCb) { /* use publish callback to get data */
2191
0
        word32 tmp_len;
2192
2193
0
        do {
2194
            /* use the client->write.len to handle non-blocking re-entry when
2195
             * new publish callback data is needed */
2196
0
            if (client->write.len == 0) {
2197
                /* Use the callback to get payload */
2198
0
                if ((client->write.len = pubCb(publish)) < 0) {
2199
                #ifdef WOLFMQTT_DEBUG_CLIENT
2200
                    PRINTF("Publish callback error %d", client->write.len);
2201
                #endif
2202
0
                    return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_CALLBACK);
2203
0
                }
2204
2205
                /* Record how many valid bytes this callback produced (a short
2206
                 * return marks the last read). Persisting it means a
2207
                 * non-blocking resume does not mistake the in-progress chunk
2208
                 * length for the fill length and drop the tail. */
2209
0
                publish->intBuf_cb_len =
2210
0
                    ((word32)client->write.len < publish->buffer_len) ?
2211
0
                        (word32)client->write.len : publish->buffer_len;
2212
0
            }
2213
2214
0
            tmp_len = publish->intBuf_cb_len;
2215
2216
            /* Send payload */
2217
0
            do {
2218
                /* Recompute the bytes remaining each pass so the final partial
2219
                 * chunk copies only valid data and never reads past the end of
2220
                 * the caller's payload buffer. */
2221
0
                client->write.len = (int)(tmp_len - publish->intBuf_pos);
2222
0
                if (client->write.len > client->tx_buf_len) {
2223
0
                    client->write.len = client->tx_buf_len;
2224
0
                }
2225
0
                publish->intBuf_len = client->write.len;
2226
0
                XMEMCPY(client->tx_buf, &publish->buffer[publish->intBuf_pos],
2227
0
                    client->write.len);
2228
2229
0
                rc = MqttPacket_Write(client, client->tx_buf,
2230
0
                        client->write.len);
2231
0
                if (rc < 0) {
2232
0
                    return rc;
2233
0
                }
2234
2235
0
                publish->intBuf_pos += publish->intBuf_len;
2236
0
                publish->intBuf_len = 0;
2237
2238
0
            } while (publish->intBuf_pos < tmp_len);
2239
2240
0
            publish->buffer_pos += publish->intBuf_pos;
2241
0
            publish->intBuf_pos = 0;
2242
0
            client->write.len = 0; /* reset current write len */
2243
2244
0
        } while (publish->buffer_pos < publish->total_len);
2245
0
    }
2246
333
    else if (publish->buffer_pos < publish->total_len) {
2247
206
        if (publish->buffer_pos > 0) {
2248
199
            client->write.len = (publish->total_len - publish->buffer_pos);
2249
199
            if (client->write.len > client->tx_buf_len) {
2250
112
                client->write.len = client->tx_buf_len;
2251
112
            }
2252
2253
199
            XMEMCPY(client->tx_buf, &publish->buffer[publish->buffer_pos],
2254
199
                client->write.len);
2255
2256
199
        #ifndef WOLFMQTT_NONBLOCK
2257
199
            publish->intBuf_pos += client->write.len;
2258
199
        #endif
2259
199
        }
2260
2261
        /* Send packet and payload */
2262
    #ifdef WOLFMQTT_NONBLOCK
2263
            rc = MqttPacket_Write(client, client->tx_buf, client->write.len);
2264
            if (rc < 0) {
2265
                return rc;
2266
            }
2267
2268
            /* ONLY if send was successful, update buffer position.
2269
             * Otherwise, MqttPacket_Write() will resume where it left off. */
2270
            publish->buffer_pos += client->write.len;
2271
2272
            /* Check if we are done sending publish message */
2273
            if (publish->buffer_pos < publish->buffer_len) {
2274
            #ifdef WOLFMQTT_DEBUG_CLIENT
2275
                PRINTF("Publish Write: not done (%d remain)",
2276
                    publish->buffer_len - publish->buffer_pos);
2277
            #endif
2278
                return MQTT_CODE_PUB_CONTINUE;
2279
            }
2280
        #ifdef WOLFMQTT_DEBUG_CLIENT
2281
            else {
2282
                PRINTF("Publish Write: done");
2283
            }
2284
        #endif
2285
    #else
2286
574
        do {
2287
574
            rc = MqttPacket_Write(client, client->tx_buf, client->write.len);
2288
574
            if (rc < 0) {
2289
146
                return rc;
2290
146
            }
2291
2292
428
            publish->intBuf_pos += publish->intBuf_len;
2293
428
            publish->intBuf_len = 0;
2294
2295
            /* Check if we are done sending publish message */
2296
428
            if (publish->intBuf_pos >= publish->buffer_len) {
2297
60
                rc = MQTT_CODE_SUCCESS;
2298
60
                break;
2299
60
            }
2300
2301
            /* Build packet payload to send */
2302
368
            client->write.len = (publish->buffer_len - publish->intBuf_pos);
2303
368
            if (client->write.len > client->tx_buf_len) {
2304
303
                client->write.len = client->tx_buf_len;
2305
303
            }
2306
368
            publish->intBuf_len = client->write.len;
2307
368
            XMEMCPY(client->tx_buf, &publish->buffer[publish->intBuf_pos],
2308
368
                client->write.len);
2309
368
        } while (publish->intBuf_pos < publish->buffer_len);
2310
60
    #endif
2311
2312
60
        if (rc >= 0) {
2313
            /* If transferring more chunks */
2314
60
            publish->buffer_pos += publish->intBuf_pos;
2315
60
            if (publish->buffer_pos < publish->total_len) {
2316
            #ifdef WOLFMQTT_DEBUG_CLIENT
2317
                PRINTF("Publish Write: chunk (%d remain)",
2318
                    publish->total_len - publish->buffer_pos);
2319
            #endif
2320
2321
                /* Build next payload to send */
2322
0
                client->write.len = (publish->total_len - publish->buffer_pos);
2323
0
                if (client->write.len > client->tx_buf_len) {
2324
0
                    client->write.len = client->tx_buf_len;
2325
0
                }
2326
0
                rc = MQTT_CODE_PUB_CONTINUE;
2327
0
            }
2328
        #ifdef WOLFMQTT_DEBUG_CLIENT
2329
            else {
2330
                PRINTF("Publish Write: chunked done");
2331
            }
2332
        #endif
2333
60
        }
2334
60
    }
2335
187
    return rc;
2336
333
}
2337
2338
static int MqttPublishMsg(MqttClient *client, MqttPublish *publish,
2339
                          MqttPublishCb pubCb, int writeOnly)
2340
997
{
2341
997
    int rc = MQTT_CODE_SUCCESS;
2342
997
    MqttPacketType resp_type;
2343
2344
    /* Validate required arguments */
2345
997
    if (client == NULL || publish == NULL) {
2346
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
2347
0
    }
2348
2349
997
#ifdef WOLFMQTT_V5
2350
    /* Use specified protocol version if set */
2351
997
    publish->protocol_level = client->protocol_level;
2352
2353
    /* Validate publish request against server properties */
2354
997
    if ((publish->qos > client->max_qos) ||
2355
997
        ((publish->retain != 0) && (client->retain_avail == 0)))
2356
0
    {
2357
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_SERVER_PROP);
2358
0
    }
2359
997
#endif
2360
2361
997
    switch (publish->stat.write)
2362
997
    {
2363
997
        case MQTT_MSG_BEGIN:
2364
997
        {
2365
            /* Flag write active / lock mutex */
2366
997
            if ((rc = MqttWriteStart(client, &publish->stat)) != 0) {
2367
0
                return rc;
2368
0
            }
2369
2370
            /* Encode the publish packet */
2371
997
            rc = MqttEncode_Publish(client->tx_buf, client->tx_buf_len,
2372
997
                    publish, pubCb ? 1 : 0);
2373
        #ifdef WOLFMQTT_DEBUG_CLIENT
2374
            PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d,"
2375
                    " QoS %d",
2376
                rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_PUBLISH),
2377
                MQTT_PACKET_TYPE_PUBLISH, publish->packet_id,
2378
                publish->qos);
2379
        #endif
2380
997
            if (rc <= 0) {
2381
339
                MqttWriteStop(client, &publish->stat);
2382
339
                return rc;
2383
339
            }
2384
658
            client->write.len = rc;
2385
2386
        #ifdef WOLFMQTT_MULTITHREAD
2387
            if (publish->qos > MQTT_QOS_0) {
2388
                resp_type = (publish->qos == MQTT_QOS_1) ?
2389
                        MQTT_PACKET_TYPE_PUBLISH_ACK :
2390
                        MQTT_PACKET_TYPE_PUBLISH_COMP;
2391
2392
                rc = wm_SemLock(&client->lockClient);
2393
                if (rc == 0) {
2394
                    /* inform other threads of expected response */
2395
                    rc = MqttClient_RespList_Add(client, resp_type,
2396
                        publish->packet_id, &publish->pendResp, &publish->resp);
2397
                    wm_SemUnlock(&client->lockClient);
2398
                }
2399
                if (rc != 0) {
2400
                    MqttWriteStop(client, &publish->stat);
2401
                    return rc; /* Error locking client */
2402
                }
2403
            }
2404
        #endif
2405
2406
658
            publish->stat.write = MQTT_MSG_HEADER;
2407
658
        }
2408
658
        FALL_THROUGH;
2409
2410
658
        case MQTT_MSG_HEADER:
2411
658
        {
2412
658
            int xfer = client->write.len;
2413
2414
            /* Send publish packet */
2415
658
            rc = MqttPacket_Write(client, client->tx_buf, xfer);
2416
        #ifdef WOLFMQTT_NONBLOCK
2417
            if (rc == MQTT_CODE_CONTINUE
2418
            #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK
2419
                && client->write.total > 0
2420
            #endif
2421
            ) {
2422
                /* keep send locked and return early */
2423
                return rc;
2424
            }
2425
        #endif
2426
658
            client->write.len = 0; /* reset len, so publish chunk resets */
2427
2428
            /* if failure or no data was written yet */
2429
658
            if (rc != xfer) {
2430
325
                MqttWriteStop(client, &publish->stat);
2431
325
                MqttClient_CancelMessage(client, (MqttObject*)publish);
2432
325
                return rc;
2433
325
            }
2434
2435
            /* advance state */
2436
333
            publish->stat.write = MQTT_MSG_PAYLOAD;
2437
333
        }
2438
333
        FALL_THROUGH;
2439
2440
333
        case MQTT_MSG_PAYLOAD:
2441
333
        {
2442
333
            rc = MqttClient_Publish_WritePayload(client, publish, pubCb);
2443
        #ifdef WOLFMQTT_NONBLOCK
2444
            if (rc == MQTT_CODE_CONTINUE || rc == MQTT_CODE_PUB_CONTINUE)
2445
                return rc;
2446
        #endif
2447
333
            MqttWriteStop(client, &publish->stat);
2448
333
            if (rc < 0) {
2449
146
                MqttClient_CancelMessage(client, (MqttObject*)publish);
2450
146
                break;
2451
146
            }
2452
2453
            /* if not expecting a reply then we are done */
2454
187
            if (publish->qos == MQTT_QOS_0) {
2455
70
                break;
2456
70
            }
2457
117
            publish->stat.write = MQTT_MSG_WAIT;
2458
117
        }
2459
117
        FALL_THROUGH;
2460
2461
117
        case MQTT_MSG_WAIT:
2462
117
        {
2463
            /* Handle QoS */
2464
117
            if (publish->qos > MQTT_QOS_0) {
2465
                /* Determine packet type to wait for */
2466
117
                resp_type = (publish->qos == MQTT_QOS_1) ?
2467
91
                    MQTT_PACKET_TYPE_PUBLISH_ACK :
2468
117
                    MQTT_PACKET_TYPE_PUBLISH_COMP;
2469
2470
            #ifdef WOLFMQTT_MULTITHREAD
2471
                if (writeOnly) {
2472
                    /* another thread will handle response */
2473
                    /* check if response already received from other thread */
2474
                    rc = MqttClient_CheckPendResp(client, resp_type,
2475
                        publish->packet_id);
2476
                #ifndef WOLFMQTT_NONBLOCK
2477
                    if (rc == MQTT_CODE_CONTINUE) {
2478
                        /* mark success, let other thread handle response */
2479
                        rc = MQTT_CODE_SUCCESS;
2480
                    }
2481
                #endif
2482
                }
2483
                else
2484
            #endif
2485
117
                {
2486
117
                    (void)writeOnly; /* not used */
2487
2488
                    /* Wait for publish response packet */
2489
117
                    rc = MqttClient_WaitType(client, &publish->resp, resp_type,
2490
117
                        publish->packet_id, client->cmd_timeout_ms);
2491
2492
117
                #ifdef WOLFMQTT_V5
2493
                    /* A v5 broker can acknowledge a QoS>0 PUBLISH at the
2494
                     * protocol layer yet still reject the message via a
2495
                     * PUBACK/PUBCOMP reason code >= 0x80 (e.g. not authorized,
2496
                     * quota exceeded, topic name invalid, payload format
2497
                     * invalid). Surface that as an error so the caller does not
2498
                     * treat a rejected message as delivered. Mirrors the
2499
                     * CONNECT/SUBSCRIBE/UNSUBSCRIBE rejection handling. The
2500
                     * protocol_level guard avoids misreading a stale byte for
2501
                     * v3.1.1 ACKs, which carry no reason code (same guard the
2502
                     * PUBREC check in MqttClient_HandlePacket uses). */
2503
117
                    if (rc == MQTT_CODE_SUCCESS &&
2504
34
                        client->protocol_level >=
2505
34
                            MQTT_CONNECT_PROTOCOL_LEVEL_5 &&
2506
0
                        (publish->resp.reason_code & 0x80)) {
2507
0
                        rc = MQTT_TRACE_ERROR(
2508
0
                            MQTT_CODE_ERROR_PUBLISH_REJECTED);
2509
0
                    }
2510
117
                #endif
2511
117
                }
2512
2513
            #if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD)
2514
                if (rc == MQTT_CODE_CONTINUE)
2515
                    break;
2516
            #endif
2517
            #ifdef WOLFMQTT_MULTITHREAD
2518
                if (wm_SemLock(&client->lockClient) == 0) {
2519
                    MqttClient_RespList_Remove(client, &publish->pendResp);
2520
                    wm_SemUnlock(&client->lockClient);
2521
                }
2522
            #endif
2523
117
            }
2524
117
            break;
2525
117
        }
2526
2527
0
        case MQTT_MSG_ACK:
2528
0
        case MQTT_MSG_AUTH:
2529
0
        case MQTT_MSG_PAYLOAD2:
2530
        #ifdef WOLFMQTT_DEBUG_CLIENT
2531
            PRINTF("MqttClient_Publish: Invalid state %d!",
2532
                publish->stat.write);
2533
        #endif
2534
0
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT);
2535
0
            break;
2536
997
    } /* switch (publish->stat) */
2537
2538
    /* reset state */
2539
333
    if ((rc != MQTT_CODE_PUB_CONTINUE)
2540
#ifdef WOLFMQTT_NONBLOCK
2541
         && (rc != MQTT_CODE_CONTINUE)
2542
#endif
2543
333
        )
2544
333
    {
2545
333
        publish->stat.write = MQTT_MSG_BEGIN;
2546
333
    }
2547
333
    if (rc > 0) {
2548
0
        rc = MQTT_CODE_SUCCESS;
2549
0
    }
2550
2551
333
    return rc;
2552
997
}
2553
2554
int MqttClient_Publish(MqttClient *client, MqttPublish *publish)
2555
997
{
2556
997
    return MqttPublishMsg(client, publish, NULL, 0);
2557
997
}
2558
2559
int MqttClient_Publish_ex(MqttClient *client, MqttPublish *publish,
2560
    MqttPublishCb pubCb)
2561
0
{
2562
0
    return MqttPublishMsg(client, publish, pubCb, 0);
2563
0
}
2564
2565
#ifdef WOLFMQTT_MULTITHREAD
2566
int MqttClient_Publish_WriteOnly(MqttClient *client, MqttPublish *publish,
2567
    MqttPublishCb pubCb)
2568
{
2569
    return MqttPublishMsg(client, publish, pubCb, 1);
2570
}
2571
#endif
2572
2573
2574
int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe)
2575
1.53k
{
2576
1.53k
    int rc, i;
2577
1.53k
    MqttTopic* topic;
2578
2579
    /* Validate required arguments */
2580
1.53k
    if (client == NULL || subscribe == NULL) {
2581
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
2582
0
    }
2583
2584
1.53k
#ifdef WOLFMQTT_V5
2585
    /* Use specified protocol version if set */
2586
1.53k
    subscribe->protocol_level = client->protocol_level;
2587
1.53k
#endif
2588
2589
1.53k
    if (subscribe->stat.write == MQTT_MSG_BEGIN) {
2590
        /* Flag write active / lock mutex */
2591
1.53k
        if ((rc = MqttWriteStart(client, &subscribe->stat)) != 0) {
2592
0
            return rc;
2593
0
        }
2594
2595
        /* Encode the subscribe packet */
2596
1.53k
        rc = MqttEncode_Subscribe(client->tx_buf, client->tx_buf_len,
2597
1.53k
                subscribe);
2598
    #ifdef WOLFMQTT_DEBUG_CLIENT
2599
        PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d",
2600
            rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_SUBSCRIBE),
2601
            MQTT_PACKET_TYPE_SUBSCRIBE, subscribe->packet_id);
2602
    #endif
2603
1.53k
        if (rc <= 0) {
2604
1.11k
            MqttWriteStop(client, &subscribe->stat);
2605
1.11k
            return rc;
2606
1.11k
        }
2607
425
        client->write.len = rc;
2608
2609
    #ifdef WOLFMQTT_MULTITHREAD
2610
        rc = wm_SemLock(&client->lockClient);
2611
        if (rc == 0) {
2612
            /* inform other threads of expected response */
2613
            rc = MqttClient_RespList_Add(client, MQTT_PACKET_TYPE_SUBSCRIBE_ACK,
2614
                subscribe->packet_id, &subscribe->pendResp, &subscribe->ack);
2615
            wm_SemUnlock(&client->lockClient);
2616
        }
2617
        if (rc != 0) {
2618
            MqttWriteStop(client, &subscribe->stat);
2619
            return rc; /* Error locking client */
2620
        }
2621
    #endif
2622
2623
425
        subscribe->stat.write = MQTT_MSG_HEADER;
2624
425
    }
2625
425
    if (subscribe->stat.write == MQTT_MSG_HEADER) {
2626
425
        int xfer = client->write.len;
2627
2628
        /* Send subscribe packet */
2629
425
        rc = MqttPacket_Write(client, client->tx_buf, xfer);
2630
    #ifdef WOLFMQTT_NONBLOCK
2631
        if (rc == MQTT_CODE_CONTINUE
2632
        #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK
2633
            && client->write.total > 0
2634
        #endif
2635
        ) {
2636
            /* keep send locked and return early */
2637
            return rc;
2638
        }
2639
    #endif
2640
425
        MqttWriteStop(client, &subscribe->stat);
2641
425
        if (rc != xfer) {
2642
186
            MqttClient_CancelMessage(client, (MqttObject*)subscribe);
2643
186
            return rc;
2644
186
        }
2645
2646
239
        subscribe->stat.write = MQTT_MSG_WAIT;
2647
239
    }
2648
2649
    /* Wait for subscribe ack packet */
2650
239
    rc = MqttClient_WaitType(client, &subscribe->ack,
2651
239
        MQTT_PACKET_TYPE_SUBSCRIBE_ACK, subscribe->packet_id,
2652
239
        client->cmd_timeout_ms);
2653
#if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD)
2654
    if (rc == MQTT_CODE_CONTINUE)
2655
        return rc;
2656
#endif
2657
2658
#ifdef WOLFMQTT_MULTITHREAD
2659
    if (wm_SemLock(&client->lockClient) == 0) {
2660
        MqttClient_RespList_Remove(client, &subscribe->pendResp);
2661
        wm_SemUnlock(&client->lockClient);
2662
    }
2663
#endif
2664
2665
    /* Populate return codes and detect broker rejection. A v3.1.1 SUBACK
2666
     * uses MQTT_SUBSCRIBE_ACK_CODE_FAILURE (0x80) to indicate failure;
2667
     * a v5 SUBACK uses any reason code >= 0x80. In either case, any
2668
     * per-topic code with the high bit set means the broker rejected
2669
     * that filter. */
2670
239
    if (rc == MQTT_CODE_SUCCESS) {
2671
180
        byte any_rejected = 0;
2672
        /* [MQTT-3.9.3-1] a SUBACK carries exactly one reason code per
2673
         * requested topic; too few would be read as granted QoS 0 (fail-open). */
2674
180
        if (subscribe->ack.return_code_count != subscribe->topic_count) {
2675
37
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA);
2676
37
        }
2677
143
        else {
2678
360
            for (i = 0; i < subscribe->topic_count && i < MAX_MQTT_TOPICS; i++) {
2679
217
                topic = &subscribe->topics[i];
2680
217
                topic->return_code = subscribe->ack.return_codes[i];
2681
217
                if (topic->return_code & MQTT_SUBSCRIBE_ACK_CODE_FAILURE) {
2682
108
                    any_rejected = 1;
2683
108
                }
2684
217
            }
2685
143
            if (any_rejected) {
2686
76
                rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_SUBSCRIBE_REJECTED);
2687
76
            }
2688
143
        }
2689
180
    }
2690
2691
    /* reset state */
2692
239
    subscribe->stat.write = MQTT_MSG_BEGIN;
2693
2694
239
    return rc;
2695
425
}
2696
2697
int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe)
2698
929
{
2699
929
    int rc;
2700
929
#ifdef WOLFMQTT_V5
2701
929
    int i;
2702
929
    word16 reason_count;
2703
929
#endif
2704
2705
    /* Validate required arguments */
2706
929
    if (client == NULL || unsubscribe == NULL) {
2707
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
2708
0
    }
2709
2710
929
#ifdef WOLFMQTT_V5
2711
    /* Use specified protocol version if set */
2712
929
    unsubscribe->protocol_level = client->protocol_level;
2713
929
#endif
2714
2715
929
    if (unsubscribe->stat.write == MQTT_MSG_BEGIN) {
2716
        /* Flag write active / lock mutex */
2717
929
        if ((rc = MqttWriteStart(client, &unsubscribe->stat)) != 0) {
2718
0
            return rc;
2719
0
        }
2720
2721
        /* Encode the subscribe packet */
2722
929
        rc = MqttEncode_Unsubscribe(client->tx_buf, client->tx_buf_len,
2723
929
            unsubscribe);
2724
    #ifdef WOLFMQTT_DEBUG_CLIENT
2725
        PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d",
2726
            rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_UNSUBSCRIBE),
2727
            MQTT_PACKET_TYPE_UNSUBSCRIBE, unsubscribe->packet_id, 0);
2728
    #endif
2729
929
        if (rc <= 0) {
2730
651
            MqttWriteStop(client, &unsubscribe->stat);
2731
651
            return rc;
2732
651
        }
2733
278
        client->write.len = rc;
2734
2735
    #ifdef WOLFMQTT_MULTITHREAD
2736
        rc = wm_SemLock(&client->lockClient);
2737
        if (rc == 0) {
2738
            /* inform other threads of expected response */
2739
            rc = MqttClient_RespList_Add(client,
2740
                MQTT_PACKET_TYPE_UNSUBSCRIBE_ACK, unsubscribe->packet_id,
2741
                &unsubscribe->pendResp, &unsubscribe->ack);
2742
            wm_SemUnlock(&client->lockClient);
2743
        }
2744
        if (rc != 0) {
2745
            MqttWriteStop(client, &unsubscribe->stat);
2746
            return rc;
2747
        }
2748
    #endif
2749
2750
278
        unsubscribe->stat.write = MQTT_MSG_HEADER;
2751
278
    }
2752
278
    if (unsubscribe->stat.write == MQTT_MSG_HEADER) {
2753
278
        int xfer = client->write.len;
2754
2755
        /* Send unsubscribe packet */
2756
278
        rc = MqttPacket_Write(client, client->tx_buf, xfer);
2757
    #ifdef WOLFMQTT_NONBLOCK
2758
        if (rc == MQTT_CODE_CONTINUE
2759
        #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK
2760
            && client->write.total > 0
2761
        #endif
2762
        ) {
2763
            /* keep send locked and return early */
2764
            return rc;
2765
        }
2766
    #endif
2767
278
        MqttWriteStop(client, &unsubscribe->stat);
2768
278
        if (rc != xfer) {
2769
184
            MqttClient_CancelMessage(client, (MqttObject*)unsubscribe);
2770
184
            return rc;
2771
184
        }
2772
2773
94
        unsubscribe->stat.write = MQTT_MSG_WAIT;
2774
94
    }
2775
2776
    /* Wait for unsubscribe ack packet */
2777
94
    rc = MqttClient_WaitType(client, &unsubscribe->ack,
2778
94
        MQTT_PACKET_TYPE_UNSUBSCRIBE_ACK, unsubscribe->packet_id,
2779
94
        client->cmd_timeout_ms);
2780
#if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD)
2781
    if (rc == MQTT_CODE_CONTINUE)
2782
        return rc;
2783
#endif
2784
2785
#ifdef WOLFMQTT_MULTITHREAD
2786
    if (wm_SemLock(&client->lockClient) == 0) {
2787
        MqttClient_RespList_Remove(client, &unsubscribe->pendResp);
2788
        wm_SemUnlock(&client->lockClient);
2789
    }
2790
#endif
2791
2792
94
#ifdef WOLFMQTT_V5
2793
    /* Detect broker rejection. A v5 UNSUBACK carries one reason code per
2794
     * topic filter; any code with the high bit set (>= 0x80) means the
2795
     * broker refused to remove that subscription, so the caller must not
2796
     * assume the filter is gone. Mirrors the SUBSCRIBE rejection path. */
2797
94
    if (rc == MQTT_CODE_SUCCESS &&
2798
34
        unsubscribe->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5 &&
2799
0
        unsubscribe->ack.reason_codes != NULL) {
2800
0
        reason_count = unsubscribe->ack.reason_code_count;
2801
0
        if (reason_count > (word16)unsubscribe->topic_count) {
2802
0
            reason_count = (word16)unsubscribe->topic_count;
2803
0
        }
2804
0
        for (i = 0; i < (int)reason_count; i++) {
2805
0
            if (unsubscribe->ack.reason_codes[i] & 0x80) {
2806
0
                rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_UNSUBSCRIBE_REJECTED);
2807
0
                break;
2808
0
            }
2809
0
        }
2810
0
    }
2811
2812
94
    if (unsubscribe->ack.props != NULL) {
2813
        /* Release the allocated properties and clear the caller-visible
2814
         * pointer so a stale reference cannot be observed, reused, or freed
2815
         * again after this API returns. */
2816
0
        MqttClient_PropsFree(unsubscribe->ack.props);
2817
0
        unsubscribe->ack.props = NULL;
2818
0
    }
2819
94
#endif
2820
2821
    /* reset state */
2822
94
    unsubscribe->stat.write = MQTT_MSG_BEGIN;
2823
2824
94
    return rc;
2825
278
}
2826
2827
int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping)
2828
1.51k
{
2829
1.51k
    int rc;
2830
2831
    /* Validate required arguments */
2832
1.51k
    if (client == NULL || ping == NULL) {
2833
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
2834
0
    }
2835
2836
1.51k
    if (ping->stat.write == MQTT_MSG_BEGIN) {
2837
        /* Flag write active / lock mutex */
2838
1.51k
        if ((rc = MqttWriteStart(client, &ping->stat)) != 0) {
2839
0
            return rc;
2840
0
        }
2841
2842
        /* Encode the subscribe packet */
2843
1.51k
        rc = MqttEncode_Ping(client->tx_buf, client->tx_buf_len, ping);
2844
    #ifdef WOLFMQTT_DEBUG_CLIENT
2845
        PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d",
2846
            rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_PING_REQ),
2847
            MQTT_PACKET_TYPE_PING_REQ, 0, 0);
2848
    #endif
2849
1.51k
        if (rc <= 0) {
2850
0
            MqttWriteStop(client, &ping->stat);
2851
0
            return rc;
2852
0
        }
2853
1.51k
        client->write.len = rc;
2854
2855
    #ifdef WOLFMQTT_MULTITHREAD
2856
        rc = wm_SemLock(&client->lockClient);
2857
        if (rc == 0) {
2858
            /* inform other threads of expected response */
2859
            rc = MqttClient_RespList_Add(client, MQTT_PACKET_TYPE_PING_RESP, 0,
2860
                &ping->pendResp, ping);
2861
            wm_SemUnlock(&client->lockClient);
2862
        }
2863
        if (rc != 0) {
2864
            MqttWriteStop(client, &ping->stat);
2865
            return rc; /* Error locking client */
2866
        }
2867
    #endif
2868
2869
1.51k
        ping->stat.write = MQTT_MSG_HEADER;
2870
1.51k
    }
2871
1.51k
    if (ping->stat.write == MQTT_MSG_HEADER) {
2872
1.51k
        int xfer = client->write.len;
2873
2874
        /* Send ping req packet */
2875
1.51k
        rc = MqttPacket_Write(client, client->tx_buf, xfer);
2876
    #ifdef WOLFMQTT_NONBLOCK
2877
        if (rc == MQTT_CODE_CONTINUE
2878
        #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK
2879
            && client->write.total > 0
2880
        #endif
2881
        ) {
2882
            /* keep send locked and return early */
2883
            return rc;
2884
        }
2885
    #endif
2886
1.51k
        MqttWriteStop(client, &ping->stat);
2887
1.51k
        if (rc != xfer) {
2888
189
            MqttClient_CancelMessage(client, (MqttObject*)ping);
2889
189
            return rc;
2890
189
        }
2891
2892
1.32k
        ping->stat.write = MQTT_MSG_WAIT;
2893
1.32k
    }
2894
2895
    /* Wait for ping resp packet */
2896
1.32k
    rc = MqttClient_WaitType(client, ping, MQTT_PACKET_TYPE_PING_RESP, 0,
2897
1.32k
        client->cmd_timeout_ms);
2898
#if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD)
2899
    if (rc == MQTT_CODE_CONTINUE)
2900
        return rc;
2901
#endif
2902
2903
#ifdef WOLFMQTT_MULTITHREAD
2904
    if (wm_SemLock(&client->lockClient) == 0) {
2905
        MqttClient_RespList_Remove(client, &ping->pendResp);
2906
        wm_SemUnlock(&client->lockClient);
2907
    }
2908
#endif
2909
2910
    /* reset state */
2911
1.32k
    ping->stat.write = MQTT_MSG_BEGIN;
2912
2913
1.32k
    return rc;
2914
1.51k
}
2915
2916
int MqttClient_Ping(MqttClient *client)
2917
0
{
2918
0
    if (client == NULL) {
2919
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
2920
0
    }
2921
0
    return MqttClient_Ping_ex(client, &client->msg.ping);
2922
0
}
2923
2924
int MqttClient_Disconnect(MqttClient *client)
2925
0
{
2926
0
    return MqttClient_Disconnect_ex(client, NULL);
2927
0
}
2928
2929
int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *p_disconnect)
2930
0
{
2931
0
    int rc, xfer;
2932
0
    MqttDisconnect *disconnect = p_disconnect, lcl_disconnect;
2933
2934
    /* Validate required arguments */
2935
0
    if (client == NULL) {
2936
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
2937
0
    }
2938
0
    if (disconnect == NULL) {
2939
0
        disconnect = &lcl_disconnect;
2940
0
        XMEMSET(disconnect, 0, sizeof(*disconnect));
2941
0
    }
2942
2943
0
    if (disconnect->stat.write == MQTT_MSG_BEGIN) {
2944
0
    #ifndef WOLFMQTT_NO_TIME
2945
        /* Stop auto keep-alive for a client being torn down, and fully cancel
2946
         * any ping left mid-exchange so its held locks and pending response are
2947
         * released before the disconnect write starts. A bare stat reset would
2948
         * strand client->write.isActive and livelock MqttClient_Disconnect.
2949
         * Propagate a cancel failure - only reachable if wm_SemLock itself
2950
         * errors (e.g. on ThreadX), since it otherwise blocks until acquired -
2951
         * rather than starting the write with locks the abandoned ping may
2952
         * still hold. */
2953
0
        client->keep_alive_sec = 0;
2954
0
        rc = MqttClient_CancelMessage(client,
2955
0
            (MqttObject*)&client->keep_alive_ping);
2956
0
        if (rc != MQTT_CODE_SUCCESS) {
2957
0
            return rc;
2958
0
        }
2959
0
    #endif
2960
0
    #ifdef WOLFMQTT_V5
2961
        /* Use specified protocol version if set */
2962
0
        disconnect->protocol_level = client->protocol_level;
2963
0
    #endif
2964
2965
        /* Flag write active / lock mutex */
2966
0
        if ((rc = MqttWriteStart(client, &disconnect->stat)) != 0) {
2967
0
            return rc;
2968
0
        }
2969
2970
        /* Encode the disconnect packet */
2971
0
        rc = MqttEncode_Disconnect(client->tx_buf, client->tx_buf_len,
2972
0
            disconnect);
2973
    #ifdef WOLFMQTT_DEBUG_CLIENT
2974
        PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d",
2975
            rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_DISCONNECT),
2976
            MQTT_PACKET_TYPE_DISCONNECT, 0, 0);
2977
    #endif
2978
0
        if (rc <= 0) {
2979
            /* Encode failed: tx_buf may hold partial v5 DISCONNECT property
2980
             * data. Zero the full buffer before MqttWriteStop releases
2981
             * lockSend so no other thread can see residual data. */
2982
0
            CLIENT_FORCE_ZERO(client->tx_buf, client->tx_buf_len);
2983
0
            MqttWriteStop(client, &disconnect->stat);
2984
0
            return rc;
2985
0
        }
2986
0
        client->write.len = rc;
2987
2988
0
        disconnect->stat.write = MQTT_MSG_HEADER;
2989
0
    }
2990
2991
    /* Send disconnect packet */
2992
0
    xfer = client->write.len;
2993
0
    rc = MqttPacket_Write(client, client->tx_buf, xfer);
2994
#ifdef WOLFMQTT_NONBLOCK
2995
    /* if disconnect context avail allow partial write in non-blocking mode */
2996
    if (p_disconnect != NULL && rc == MQTT_CODE_CONTINUE
2997
    #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK
2998
        && client->write.total > 0
2999
    #endif
3000
    ) {
3001
        /* keep send locked and return early (tx_buf still holds property
3002
         * data until the write completes) */
3003
        return rc;
3004
    }
3005
#endif
3006
    /* Clear tx_buf to remove any v5 DISCONNECT property data BEFORE
3007
     * MqttWriteStop releases lockSend, so another thread cannot race in and
3008
     * populate tx_buf before it is scrubbed. */
3009
0
    CLIENT_FORCE_ZERO(client->tx_buf, xfer);
3010
0
    MqttWriteStop(client, &disconnect->stat);
3011
0
    if (rc == xfer) {
3012
0
        rc = MQTT_CODE_SUCCESS;
3013
0
    }
3014
3015
#if defined(WOLFMQTT_DISCONNECT_CB) && defined(WOLFMQTT_USE_CB_ON_DISCONNECT)
3016
    /* Trigger disconnect callback - for intentional disconnect
3017
     * This callback may occur on a network failure during an intentional
3018
     * disconnect if the transport/socket is not setup yet. */
3019
    if (client->disconnect_cb
3020
    #ifdef WOLFMQTT_NONBLOCK
3021
        && rc != MQTT_CODE_CONTINUE
3022
    #endif
3023
        ) {
3024
        client->disconnect_cb(client, rc, client->disconnect_ctx);
3025
    }
3026
#endif
3027
3028
    /* No response for MQTT disconnect packet */
3029
3030
    /* reset state */
3031
0
    disconnect->stat.write = MQTT_MSG_BEGIN;
3032
3033
0
    return rc;
3034
0
}
3035
3036
#ifdef WOLFMQTT_V5
3037
int MqttClient_Auth(MqttClient *client, MqttAuth* auth)
3038
0
{
3039
0
    int rc;
3040
3041
    /* Validate required arguments */
3042
0
    if (client == NULL || auth == NULL) {
3043
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
3044
0
    }
3045
3046
0
    if (auth->stat.write == MQTT_MSG_BEGIN) {
3047
        /* Flag write active / lock mutex */
3048
0
        if ((rc = MqttWriteStart(client, &auth->stat)) != 0) {
3049
0
            return rc;
3050
0
        }
3051
3052
        /* Encode the authentication packet */
3053
0
        rc = MqttEncode_Auth(client->tx_buf, client->tx_buf_len, auth);
3054
    #ifdef WOLFMQTT_DEBUG_CLIENT
3055
        PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d",
3056
            rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_AUTH),
3057
            MQTT_PACKET_TYPE_AUTH, 0, 0);
3058
    #endif
3059
0
        if (rc <= 0) {
3060
            /* Encode failed: tx_buf may hold partial SASL auth data.
3061
             * Zero the full buffer before MqttWriteStop releases lockSend
3062
             * so no other thread can see residual data. */
3063
0
            CLIENT_FORCE_ZERO(client->tx_buf, client->tx_buf_len);
3064
0
            MqttWriteStop(client, &auth->stat);
3065
0
            return rc;
3066
0
        }
3067
0
        client->write.len = rc;
3068
3069
    #ifdef WOLFMQTT_MULTITHREAD
3070
        rc = wm_SemLock(&client->lockClient);
3071
        if (rc == 0) {
3072
            /* inform other threads of expected response */
3073
            rc = MqttClient_RespList_Add(client, MQTT_PACKET_TYPE_AUTH, 0,
3074
                &auth->pendResp, auth);
3075
            wm_SemUnlock(&client->lockClient);
3076
        }
3077
        if (rc != 0) {
3078
            /* Save write.len before MqttWriteStop zeroes client->write */
3079
            int xfer = client->write.len;
3080
            /* Clear tx_buf to remove SASL auth data BEFORE MqttWriteStop
3081
             * releases lockSend, to prevent a racing thread from
3082
             * repopulating tx_buf before it is scrubbed. */
3083
            CLIENT_FORCE_ZERO(client->tx_buf, xfer);
3084
            MqttWriteStop(client, &auth->stat);
3085
            return rc; /* Error locking client */
3086
        }
3087
    #endif
3088
3089
0
        auth->stat.write = MQTT_MSG_HEADER;
3090
0
    }
3091
0
    if (auth->stat.write == MQTT_MSG_HEADER) {
3092
0
        int xfer = client->write.len;
3093
3094
        /* Send authentication packet */
3095
0
        rc = MqttPacket_Write(client, client->tx_buf, xfer);
3096
    #ifdef WOLFMQTT_NONBLOCK
3097
        if (rc == MQTT_CODE_CONTINUE
3098
        #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK
3099
            && client->write.total > 0
3100
        #endif
3101
        ) {
3102
            /* keep send locked and return early */
3103
            return rc;
3104
        }
3105
    #endif
3106
        /* Clear tx_buf to remove any SASL auth data from memory BEFORE
3107
         * MqttWriteStop releases lockSend, to prevent a racing thread
3108
         * from populating tx_buf before it is scrubbed.
3109
         * Use xfer (saved before MqttWriteStop zeroes client->write). */
3110
0
        CLIENT_FORCE_ZERO(client->tx_buf, xfer);
3111
0
        MqttWriteStop(client, &auth->stat);
3112
3113
0
        if (rc != xfer) {
3114
0
            MqttClient_CancelMessage(client, (MqttObject*)auth);
3115
0
            return rc;
3116
0
        }
3117
3118
0
        auth->stat.write = MQTT_MSG_WAIT;
3119
0
    }
3120
3121
    /* Wait for auth packet */
3122
0
    rc = MqttClient_WaitType(client, auth, MQTT_PACKET_TYPE_AUTH, 0,
3123
0
        client->cmd_timeout_ms);
3124
#if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD)
3125
    if (rc == MQTT_CODE_CONTINUE)
3126
        return rc;
3127
#endif
3128
3129
#ifdef WOLFMQTT_MULTITHREAD
3130
    if (wm_SemLock(&client->lockClient) == 0) {
3131
        MqttClient_RespList_Remove(client, &auth->pendResp);
3132
        wm_SemUnlock(&client->lockClient);
3133
    }
3134
#endif
3135
3136
    /* Scrub the decoded AUTH response from rx_buf. Its properties (e.g. the
3137
     * MQTT_PROP_AUTH_DATA SASL blob) point into rx_buf and would otherwise
3138
     * linger until the next read overwrites them. MqttClient_WaitType above
3139
     * already delivered and freed auth->props, so the bytes are consumed
3140
     * before this scrub and the caller has no live pointer into rx_buf. */
3141
#ifdef WOLFMQTT_MULTITHREAD
3142
    /* Hold lockRecv so the scrub cannot race a concurrent read into rx_buf. If
3143
     * the lock cannot be taken, still scrub: leaving the AUTH_DATA plaintext
3144
     * behind is worse than an unsynchronized wipe. */
3145
    if (wm_SemLock(&client->lockRecv) == 0) {
3146
        CLIENT_FORCE_ZERO(client->rx_buf, client->rx_buf_len);
3147
        wm_SemUnlock(&client->lockRecv);
3148
    }
3149
    else {
3150
        CLIENT_FORCE_ZERO(client->rx_buf, client->rx_buf_len);
3151
    }
3152
#else
3153
0
    CLIENT_FORCE_ZERO(client->rx_buf, client->rx_buf_len);
3154
0
#endif
3155
3156
    /* reset state */
3157
0
    auth->stat.write = MQTT_MSG_BEGIN;
3158
3159
0
    return rc;
3160
0
}
3161
3162
MqttProp* MqttClient_PropsAdd(MqttProp **head)
3163
0
{
3164
0
    return MqttProps_Add(head);
3165
0
}
3166
3167
int MqttClient_PropsFree(MqttProp *head)
3168
0
{
3169
0
    return MqttProps_Free(head);
3170
0
}
3171
3172
#endif /* WOLFMQTT_V5 */
3173
3174
#ifndef WOLFMQTT_NO_TIME
3175
/* Send a keep-alive PINGREQ when the outbound link has been idle for about
3176
 * three quarters of the negotiated keep-alive interval, so the application
3177
 * does not have to schedule pings itself. Called from the wait path.
3178
 * [MQTT-3.1.2-23] */
3179
static int MqttClient_KeepAlive(MqttClient *client, MqttObject* msg)
3180
6.45k
{
3181
6.45k
    int rc = MQTT_CODE_SUCCESS;
3182
6.45k
    int mid_transfer;
3183
6.45k
    word32 now, elapsed, threshold;
3184
3185
6.45k
    if (client == NULL) {
3186
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
3187
0
    }
3188
3189
    /* Disabled at runtime by the application (e.g. it schedules its own ping),
3190
     * independent of the keep-alive negotiated with the broker. */
3191
6.45k
    if ((client->flags & MQTT_CLIENT_FLAG_NO_AUTO_KEEPALIVE) != 0) {
3192
0
        return MQTT_CODE_SUCCESS;
3193
0
    }
3194
3195
    /* Disabled until a non-zero keep-alive has been negotiated in CONNECT. */
3196
6.45k
    if (client->keep_alive_sec == 0) {
3197
6.32k
        return MQTT_CODE_SUCCESS;
3198
6.32k
    }
3199
3200
    /* Resume an in-progress ping exchange before evaluating the threshold. In
3201
     * WOLFMQTT_NONBLOCK mode MqttClient_Ping_ex can return MQTT_CODE_CONTINUE
3202
     * before the PINGRESP arrives, leaving the ping state machine mid-exchange.
3203
     * Drive it to completion here so a later ping is not entered with stale
3204
     * state, which would skip the PINGREQ and stretch the real ping interval. */
3205
132
    if (client->keep_alive_ping.stat.write != MQTT_MSG_BEGIN) {
3206
0
        rc = MqttClient_Ping_ex(client, &client->keep_alive_ping);
3207
0
        if (rc == MQTT_CODE_ERROR_TIMEOUT) {
3208
0
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK);
3209
0
        }
3210
0
        return rc;
3211
0
    }
3212
3213
    /* Do not inject a ping while a message is mid-transfer: a partially read
3214
     * fixed header (packet.stat), or a payload read still in progress on the
3215
     * wait object (msg->read), which packet.stat alone does not catch once the
3216
     * header has been consumed but the read is not finished. Under
3217
     * WOLFMQTT_MULTITHREAD client->packet and client->read are protected by the
3218
     * read lock, so evaluate this under lockClient (which MqttReadStart and
3219
     * MqttReadStop also hold when they set read.isActive and reset
3220
     * packet.stat). read.isActive - an in-progress read holding lockRecv - is
3221
     * tested first, so packet.stat is only read when no read is active and thus
3222
     * cannot be concurrently advanced. wm_SemLock blocks until lockClient is
3223
     * available and returns an error only on a system fault (e.g. ThreadX), in
3224
     * which case the ping is skipped this round. */
3225
132
    mid_transfer = 0;
3226
#ifdef WOLFMQTT_MULTITHREAD
3227
    if (wm_SemLock(&client->lockClient) == 0) {
3228
        if (client->read.isActive ||
3229
                client->packet.stat != MQTT_PK_BEGIN ||
3230
                (msg != NULL && ((MqttMsgStat*)msg)->read != MQTT_MSG_BEGIN)) {
3231
            mid_transfer = 1;
3232
        }
3233
        wm_SemUnlock(&client->lockClient);
3234
    }
3235
    else {
3236
        mid_transfer = 1;
3237
    }
3238
#else
3239
132
    if (client->packet.stat != MQTT_PK_BEGIN ||
3240
98
            (msg != NULL && ((MqttMsgStat*)msg)->read != MQTT_MSG_BEGIN)) {
3241
78
        mid_transfer = 1;
3242
78
    }
3243
132
#endif
3244
132
    if (mid_transfer) {
3245
78
        return MQTT_CODE_SUCCESS;
3246
78
    }
3247
3248
54
    now = WOLFMQTT_GET_TIME_S();
3249
54
    if (now < client->last_tx_time) {
3250
        /* Clock stepped backward: re-baseline instead of pinging early. */
3251
0
        client->last_tx_time = now;
3252
0
        return MQTT_CODE_SUCCESS;
3253
0
    }
3254
3255
    /* Ping at ~3/4 of the interval so the PINGREQ reaches the broker before
3256
     * the hard deadline, leaving headroom for network latency and the
3257
     * one-second clock granularity. Floor at one second so a small keep-alive
3258
     * still schedules a single ping instead of firing on every poll. */
3259
54
    threshold = (word32)client->keep_alive_sec * 3 / 4;
3260
54
    if (threshold == 0) {
3261
54
        threshold = 1;
3262
54
    }
3263
3264
54
    elapsed = now - client->last_tx_time;
3265
54
    if (elapsed >= threshold) {
3266
        /* MqttPacket_Write refreshes last_tx_time as the PINGREQ is sent. */
3267
0
        rc = MqttClient_Ping_ex(client, &client->keep_alive_ping);
3268
0
        if (rc == MQTT_CODE_ERROR_TIMEOUT) {
3269
            /* No PINGRESP within cmd_timeout_ms: the link is unresponsive, not
3270
             * merely idle. Surface a distinct error so a caller does not treat
3271
             * a failed keep-alive as an ordinary read timeout and keep looping
3272
             * on a dead connection. */
3273
0
            rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK);
3274
0
        }
3275
0
    }
3276
54
    return rc;
3277
54
}
3278
#endif /* !WOLFMQTT_NO_TIME */
3279
3280
int MqttClient_WaitMessage_ex(MqttClient *client, MqttObject* msg,
3281
        int timeout_ms)
3282
6.45k
{
3283
6.45k
#ifndef WOLFMQTT_NO_TIME
3284
6.45k
    int rc;
3285
6.45k
#endif
3286
3287
6.45k
    if (client == NULL || msg == NULL) {
3288
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
3289
0
    }
3290
3291
6.45k
#ifndef WOLFMQTT_NO_TIME
3292
    /* Send an automatic keep-alive PINGREQ if the outbound link has been idle
3293
     * long enough. A deferred ping (another thread holds the write lock) comes
3294
     * back as MQTT_CODE_CONTINUE, which is not an error, so in a blocking build
3295
     * fall through to the normal wait rather than returning it to the caller. */
3296
6.45k
    rc = MqttClient_KeepAlive(client, msg);
3297
6.45k
    if (rc != MQTT_CODE_SUCCESS
3298
0
    #ifndef WOLFMQTT_NONBLOCK
3299
0
            && rc != MQTT_CODE_CONTINUE
3300
6.45k
    #endif
3301
6.45k
        ) {
3302
0
        return rc;
3303
0
    }
3304
6.45k
#endif
3305
6.45k
    return MqttClient_WaitType(client, msg, MQTT_PACKET_TYPE_ANY, 0,
3306
6.45k
        timeout_ms);
3307
6.45k
}
3308
int MqttClient_WaitMessage(MqttClient *client, int timeout_ms)
3309
6.45k
{
3310
6.45k
    if (client == NULL)
3311
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
3312
6.45k
    return MqttClient_WaitMessage_ex(client, &client->msg, timeout_ms);
3313
6.45k
}
3314
3315
#if !defined(WOLFMQTT_MULTITHREAD) && !defined(WOLFMQTT_NONBLOCK)
3316
static
3317
#endif
3318
int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg)
3319
8.65k
{
3320
8.65k
    int rc = MQTT_CODE_SUCCESS;
3321
8.65k
    MqttMsgStat* mms_stat;
3322
#ifdef WOLFMQTT_MULTITHREAD
3323
    MqttPendResp* tmpResp;
3324
#endif
3325
3326
8.65k
    if (client == NULL || msg == NULL) {
3327
0
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
3328
0
    }
3329
3330
    /* all packet type structures must have MqttMsgStat at top */
3331
8.65k
    mms_stat = (MqttMsgStat*)msg;
3332
3333
#ifdef WOLFMQTT_DEBUG_CLIENT
3334
    PRINTF("Cancel Msg: %p", msg);
3335
#endif
3336
3337
    /* reset states */
3338
8.65k
    mms_stat->write = MQTT_MSG_BEGIN;
3339
8.65k
    mms_stat->read = MQTT_MSG_BEGIN;
3340
3341
#ifdef WOLFMQTT_MULTITHREAD
3342
    /* Remove any pending responses expected */
3343
    rc = wm_SemLock(&client->lockClient);
3344
    if (rc != MQTT_CODE_SUCCESS) {
3345
        return rc;
3346
    }
3347
3348
    for (tmpResp = client->firstPendResp;
3349
         tmpResp != NULL;
3350
         tmpResp = tmpResp->next)
3351
    {
3352
    #ifdef WOLFMQTT_DEBUG_CLIENT
3353
        PRINTF("\tMsg: %p (obj %p), Type %s (%d), ID %d, InProc %d, Done %d",
3354
            tmpResp, tmpResp->packet_obj,
3355
            MqttPacket_TypeDesc(tmpResp->packet_type),
3356
            tmpResp->packet_type, tmpResp->packet_id,
3357
            tmpResp->packetProcessing, tmpResp->packetDone);
3358
    #endif
3359
        if ((size_t)tmpResp->packet_obj == (size_t)msg ||
3360
            (size_t)tmpResp - OFFSETOF(MqttMessage, pendResp) == (size_t)msg) {
3361
        #ifdef WOLFMQTT_DEBUG_CLIENT
3362
            PRINTF("Found Cancel Msg: %p (obj %p), Type %s (%d), ID %d, "
3363
                   "InProc %d, Done %d",
3364
                tmpResp, tmpResp->packet_obj,
3365
                MqttPacket_TypeDesc(tmpResp->packet_type),
3366
                tmpResp->packet_type, tmpResp->packet_id,
3367
                tmpResp->packetProcessing, tmpResp->packetDone);
3368
        #endif
3369
            MqttClient_RespList_Remove(client, tmpResp);
3370
            break;
3371
        }
3372
    }
3373
    wm_SemUnlock(&client->lockClient);
3374
#endif /* WOLFMQTT_MULTITHREAD */
3375
3376
    /* cancel any active flags / locks */
3377
8.65k
    if (mms_stat->isReadActive) {
3378
    #ifdef WOLFMQTT_DEBUG_CLIENT
3379
        PRINTF("Cancel Read Lock");
3380
    #endif
3381
0
        MqttReadStop(client, mms_stat);
3382
0
    }
3383
8.65k
    if (mms_stat->isWriteActive) {
3384
    #ifdef WOLFMQTT_DEBUG_CLIENT
3385
        PRINTF("Cancel Write Lock");
3386
    #endif
3387
        /* An abandoned write (e.g. a partial nonblocking CONNECT) leaves the
3388
         * encoded packet - possibly plaintext credentials - in tx_buf. Scrub
3389
         * it before MqttWriteStop releases lockSend. */
3390
0
        CLIENT_FORCE_ZERO(client->tx_buf, client->tx_buf_len);
3391
0
        MqttWriteStop(client, mms_stat);
3392
0
    }
3393
3394
8.65k
    return rc;
3395
8.65k
}
3396
3397
#ifdef WOLFMQTT_NONBLOCK
3398
static inline int IsMessageActive(MqttObject *msg)
3399
{
3400
    return (msg->stat.read  != MQTT_MSG_BEGIN ||
3401
            msg->stat.write != MQTT_MSG_BEGIN);
3402
}
3403
3404
int MqttClient_IsMessageActive(
3405
    MqttClient *client,
3406
    MqttObject *msg)
3407
{
3408
    int rc;
3409
3410
    /* must supply either client or msg */
3411
    if (client == NULL && msg == NULL) {
3412
        return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG);
3413
    }
3414
3415
    /* if msg is null then client->msg is used */
3416
    if ((client != NULL && &client->msg == msg) || msg == NULL) {
3417
    #ifdef WOLFMQTT_MULTITHREAD
3418
        rc = wm_SemLock(&client->lockClient);
3419
        if (rc == 0)
3420
    #endif
3421
        {
3422
            rc = IsMessageActive(&client->msg);
3423
        #ifdef WOLFMQTT_MULTITHREAD
3424
            wm_SemUnlock(&client->lockClient);
3425
        #endif
3426
        }
3427
    }
3428
    else {
3429
        rc = IsMessageActive(msg);
3430
    }
3431
    return rc;
3432
}
3433
3434
3435
#endif /* WOLFMQTT_NONBLOCK */
3436
3437
3438
int MqttClient_NetConnect(MqttClient *client, const char* host,
3439
    word16 port, int timeout_ms, int use_tls, MqttTlsCb cb)
3440
4.61k
{
3441
4.61k
    return MqttSocket_Connect(client, host, port, timeout_ms, use_tls, cb);
3442
4.61k
}
3443
3444
int MqttClient_NetDisconnect(MqttClient *client)
3445
138
{
3446
#ifdef WOLFMQTT_MULTITHREAD
3447
    MqttPendResp *tmpResp;
3448
    MqttPendResp *nextResp;
3449
    int rc;
3450
#endif
3451
3452
138
    if (client == NULL) {
3453
0
        return MQTT_CODE_ERROR_BAD_ARG;
3454
0
    }
3455
3456
#ifdef WOLFMQTT_MULTITHREAD
3457
    /* Get client lock on to ensure no other threads are active */
3458
    rc = wm_SemLock(&client->lockClient);
3459
    if (rc == 0) {
3460
    #ifdef WOLFMQTT_DEBUG_CLIENT
3461
        PRINTF("Net Disconnect: Removing pending responses");
3462
    #endif
3463
        for (tmpResp = client->firstPendResp;
3464
             tmpResp != NULL;
3465
             tmpResp = nextResp) {
3466
            nextResp = tmpResp->next;
3467
        #ifdef WOLFMQTT_DEBUG_CLIENT
3468
            PRINTF("\tPendResp: %p (obj %p), Type %s (%d), ID %d, InProc %d, Done %d",
3469
                tmpResp, tmpResp->packet_obj,
3470
                MqttPacket_TypeDesc(tmpResp->packet_type),
3471
                tmpResp->packet_type, tmpResp->packet_id,
3472
                tmpResp->packetProcessing, tmpResp->packetDone);
3473
        #endif
3474
            MqttClient_RespList_Remove(client, tmpResp);
3475
        }
3476
        wm_SemUnlock(&client->lockClient);
3477
    }
3478
    else {
3479
        return rc;
3480
    }
3481
#endif
3482
3483
138
    return MqttSocket_Disconnect(client);
3484
138
}
3485
3486
int MqttClient_GetProtocolVersion(MqttClient *client)
3487
0
{
3488
0
#ifdef WOLFMQTT_V5
3489
0
    if (client && client->protocol_level == MQTT_CONNECT_PROTOCOL_LEVEL_5)
3490
0
        return MQTT_CONNECT_PROTOCOL_LEVEL_5;
3491
#else
3492
    (void)client;
3493
#endif
3494
0
    return MQTT_CONNECT_PROTOCOL_LEVEL_4;
3495
0
}
3496
const char* MqttClient_GetProtocolVersionString(MqttClient *client)
3497
0
{
3498
0
    const char* str = NULL;
3499
0
    int ver = MqttClient_GetProtocolVersion(client);
3500
0
    switch (ver) {
3501
0
        case MQTT_CONNECT_PROTOCOL_LEVEL_4:
3502
0
            return "v3.1.1";
3503
0
    #ifdef WOLFMQTT_V5
3504
0
        case MQTT_CONNECT_PROTOCOL_LEVEL_5:
3505
0
            return "v5";
3506
0
    #endif
3507
0
        default:
3508
0
            break;
3509
0
    }
3510
0
    return str;
3511
0
}
3512
3513
#ifndef WOLFMQTT_NO_ERROR_STRINGS
3514
const char* MqttClient_ReturnCodeToString(int return_code)
3515
0
{
3516
0
    switch(return_code) {
3517
0
        case MQTT_CODE_SUCCESS:
3518
0
            return "Success";
3519
0
        case MQTT_CODE_CONTINUE:
3520
0
            return "Continue"; /* would block */
3521
0
        case MQTT_CODE_STDIN_WAKE:
3522
0
            return "STDIN Wake";
3523
0
        case MQTT_CODE_PUB_CONTINUE:
3524
0
            return "Continue calling publish"; /* Chunked publish */
3525
0
        case MQTT_CODE_ERROR_BAD_ARG:
3526
0
            return "Error (Bad argument)";
3527
0
        case MQTT_CODE_ERROR_OUT_OF_BUFFER:
3528
0
            return "Error (Out of buffer)";
3529
0
        case MQTT_CODE_ERROR_MALFORMED_DATA:
3530
0
            return "Error (Malformed Remaining Length)";
3531
0
        case MQTT_CODE_ERROR_PACKET_TYPE:
3532
0
            return "Error (Packet Type Mismatch)";
3533
0
        case MQTT_CODE_ERROR_PACKET_ID:
3534
0
            return "Error (Packet Id Mismatch)";
3535
0
        case MQTT_CODE_ERROR_TLS_CONNECT:
3536
0
            return "Error (TLS Connect)";
3537
0
        case MQTT_CODE_ERROR_TIMEOUT:
3538
0
            return "Error (Timeout)";
3539
0
        case MQTT_CODE_ERROR_NETWORK:
3540
0
            return "Error (Network)";
3541
0
        case MQTT_CODE_ERROR_MEMORY:
3542
0
            return "Error (Memory)";
3543
0
        case MQTT_CODE_ERROR_STAT:
3544
0
            return "Error (State)";
3545
0
        case MQTT_CODE_ERROR_PROPERTY:
3546
0
            return "Error (Property)";
3547
0
        case MQTT_CODE_ERROR_SERVER_PROP:
3548
0
            return "Error (Server Property)";
3549
0
        case MQTT_CODE_ERROR_CALLBACK:
3550
0
            return "Error (Error in Callback)";
3551
0
        case MQTT_CODE_ERROR_SYSTEM:
3552
0
            return "Error (System resource failed)";
3553
0
        case MQTT_CODE_ERROR_NOT_FOUND:
3554
0
            return "Error (Not found)";
3555
0
        case MQTT_CODE_ERROR_CONNECT_REFUSED:
3556
0
            return "Error (Broker refused connection)";
3557
0
        case MQTT_CODE_ERROR_SUBSCRIBE_REJECTED:
3558
0
            return "Error (Broker rejected subscription)";
3559
0
        case MQTT_CODE_ERROR_UNSUBSCRIBE_REJECTED:
3560
0
            return "Error (Broker rejected unsubscribe)";
3561
0
        case MQTT_CODE_ERROR_PUBLISH_REJECTED:
3562
0
            return "Error (Broker rejected publish)";
3563
#if defined(ENABLE_MQTT_CURL)
3564
        case MQTT_CODE_ERROR_CURL:
3565
            return "Error (libcurl)";
3566
#endif
3567
3568
0
#ifdef WOLFMQTT_V5
3569
        /* MQTT v5 Reason code strings */
3570
0
        case MQTT_REASON_UNSPECIFIED_ERR:
3571
0
            return "Unspecified error";
3572
0
        case MQTT_REASON_MALFORMED_PACKET:
3573
0
            return "Malformed Packet";
3574
0
        case MQTT_REASON_PROTOCOL_ERR:
3575
0
            return "Protocol Error";
3576
0
        case MQTT_REASON_IMPL_SPECIFIC_ERR:
3577
0
            return "Implementation specific error";
3578
0
        case MQTT_REASON_UNSUP_PROTO_VER:
3579
0
            return "Unsupported Protocol Version";
3580
0
        case MQTT_REASON_CLIENT_ID_NOT_VALID:
3581
0
            return "Client Identifier not valid";
3582
0
        case MQTT_REASON_BAD_USER_OR_PASS:
3583
0
            return "Bad User Name or Password";
3584
0
        case MQTT_REASON_NOT_AUTHORIZED:
3585
0
            return "Not authorized";
3586
0
        case MQTT_REASON_SERVER_UNAVAILABLE:
3587
0
            return "Server unavailable";
3588
0
        case MQTT_REASON_SERVER_BUSY:
3589
0
            return "Server busy";
3590
0
        case MQTT_REASON_BANNED:
3591
0
            return "Banned";
3592
0
        case MQTT_REASON_SERVER_SHUTTING_DOWN:
3593
0
            return "Server shutting down";
3594
0
        case MQTT_REASON_BAD_AUTH_METHOD:
3595
0
            return "Bad authentication method";
3596
0
        case MQTT_REASON_KEEP_ALIVE_TIMEOUT:
3597
0
            return "Keep Alive timeout";
3598
0
        case MQTT_REASON_SESSION_TAKEN_OVER:
3599
0
            return "Session taken over";
3600
0
        case MQTT_REASON_TOPIC_FILTER_INVALID:
3601
0
            return "Topic Filter invalid";
3602
0
        case MQTT_REASON_TOPIC_NAME_INVALID:
3603
0
            return "Topic Name invalid";
3604
0
        case MQTT_REASON_PACKET_ID_IN_USE:
3605
0
            return "Packet Identifier in use";
3606
0
        case MQTT_REASON_PACKET_ID_NOT_FOUND:
3607
0
            return "Packet Identifier not found";
3608
0
        case MQTT_REASON_RX_MAX_EXCEEDED:
3609
0
            return "Receive Maximum exceeded";
3610
0
        case MQTT_REASON_TOPIC_ALIAS_INVALID:
3611
0
            return "Topic Alias invalid";
3612
0
        case MQTT_REASON_PACKET_TOO_LARGE:
3613
0
            return "Packet too large";
3614
0
        case MQTT_REASON_MSG_RATE_TOO_HIGH:
3615
0
            return "Message rate too high";
3616
0
        case MQTT_REASON_QUOTA_EXCEEDED:
3617
0
            return "Quota exceeded";
3618
0
        case MQTT_REASON_ADMIN_ACTION:
3619
0
            return "Administrative action";
3620
0
        case MQTT_REASON_PAYLOAD_FORMAT_INVALID:
3621
0
            return "Payload format invalid";
3622
0
        case MQTT_REASON_RETAIN_NOT_SUPPORTED:
3623
0
            return "Retain not supported";
3624
0
        case MQTT_REASON_QOS_NOT_SUPPORTED:
3625
0
            return "QoS not supported";
3626
0
        case MQTT_REASON_USE_ANOTHER_SERVER:
3627
0
            return "Use another server";
3628
0
        case MQTT_REASON_SERVER_MOVED:
3629
0
            return "Server moved";
3630
0
        case MQTT_REASON_SS_NOT_SUPPORTED:
3631
0
            return "Shared Subscriptions not supported";
3632
0
        case MQTT_REASON_CON_RATE_EXCEED:
3633
0
            return "Connection rate exceeded";
3634
0
        case MQTT_REASON_MAX_CON_TIME:
3635
0
            return "Maximum connect time";
3636
0
        case MQTT_REASON_SUB_ID_NOT_SUP:
3637
0
            return "Subscription Identifiers not supported";
3638
0
        case MQTT_REASON_WILDCARD_SUB_NOT_SUP:
3639
0
            return "Wildcard Subscriptions not supported";
3640
0
#endif
3641
0
    }
3642
0
    return "Unknown";
3643
0
}
3644
#endif /* !WOLFMQTT_NO_ERROR_STRINGS */
3645
3646
word32 MqttClient_Flags(MqttClient *client,  word32 mask, word32 flags)
3647
52.8k
{
3648
52.8k
    word32 ret = 0;
3649
52.8k
    if (client != NULL) {
3650
#ifdef WOLFMQTT_MULTITHREAD
3651
        /* Get client lock on to ensure no other threads are active */
3652
        if (wm_SemLock(&client->lockClient) == 0)
3653
#endif
3654
52.8k
        {
3655
52.8k
            client->flags &= ~mask;
3656
52.8k
            client->flags |= flags;
3657
52.8k
            ret = client->flags;
3658
#ifdef WOLFMQTT_MULTITHREAD
3659
            wm_SemUnlock(&client->lockClient);
3660
#endif
3661
52.8k
        }
3662
52.8k
    }
3663
52.8k
    return ret;
3664
52.8k
}