• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "coap_discover.h"
17 #include <errno.h>
18 #include <stdatomic.h>
19 #include <string.h>
20 #include <securec.h>
21 
22 #include "coap_app.h"
23 #include "coap_client.h"
24 #include "nstackx_dfinder_log.h"
25 #include "nstackx_dfinder_mgt_msg_log.h"
26 #include "nstackx_util.h"
27 #include "nstackx_timer.h"
28 #include "nstackx_error.h"
29 #include "nstackx_device.h"
30 #include "json_payload.h"
31 #include "nstackx_statistics.h"
32 #include "nstackx_device_local.h"
33 #include "nstackx_device_remote.h"
34 
35 #define TAG "nStackXCoAP"
36 
37 #define COAP_URI_BUFFER_LENGTH 64 /* the size of the buffer or variable used to save uri. */
38 #define COAP_MAX_NUM_SUBSCRIBE_MODULE_COUNT 32 /* the maximum count of subscribed module */
39 
40 #define COAP_RECV_COUNT_INTERVAL 1000
41 #define COAP_DISVOCER_MAX_RATE 200
42 #define COAP_MSGID_SURVIVAL_SECONDS 100
43 #define COAP_MAX_MSGID_RESERVE_NUM 100
44 
45 typedef struct {
46     coap_mid_t msgId;
47     struct timespec recvTime;
48 } MsgIdRecord;
49 
50 typedef struct {
51     MsgIdRecord msgIdRecord[COAP_MAX_MSGID_RESERVE_NUM];
52     uint32_t startIdx;
53     uint32_t endIdx;
54 } MsgIdList;
55 
56 static int g_resourceFlags = COAP_RESOURCE_FLAGS_NOTIFY_CON;
57 static uint32_t g_coapMaxDiscoverCount = COAP_DEFAULT_DISCOVER_COUNT;
58 static uint32_t g_coapDiscoverType = COAP_BROADCAST_TYPE_DEFAULT;
59 
60 static uint8_t g_userRequest;
61 static uint8_t g_forceUpdate;
62 static Timer *g_discoverTimer = NULL;
63 static uint32_t g_discoverCount;
64 static uint32_t g_coapUserMaxDiscoverCount;
65 static uint32_t g_coapUserDiscoverInterval;
66 static uint32_t *g_coapIntervalArr = NULL;
67 static uint32_t g_coapDiscoverTargetCount;
68 static Timer *g_recvRecountTimer = NULL;
69 static uint32_t g_recvDiscoverMsgNum;
70 static MsgIdList *g_msgIdList = NULL;
71 static atomic_uint_fast8_t g_subscribeCount;
72 
73 static uint16_t *g_notificationIntervals = NULL;
74 static uint8_t g_notificationTargetCnt = 0;
75 static uint8_t g_notificationRunCnt = 0;
76 static Timer *g_notificationTimer = NULL;
77 
78 #ifdef DFINDER_SUPPORT_SET_SCREEN_STATUS
79 static bool g_isScreenOn = true;
80 
SetScreenStatus(bool isScreenOn)81 void SetScreenStatus(bool isScreenOn)
82 {
83     g_isScreenOn = isScreenOn;
84 }
85 #endif
86 
CoapUriParse(const char * uriString,coap_uri_t * uriPtr)87 static int32_t CoapUriParse(const char *uriString, coap_uri_t *uriPtr)
88 {
89     coap_uri_t localUri;
90 
91     (void)memset_s(&localUri, sizeof(localUri), 0, sizeof(localUri));
92     if ((uriString == NULL) || (uriPtr == NULL)) {
93         return NSTACKX_EFAILED;
94     }
95 
96     if (coap_split_uri((unsigned char *)uriString, strlen(uriString), &localUri) < 0) {
97         DFINDER_LOGE(TAG, "invalid CoAP URI");
98         return NSTACKX_EFAILED;
99     }
100 #ifdef DFINDER_SUPPORT_MULTI_COAP_SCHEME
101     if (!coap_dtls_is_supported() && localUri.scheme == COAP_URI_SCHEME_COAPS) {
102         DFINDER_LOGE(TAG, "coap uri sheme coaps with no dtls support");
103         return NSTACKX_EFAILED;
104     }
105     if (((localUri.scheme == COAP_URI_SCHEME_COAPS_TCP) || (localUri.scheme == COAP_URI_SCHEME_COAPS)) &&
106         !coap_tls_is_supported()) {
107         DFINDER_LOGE(TAG, "coaps + tcp uri scheme not supported in this version of libcoap");
108         return NSTACKX_EFAILED;
109     }
110 #else
111     if (localUri.scheme != COAP_URI_SCHEME_COAP) {
112         DFINDER_LOGE(TAG, "coaps uri scheme not supported in this version of libcoap");
113         return NSTACKX_EFAILED;
114     }
115 #endif
116     (void)memcpy_s(uriPtr, sizeof(coap_uri_t), &localUri, sizeof(coap_uri_t));
117     return NSTACKX_EOK;
118 }
119 
CoapPackToPdu(const CoapRequest * coapRequest,const coap_uri_t * uriPtr,coap_session_t * session)120 static coap_pdu_t *CoapPackToPdu(const CoapRequest *coapRequest, const coap_uri_t *uriPtr, coap_session_t *session)
121 {
122     coap_pdu_t *pdu = NULL;
123     if (coapRequest == NULL || coapRequest->remoteUrl == NULL || session == NULL) {
124         return NULL;
125     }
126     pdu = coap_new_pdu(coapRequest->type, coapRequest->code, session);
127     if (pdu == NULL) {
128         return NULL;
129     }
130     if (coapRequest->tokenLength) {
131         if (!coap_add_token(pdu, coapRequest->tokenLength, coapRequest->token)) {
132             DFINDER_LOGW(TAG, "cannot add token to request");
133         }
134     }
135     coap_add_option(pdu, COAP_OPTION_URI_HOST, uriPtr->host.length, uriPtr->host.s);
136     coap_add_option(pdu, COAP_OPTION_URI_PATH, uriPtr->path.length, uriPtr->path.s);
137     if (coapRequest->dataLength) {
138         coap_add_data(pdu, coapRequest->dataLength, (uint8_t *)(coapRequest->data));
139     }
140 
141     return pdu;
142 }
143 
FillCoapRequest(CoapRequest * coapRequest,uint8_t coapType,const char * url,char * data,size_t dataLen)144 static void FillCoapRequest(CoapRequest *coapRequest, uint8_t coapType, const char *url, char *data, size_t dataLen)
145 {
146     (void)memset_s(coapRequest, sizeof(CoapRequest), 0, sizeof(CoapRequest));
147     coapRequest->type = coapType;
148     coapRequest->code = COAP_REQUEST_POST;
149     coapRequest->remoteUrl = url;
150     coapRequest->data = data;
151     coapRequest->dataLength = dataLen;
152 }
153 
CoapSendRequestEx(CoapCtxType * ctx,uint8_t coapType,const char * url,char * data,size_t dataLen)154 static int32_t CoapSendRequestEx(CoapCtxType *ctx, uint8_t coapType, const char *url, char *data, size_t dataLen)
155 {
156     CoapRequest coapRequest;
157     coap_session_t *session = NULL;
158     coap_address_t dst = {0};
159     coap_str_const_t remote;
160     int32_t tid;
161     int32_t res;
162     coap_pdu_t *pdu = NULL;
163     coap_uri_t coapUri;
164     CoapServerParameter coapServerParameter = {0};
165 
166     FillCoapRequest(&coapRequest, coapType, url, data, dataLen);
167 
168     (void)memset_s(&remote, sizeof(remote), 0, sizeof(remote));
169     (void)memset_s(&coapUri, sizeof(coapUri), 0, sizeof(coapUri));
170     if (CoapUriParse(coapRequest.remoteUrl, &coapUri) != NSTACKX_EOK) {
171         DFINDER_LOGE(TAG, "fail to parse uri");
172         return NSTACKX_EFAILED;
173     }
174     remote = coapUri.host;
175     res = CoapResolveAddress(&remote, &dst.addr.sa);
176     if (res < 0) {
177         DFINDER_LOGE(TAG, "fail to resolve address");
178         return NSTACKX_EFAILED;
179     }
180 
181     dst.size = (uint32_t)res;
182     dst.addr.sin.sin_port = htons(COAP_DEFAULT_PORT);
183     coapServerParameter.proto = COAP_PROTO_UDP;
184     coapServerParameter.dst = &dst;
185     session = CoapGetSession(ctx->ctx, GetLocalIfaceIpStr(ctx->iface), COAP_SRV_DEFAULT_PORT, &coapServerParameter);
186     if (session == NULL) {
187         DFINDER_LOGE(TAG, "get client session failed");
188         return NSTACKX_EFAILED;
189     }
190     pdu = CoapPackToPdu(&coapRequest, &coapUri, session);
191     if (pdu == NULL) {
192         DFINDER_LOGE(TAG, "pack to pdu failed");
193         goto SESSION_RELEASE;
194     }
195     DFINDER_LOGD("MYCOAP", "send coap pdu mid: %d", coap_pdu_get_mid(pdu));
196     DFINDER_MGT_REQ_LOG(&coapRequest);
197     tid = coap_send(session, pdu);
198     if (tid == COAP_INVALID_MID) {
199         DFINDER_LOGE(TAG, "coap send failed");
200         goto SESSION_RELEASE;
201     }
202 
203     coap_session_release(session);
204     return NSTACKX_EOK;
205 
206 SESSION_RELEASE:
207     coap_session_release(session);
208     return NSTACKX_EFAILED;
209 }
210 
CoapSendRequest(CoapCtxType * ctx,uint8_t coapType,const char * url,char * data,size_t dataLen)211 static int32_t CoapSendRequest(CoapCtxType *ctx, uint8_t coapType, const char *url, char *data, size_t dataLen)
212 {
213     int32_t ret = CoapSendRequestEx(ctx, coapType, url, data, dataLen);
214     if (ret != NSTACKX_EOK) {
215         IncStatistics(STATS_SEND_REQUEST_FAILED);
216     }
217     return ret;
218 }
219 
CoapResponseService(CoapCtxType * ctx,const char * remoteUrl,uint8_t businessType)220 static int32_t CoapResponseService(CoapCtxType *ctx, const char *remoteUrl, uint8_t businessType)
221 {
222     char *data = PrepareServiceDiscover(GetLocalIfaceIpStr(ctx->iface), NSTACKX_FALSE, businessType);
223     if (data == NULL) {
224         DFINDER_LOGE(TAG, "prepare service failed");
225         return NSTACKX_EFAILED;
226     }
227 
228     // for internal auto-reply unicast, make its type to NON-confirmable
229     // reliablity depends on the number of broadcast
230     uint8_t coapMsgType = (ShouldAutoReplyUnicast(businessType) == NSTACKX_TRUE) ? COAP_MESSAGE_NON : COAP_MESSAGE_CON;
231     int32_t ret = CoapSendRequest(ctx, coapMsgType, remoteUrl, data, strlen(data) + 1);
232 
233     cJSON_free(data);
234     return ret;
235 }
236 
IncreaseRecvDiscoverNum(void)237 static void IncreaseRecvDiscoverNum(void)
238 {
239     if (g_recvDiscoverMsgNum < UINT32_MAX) {
240         g_recvDiscoverMsgNum++;
241     }
242 }
243 
HndPostServiceDiscoverInner(const coap_pdu_t * request,char ** remoteUrl,DeviceInfo * deviceInfo)244 static int32_t HndPostServiceDiscoverInner(const coap_pdu_t *request, char **remoteUrl, DeviceInfo *deviceInfo)
245 {
246     size_t size;
247     const uint8_t *buf = NULL;
248     IncreaseRecvDiscoverNum();
249     if (g_recvDiscoverMsgNum > COAP_DISVOCER_MAX_RATE) {
250         DFINDER_LOGD(TAG, "too many dicover messages received");
251         return NSTACKX_EFAILED;
252     }
253     if (coap_get_data(request, &size, &buf) == 0 || size == 0 || size > COAP_RXBUFFER_SIZE) {
254         DFINDER_LOGE(TAG, "coap_get_data failed, size: %zu", size);
255         return NSTACKX_EFAILED;
256     }
257     if (GetServiceDiscoverInfo(buf, size, deviceInfo, remoteUrl) != NSTACKX_EOK) {
258         return NSTACKX_EFAILED;
259     }
260     /* receive coap broadcast, set peer device's discovery type to passive,
261      * to identify the local device is in passive discovery
262      */
263     deviceInfo->discoveryType = (*remoteUrl != NULL) ? NSTACKX_DISCOVERY_TYPE_PASSIVE : NSTACKX_DISCOVERY_TYPE_ACTIVE;
264     if (deviceInfo->mode == PUBLISH_MODE_UPLINE || deviceInfo->mode == PUBLISH_MODE_OFFLINE) {
265         DFINDER_LOGD(TAG, "peer is not DISCOVER_MODE");
266         NSTACKX_DeviceInfo deviceList;
267         (void)memset_s(&deviceList, sizeof(NSTACKX_DeviceInfo), 0, sizeof(NSTACKX_DeviceInfo));
268         if (GetNotifyDeviceInfo(&deviceList, deviceInfo) == NSTACKX_EOK) {
269             NotifyDeviceFound(&deviceList, 1);
270         } else {
271             DFINDER_LOGE(TAG, "GetNotifyDeviceInfo failed");
272         }
273         return NSTACKX_EFAILED;
274     }
275     return NSTACKX_EOK;
276 }
277 
CoapResponseServiceDiscovery(const char * remoteUrl,const coap_context_t * currCtx,coap_pdu_t * response,uint8_t businessType)278 static void CoapResponseServiceDiscovery(const char *remoteUrl, const coap_context_t *currCtx,
279     coap_pdu_t *response, uint8_t businessType)
280 {
281     if (remoteUrl != NULL) {
282         if (ShouldAutoReplyUnicast(businessType) == NSTACKX_TRUE) {
283             CoapCtxType *ctx = CoapGetCoapCtxType(currCtx);
284             if (ctx != NULL) {
285                 (void)CoapResponseService(ctx, remoteUrl, businessType);
286             }
287         }
288     } else {
289         coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
290     }
291 }
292 
HndPostServiceDiscoverEx(coap_session_t * session,const coap_pdu_t * request,coap_pdu_t * response)293 static int32_t HndPostServiceDiscoverEx(coap_session_t *session, const coap_pdu_t *request, coap_pdu_t *response)
294 {
295     int32_t ret = NSTACKX_EFAILED;
296     coap_context_t *currCtx = coap_session_get_context(session);
297     if (currCtx == NULL) {
298         DFINDER_LOGE(TAG, "coap_session_get_context return null");
299         return ret;
300     }
301     char *remoteUrl = NULL;
302     DeviceInfo *deviceInfo = calloc(1, sizeof(DeviceInfo));
303     if (deviceInfo == NULL) {
304         DFINDER_LOGE(TAG, "calloc for device info failed");
305         return ret;
306     }
307 
308     const CoapCtxType *coapCtx = CoapGetCoapCtxType(currCtx);
309     if (coapCtx == NULL) {
310         DFINDER_LOGE(TAG, "get coap ctx type failed");
311         goto L_ERR;
312     }
313 
314     if (strcpy_s(deviceInfo->networkName, sizeof(deviceInfo->networkName),
315         GetLocalIfaceName(coapCtx->iface)) != EOK) {
316         DFINDER_LOGE(TAG, "copy network name failed");
317         goto L_ERR;
318     }
319 
320     if (HndPostServiceDiscoverInner(request, &remoteUrl, deviceInfo) != NSTACKX_EOK) {
321         goto L_ERR;
322     }
323     if (GetModeInfo() == PUBLISH_MODE_UPLINE || GetModeInfo() == PUBLISH_MODE_OFFLINE) {
324         DFINDER_LOGD(TAG, "local is not DISCOVER_MODE");
325         goto L_ERR;
326     }
327 
328     uint8_t receiveBcast = (remoteUrl == NULL) ? NSTACKX_FALSE : NSTACKX_TRUE;
329     if (ReportDiscoveredDevice(coapCtx, deviceInfo, g_forceUpdate, receiveBcast) != NSTACKX_EOK) {
330         goto L_ERR;
331     }
332 
333     g_forceUpdate = NSTACKX_FALSE;
334     if (deviceInfo->mode == PUBLISH_MODE_PROACTIVE) {
335         DFINDER_LOGD(TAG, "peer is PUBLISH_MODE_PROACTIVE");
336         goto L_ERR;
337     }
338     CoapResponseServiceDiscovery(remoteUrl, currCtx, response, deviceInfo->businessType);
339 
340     ret = NSTACKX_EOK;
341 L_ERR:
342     free(deviceInfo);
343     free(remoteUrl);
344     return ret;
345 }
346 
HndPostServiceDiscover(coap_resource_t * resource,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query,coap_pdu_t * response)347 static void HndPostServiceDiscover(coap_resource_t *resource, coap_session_t *session,
348     const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response)
349 {
350     Coverity_Tainted_Set((void *)request);
351 
352     (void)resource;
353     (void)query;
354 #ifdef DFINDER_SUPPORT_SET_SCREEN_STATUS
355     if (request == NULL || response == NULL || !g_isScreenOn) {
356 #else
357     if (request == NULL || response == NULL) {
358 #endif
359         DFINDER_LOGD(TAG, "invalid params");
360         return;
361     }
362     DFINDER_LOGD("MYCOAP", "recv coap pdu mid: %d", coap_pdu_get_mid(request));
363     if (HndPostServiceDiscoverEx(session, request, response) != NSTACKX_EOK) {
364         IncStatistics(STATS_HANDLE_DEVICE_DISCOVER_MSG_FAILED);
365     }
366 }
367 
368 static void DeleteOverTimeMsgIdRecord(MsgIdList *msgIdList, struct timespec *curTime)
369 {
370     uint32_t i = msgIdList->startIdx;
371     if (msgIdList->startIdx >= COAP_MAX_MSGID_RESERVE_NUM || msgIdList->endIdx >= COAP_MAX_MSGID_RESERVE_NUM) {
372         return;
373     }
374     uint32_t cycleTimes = 0;
375     while (NSTACKX_TRUE) {
376         if (curTime->tv_sec - msgIdList->msgIdRecord[i].recvTime.tv_sec < COAP_MSGID_SURVIVAL_SECONDS) {
377             return;
378         }
379         if (i == msgIdList->endIdx) {
380             msgIdList->startIdx = COAP_MAX_MSGID_RESERVE_NUM;
381             msgIdList->endIdx = COAP_MAX_MSGID_RESERVE_NUM;
382             return;
383         }
384         msgIdList->startIdx = (msgIdList->startIdx + 1) % COAP_MAX_MSGID_RESERVE_NUM;
385         i = msgIdList->startIdx;
386         if (cycleTimes > COAP_MAX_MSGID_RESERVE_NUM) {
387             IncStatistics(STATS_DROP_MSG_ID);
388             DFINDER_LOGE(TAG, "cycle too many times, error must occurred and init msgList");
389             msgIdList->startIdx = COAP_MAX_MSGID_RESERVE_NUM;
390             msgIdList->endIdx = COAP_MAX_MSGID_RESERVE_NUM;
391             break;
392         }
393         cycleTimes++;
394     }
395 }
396 
397 static void AddMsgIdRecord(MsgIdList *msgIdList, coap_mid_t msgId, struct timespec *curTime)
398 {
399     int32_t ret;
400     uint32_t idx;
401     if (msgIdList->endIdx == COAP_MAX_MSGID_RESERVE_NUM) {
402         msgIdList->endIdx = 0;
403         msgIdList->startIdx = 0;
404         idx = 0;
405     } else {
406         idx = (msgIdList->endIdx + 1) % COAP_MAX_MSGID_RESERVE_NUM;
407         if (idx == msgIdList->startIdx) {
408             msgIdList->startIdx = (msgIdList->startIdx + 1) % COAP_MAX_MSGID_RESERVE_NUM;
409         }
410     }
411     msgIdList->msgIdRecord[idx].msgId = msgId;
412     ret = memcpy_s(&msgIdList->msgIdRecord[idx].recvTime, sizeof(struct timespec), curTime, sizeof(struct timespec));
413     if (ret != NSTACKX_EOK) {
414         DFINDER_LOGE(TAG, "set msg id time error");
415         return;
416     }
417     msgIdList->endIdx = idx;
418 }
419 
420 static uint8_t RefreshMsgIdList(coap_mid_t msgId)
421 {
422     struct timespec curTime;
423     uint32_t i;
424     if (g_msgIdList == NULL) {
425         return NSTACKX_TRUE;
426     }
427     ClockGetTime(CLOCK_MONOTONIC, &curTime);
428     DeleteOverTimeMsgIdRecord(g_msgIdList, &curTime);
429     if (g_msgIdList->startIdx >= COAP_MAX_MSGID_RESERVE_NUM || g_msgIdList->endIdx >= COAP_MAX_MSGID_RESERVE_NUM) {
430         AddMsgIdRecord(g_msgIdList, msgId, &curTime);
431         return NSTACKX_TRUE;
432     }
433     i = g_msgIdList->startIdx;
434     uint32_t cycleTimes = 0;
435     while (NSTACKX_TRUE) {
436         if (g_msgIdList->msgIdRecord[i].msgId == msgId) {
437             (void)memcpy_s(&g_msgIdList->msgIdRecord[i].recvTime, sizeof(struct timespec), &curTime,
438                            sizeof(struct timespec));
439             return NSTACKX_FALSE;
440         }
441         if (i == g_msgIdList->endIdx) {
442             break;
443         }
444         i = (i + 1) % COAP_MAX_MSGID_RESERVE_NUM;
445         if (cycleTimes > COAP_MAX_MSGID_RESERVE_NUM) {
446             IncStatistics(STATS_DROP_MSG_ID);
447             DFINDER_LOGE(TAG, "cycle too many times, error must occurred and init msgList");
448             g_msgIdList->startIdx = COAP_MAX_MSGID_RESERVE_NUM;
449             g_msgIdList->endIdx = COAP_MAX_MSGID_RESERVE_NUM;
450             break;
451         }
452         cycleTimes++;
453     }
454     AddMsgIdRecord(g_msgIdList, msgId, &curTime);
455     return NSTACKX_TRUE;
456 }
457 
458 static uint16_t GetServiceMsgFrameLen(const uint8_t *frame, uint16_t size)
459 {
460     uint16_t frameLen, ret;
461     if (size < sizeof(frameLen)) {
462         DFINDER_LOGE(TAG, "input size %u is too small", size);
463         return 0;
464     }
465     if (memcpy_s(&frameLen, sizeof(frameLen), frame, sizeof(frameLen)) != EOK) {
466         DFINDER_LOGE(TAG, "memcpy frame len failed");
467         return 0;
468     }
469     ret = ntohs(frameLen);
470     if (size < ret) {
471         DFINDER_LOGE(TAG, "input size %u is smaller than decoded frame len %u", size, ret);
472         return 0;
473     }
474     return ret;
475 }
476 
477 static uint16_t GetUnitInfo(const uint8_t *data, uint16_t dataLen, uint8_t *outBuf, uint32_t outLen, uint8_t unitType)
478 {
479     if (dataLen < sizeof(CoapMsgUnit)) {
480         DFINDER_LOGE(TAG, "dataLen %u is too small", dataLen);
481         return 0;
482     }
483     CoapMsgUnit *unit = (CoapMsgUnit *)data;
484     if (unit->type != unitType) {
485         DFINDER_LOGE(TAG, "unit type %u does match target type %u", unit->type, unitType);
486         return 0;
487     }
488     uint16_t valueLen = ntohs(unit->len);
489     if (valueLen == 0 || valueLen > outLen || valueLen + sizeof(CoapMsgUnit) > dataLen) {
490         DFINDER_LOGE(TAG, "valueLen %u is illegal", valueLen);
491         return 0;
492     }
493     if (memcpy_s(outBuf, outLen, unit->value, valueLen) != EOK) {
494         DFINDER_LOGE(TAG, "memcpy unit->value failed");
495         return 0;
496     }
497     if (unitType == COAP_MODULE_NAME_TYPE || unitType == COAP_DEVICE_ID_TYPE) {
498         if (outBuf[valueLen - 1] != '\0') {
499             DFINDER_LOGE(TAG, "uint type is %u but value is not end with 0", unitType);
500             return 0;
501         }
502     }
503     return valueLen;
504 }
505 
506 static uint16_t ParseServiceMsgFrame(const uint8_t *frame, size_t size, char *moduleName, char *deviceId,
507                                      uint8_t **msg)
508 {
509     if (frame == NULL || size == 0) {
510         return 0;
511     }
512     uint16_t frameLen = GetServiceMsgFrameLen(frame, size);
513     if (frameLen < sizeof(frameLen) + sizeof(CoapMsgUnit)) {
514         return 0;
515     }
516 
517     /* get modulename info */
518     uint16_t len = sizeof(frameLen);
519     uint16_t moduleNameLen = GetUnitInfo(frame + len, frameLen - len, (uint8_t *)moduleName,
520                                          NSTACKX_MAX_MODULE_NAME_LEN, COAP_MODULE_NAME_TYPE);
521     if (moduleNameLen == 0 || moduleNameLen + sizeof(CoapMsgUnit) >= frameLen - len) {
522         return 0;
523     }
524 
525     /* get deviceIdLen info */
526     len += moduleNameLen + sizeof(CoapMsgUnit);
527     uint16_t deviceIdLen = GetUnitInfo(frame + len, frameLen - len, (uint8_t *)deviceId,
528                                        NSTACKX_MAX_DEVICE_ID_LEN, COAP_DEVICE_ID_TYPE);
529     if (deviceIdLen == 0 || deviceIdLen + sizeof(CoapMsgUnit) >= frameLen - len) {
530         return 0;
531     }
532 
533     /* get msg info */
534     len += deviceIdLen + sizeof(CoapMsgUnit);
535     uint8_t *msgPtr = (uint8_t *)calloc(1U, frameLen - len);
536     if (msgPtr == NULL) {
537         return 0;
538     }
539     uint16_t msgLen = GetUnitInfo(frame + len, frameLen - len, msgPtr, frameLen - len, COAP_MSG_TYPE);
540     if (msgLen == 0) {
541         free(msgPtr);
542         return 0;
543     }
544     *msg = msgPtr;
545     return msgLen;
546 }
547 
548 static int32_t HndPostServiceMsgEx(coap_resource_t *resource, coap_session_t *session,
549     const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response)
550 {
551     Coverity_Tainted_Set((void *)request);
552 
553     (void)resource;
554     (void)session;
555     (void)query;
556     if (request == NULL || response == NULL) {
557         return NSTACKX_EFAILED;
558     }
559     char deviceId[NSTACKX_MAX_DEVICE_ID_LEN] = {0};
560     char moduleName[NSTACKX_MAX_MODULE_NAME_LEN] = {0};
561     uint8_t *msg = NULL;
562     const uint8_t *buf = NULL;
563     uint16_t msgLen;
564     size_t size;
565 
566     if (coap_get_data(request, &size, &buf) == 0 || size == 0 || size > COAP_RXBUFFER_SIZE) {
567         return NSTACKX_EFAILED;
568     }
569 
570     if (!RefreshMsgIdList(coap_pdu_get_mid(request))) {
571         DFINDER_LOGE(TAG, "repeated msg id");
572         return NSTACKX_EFAILED;
573     }
574 
575     DFINDER_LOGD(TAG, "handling post service msg request");
576     msgLen = ParseServiceMsgFrame(buf, size, moduleName, deviceId, &msg);
577     if (msgLen == 0) {
578         DFINDER_LOGD(TAG, "parse service msg frame error");
579         return NSTACKX_EFAILED;
580     }
581     const coap_address_t *addPtr = coap_session_get_addr_remote(session);
582     if (addPtr == NULL) {
583         DFINDER_LOGE(TAG, "coap session get remote addr failed");
584         free(msg);
585         return NSTACKX_EFAILED;
586     }
587     char srcIp[NSTACKX_MAX_IP_STRING_LEN] = {0};
588     if (inet_ntop(AF_INET, &((addPtr->addr).sin.sin_addr), srcIp, sizeof(srcIp)) == NULL) {
589         free(msg);
590         return NSTACKX_EFAILED;
591     }
592     NotifyMsgReceived(moduleName, deviceId, msg, msgLen, srcIp);
593 
594     coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
595     free(msg);
596     return NSTACKX_EOK;
597 }
598 
599 static void HndPostServiceMsg(coap_resource_t *resource, coap_session_t *session,
600     const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response)
601 {
602     if (HndPostServiceMsgEx(resource, session, request, query, response) != NSTACKX_EOK) {
603         IncStatistics(STATS_HANDLE_SERVICE_MSG_FAILED);
604     }
605 }
606 
607 static int32_t CoapPostServiceDiscoverEx(CoapCtxType *ctx)
608 {
609     char broadcastIp[NSTACKX_MAX_IP_STRING_LEN] = {0};
610     if (GetBroadcastIp(ctx->iface, broadcastIp, sizeof(broadcastIp)) != NSTACKX_EOK) {
611         DFINDER_LOGE(TAG, "get broadcast ip failed");
612         return NSTACKX_EFAILED;
613     }
614 
615     char discoverUri[COAP_URI_BUFFER_LENGTH] = {0};
616     if (sprintf_s(discoverUri, sizeof(discoverUri), "coap://%s/%s", broadcastIp, COAP_DEVICE_DISCOVER_URI) < 0) {
617         DFINDER_LOGE(TAG, "formate uri failed");
618         return NSTACKX_EFAILED;
619     }
620 
621     char *data = PrepareServiceDiscover(GetLocalIfaceIpStr(ctx->iface), NSTACKX_TRUE, GetLocalDeviceBusinessType());
622     if (data == NULL) {
623         DFINDER_LOGE(TAG, "prepare json failed");
624         return NSTACKX_EFAILED;
625     }
626 
627     int ret = CoapSendRequest(ctx, COAP_MESSAGE_NON, discoverUri, data, strlen(data) + 1);
628     cJSON_free(data);
629     return ret;
630 }
631 
632 static int32_t CoapPostServiceDiscover(void)
633 {
634     List *pos = NULL;
635     List *head = GetCoapContextList();
636     int successCnt = 0;
637     LIST_FOR_EACH(pos, head) {
638         int ret = CoapPostServiceDiscoverEx((CoapCtxType *)pos);
639         if (ret == NSTACKX_EOK) {
640             successCnt++;
641         }
642     }
643 
644     if (successCnt == 0) {
645         DFINDER_LOGE(TAG, "no iface send request");
646         IncStatistics(STATS_POST_SD_REQUEST_FAILED);
647         return NSTACKX_EFAILED;
648     }
649 
650     return NSTACKX_EOK;
651 }
652 
653 static uint32_t GetUserDefineInterval(uint32_t discoverCount)
654 {
655     if (discoverCount >= (g_coapMaxDiscoverCount - 1)) {
656         DFINDER_LOGD(TAG, "discover end");
657         return 0;
658     }
659     return g_coapIntervalArr[discoverCount];
660 }
661 
662 static uint32_t GetDiscoverInterval(uint32_t discoverCount)
663 {
664     switch (g_coapDiscoverType) {
665         case COAP_BROADCAST_TYPE_USER:
666             return g_coapUserDiscoverInterval;
667         case COAP_BROADCAST_TYPE_USER_DEFINE_INTERVAL:
668             return GetUserDefineInterval(discoverCount);
669         case COAP_BROADCAST_TYPE_DEFAULT:
670             return GetDefaultDiscoverInterval(discoverCount);
671         default:
672             return GetDefaultDiscoverInterval(discoverCount);
673     }
674 }
675 
676 static void CoapServiceDiscoverStop(void)
677 {
678     g_forceUpdate = NSTACKX_FALSE;
679     g_discoverCount = 0;
680     SetModeInfo(DISCOVER_MODE);
681 #ifdef DFINDER_SAVE_DEVICE_LIST
682     ClearRemoteDeviceListBackup();
683     DFINDER_LOGD(TAG, "clear device list backup");
684 #endif
685     g_coapDiscoverType = COAP_BROADCAST_TYPE_DEFAULT;
686     g_coapMaxDiscoverCount = COAP_DEFAULT_DISCOVER_COUNT;
687     /* Can call PostDeviceFindWrapper() to notify user if needed. */
688     g_userRequest = NSTACKX_FALSE;
689     // release interval array
690     if (g_coapIntervalArr != NULL) {
691         free(g_coapIntervalArr);
692         g_coapIntervalArr = NULL;
693     }
694 }
695 
696 static void CoapServiceDiscoverTimerHandle(void *argument)
697 {
698     uint32_t discoverInterval;
699 
700     (void)argument;
701 
702     if (g_discoverCount >= g_coapDiscoverTargetCount || !IsCoapContextReady()) {
703         IncStatistics(STATS_ABORT_SD);
704         CoapServiceDiscoverStop();
705         return;
706     }
707 
708     if (CoapPostServiceDiscover() != NSTACKX_EOK) {
709         DFINDER_LOGE(TAG, "failed when posting service discover request");
710         goto L_ERR_DISCOVER;
711     }
712 
713     /* Restart timer */
714     discoverInterval = GetDiscoverInterval(g_discoverCount);
715 
716     ++g_discoverCount;
717     if (TimerSetTimeout(g_discoverTimer, discoverInterval, NSTACKX_FALSE) != NSTACKX_EOK) {
718         DFINDER_LOGE(TAG, "failed to set timer for service discovery");
719         goto L_ERR_DISCOVER;
720     }
721     return;
722 
723 L_ERR_DISCOVER:
724     IncStatistics(STATS_ABORT_SD);
725     /* Abort service discover by not starting timer. */
726     DFINDER_LOGE(TAG, "abort service discovery, have tried %u request", g_discoverCount);
727     /* Reset g_discoverCount to allow new request from user. */
728     g_discoverCount = 0;
729     return;
730 }
731 
732 void CoapInitSubscribeModuleInner(void)
733 {
734     g_subscribeCount = 0;
735     return;
736 }
737 
738 void CoapSubscribeModuleInner(uint8_t isSubscribe)
739 {
740     if (isSubscribe && (g_subscribeCount < COAP_MAX_NUM_SUBSCRIBE_MODULE_COUNT)) {
741         g_subscribeCount++;
742     }
743     return;
744 }
745 
746 void CoapUnsubscribeModuleInner(uint8_t isUnsubscribe)
747 {
748     if (isUnsubscribe && (g_subscribeCount > 0)) {
749         g_subscribeCount--;
750     }
751 }
752 
753 static void SetCoapMaxDiscoverCount(void)
754 {
755     switch (g_coapDiscoverType) {
756         case COAP_BROADCAST_TYPE_DEFAULT:
757             g_coapMaxDiscoverCount = COAP_DEFAULT_DISCOVER_COUNT;
758             break;
759         case COAP_BROADCAST_TYPE_USER:
760         case COAP_BROADCAST_TYPE_USER_DEFINE_INTERVAL:
761             g_coapMaxDiscoverCount = g_coapUserMaxDiscoverCount;
762             break;
763         default:
764             g_coapMaxDiscoverCount = COAP_DEFAULT_DISCOVER_COUNT;
765             break;
766     }
767 }
768 
769 static void CoapServiceDiscoverFirstTime(void)
770 {
771     SetCoapMaxDiscoverCount();
772     g_coapDiscoverTargetCount = g_coapMaxDiscoverCount;
773     if (CoapPostServiceDiscover() != NSTACKX_EOK) {
774         DFINDER_LOGE(TAG, "failed to send service discover request");
775         return;
776     }
777 
778     uint32_t discoverInterval = GetDiscoverInterval(g_discoverCount);
779     if (TimerSetTimeout(g_discoverTimer, discoverInterval, NSTACKX_FALSE) != NSTACKX_EOK) {
780         DFINDER_LOGE(TAG, "failed to set timer when doing service discover");
781         return;
782     }
783     ++g_discoverCount;
784     DFINDER_LOGI(TAG, "first time for device discover.");
785 }
786 
787 void CoapServiceDiscoverInner(uint8_t userRequest)
788 {
789     if (!IsCoapContextReady()) {
790         IncStatistics(STATS_START_SD_FAILED);
791         DFINDER_LOGI(TAG, "Network not connected when discovery inner");
792         return;
793     }
794 
795     if (userRequest) {
796         g_userRequest = NSTACKX_TRUE;
797         g_forceUpdate = NSTACKX_TRUE;
798     }
799 
800     if (g_coapDiscoverTargetCount > 0 &&
801         g_discoverCount >= g_coapDiscoverTargetCount) {
802         g_discoverCount = 0;
803         SetModeInfo(DISCOVER_MODE);
804 #ifdef DFINDER_SAVE_DEVICE_LIST
805         ClearRemoteDeviceListBackup();
806         DFINDER_LOGW(TAG, "clear device list backup");
807 #endif
808         (void)TimerSetTimeout(g_discoverTimer, 0, NSTACKX_FALSE);
809     }
810 
811     if (g_discoverCount) {
812         /* Service discover is ongoing, return. */
813         return;
814     }
815 #ifdef DFINDER_SAVE_DEVICE_LIST
816     /* First discover */
817     BackupRemoteDeviceList();
818     DFINDER_LOGW(TAG, "clear device list when discovery inner");
819 #endif /* END OF DFINDER_SAVE_DEVICE_LIST */
820     SetModeInfo(DISCOVER_MODE);
821     CoapServiceDiscoverFirstTime();
822     return;
823 }
824 
825 void CoapServiceDiscoverInnerAn(uint8_t userRequest)
826 {
827     if (!IsCoapContextReady()) {
828         IncStatistics(STATS_START_SD_FAILED);
829         DFINDER_LOGI(TAG, "Network not connected when discovery inner AN");
830         return;
831     }
832 
833     if (userRequest) {
834         g_userRequest = NSTACKX_TRUE;
835     }
836 
837     if (g_discoverCount != 0) {
838         g_discoverCount = 0;
839         /* Service discover is ongoing, reset. */
840         (void)TimerSetTimeout(g_discoverTimer, 0, NSTACKX_FALSE);
841     }
842 
843     CoapServiceDiscoverFirstTime();
844     return;
845 }
846 
847 void CoapServiceDiscoverInnerConfigurable(uint8_t userRequest)
848 {
849     if (!IsCoapContextReady()) {
850         IncStatistics(STATS_START_SD_FAILED);
851         DFINDER_LOGI(TAG, "Network not connected when discovery configurable");
852         return;
853     }
854 
855     if (userRequest) {
856         g_userRequest = NSTACKX_TRUE;
857         g_forceUpdate = NSTACKX_TRUE;
858     }
859 
860     if (g_coapDiscoverTargetCount > 0 && g_discoverCount >= g_coapDiscoverTargetCount) {
861         g_discoverCount = 0;
862 #ifdef DFINDER_SAVE_DEVICE_LIST
863         ClearRemoteDeviceListBackup();
864         DFINDER_LOGW(TAG, "clear device list backup when discovery configurable");
865 #endif
866         (void)TimerSetTimeout(g_discoverTimer, 0, NSTACKX_FALSE);
867     }
868 
869     if (g_discoverCount != 0) {
870         g_discoverCount = 0;
871         /* Service discover is ongoing, return. */
872         (void)TimerSetTimeout(g_discoverTimer, 0, NSTACKX_FALSE);
873     } else {
874         /* First discover */
875 #ifdef DFINDER_SAVE_DEVICE_LIST
876         BackupRemoteDeviceList();
877         DFINDER_LOGW(TAG, "clear device list when discovery configurable");
878 #endif
879     }
880     CoapServiceDiscoverFirstTime();
881     return;
882 }
883 
884 void CoapServiceDiscoverStopInner(void)
885 {
886     (void)TimerSetTimeout(g_discoverTimer, 0, NSTACKX_FALSE);
887     CoapServiceDiscoverStop();
888     DFINDER_LOGI(TAG, "device discover inner stopped");
889 }
890 
891 uint8_t CoapDiscoverRequestOngoing(void)
892 {
893     return ((g_discoverCount > 0 && g_userRequest) || (g_subscribeCount > 0));
894 }
895 
896 static uint8_t *CreateServiceMsgFrame(const char *moduleName, const char *deviceId, const uint8_t *msg, uint32_t msgLen,
897                                       uint16_t *dataLen)
898 {
899     uint16_t frameLen, moduleNameUnitLen, deviceIdUnitLen, msgUnitLen, bufferLen;
900     uint8_t *frame = NULL;
901     uint16_t len = 0;
902     CoapMsgUnit *unit = NULL;
903     moduleNameUnitLen = sizeof(CoapMsgUnit) + strlen(moduleName) + 1;
904     deviceIdUnitLen = sizeof(CoapMsgUnit) + strlen(deviceId) + 1;
905     msgUnitLen = sizeof(CoapMsgUnit) + msgLen;
906     bufferLen = sizeof(frameLen) + moduleNameUnitLen + deviceIdUnitLen + msgUnitLen;
907     frameLen = htons(bufferLen);
908 
909     frame = (uint8_t *)calloc(1U, bufferLen);
910     if (frame == NULL) {
911         IncStatistics(STATS_CREATE_SERVICE_MSG_FAILED);
912         return NULL;
913     }
914     if (memcpy_s(frame, bufferLen, &frameLen, sizeof(frameLen)) != EOK) {
915         goto L_ERR_SEND_MSG;
916     }
917     len += sizeof(frameLen);
918 
919     unit = (CoapMsgUnit *)(frame + len);
920     unit->type = COAP_MODULE_NAME_TYPE;
921     unit->len = htons(moduleNameUnitLen - sizeof(CoapMsgUnit));
922     if (memcpy_s(unit->value, bufferLen - len - sizeof(CoapMsgUnit), moduleName, strlen(moduleName) + 1) != EOK) {
923         goto L_ERR_SEND_MSG;
924     }
925     len += moduleNameUnitLen;
926 
927     unit = (CoapMsgUnit *)(frame + len);
928     unit->type = COAP_DEVICE_ID_TYPE;
929     unit->len = htons(deviceIdUnitLen - sizeof(CoapMsgUnit));
930     if (memcpy_s(unit->value, bufferLen - len - sizeof(CoapMsgUnit), deviceId, strlen(deviceId) + 1) != EOK) {
931         goto L_ERR_SEND_MSG;
932     }
933     len += deviceIdUnitLen;
934 
935     unit = (CoapMsgUnit *)(frame + len);
936     unit->type = COAP_MSG_TYPE;
937     unit->len = htons(msgUnitLen - sizeof(CoapMsgUnit));
938     if (memcpy_s(unit->value, bufferLen - len - sizeof(CoapMsgUnit), msg, msgLen) != EOK) {
939         goto L_ERR_SEND_MSG;
940     }
941     *dataLen = bufferLen;
942     return frame;
943 L_ERR_SEND_MSG:
944     IncStatistics(STATS_CREATE_SERVICE_MSG_FAILED);
945     free(frame);
946     return NULL;
947 }
948 
949 int32_t CoapSendServiceMsg(MsgCtx *msgCtx, const char *remoteIpStr, const struct in_addr *remoteIp)
950 {
951     char uriBuffer[COAP_URI_BUFFER_LENGTH] = {0};
952     uint16_t dataLen = 0;
953 
954     CoapCtxType *ctx = LocalIfaceGetCoapCtxByRemoteIp(remoteIp, msgCtx->type);
955     if (ctx == NULL) {
956         DFINDER_LOGE(TAG, "can not find the local iface");
957         return NSTACKX_EFAILED;
958     }
959 
960     if (sprintf_s(uriBuffer, sizeof(uriBuffer), "coap://%s/" COAP_SERVICE_MSG_URI, remoteIpStr) < 0) {
961         DFINDER_LOGE(TAG, "sprintf_s for coap service msg uri failed");
962         return NSTACKX_EFAILED;
963     }
964 
965     char *data = (char *)CreateServiceMsgFrame(msgCtx->moduleName,
966         GetLocalDeviceId(), msgCtx->data, msgCtx->len, &dataLen);
967     if (data == NULL) {
968         DFINDER_LOGE(TAG, "failed to prepare msg data");
969         return NSTACKX_EFAILED;
970     }
971 
972     int ret = CoapSendRequest(ctx, COAP_MESSAGE_CON, uriBuffer, data, dataLen);
973     free(data);
974     return ret;
975 }
976 
977 static void CoapRecvRecountTimerHandle(void *argument)
978 {
979     (void)argument;
980     if (g_recvDiscoverMsgNum > COAP_DISVOCER_MAX_RATE) {
981         DFINDER_LOGI(TAG, "received %u discover msg in this interval", g_recvDiscoverMsgNum);
982     }
983     g_recvDiscoverMsgNum = 0;
984     return;
985 }
986 
987 static int32_t CoapPostServiceNotificationEx(CoapCtxType *ctx)
988 {
989     char broadcastIp[NSTACKX_MAX_IP_STRING_LEN] = {0};
990     if (GetBroadcastIp(ctx->iface, broadcastIp, sizeof(broadcastIp)) != NSTACKX_EOK) {
991         DFINDER_LOGE(TAG, "get %s broadcast ip failed, please check nic status with ifconfig or reconnect to network",
992             GetLocalIfaceName(ctx->iface));
993         return NSTACKX_EFAILED;
994     }
995     char notificationUri[COAP_URI_BUFFER_LENGTH] = {0};
996     if (sprintf_s(notificationUri, sizeof(notificationUri),
997         "coap://%s/%s", broadcastIp, COAP_SERVICE_NOTIFICATION_URI) < 0) {
998         DFINDER_LOGE(TAG, "format coap service notification uri failed");
999         return NSTACKX_EFAILED;
1000     }
1001     char *data = PrepareServiceNotification();
1002     if (data == NULL) {
1003         DFINDER_LOGE(TAG, "prepare service notification data fail");
1004         return NSTACKX_EFAILED;
1005     }
1006     int32_t ret = CoapSendRequest(ctx, COAP_MESSAGE_NON, notificationUri, data, strlen(data) + 1);
1007     cJSON_free(data);
1008     return ret;
1009 }
1010 
1011 static int32_t CoapPostServiceNotification(void)
1012 {
1013     List *pos = NULL;
1014     List *head = GetCoapContextList();
1015     int successCnt = 0;
1016     LIST_FOR_EACH(pos, head) {
1017         int32_t ret = CoapPostServiceNotificationEx((CoapCtxType *)pos);
1018         if (ret == NSTACKX_EOK) {
1019             successCnt++;
1020         }
1021     }
1022     if (successCnt == 0) {
1023         DFINDER_LOGE(TAG, "no iface to send request, coap context ready: %d", IsCoapContextReady());
1024         IncStatistics(STATS_POST_SD_REQUEST_FAILED);
1025         return NSTACKX_EFAILED;
1026     }
1027     return NSTACKX_EOK;
1028 }
1029 
1030 static inline uint16_t GetNextNotificationInterval(uint8_t runCnt)
1031 {
1032     return (runCnt >= g_notificationTargetCnt) ? 0 : g_notificationIntervals[runCnt];
1033 }
1034 
1035 static void ResetNotificationConfig(void)
1036 {
1037     g_notificationRunCnt = 0;
1038     g_notificationTargetCnt = 0;
1039     if (g_notificationIntervals != NULL) {
1040         free(g_notificationIntervals);
1041         g_notificationIntervals = NULL;
1042     }
1043 }
1044 
1045 void CoapServiceNotificationStop(void)
1046 {
1047     (void)TimerSetTimeout(g_notificationTimer, 0, NSTACKX_FALSE);
1048     ResetNotificationConfig();
1049     DFINDER_LOGI(TAG, "caller stop send notifications, reset run cnt, target cnt all to 0");
1050 }
1051 
1052 static void CoapServiceNotificationTimerHandle(void *argument)
1053 {
1054     (void)argument;
1055     if (!IsCoapContextReady()) {
1056         DFINDER_LOGE(TAG, "coap context not ready, check nic status");
1057         return;
1058     }
1059     if (CoapPostServiceNotification() != NSTACKX_EOK) {
1060         DFINDER_LOGE(TAG, "failed when post service notification");
1061         goto L_ERR_NOTIFICATION;
1062     }
1063     DFINDER_LOGI(TAG, "the %hhu time for sending notification", g_notificationRunCnt + 1);
1064     uint16_t nextInterval = GetNextNotificationInterval(++g_notificationRunCnt);
1065     if (TimerSetTimeout(g_notificationTimer, nextInterval, NSTACKX_FALSE) != NSTACKX_EOK) {
1066         DFINDER_LOGE(TAG, "failed to set timer for service notification");
1067         goto L_ERR_NOTIFICATION;
1068     }
1069     return;
1070 L_ERR_NOTIFICATION:
1071     DFINDER_LOGE(TAG, "abort notification, tried %hhu request, now reset notification cnt to 0", g_notificationRunCnt);
1072     g_notificationRunCnt = 0;
1073 }
1074 
1075 static int32_t HndPostServiceNotificationEx(const coap_pdu_t *request)
1076 {
1077     size_t size = 0;
1078     const uint8_t *buf = NULL;
1079     if (coap_get_data(request, &size, &buf) == 0 || size == 0 || size > COAP_RXBUFFER_SIZE) {
1080         DFINDER_LOGE(TAG, "coap_get_data fail, size: %zu, coap rx buffer size: %d", size, COAP_RXBUFFER_SIZE);
1081         return NSTACKX_EFAILED;
1082     }
1083     NSTACKX_NotificationConfig *notification =
1084         (NSTACKX_NotificationConfig *)calloc(1, sizeof(NSTACKX_NotificationConfig));
1085     if (notification == NULL) {
1086         DFINDER_LOGE(TAG, "calloc for notification fail, size wanted: %zu", sizeof(NSTACKX_NotificationConfig));
1087         return NSTACKX_ENOMEM;
1088     }
1089     notification->msg = (char *)calloc(NSTACKX_MAX_NOTIFICATION_DATA_LEN, sizeof(char));
1090     if (notification->msg == NULL) {
1091         DFINDER_LOGE(TAG, "calloc for notification msg failed");
1092         free(notification);
1093         return NSTACKX_ENOMEM;
1094     }
1095     if (GetServiceNotificationInfo(buf, size, notification) != NSTACKX_EOK) {
1096         free(notification->msg);
1097         free(notification);
1098         return NSTACKX_EFAILED;
1099     }
1100     NotificationReceived(notification);
1101     free(notification->msg);
1102     free(notification);
1103     return NSTACKX_EOK;
1104 }
1105 
1106 static void HndPostServiceNotification(coap_resource_t *resource, coap_session_t *session,
1107     const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response)
1108 {
1109     (void)resource;
1110     (void)query;
1111     (void)session;
1112     (void)response;
1113 
1114     if (request == NULL) {
1115         DFINDER_LOGW(TAG, "request pdu is null, return");
1116         return;
1117     }
1118 #ifdef DFINDER_SUPPORT_SET_SCREEN_STATUS
1119     if (!g_isScreenOn) {
1120         DFINDER_LOGD(TAG, "device screen is off, ignore pdu received");
1121         return;
1122     }
1123 #endif
1124     DFINDER_LOGI(TAG, "recv coap notification pdu mid: %d", coap_pdu_get_mid(request));
1125 
1126     if (HndPostServiceNotificationEx(request) != NSTACKX_EOK) {
1127         DFINDER_LOGE(TAG, " hnd post service notificatioin failed for pdu mid: %d", coap_pdu_get_mid(request));
1128     }
1129 }
1130 
1131 int32_t CoapInitResources(coap_context_t *ctx)
1132 {
1133     coap_resource_t *r =
1134         coap_resource_init(coap_make_str_const(COAP_DEVICE_DISCOVER_URI), g_resourceFlags);
1135     if (r == NULL) {
1136         DFINDER_LOGE(TAG, "coap resource init discover failed");
1137         return NSTACKX_ENOMEM;
1138     }
1139     coap_register_request_handler(r, COAP_REQUEST_POST, HndPostServiceDiscover);
1140     coap_resource_set_get_observable(r, NSTACKX_TRUE);
1141     coap_add_resource(ctx, r);
1142 
1143     coap_resource_t *msg =
1144         coap_resource_init(coap_make_str_const(COAP_SERVICE_MSG_URI), 0);
1145     if (msg == NULL) {
1146         DFINDER_LOGE(TAG, "coap resource init msg failed");
1147         (void)coap_delete_resource(ctx, r);
1148         return NSTACKX_ENOMEM;
1149     }
1150     coap_register_request_handler(msg, COAP_REQUEST_POST, HndPostServiceMsg);
1151     coap_add_resource(ctx, msg);
1152 
1153     coap_resource_t *notification = coap_resource_init(coap_make_str_const(COAP_SERVICE_NOTIFICATION_URI), 0);
1154     if (notification == NULL) {
1155         DFINDER_LOGE(TAG, "coap_resource_init for service notification failed");
1156         (void)coap_delete_resource(ctx, r);
1157         (void)coap_delete_resource(ctx, msg);
1158         return NSTACKX_ENOMEM;
1159     }
1160     coap_register_request_handler(notification, COAP_REQUEST_POST, HndPostServiceNotification);
1161     coap_add_resource(ctx, notification);
1162 
1163     return NSTACKX_EOK;
1164 }
1165 
1166 int32_t CoapDiscoverInit(EpollDesc epollfd)
1167 {
1168     if (g_recvRecountTimer == NULL) {
1169         g_recvRecountTimer = TimerStart(epollfd, COAP_RECV_COUNT_INTERVAL, NSTACKX_TRUE,
1170                                         CoapRecvRecountTimerHandle, NULL);
1171     }
1172     if (g_recvRecountTimer == NULL) {
1173         DFINDER_LOGE(TAG, "failed to start timer for receive discover message recount");
1174         return NSTACKX_EFAILED;
1175     }
1176 
1177     if (g_discoverTimer == NULL) {
1178         g_discoverTimer = TimerStart(epollfd, 0, NSTACKX_FALSE, CoapServiceDiscoverTimerHandle, NULL);
1179     }
1180     if (g_discoverTimer == NULL) {
1181         DFINDER_LOGE(TAG, "failed to start timer for service discover");
1182         TimerDelete(g_recvRecountTimer);
1183         g_recvRecountTimer = NULL;
1184         return NSTACKX_EFAILED;
1185     }
1186 
1187     if (g_notificationTimer == NULL) {
1188         g_notificationTimer = TimerStart(epollfd, 0, NSTACKX_FALSE, CoapServiceNotificationTimerHandle, NULL);
1189     }
1190     if (g_notificationTimer == NULL) {
1191         DFINDER_LOGE(TAG, "failed to start timer for service notification");
1192         TimerDelete(g_recvRecountTimer);
1193         g_recvRecountTimer = NULL;
1194         TimerDelete(g_discoverTimer);
1195         g_discoverTimer = NULL;
1196         return NSTACKX_EFAILED;
1197     }
1198 
1199     g_msgIdList = (MsgIdList *)calloc(1U, sizeof(MsgIdList));
1200     if (g_msgIdList == NULL) {
1201         DFINDER_LOGE(TAG, "message Id record list calloc error");
1202         TimerDelete(g_discoverTimer);
1203         g_discoverTimer = NULL;
1204         TimerDelete(g_recvRecountTimer);
1205         g_recvRecountTimer = NULL;
1206         TimerDelete(g_notificationTimer);
1207         g_notificationTimer = NULL;
1208         return NSTACKX_EFAILED;
1209     }
1210 
1211     g_msgIdList->startIdx = COAP_MAX_MSGID_RESERVE_NUM;
1212     g_msgIdList->endIdx = COAP_MAX_MSGID_RESERVE_NUM;
1213     g_userRequest = NSTACKX_FALSE;
1214     g_forceUpdate = NSTACKX_FALSE;
1215     g_recvDiscoverMsgNum = 0;
1216     g_subscribeCount = 0;
1217     g_discoverCount = 0;
1218     g_notificationRunCnt = 0;
1219     return NSTACKX_EOK;
1220 }
1221 
1222 void CoapDiscoverDeinit(void)
1223 {
1224     if (g_discoverTimer != NULL) {
1225         TimerDelete(g_discoverTimer);
1226         g_discoverTimer = NULL;
1227     }
1228     if (g_recvRecountTimer != NULL) {
1229         TimerDelete(g_recvRecountTimer);
1230         g_recvRecountTimer = NULL;
1231     }
1232     if (g_notificationTimer != NULL) {
1233         TimerDelete(g_notificationTimer);
1234         g_notificationTimer = NULL;
1235     }
1236     if (g_msgIdList != NULL) {
1237         free(g_msgIdList);
1238         g_msgIdList = NULL;
1239     }
1240     if (g_coapIntervalArr != NULL) {
1241         free(g_coapIntervalArr);
1242         g_coapIntervalArr = NULL;
1243     }
1244     ResetNotificationConfig();
1245 }
1246 
1247 void ResetCoapDiscoverTaskCount(uint8_t isBusy)
1248 {
1249     if (g_discoverTimer != NULL) {
1250         if (isBusy) {
1251             DFINDER_LOGI(TAG, "in busy state: g_discoverTimer task count %llu", g_discoverTimer->task.count);
1252         }
1253         g_discoverTimer->task.count = 0;
1254     }
1255     if (g_recvRecountTimer != NULL) {
1256         if (isBusy) {
1257             DFINDER_LOGI(TAG, "in busy state: g_recvRecountTimer task count %llu",
1258                          g_recvRecountTimer->task.count);
1259         }
1260         g_recvRecountTimer->task.count = 0;
1261     }
1262 }
1263 
1264 void SetCoapDiscoverType(CoapBroadcastType type)
1265 {
1266     g_coapDiscoverType = (uint32_t)type;
1267 }
1268 
1269 void SetCoapUserDiscoverInfo(uint32_t advCount, uint32_t advDuration)
1270 {
1271     g_coapUserMaxDiscoverCount = advCount;
1272     if (advCount != 0) {
1273         g_coapUserDiscoverInterval = advDuration / advCount;
1274     }
1275     DFINDER_LOGD(TAG, "SetCoapUserDiscoverInfo advCount %u, interval %u",
1276         g_coapUserMaxDiscoverCount, g_coapUserDiscoverInterval);
1277 }
1278 
1279 int32_t SetCoapDiscConfig(const DFinderDiscConfig *discConfig)
1280 {
1281     uint32_t *tmp = (uint32_t *)malloc(discConfig->intervalArrLen * sizeof(uint32_t));
1282     if (tmp != NULL) {
1283         if (g_coapIntervalArr != NULL) {
1284             free(g_coapIntervalArr);
1285         }
1286         g_coapIntervalArr = tmp;
1287         for (size_t i = 0; i < discConfig->intervalArrLen; ++i) {
1288             g_coapIntervalArr[i] = (discConfig->bcastInterval)[i];
1289         }
1290         // add 1: first broadcast starts immediately
1291         g_coapUserMaxDiscoverCount = discConfig->intervalArrLen + 1;
1292         return NSTACKX_EOK;
1293     }
1294     DFINDER_LOGE(TAG, "malloc for user define interval array failed");
1295     if (g_coapIntervalArr != NULL) {
1296         DFINDER_LOGD(TAG, "going to use last interval config");
1297         return NSTACKX_EOK;
1298     }
1299     DFINDER_LOGE(TAG, "failed to use last interval config");
1300     return NSTACKX_EFAILED;
1301 }
1302 
1303 static int32_t SendDiscoveryRspEx(CoapCtxType *ctx, const NSTACKX_ResponseSettings *responseSettings)
1304 {
1305     char remoteUrl[NSTACKX_MAX_URI_BUFFER_LENGTH] = {0};
1306     char host[NSTACKX_MAX_IP_STRING_LEN] = {0};
1307 
1308     if (SetLocalDeviceBusinessData(responseSettings->businessData, NSTACKX_TRUE) != NSTACKX_EOK) {
1309         return NSTACKX_EFAILED;
1310     }
1311     DFINDER_LOGD(TAG, "response settings with business type: %hu", responseSettings->businessType);
1312 
1313     if (strncpy_s(host, sizeof(host), responseSettings->remoteIp, strlen(responseSettings->remoteIp)) != EOK) {
1314         DFINDER_LOGE(TAG, "discoveryRsp remoteIp copy error");
1315         return NSTACKX_EFAILED;
1316     }
1317     if (sprintf_s(remoteUrl, sizeof(remoteUrl), "coap://%s/" COAP_DEVICE_DISCOVER_URI, host) < 0) {
1318         DFINDER_LOGE(TAG, "failed to get discoveryRsp remoteUrl");
1319         return NSTACKX_EFAILED;
1320     }
1321     IncreaseSequenceNumber(NSTACKX_FALSE);
1322     return CoapResponseService(ctx, remoteUrl, responseSettings->businessType);
1323 }
1324 
1325 void SendDiscoveryRsp(const NSTACKX_ResponseSettings *responseSettings)
1326 {
1327     CoapCtxType *ctx = LocalIfaceGetCoapCtx(responseSettings->localNetworkName);
1328     if (ctx == NULL) {
1329         DFINDER_LOGE(TAG, "local iface get coap context return null");
1330         IncStatistics(STATS_SEND_SD_RESPONSE_FAILED);
1331         return;
1332     }
1333 
1334     if (SendDiscoveryRspEx(ctx, responseSettings) != NSTACKX_EOK) {
1335         IncStatistics(STATS_SEND_SD_RESPONSE_FAILED);
1336     }
1337 }
1338 
1339 int32_t LocalizeNotificationInterval(const uint16_t *intervals, const uint8_t intervalLen)
1340 {
1341     uint16_t *tmp = (uint16_t *)calloc(intervalLen, sizeof(uint16_t));
1342     if (tmp != NULL) {
1343         if (g_notificationIntervals != NULL) {
1344             free(g_notificationIntervals);
1345         }
1346         g_notificationIntervals = tmp;
1347         for (size_t i = 0; i < intervalLen; ++i) {
1348             g_notificationIntervals[i] = intervals[i];
1349         }
1350         g_notificationTargetCnt = intervalLen;
1351         return NSTACKX_EOK;
1352     }
1353     DFINDER_LOGW(TAG, "calloc for notification intervals fail, interval len %hhu", intervalLen);
1354     if (g_notificationIntervals != NULL) {
1355         DFINDER_LOGW(TAG, "going to use last success notification config");
1356         return NSTACKX_EOK;
1357     }
1358     DFINDER_LOGE(TAG, "set notification intervals fail and can not use last success config");
1359     return NSTACKX_EFAILED;
1360 }
1361 
1362 static void CoapServiceNotificationFirstTime(void)
1363 {
1364     if (CoapPostServiceNotification() != NSTACKX_EOK) {
1365         DFINDER_LOGE(TAG, "failed to send service notification first time");
1366         return;
1367     }
1368 
1369     uint16_t nextInterval = GetNextNotificationInterval(++g_notificationRunCnt);
1370     if (TimerSetTimeout(g_notificationTimer, nextInterval, NSTACKX_FALSE) != NSTACKX_EOK) {
1371         DFINDER_LOGE(TAG, "failed to set timer when doing service notification");
1372         return;
1373     }
1374     DFINDER_LOGI(TAG, "first time for service notification");
1375 }
1376 
1377 void CoapServiceNotification(void)
1378 {
1379     if (!IsCoapContextReady()) {
1380         DFINDER_LOGW(TAG, "no coap ctx inited, please check nic info or reconnected");
1381         return;
1382     }
1383     if (g_notificationRunCnt != 0) {
1384         DFINDER_LOGI(TAG, "reset notification run cnt to 0, run cnt: %hhu, target cnt: %hhu",
1385             g_notificationRunCnt, g_notificationTargetCnt);
1386         g_notificationRunCnt = 0;
1387         (void)TimerSetTimeout(g_notificationTimer, 0, NSTACKX_FALSE);
1388     }
1389     CoapServiceNotificationFirstTime();
1390 }
1391