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