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