1 /*
2  * Copyright (c) 2024 Alexandre Bailon
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_DECLARE(coap);
11 
12 #include "coap_utils.h"
13 #include <openthread/platform/radio.h>
14 
15 static uint8_t coap_buf[COAP_MAX_BUF_SIZE];
16 static uint8_t coap_dev_id[COAP_DEVICE_ID_SIZE];
17 
18 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
coap_default_handler(void * context,otMessage * message,const otMessageInfo * message_info)19 static void coap_default_handler(void *context, otMessage *message,
20 				 const otMessageInfo *message_info)
21 {
22 	ARG_UNUSED(context);
23 	ARG_UNUSED(message);
24 	ARG_UNUSED(message_info);
25 
26 	LOG_INF("Received CoAP message that does not match any request "
27 		"or resource");
28 }
29 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
30 
coap_req_send(const char * addr,const char * uri,uint8_t * buf,int len,otCoapResponseHandler handler,void * ctx,otCoapCode code)31 static int coap_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
32 			 otCoapResponseHandler handler, void *ctx, otCoapCode code)
33 {
34 	otInstance *ot;
35 	otMessage *msg;
36 	otMessageInfo msg_info;
37 	otError err;
38 	int ret;
39 
40 	ot = openthread_get_default_instance();
41 	if (!ot) {
42 		LOG_ERR("Failed to get an OpenThread instance");
43 		return -ENODEV;
44 	}
45 
46 	memset(&msg_info, 0, sizeof(msg_info));
47 	otIp6AddressFromString(addr, &msg_info.mPeerAddr);
48 	msg_info.mPeerPort = OT_DEFAULT_COAP_PORT;
49 
50 	msg = otCoapNewMessage(ot, NULL);
51 	if (!msg) {
52 		LOG_ERR("Failed to allocate a new CoAP message");
53 		return -ENOMEM;
54 	}
55 
56 	otCoapMessageInit(msg, OT_COAP_TYPE_CONFIRMABLE, code);
57 
58 	err = otCoapMessageAppendUriPathOptions(msg, uri);
59 	if (err != OT_ERROR_NONE) {
60 		LOG_ERR("Failed to append uri-path: %s", otThreadErrorToString(err));
61 		ret = -EBADMSG;
62 		goto err;
63 	}
64 
65 	err = otCoapMessageSetPayloadMarker(msg);
66 	if (err != OT_ERROR_NONE) {
67 		LOG_ERR("Failed to set payload marker: %s", otThreadErrorToString(err));
68 		ret = -EBADMSG;
69 		goto err;
70 	}
71 
72 	err = otMessageAppend(msg, buf, len);
73 	if (err != OT_ERROR_NONE) {
74 		LOG_ERR("Failed to set append payload to response: %s", otThreadErrorToString(err));
75 		ret = -EBADMSG;
76 		goto err;
77 	}
78 
79 	err = otCoapSendRequest(ot, msg, &msg_info, handler, ctx);
80 	if (err != OT_ERROR_NONE) {
81 		LOG_ERR("Failed to send the request: %s", otThreadErrorToString(err));
82 		ret = -EIO; /* Find a better error code */
83 		goto err;
84 	}
85 
86 	return 0;
87 
88 err:
89 	otMessageFree(msg);
90 	return ret;
91 }
92 
coap_put_req_send(const char * addr,const char * uri,uint8_t * buf,int len,otCoapResponseHandler handler,void * ctx)93 int coap_put_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
94 		      otCoapResponseHandler handler, void *ctx)
95 {
96 	return coap_req_send(addr, uri, buf, len, handler, ctx, OT_COAP_CODE_PUT);
97 }
98 
coap_get_req_send(const char * addr,const char * uri,uint8_t * buf,int len,otCoapResponseHandler handler,void * ctx)99 int coap_get_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
100 		      otCoapResponseHandler handler, void *ctx)
101 {
102 	return coap_req_send(addr, uri, buf, len, handler, ctx, OT_COAP_CODE_GET);
103 }
104 
coap_resp_send(otMessage * req,const otMessageInfo * req_info,uint8_t * buf,int len)105 int coap_resp_send(otMessage *req, const otMessageInfo *req_info, uint8_t *buf, int len)
106 {
107 	otInstance *ot;
108 	otMessage *resp;
109 	otCoapCode resp_code;
110 	otCoapType resp_type;
111 	otError err;
112 	int ret;
113 
114 	ot = openthread_get_default_instance();
115 	if (!ot) {
116 		LOG_ERR("Failed to get an OpenThread instance");
117 		return -ENODEV;
118 	}
119 
120 	resp = otCoapNewMessage(ot, NULL);
121 	if (!resp) {
122 		LOG_ERR("Failed to allocate a new CoAP message");
123 		return -ENOMEM;
124 	}
125 
126 	switch (otCoapMessageGetType(req)) {
127 	case OT_COAP_TYPE_CONFIRMABLE:
128 		resp_type = OT_COAP_TYPE_ACKNOWLEDGMENT;
129 		break;
130 	case OT_COAP_TYPE_NON_CONFIRMABLE:
131 		resp_type = OT_COAP_TYPE_NON_CONFIRMABLE;
132 		break;
133 	default:
134 		LOG_ERR("Invalid message type");
135 		ret = -EINVAL;
136 		goto err;
137 	}
138 
139 	switch (otCoapMessageGetCode(req)) {
140 	case OT_COAP_CODE_GET:
141 		resp_code = OT_COAP_CODE_CONTENT;
142 		break;
143 	case OT_COAP_CODE_PUT:
144 		resp_code = OT_COAP_CODE_CHANGED;
145 		break;
146 	default:
147 		LOG_ERR("Invalid message code");
148 		ret = -EINVAL;
149 		goto err;
150 	}
151 
152 	err = otCoapMessageInitResponse(resp, req, resp_type, resp_code);
153 	if (err != OT_ERROR_NONE) {
154 		LOG_ERR("Failed to initialize the response: %s", otThreadErrorToString(err));
155 		ret = -EBADMSG;
156 		goto err;
157 	}
158 
159 	err = otCoapMessageSetPayloadMarker(resp);
160 	if (err != OT_ERROR_NONE) {
161 		LOG_ERR("Failed to set payload marker: %s", otThreadErrorToString(err));
162 		ret = -EBADMSG;
163 		goto err;
164 	}
165 
166 	err = otMessageAppend(resp, buf, len);
167 	if (err != OT_ERROR_NONE) {
168 		LOG_ERR("Failed to set append payload to response: %s", otThreadErrorToString(err));
169 		ret = -EBADMSG;
170 		goto err;
171 	}
172 
173 	err = otCoapSendResponse(ot, resp, req_info);
174 	if (err != OT_ERROR_NONE) {
175 		LOG_ERR("Failed to send the response: %s", otThreadErrorToString(err));
176 		ret = -EIO;
177 		goto err;
178 	}
179 
180 	return 0;
181 
182 err:
183 	otMessageFree(resp);
184 	return ret;
185 }
186 
coap_req_handler(void * ctx,otMessage * msg,const otMessageInfo * msg_info,coap_req_handler_put put_fn,coap_req_handler_get get_fn)187 int coap_req_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
188 		     coap_req_handler_put put_fn, coap_req_handler_get get_fn)
189 {
190 	otCoapCode msg_code = otCoapMessageGetCode(msg);
191 	otCoapType msg_type = otCoapMessageGetType(msg);
192 	int ret;
193 
194 	if (msg_type != OT_COAP_TYPE_CONFIRMABLE && msg_type != OT_COAP_TYPE_NON_CONFIRMABLE) {
195 		return -EINVAL;
196 	}
197 
198 	if (msg_code == OT_COAP_CODE_PUT && put_fn) {
199 		int len = otMessageGetLength(msg) - otMessageGetOffset(msg);
200 
201 		otMessageRead(msg, otMessageGetOffset(msg), coap_buf, len);
202 		ret = put_fn(ctx, coap_buf, len);
203 		if (ret) {
204 			return ret;
205 		}
206 
207 		if (msg_type == OT_COAP_TYPE_CONFIRMABLE) {
208 			ret = get_fn(ctx, msg, msg_info);
209 		}
210 
211 		return ret;
212 	}
213 
214 	if (msg_code == OT_COAP_CODE_GET) {
215 		return get_fn(ctx, msg, msg_info);
216 	}
217 
218 	return -EINVAL;
219 }
220 
coap_device_id(void)221 const char *coap_device_id(void)
222 {
223 	otInstance *ot = openthread_get_default_instance();
224 	otExtAddress eui64;
225 	int i;
226 
227 	if (coap_dev_id[0] != '\0') {
228 		return coap_dev_id;
229 	}
230 
231 	otPlatRadioGetIeeeEui64(ot, eui64.m8);
232 	for (i = 0; i < 8; i++) {
233 		if (i * 2 >= COAP_DEVICE_ID_SIZE) {
234 			i = COAP_DEVICE_ID_SIZE - 1;
235 			break;
236 		}
237 		sprintf(coap_dev_id + i * 2, "%02x", eui64.m8[i]);
238 	}
239 	coap_dev_id[i * 2] = '\0';
240 
241 	return coap_dev_id;
242 }
243 
coap_get_data(otMessage * msg,void * buf,int * len)244 int coap_get_data(otMessage *msg, void *buf, int *len)
245 {
246 	int coap_len = otMessageGetLength(msg) - otMessageGetOffset(msg);
247 
248 	if (coap_len > *len) {
249 		return -ENOMEM;
250 	}
251 
252 	*len = coap_len;
253 	otMessageRead(msg, otMessageGetOffset(msg), buf, coap_len);
254 
255 	return 0;
256 }
257 
coap_init(void)258 int coap_init(void)
259 {
260 	otError err;
261 	otInstance *ot;
262 
263 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
264 	LOG_INF("Initializing OpenThread CoAP server");
265 #else  /* CONFIG_OT_COAP_SAMPLE_SERVER */
266 	LOG_INF("Initializing OpenThread CoAP client");
267 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
268 	ot = openthread_get_default_instance();
269 	if (!ot) {
270 		LOG_ERR("Failed to get an OpenThread instance");
271 		return -ENODEV;
272 	}
273 
274 #ifdef CONFIG_OT_COAP_SAMPLE_SERVER
275 	otCoapSetDefaultHandler(ot, coap_default_handler, NULL);
276 #endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
277 
278 	err = otCoapStart(ot, OT_DEFAULT_COAP_PORT);
279 	if (err != OT_ERROR_NONE) {
280 		LOG_ERR("Cannot start CoAP: %s", otThreadErrorToString(err));
281 		return -EBADMSG;
282 	}
283 
284 	return 0;
285 }
286