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