• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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