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