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_app.h"
17
18 #include <errno.h>
19 #include <netdb.h>
20 #include <securec.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include "cJSON.h"
25 #include "coap_adapter.h"
26 #include "coap_discover.h"
27 #include "json_payload.h"
28 #include "lwip/sockets.h"
29 #include "nstackx_device.h"
30 #include "nstackx_epoll.h"
31 #include "nstackx_error.h"
32 #include "nstackx_dfinder_log.h"
33 #include "sys_util.h"
34 #include "nstackx_statistics.h"
35 #include "nstackx_device_local.h"
36
37 #define TAG "nStackXCoAP"
38
39 typedef struct {
40 int32_t cliendFd;
41 struct sockaddr_in dstAddr;
42 } SocketInfo;
43
44 typedef struct CoapRequest {
45 const char *remoteUrl;
46 char *data;
47 size_t dataLength;
48 const char *remoteIp;
49 } CoapRequest;
50
51 static EpollTask g_task;
52 static uint8_t g_ctxSocketErrFlag = NSTACKX_FALSE;
53 static uint64_t g_socketEventNum[SOCKET_END_EVENT];
54
55 static CoapCtxType g_coapCtx;
56
IsCoapContextReady(void)57 bool IsCoapContextReady(void)
58 {
59 return g_coapCtx.inited;
60 }
61
CoapGetLocalIfaceName(void)62 const char *CoapGetLocalIfaceName(void)
63 {
64 if (g_coapCtx.inited) {
65 return GetLocalIfaceName(g_coapCtx.iface);
66 }
67
68 return NULL;
69 }
70
CoapGetCoapCtxType(void)71 CoapCtxType *CoapGetCoapCtxType(void)
72 {
73 if (g_coapCtx.inited) {
74 return &g_coapCtx;
75 }
76
77 return NULL;
78 }
79
IsLoopBackPacket(struct sockaddr_in * remoteAddr)80 static bool IsLoopBackPacket(struct sockaddr_in *remoteAddr)
81 {
82 if (!g_coapCtx.inited) {
83 return false;
84 }
85 struct in_addr *localAddr = GetLocalIfaceIp(g_coapCtx.iface);
86 if (remoteAddr->sin_addr.s_addr != localAddr->s_addr) {
87 return false;
88 }
89 return true;
90 }
91
HandleReadEvent(int32_t fd)92 static void HandleReadEvent(int32_t fd)
93 {
94 uint8_t *recvBuffer = calloc(1, COAP_MAX_PDU_SIZE + 1);
95 if (recvBuffer == NULL) {
96 return;
97 }
98 struct sockaddr_in remoteAddr = {0};
99 socklen_t len = sizeof(struct sockaddr_in);
100 ssize_t nRead = recvfrom(fd, recvBuffer, COAP_MAX_PDU_SIZE, 0, (struct sockaddr *)&remoteAddr, &len);
101 if ((nRead == 0) || (nRead < 0 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)) {
102 IncStatistics(STATS_SOCKET_ERROR);
103 free(recvBuffer);
104 DFINDER_LOGE(TAG, "receive from remote packet failed");
105 return;
106 }
107
108 if (IsLoopBackPacket(&remoteAddr)) {
109 IncStatistics(STATS_DROP_LOOPBACK_PKT);
110 free(recvBuffer);
111 return;
112 }
113
114 CoapPacket decodePacket;
115 (void)memset_s(&decodePacket, sizeof(CoapPacket), 0, sizeof(CoapPacket));
116 decodePacket.protocol = COAP_UDP;
117 CoapSoftBusDecode(&decodePacket, recvBuffer, nRead);
118 HndPostServiceDiscover(&decodePacket);
119 free(recvBuffer);
120 }
121
CoapCreateUdpClientEx(const struct sockaddr_in * sockAddr,uint8_t isBroadCast)122 static int32_t CoapCreateUdpClientEx(const struct sockaddr_in *sockAddr, uint8_t isBroadCast)
123 {
124 (void)sockAddr;
125 int32_t fd = socket(AF_INET, SOCK_DGRAM, 0);
126 if (fd < 0) {
127 DFINDER_LOGE(TAG, "create socket failed, errno = %d", fd);
128 return NSTACKX_EFAILED;
129 }
130
131 int32_t optVal = 1;
132 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optVal, sizeof(optVal)) != 0) {
133 DFINDER_LOGE(TAG, "set sock opt failed, errno = %d", errno);
134 goto CLOSE_FD;
135 }
136
137 struct sockaddr_in localAddr;
138 (void)memset_s(&localAddr, sizeof(localAddr), 0, sizeof(localAddr));
139 localAddr.sin_family = AF_INET;
140 localAddr.sin_addr.s_addr = GetLocalIfaceIp(g_coapCtx.iface)->s_addr;
141 localAddr.sin_port = htons(COAP_SRV_DEFAULT_PORT);
142
143 if (bind(fd, (struct sockaddr *)&localAddr, sizeof(struct sockaddr_in)) == -1) {
144 DFINDER_LOGE(TAG, "bind local addr failed, errno = %d", errno);
145 goto CLOSE_FD;
146 }
147
148 if (isBroadCast && setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optVal, sizeof(optVal)) != 0) {
149 DFINDER_LOGE(TAG, "set sock opt broadcast failed, errno = %d", errno);
150 goto CLOSE_FD;
151 }
152
153 return fd;
154 CLOSE_FD:
155 lwip_close(fd);
156 return NSTACKX_EFAILED;
157 }
158
CoapCreateUdpClient(const struct sockaddr_in * sockAddr,uint8_t isBroadCast)159 static int32_t CoapCreateUdpClient(const struct sockaddr_in *sockAddr, uint8_t isBroadCast)
160 {
161 int32_t ret = CoapCreateUdpClientEx(sockAddr, isBroadCast);
162 if (ret == NSTACKX_EFAILED) {
163 IncStatistics(STATS_CREATE_CLIENT_FAILED);
164 }
165 return ret;
166 }
167
CoapSocketSend(const SocketInfo * socket,const uint8_t * buffer,size_t length)168 static int32_t CoapSocketSend(const SocketInfo *socket, const uint8_t *buffer, size_t length)
169 {
170 if (buffer == NULL || socket == NULL) {
171 return NSTACKX_EFAILED;
172 }
173
174 socklen_t dstAddrLen = sizeof(struct sockaddr_in);
175 int32_t ret = sendto(socket->cliendFd, buffer, length, 0, (struct sockaddr *)&socket->dstAddr, dstAddrLen);
176 if (ret != length) {
177 IncStatistics(STATS_SOCKET_ERROR);
178 DFINDER_LOGE(TAG, "sendto failed, ret = %d, errno = %d", ret, errno);
179 }
180 return ret;
181 }
182
CoapSendMsg(const CoapRequest * coapRequest,uint8_t isBroadcast)183 static int32_t CoapSendMsg(const CoapRequest *coapRequest, uint8_t isBroadcast)
184 {
185 if (coapRequest == NULL || coapRequest->remoteIp == NULL) {
186 return NSTACKX_EFAILED;
187 }
188
189 struct sockaddr_in sockAddr = {0};
190 sockAddr.sin_addr.s_addr = inet_addr(coapRequest->remoteIp);
191 sockAddr.sin_port = htons(COAP_SRV_DEFAULT_PORT);
192 sockAddr.sin_family = AF_INET;
193
194 int32_t fd = CoapCreateUdpClient(&sockAddr, isBroadcast);
195 if (fd < 0) {
196 DFINDER_LOGE(TAG, "Create coap udp client failed");
197 return NSTACKX_EFAILED;
198 }
199
200 SocketInfo socket = {0};
201 socket.cliendFd = fd;
202 socket.dstAddr = sockAddr;
203 if (CoapSocketSend(&socket, (uint8_t *)coapRequest->data, coapRequest->dataLength) == -1) {
204 DFINDER_LOGE(TAG, "Coap socket send response message failed");
205 lwip_close(fd);
206 return NSTACKX_EFAILED;
207 }
208 lwip_close(fd);
209 return NSTACKX_EOK;
210 }
211
CoapSendMessageEx(const CoapBuildParam * param,uint8_t isBroadcast,uint8_t businessType,bool isAckMsg)212 static int32_t CoapSendMessageEx(const CoapBuildParam *param, uint8_t isBroadcast, uint8_t businessType, bool isAckMsg)
213 {
214 if (param == NULL) {
215 DFINDER_LOGE(TAG, "coap build param is null");
216 return NSTACKX_EFAILED;
217 }
218
219 int32_t ret;
220 char *payload = NULL;
221 CoapRequest coapRequest = {0};
222 coapRequest.remoteIp = param->remoteIp;
223 CoapReadWriteBuffer sndPktBuff = {0};
224 sndPktBuff.readWriteBuf = (char *)calloc(COAP_MAX_PDU_SIZE, sizeof(char));
225 if (sndPktBuff.readWriteBuf == NULL) {
226 DFINDER_LOGE(TAG, "mem calloc failed, size = %u", COAP_MAX_PDU_SIZE * sizeof(char));
227 return NSTACKX_EFAILED;
228 }
229 sndPktBuff.size = COAP_MAX_PDU_SIZE;
230 sndPktBuff.len = 0;
231 if (!isAckMsg) {
232 payload = PrepareServiceDiscover(GetLocalIfaceIpStr(g_coapCtx.iface), isBroadcast, businessType);
233 if (payload == NULL) {
234 free(sndPktBuff.readWriteBuf);
235 DFINDER_LOGE(TAG, "prepare payload data failed");
236 return NSTACKX_EFAILED;
237 }
238 }
239
240 ret = BuildCoapPkt(param, payload, &sndPktBuff, isAckMsg);
241 if (payload != NULL) {
242 cJSON_free(payload);
243 payload = NULL;
244 }
245 if (ret != DISCOVERY_ERR_SUCCESS) {
246 free(sndPktBuff.readWriteBuf);
247 sndPktBuff.readWriteBuf = NULL;
248 DFINDER_LOGE(TAG, "build coap packet failed, ret = %d", ret);
249 return ret;
250 }
251 coapRequest.data = sndPktBuff.readWriteBuf;
252 coapRequest.dataLength = sndPktBuff.len;
253 ret = CoapSendMsg(&coapRequest, isBroadcast);
254 free(sndPktBuff.readWriteBuf);
255 sndPktBuff.readWriteBuf = NULL;
256 return ret;
257 }
258
CoapSendMessage(const CoapBuildParam * param,uint8_t isBroadcast,uint8_t businessType,bool isAckMsg)259 int32_t CoapSendMessage(const CoapBuildParam *param, uint8_t isBroadcast, uint8_t businessType, bool isAckMsg)
260 {
261 if (!g_coapCtx.inited) {
262 DFINDER_LOGE(TAG, "coap context not inited");
263 return NSTACKX_EFAILED;
264 }
265
266 int32_t ret = CoapSendMessageEx(param, isBroadcast, businessType, isAckMsg);
267 if (ret != DISCOVERY_ERR_SUCCESS) {
268 IncStatistics(STATS_SEND_MSG_FAILED);
269 }
270 return ret;
271 }
272
DeRegisteCoAPEpollTaskCtx(void)273 static void DeRegisteCoAPEpollTaskCtx(void)
274 {
275 DeRegisterEpollTask(&g_task);
276 CloseDesc(g_task.taskfd);
277 g_task.taskfd = -1;
278 }
279
DeRegisterCoAPEpollTask(void)280 static void DeRegisterCoAPEpollTask(void)
281 {
282 if (g_ctxSocketErrFlag) {
283 DFINDER_LOGI(TAG, "error of g_ctx's socket occurred and destroy g_ctx");
284 g_ctxSocketErrFlag = NSTACKX_FALSE;
285 NotifyDFinderMsgRecver(DFINDER_ON_INNER_ERROR);
286 } else {
287 DeRegisteCoAPEpollTaskCtx();
288 }
289 }
290
CoAPEpollReadHandle(void * data)291 static void CoAPEpollReadHandle(void *data)
292 {
293 if (data == NULL) {
294 return;
295 }
296 EpollTask *task = (EpollTask*)data;
297 if (task->taskfd < 0) {
298 return;
299 }
300 g_socketEventNum[SOCKET_READ_EVENT]++;
301 HandleReadEvent(task->taskfd);
302 }
303
CoAPEpollErrorHandle(void * data)304 static void CoAPEpollErrorHandle(void *data)
305 {
306 if (data == NULL) {
307 return;
308 }
309 EpollTask *task = data;
310 if (task->taskfd < 0) {
311 return;
312 }
313 IncStatistics(STATS_SOCKET_ERROR);
314 g_socketEventNum[SOCKET_ERROR_EVENT]++;
315 g_ctxSocketErrFlag = NSTACKX_TRUE;
316 DFINDER_LOGE(TAG, "coap socket error occurred and close it");
317 DeRegisterCoAPEpollTask();
318 CloseDesc(task->taskfd);
319 task->taskfd = -1;
320 }
321
CoapCreateUdpServer(const char * ipAddr,int32_t port)322 static int32_t CoapCreateUdpServer(const char *ipAddr, int32_t port)
323 {
324 struct sockaddr_in localAddr;
325 socklen_t len = sizeof(localAddr);
326 int32_t fd = socket(AF_INET, SOCK_DGRAM, 0);
327 if (fd < 0) {
328 return -1;
329 }
330
331 int32_t optVal = 1;
332 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optVal, sizeof(optVal)) != 0) {
333 DFINDER_LOGE(TAG, "set sock opt failed, errno = %d", errno);
334 lwip_close(fd);
335 return -1;
336 }
337
338 (void)memset_s(&localAddr, sizeof(localAddr), 0, sizeof(localAddr));
339 localAddr.sin_family = AF_INET;
340 localAddr.sin_port = htons(port);
341 localAddr.sin_addr.s_addr = inet_addr(ipAddr);
342
343 if (bind(fd, (struct sockaddr *)&localAddr, len) == -1) {
344 lwip_close(fd);
345 return -1;
346 }
347
348 if (getsockname(fd, (struct sockaddr *)&localAddr, &len) == -1) {
349 lwip_close(fd);
350 return -1;
351 }
352 return fd;
353 }
354
CoapServerInit(const struct in_addr * ip,void * iface)355 CoapCtxType *CoapServerInit(const struct in_addr *ip, void *iface)
356 {
357 if (g_coapCtx.inited) {
358 return &g_coapCtx;
359 }
360
361 EpollDesc epollFd = GetMainLoopEpollFd();
362 if (!IsEpollDescValid(epollFd)) {
363 DFINDER_LOGE(TAG, "epoll is invalid!");
364 return NULL;
365 }
366
367 int32_t listenFd = CoapCreateUdpServer(COAP_SRV_DEFAULT_ADDR, COAP_SRV_DEFAULT_PORT);
368 if (listenFd < 0) {
369 DFINDER_LOGE(TAG, "get context failed");
370 return NULL;
371 }
372
373 uint32_t events = EPOLLIN | EPOLLERR;
374 g_coapCtx.task.taskfd = listenFd;
375 g_coapCtx.task.epollfd = epollFd;
376 g_coapCtx.task.readHandle = CoAPEpollReadHandle;
377 g_coapCtx.task.writeHandle = NULL;
378 g_coapCtx.task.errorHandle = CoAPEpollErrorHandle;
379 RegisterEpollTask(&g_coapCtx.task, events);
380
381 g_coapCtx.iface = iface;
382 g_coapCtx.inited = NSTACKX_TRUE;
383 return &g_coapCtx;
384 }
385
CoapServerDestroy(CoapCtxType * ctx,bool moduleDeinit)386 void CoapServerDestroy(CoapCtxType *ctx, bool moduleDeinit)
387 {
388 if (ctx != &g_coapCtx || !g_coapCtx.inited || !moduleDeinit) {
389 DFINDER_LOGE(TAG, "do not destroy, inited: %hhd, module deinit: %hhd", g_coapCtx.inited, moduleDeinit);
390 return;
391 }
392
393 if (g_coapCtx.socketErrFlag) {
394 DFINDER_LOGI(TAG, "socket error occurred and destroy context");
395 NotifyDFinderMsgRecver(DFINDER_ON_INNER_ERROR);
396 } else {
397 DeRegisterEpollTask(&g_coapCtx.task);
398 CloseDesc(g_coapCtx.task.taskfd);
399 }
400
401 (void)memset_s(&g_coapCtx, sizeof(g_coapCtx), 0, sizeof(g_coapCtx));
402 g_coapCtx.task.taskfd = -1;
403 }
404
ResetCoapSocketTaskCount(uint8_t isBusy)405 void ResetCoapSocketTaskCount(uint8_t isBusy)
406 {
407 if (isBusy) {
408 DFINDER_LOGI(TAG, "in this busy interval, socket task count: wifi %llu,"
409 "read %llu, write %llu, error %llu", g_task.count,
410 g_socketEventNum[SOCKET_READ_EVENT],
411 g_socketEventNum[SOCKET_WRITE_EVENT], g_socketEventNum[SOCKET_ERROR_EVENT]);
412 }
413 g_task.count = 0;
414 (void)memset_s(g_socketEventNum, sizeof(g_socketEventNum), 0, sizeof(g_socketEventNum));
415 }
416