• 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_client.h"
17 #include <stdlib.h>
18 #include <string.h>
19 #include <securec.h>
20 #ifndef _WIN32
21 #include <signal.h>
22 #include <unistd.h>
23 #include <netdb.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <arpa/inet.h>
27 #include <sys/select.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #endif
31 #include <coap3/coap_session_internal.h>
32 #include "nstackx_device.h"
33 #include "nstackx_error.h"
34 #include "nstackx_dfinder_log.h"
35 #include "nstackx_util.h"
36 #include "nstackx_statistics.h"
37 
38 #define TAG "nStackXCoAP"
39 
40 #define FLAGS_BLOCK 0x01
41 #define DEFAULT_COAP_BUFFER_LENGTH 256
42 #define COAP_CERT_CHAIN_DEPTH 2
43 #define COAP_MULTICAST_ADDR "ff02::1"
44 
45 /*
46  * the initial timeout will be set to a random duration between COAP_ACK_TIMEOUT and
47  * (COAP_ACK_TIMEOUT * COAP_ACK_RANDOM_FACTOR).
48  */
49 #define DFINDER_COAP_ACK_TIMEOUT ((coap_fixed_point_t){1, 0}) // 1 seconds
50 #define DFINDER_COAP_ACK_RANDOM_FACTOR ((coap_fixed_point_t){1, 200}) // 1.2
51 #define DFINDER_COAP_MAX_RETRANSMIT_TIMES 2 // retransmit 2 times for CON packets
52 
CoapResolveAddress(const coap_str_const_t * server,struct sockaddr * dst)53 int32_t CoapResolveAddress(const coap_str_const_t *server, struct sockaddr *dst)
54 {
55     struct addrinfo *res = NULL;
56     struct addrinfo *ainfo = NULL;
57     struct addrinfo hints;
58     char addrstr[DEFAULT_COAP_BUFFER_LENGTH]; /* Get a char array with length 256 to save host name. */
59 
60     if (server == NULL || server->s == NULL || dst == NULL ||
61         (dst->sa_family != AF_INET && dst->sa_family != AF_INET6)) {
62         return NSTACKX_EINVAL;
63     }
64     (void)memset_s(addrstr, sizeof(addrstr), 0, sizeof(addrstr));
65     if (server->length) {
66         if (memcpy_s(addrstr, sizeof(addrstr), server->s, server->length) != EOK) {
67             DFINDER_LOGD(TAG, "addrstr copy error");
68             return NSTACKX_EFAILED;
69         }
70     } else {
71         if (memcpy_s(addrstr, sizeof(addrstr), "localhost", strlen("localhost")) != EOK) {
72             DFINDER_LOGD(TAG, "addrstr copy error");
73             return NSTACKX_EFAILED;
74         }
75     }
76 
77     (void)memset_s((char *)&hints, sizeof(hints), 0, sizeof(hints));
78     hints.ai_socktype = SOCK_DGRAM;
79     hints.ai_family = AF_UNSPEC;
80 
81     int32_t error = getaddrinfo(addrstr, NULL, &hints, &res);
82     if (error != 0) {
83         DFINDER_LOGE(TAG, "getaddrinfo error");
84         return error;
85     }
86 
87     socklen_t len = 0;
88     for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
89         if (ainfo->ai_family != dst->sa_family) {
90             continue;
91         }
92         len = ainfo->ai_addrlen;
93         size_t dstLen = dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
94         if (memcpy_s(dst, dstLen, ainfo->ai_addr, len) != EOK) {
95             DFINDER_LOGE(TAG, "ai_addr copy error");
96             error = NSTACKX_EFAILED;
97         }
98         break;
99     }
100     freeaddrinfo(res);
101     return (error == NSTACKX_EFAILED) ? error : (int32_t)len;
102 }
103 
CoapMessageHandler(coap_session_t * session,const coap_pdu_t * sent,const coap_pdu_t * received,const coap_mid_t id)104 coap_response_t CoapMessageHandler(coap_session_t *session,
105     const coap_pdu_t *sent, const coap_pdu_t *received, const coap_mid_t id)
106 {
107     if (received == NULL) {
108         DFINDER_LOGE(TAG, "received error");
109         goto FAIL;
110     }
111     (void)session;
112     (void)sent;
113     (void)id;
114     coap_opt_t *blockOpt1 = NULL;
115     coap_opt_t *blockOpt2 = NULL;
116     coap_opt_iterator_t optIter;
117 
118     (void)memset_s(&optIter, sizeof(optIter), 0, sizeof(optIter));
119     if (coap_pdu_get_type(received) == COAP_MESSAGE_RST) {
120         DFINDER_LOGD(TAG, "got RST");
121         goto FAIL;
122     }
123 
124     if (coap_check_option(received, COAP_OPTION_OBSERVE, &optIter)) {
125         DFINDER_LOGE(TAG, "observe not support.");
126         goto FAIL;
127     }
128     blockOpt2 = coap_check_option(received, COAP_OPTION_BLOCK2, &optIter);
129     blockOpt1 = coap_check_option(received, COAP_OPTION_BLOCK1, &optIter);
130     if ((blockOpt1 != NULL) || (blockOpt2 != NULL)) {
131         DFINDER_LOGE(TAG, "block not support.");
132         goto FAIL;
133     }
134 
135     coap_pdu_code_t rcv_code = coap_pdu_get_code(received);
136     DFINDER_LOGD(TAG, "%d.%02d", COAP_RESPONSE_CLASS(rcv_code), rcv_code & 0x1F);
137     return COAP_RESPONSE_OK;
138 
139 FAIL:
140     IncStatistics(STATS_INVALID_RESPONSE_MSG);
141     return COAP_RESPONSE_FAIL;
142 }
143 
InitAddrinfo(struct addrinfo * hints)144 static void InitAddrinfo(struct addrinfo *hints)
145 {
146     if (hints == NULL) {
147         return;
148     }
149     (void)memset_s(hints, sizeof(struct addrinfo), 0, sizeof(struct addrinfo));
150     hints->ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
151     hints->ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
152     hints->ai_flags = AI_PASSIVE | AI_NUMERICHOST;
153 }
154 
CoapBindToDevice(int sockfd,uint8_t af,const union InetAddr * addr)155 static int CoapBindToDevice(int sockfd, uint8_t af, const union InetAddr *addr)
156 {
157     struct sockaddr_in sockIp;
158     if (af == AF_INET) {
159         (void)memset_s(&sockIp, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
160         (void)memcpy_s(&sockIp.sin_addr, sizeof(struct in_addr), &(addr->in), sizeof(struct in_addr));
161         return BindToDevice(sockfd, &sockIp);
162     }
163     return NSTACKX_EOK;
164 }
165 
CoapAddIpv6Multicast(int sockfd)166 static int CoapAddIpv6Multicast(int sockfd)
167 {
168     struct ipv6_mreq mreq;
169     if (inet_pton(AF_INET6, COAP_MULTICAST_ADDR, &mreq.ipv6mr_multiaddr) <= 0) {
170         DFINDER_LOGE(TAG, "inet_pton multicast addr fail");
171         return NSTACKX_EFAILED;
172     }
173     mreq.ipv6mr_interface = 0; // use the default interface
174     if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) {
175         DFINDER_LOGE(TAG, "setsockopt add multicast group fail");
176         return NSTACKX_EFAILED;
177     }
178 
179     return NSTACKX_EOK;
180 }
181 
CoapCreateEndpoint(coap_context_t * ctx,coap_address_t * addr,uint8_t af,const union InetAddr * ip)182 coap_endpoint_t *CoapCreateEndpoint(coap_context_t *ctx, coap_address_t *addr,
183     uint8_t af,  const union InetAddr *ip)
184 {
185     DFINDER_LOGI(TAG, "Initializing CoapCreateEndpoint");
186     coap_endpoint_t *ep = coap_new_endpoint(ctx, addr, COAP_PROTO_UDP);
187     if (ep == NULL) {
188         DFINDER_LOGE(TAG, "coap_new_endpoint get null");
189         return NULL;
190     }
191     if (CoapBindToDevice(ep->sock.fd, af, ip) != NSTACKX_EOK) {
192         DFINDER_LOGE(TAG, "bind to device fail");
193         coap_free_endpoint(ep);
194         return NULL;
195     }
196     if (af == AF_INET6 && CoapAddIpv6Multicast(ep->sock.fd) != NSTACKX_EOK) {
197         DFINDER_LOGE(TAG, "add ipv6 multicast fail");
198         coap_free_endpoint(ep);
199         return NULL;
200     }
201     return ep;
202 }
203 
CoapGetContextEx(const char * node,const char * port,uint8_t af,const union InetAddr * ip)204 static coap_context_t *CoapGetContextEx(const char *node, const char *port,
205     uint8_t af, const union InetAddr *ip)
206 {
207     struct addrinfo hints;
208     struct addrinfo *result = NULL;
209     struct addrinfo *rp = NULL;
210     coap_endpoint_t *ep = NULL;
211     coap_context_t *ctx = coap_new_context(NULL);
212     if (ctx == NULL) {
213         DFINDER_LOGE(TAG, "coap_new_context return null");
214         return NULL;
215     }
216     InitAddrinfo(&hints);
217 
218     if (getaddrinfo(node, port, &hints, &result) != 0) {
219         DFINDER_LOGE(TAG, "getaddrinfo fail, errno: %d, desc: %s", errno, strerror(errno));
220         coap_free_context(ctx);
221         return NULL;
222     }
223     coap_address_t addr;
224     /* iterate through results until success */
225     for (rp = result; rp != NULL; rp = rp->ai_next) {
226         if (rp->ai_addrlen > (socklen_t)sizeof(addr.addr)) {
227             continue;
228         }
229 
230         coap_address_init(&addr);
231         addr.size = rp->ai_addrlen;
232         if (rp->ai_family != af || memcpy_s(&addr.addr, sizeof(addr.addr), rp->ai_addr, rp->ai_addrlen) != EOK) {
233             continue;
234         }
235         ep = CoapCreateEndpoint(ctx, &addr, af, ip);
236         if (ep == NULL) {
237             DFINDER_LOGE(TAG, "coap_new_endpoint return null");
238             coap_free_context(ctx);
239             ctx = NULL;
240         }
241         break;
242     }
243     freeaddrinfo(result);
244     return ctx;
245 }
246 
CoapGetContext(const char * node,const char * port,uint8_t af,const union InetAddr * ip)247 coap_context_t *CoapGetContext(const char *node, const char *port,
248     uint8_t af, const union InetAddr *ip)
249 {
250     coap_context_t *context = CoapGetContextEx(node, port, af, ip);
251     if (context == NULL) {
252         IncStatistics(STATS_CREATE_CONTEX_FAILED);
253     }
254     return context;
255 }
256 
CoapGetSessionInner(struct addrinfo * result,coap_context_t * ctx,const CoapServerParameter * coapServerParameter)257 static coap_session_t *CoapGetSessionInner(struct addrinfo *result, coap_context_t *ctx,
258     const CoapServerParameter *coapServerParameter)
259 {
260     coap_session_t *session = NULL;
261     struct addrinfo *rp = NULL;
262     coap_proto_t proto = coapServerParameter->proto;
263     const coap_address_t *dst = coapServerParameter->dst;
264 
265     if (proto != COAP_PROTO_UDP) {
266         DFINDER_LOGE(TAG, "unsupported proto");
267         return NULL;
268     }
269 
270     for (rp = result; rp != NULL; rp = rp->ai_next) {
271         coap_address_t bindAddr;
272         if (rp->ai_addrlen > (socklen_t)sizeof(bindAddr.addr) || rp->ai_addr == NULL ||
273             dst->addr.sa.sa_family != rp->ai_addr->sa_family) {
274             continue;
275         }
276         (void)memset_s(&bindAddr, sizeof(bindAddr), 0, sizeof(bindAddr));
277         coap_address_init(&bindAddr);
278         bindAddr.size = rp->ai_addrlen;
279         if (memcpy_s(&bindAddr.addr, sizeof(bindAddr.addr), rp->ai_addr, rp->ai_addrlen) != EOK) {
280             DFINDER_LOGE(TAG, "ai_addr copy error");
281             continue;
282         }
283         char ip[NSTACKX_MAX_IP_STRING_LEN];
284         if (bindAddr.addr.sa.sa_family == AF_INET) {
285             (void)inet_ntop(AF_INET, &(bindAddr.addr.sin.sin_addr), ip, sizeof(ip));
286         } else {
287             (void)inet_ntop(AF_INET6, &(bindAddr.addr.sin6.sin6_addr), ip, sizeof(ip));
288         }
289         session = coap_new_client_session(ctx, &bindAddr, dst, proto);
290         if (session != NULL) {
291             break;
292         } else {
293             DFINDER_LOGE(TAG, "coap_new_client_session error");
294         }
295     }
296     return session;
297 }
298 
CoapSetAckTimeOut(coap_session_t * session)299 static void CoapSetAckTimeOut(coap_session_t *session)
300 {
301     if (session == NULL) {
302         return;
303     }
304     coap_session_set_ack_timeout(session, DFINDER_COAP_ACK_TIMEOUT);
305     coap_session_set_ack_random_factor(session, DFINDER_COAP_ACK_RANDOM_FACTOR);
306     coap_session_set_max_retransmit(session, DFINDER_COAP_MAX_RETRANSMIT_TIMES);
307 }
308 
CoapGetSessionEx(coap_context_t * ctx,const char * localAddr,const char * localPort,const CoapServerParameter * coapServerParameter)309 static coap_session_t *CoapGetSessionEx(coap_context_t *ctx, const char *localAddr, const char *localPort,
310     const CoapServerParameter *coapServerParameter)
311 {
312     coap_session_t *session = NULL;
313     coap_proto_t proto;
314     const coap_address_t *dst = NULL;
315 
316     if (coapServerParameter == NULL) {
317         return NULL;
318     }
319 
320     proto = coapServerParameter->proto;
321     dst = coapServerParameter->dst;
322     if (proto != COAP_PROTO_UDP) {
323         DFINDER_LOGE(TAG, "unsupported proto");
324         return NULL;
325     }
326 
327     if (dst == NULL) {
328         return session;
329     }
330 
331     /* reuse the existed session */
332     session = coap_session_get_by_peer(ctx, dst, 0);
333     if (session != NULL) {
334         CoapSetAckTimeOut(session);
335         (void)coap_session_reference(session);
336         return session;
337     }
338 
339     if (localAddr != NULL) {
340         struct addrinfo hints;
341         struct addrinfo *result = NULL;
342         (void)memset_s(&hints, sizeof(struct addrinfo), 0, sizeof(struct addrinfo));
343         hints.ai_family = AF_UNSPEC;  /* Allow IPv4 or IPv6 */
344         hints.ai_socktype = COAP_PROTO_RELIABLE(proto) ? SOCK_STREAM : SOCK_DGRAM; /* Coap uses UDP */
345         hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
346         int s = getaddrinfo(localAddr, localPort, &hints, &result);
347         if (s != 0) {
348             DFINDER_LOGE(TAG, "getaddrinfo failed, error: %d, desc: %s", errno, strerror(errno));
349             return NULL;
350         }
351         session = CoapGetSessionInner(result, ctx, coapServerParameter);
352         freeaddrinfo(result);
353     } else {
354         session = coap_new_client_session(ctx, NULL, dst, proto);
355         if (session == NULL) {
356             DFINDER_LOGE(TAG, "coap_new_client_session failed with null local addr");
357         }
358     }
359     CoapSetAckTimeOut(session);
360     return session;
361 }
362 
CoapGetSession(coap_context_t * ctx,const char * localAddr,const char * localPort,const CoapServerParameter * coapServerParameter)363 coap_session_t *CoapGetSession(coap_context_t *ctx, const char *localAddr, const char *localPort,
364     const CoapServerParameter *coapServerParameter)
365 {
366     coap_session_t *session = CoapGetSessionEx(ctx, localAddr, localPort, coapServerParameter);
367     if (session == NULL) {
368         IncStatistics(STATS_CREATE_SESSION_FAILED);
369     }
370     return session;
371 }
372 
IsCoapCtxEndpointSocket(const coap_context_t * ctx,int fd)373 uint8_t IsCoapCtxEndpointSocket(const coap_context_t *ctx, int fd)
374 {
375     coap_endpoint_t *iterator = NULL;
376     coap_endpoint_t *listeningEndpoints = NULL;
377     coap_endpoint_t *tmp = NULL;
378     if (ctx == NULL) {
379         DFINDER_LOGW(TAG, "coap context passed in is null");
380         return NSTACKX_FALSE;
381     }
382     listeningEndpoints = coap_context_get_endpoint(ctx);
383     LL_FOREACH_SAFE(listeningEndpoints, iterator, tmp) {
384         if (iterator->sock.fd == fd) {
385             return NSTACKX_TRUE;
386         }
387     }
388     return NSTACKX_FALSE;
389 }
390