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