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 
44 /*
45  * the initial timeout will be set to a random duration between COAP_ACK_TIMEOUT and
46  * (COAP_ACK_TIMEOUT * COAP_ACK_RANDOM_FACTOR).
47  */
48 #define DFINDER_COAP_ACK_TIMEOUT ((coap_fixed_point_t){1, 0}) // 1 seconds
49 #define DFINDER_COAP_ACK_RANDOM_FACTOR ((coap_fixed_point_t){1, 200}) // 1.2
50 #define DFINDER_COAP_MAX_RETRANSMIT_TIMES 2 // retransmit 2 times for CON packets
51 
CoapResolveAddress(const coap_str_const_t * server,struct sockaddr * dst)52 int32_t CoapResolveAddress(const coap_str_const_t *server, struct sockaddr *dst)
53 {
54     struct addrinfo *res = NULL;
55     struct addrinfo *ainfo = NULL;
56     struct addrinfo hints;
57     char addrstr[DEFAULT_COAP_BUFFER_LENGTH]; /* Get a char array with length 256 to save host name. */
58 
59     if (server == NULL || server->s == NULL || dst == NULL) {
60         return NSTACKX_EINVAL;
61     }
62     (void)memset_s(addrstr, sizeof(addrstr), 0, sizeof(addrstr));
63     if (server->length) {
64         if (memcpy_s(addrstr, sizeof(addrstr), server->s, server->length) != EOK) {
65             DFINDER_LOGD(TAG, "addrstr copy error");
66             return NSTACKX_EFAILED;
67         }
68     } else {
69         if (memcpy_s(addrstr, sizeof(addrstr), "localhost", strlen("localhost")) != EOK) {
70             DFINDER_LOGD(TAG, "addrstr copy error");
71             return NSTACKX_EFAILED;
72         }
73     }
74 
75     (void)memset_s((char *)&hints, sizeof(hints), 0, sizeof(hints));
76     hints.ai_socktype = SOCK_DGRAM;
77     hints.ai_family = AF_UNSPEC;
78 
79     int32_t error = getaddrinfo(addrstr, NULL, &hints, &res);
80     if (error != 0) {
81         DFINDER_LOGE(TAG, "getaddrinfo error");
82         return error;
83     }
84 
85     socklen_t len = 0;
86     for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
87         switch (ainfo->ai_family) {
88             case AF_INET6:
89                 /* fall-through */
90             case AF_INET:
91                 len = ainfo->ai_addrlen;
92                 if (memcpy_s(dst, sizeof(struct sockaddr), ainfo->ai_addr, (size_t)len) != EOK) {
93                     DFINDER_LOGE(TAG, "ai_addr copy error");
94                     error = NSTACKX_EFAILED;
95                     goto finish;
96                 }
97                 break;
98             default:
99                 break;
100         }
101     }
102 
103 finish:
104     freeaddrinfo(res);
105     return (error == NSTACKX_EFAILED) ? error : (int32_t)len;
106 }
107 
CoapMessageHandler(coap_session_t * session,const coap_pdu_t * sent,const coap_pdu_t * received,const coap_mid_t id)108 coap_response_t CoapMessageHandler(coap_session_t *session,
109     const coap_pdu_t *sent, const coap_pdu_t *received, const coap_mid_t id)
110 {
111     if (received == NULL) {
112         DFINDER_LOGE(TAG, "received error");
113         goto FAIL;
114     }
115     (void)session;
116     (void)sent;
117     (void)id;
118     coap_opt_t *blockOpt1 = NULL;
119     coap_opt_t *blockOpt2 = NULL;
120     coap_opt_iterator_t optIter;
121 
122     (void)memset_s(&optIter, sizeof(optIter), 0, sizeof(optIter));
123     if (coap_pdu_get_type(received) == COAP_MESSAGE_RST) {
124         DFINDER_LOGD(TAG, "got RST");
125         goto FAIL;
126     }
127 
128     if (coap_check_option(received, COAP_OPTION_OBSERVE, &optIter)) {
129         DFINDER_LOGE(TAG, "observe not support.");
130         goto FAIL;
131     }
132     blockOpt2 = coap_check_option(received, COAP_OPTION_BLOCK2, &optIter);
133     blockOpt1 = coap_check_option(received, COAP_OPTION_BLOCK1, &optIter);
134     if ((blockOpt1 != NULL) || (blockOpt2 != NULL)) {
135         DFINDER_LOGE(TAG, "block not support.");
136         goto FAIL;
137     }
138 
139     coap_pdu_code_t rcv_code = coap_pdu_get_code(received);
140     DFINDER_LOGD(TAG, "%d.%02d", COAP_RESPONSE_CLASS(rcv_code), rcv_code & 0x1F);
141     return COAP_RESPONSE_OK;
142 
143 FAIL:
144     IncStatistics(STATS_INVALID_RESPONSE_MSG);
145     return COAP_RESPONSE_FAIL;
146 }
147 
InitAddrinfo(struct addrinfo * hints)148 static void InitAddrinfo(struct addrinfo *hints)
149 {
150     if (hints == NULL) {
151         return;
152     }
153     (void)memset_s(hints, sizeof(struct addrinfo), 0, sizeof(struct addrinfo));
154     hints->ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
155     hints->ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
156     hints->ai_flags = AI_PASSIVE | AI_NUMERICHOST;
157 }
158 
CoapGetContextEx(const char * node,const char * port,uint8_t needBind,const struct in_addr * ip)159 static coap_context_t *CoapGetContextEx(const char *node, const char *port,
160     uint8_t needBind, const struct in_addr *ip)
161 {
162     struct addrinfo hints;
163     struct addrinfo *result = NULL;
164     struct addrinfo *rp = NULL;
165     coap_endpoint_t *ep = NULL;
166     coap_context_t *ctx = coap_new_context(NULL);
167     if (ctx == NULL) {
168         return NULL;
169     }
170     InitAddrinfo(&hints);
171 
172     if (getaddrinfo(node, port, &hints, &result) != 0) {
173         coap_free_context(ctx);
174         return NULL;
175     }
176     coap_address_t addr;
177     /* iterate through results until success */
178     for (rp = result; rp != NULL; rp = rp->ai_next) {
179         if (rp->ai_addrlen > (socklen_t)sizeof(addr.addr)) {
180             continue;
181         }
182 
183         coap_address_init(&addr);
184         addr.size = rp->ai_addrlen;
185         if (memcpy_s(&addr.addr, sizeof(addr.addr), rp->ai_addr, rp->ai_addrlen) != EOK ||
186             (addr.addr.sa.sa_family != AF_INET && addr.addr.sa.sa_family != AF_INET6)) {
187             continue;
188         }
189 
190         ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);
191         if (ep != NULL) {
192             struct sockaddr_in sockIp;
193             struct sockaddr_in *sockIpPtr = NULL;
194             (void)memset_s(&sockIp, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
195             if (ip != NULL && needBind) {
196                 (void)memcpy_s(&sockIp.sin_addr, sizeof(struct in_addr), ip, sizeof(struct in_addr));
197                 sockIpPtr = &sockIp;
198             }
199             if (needBind && BindToDevice(ep->sock.fd, sockIpPtr) != NSTACKX_EOK) {
200                 DFINDER_LOGE(TAG, "bind to device fail");
201                 coap_free_context(ctx);
202                 ctx = NULL;
203             }
204         } else {
205             DFINDER_LOGE(TAG, "coap_new_endpoint get null");
206             coap_free_context(ctx);
207             ctx = NULL;
208         }
209         break;
210     }
211     freeaddrinfo(result);
212     return ctx;
213 }
214 
CoapGetContext(const char * node,const char * port,uint8_t needBind,const struct in_addr * ip)215 coap_context_t *CoapGetContext(const char *node, const char *port, uint8_t needBind, const struct in_addr *ip)
216 {
217     coap_context_t *context = CoapGetContextEx(node, port, needBind, ip);
218     if (context == NULL) {
219         IncStatistics(STATS_CREATE_CONTEX_FAILED);
220     }
221     return context;
222 }
223 
CoapGetSessionInner(struct addrinfo * result,coap_context_t * ctx,const CoapServerParameter * coapServerParameter)224 static coap_session_t *CoapGetSessionInner(struct addrinfo *result, coap_context_t *ctx,
225     const CoapServerParameter *coapServerParameter)
226 {
227     coap_session_t *session = NULL;
228     struct addrinfo *rp = NULL;
229     coap_proto_t proto = coapServerParameter->proto;
230     const coap_address_t *dst = coapServerParameter->dst;
231 
232     if (proto != COAP_PROTO_UDP) {
233         DFINDER_LOGE(TAG, "unsupported proto");
234         return NULL;
235     }
236 
237     for (rp = result; rp != NULL; rp = rp->ai_next) {
238         coap_address_t bindAddr;
239         if (rp->ai_addrlen > (socklen_t)sizeof(bindAddr.addr) || rp->ai_addr == NULL) {
240             continue;
241         }
242         coap_address_init(&bindAddr);
243         bindAddr.size = rp->ai_addrlen;
244         if (memcpy_s(&bindAddr.addr, sizeof(bindAddr.addr), rp->ai_addr, rp->ai_addrlen) != EOK) {
245             DFINDER_LOGE(TAG, "ai_addr copy error");
246             continue;
247         }
248         session = coap_new_client_session(ctx, &bindAddr, dst, proto);
249         if (session != NULL) {
250             break;
251         }
252     }
253     return session;
254 }
255 
CoapSetAckTimeOut(coap_session_t * session)256 static void CoapSetAckTimeOut(coap_session_t *session)
257 {
258     if (session == NULL) {
259         return;
260     }
261     coap_session_set_ack_timeout(session, DFINDER_COAP_ACK_TIMEOUT);
262     coap_session_set_ack_random_factor(session, DFINDER_COAP_ACK_RANDOM_FACTOR);
263     coap_session_set_max_retransmit(session, DFINDER_COAP_MAX_RETRANSMIT_TIMES);
264 }
265 
CoapGetSessionEx(coap_context_t * ctx,const char * localAddr,const char * localPort,const CoapServerParameter * coapServerParameter)266 static coap_session_t *CoapGetSessionEx(coap_context_t *ctx, const char *localAddr, const char *localPort,
267     const CoapServerParameter *coapServerParameter)
268 {
269     coap_session_t *session = NULL;
270     coap_proto_t proto;
271     const coap_address_t *dst = NULL;
272 
273     if (coapServerParameter == NULL) {
274         return NULL;
275     }
276 
277     proto = coapServerParameter->proto;
278     dst = coapServerParameter->dst;
279     if (proto != COAP_PROTO_UDP) {
280         DFINDER_LOGE(TAG, "unsupported proto");
281         return NULL;
282     }
283 
284     if (dst == NULL) {
285         return session;
286     }
287 
288     /* reuse the existed session */
289     session = coap_session_get_by_peer(ctx, dst, 0);
290     if (session != NULL) {
291         (void)coap_session_reference(session);
292         return session;
293     }
294 
295     if (localAddr != NULL) {
296         struct addrinfo hints;
297         struct addrinfo *result = NULL;
298 
299         (void)memset_s(&hints, sizeof(struct addrinfo), 0, sizeof(struct addrinfo));
300         hints.ai_family = AF_UNSPEC;  /* Allow IPv4 or IPv6 */
301         hints.ai_socktype = COAP_PROTO_RELIABLE(proto) ? SOCK_STREAM : SOCK_DGRAM; /* Coap uses UDP */
302         hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
303         int s = getaddrinfo(localAddr, localPort, &hints, &result);
304         if (s != 0) {
305             DFINDER_LOGE(TAG, "getaddrinfo error");
306             return NULL;
307         }
308         session = CoapGetSessionInner(result, ctx, coapServerParameter);
309         freeaddrinfo(result);
310     } else {
311         session = coap_new_client_session(ctx, NULL, dst, proto);
312     }
313     CoapSetAckTimeOut(session);
314     return session;
315 }
316 
CoapGetSession(coap_context_t * ctx,const char * localAddr,const char * localPort,const CoapServerParameter * coapServerParameter)317 coap_session_t *CoapGetSession(coap_context_t *ctx, const char *localAddr, const char *localPort,
318     const CoapServerParameter *coapServerParameter)
319 {
320     coap_session_t *session = CoapGetSessionEx(ctx, localAddr, localPort, coapServerParameter);
321     if (session == NULL) {
322         IncStatistics(STATS_CREATE_SESSION_FAILED);
323     }
324     return session;
325 }
326 
IsCoapCtxEndpointSocket(const coap_context_t * ctx,int fd)327 uint8_t IsCoapCtxEndpointSocket(const coap_context_t *ctx, int fd)
328 {
329     coap_endpoint_t *iterator = NULL;
330     coap_endpoint_t *listeningEndpoints = NULL;
331     coap_endpoint_t *tmp = NULL;
332     if (ctx == NULL) {
333         return NSTACKX_FALSE;
334     }
335     listeningEndpoints = coap_context_get_endpoint(ctx);
336     LL_FOREACH_SAFE(listeningEndpoints, iterator, tmp) {
337         if (iterator->sock.fd == fd) {
338             return NSTACKX_TRUE;
339         }
340     }
341     return NSTACKX_FALSE;
342 }
343