1 /* libcoap unit tests
2 *
3 * Copyright (C) 2013--2021 Olaf Bergmann <bergmann@tzi.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 *
7 * This file is part of the CoAP library libcoap. Please see
8 * README for terms of use.
9 */
10
11 #include "test_common.h"
12 #include "test_wellknown.h"
13
14 #include <assert.h>
15 #ifdef HAVE_NETINET_IN_H
16 #include <netinet/in.h>
17 #endif
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #define TEST_PDU_SIZE 120
23 #define TEST_URI_LEN 4
24
25 static coap_context_t *ctx; /* Holds the coap context for most tests */
26 static coap_pdu_t *pdu; /* Holds the parsed PDU for most tests */
27 static coap_session_t *session; /* Holds a reference-counted session object
28 * that is passed to coap_wellknown_response(). */
29
30 static void
t_wellknown1(void)31 t_wellknown1(void) {
32 coap_print_status_t result;
33 coap_resource_t *r;
34 unsigned char buf[40];
35 size_t buflen, offset, ofs;
36
37 char teststr[] = { /* </>;title="some attribute";ct=0 (31 chars) */
38 '<', '/', '>', ';', 't', 'i', 't', 'l',
39 'e', '=', '"', 's', 'o', 'm', 'e', ' ',
40 'a', 't', 't', 'r', 'i', 'b', 'u', 't',
41 'e', '"', ';', 'c', 't', '=', '0'
42 };
43
44 r = coap_resource_init(NULL, 0);
45
46 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
47 coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"some attribute\""), 0);
48
49 coap_add_resource(ctx, r);
50
51 for (offset = 0; offset < sizeof(teststr); offset++) {
52 ofs = offset;
53 buflen = sizeof(buf);
54
55 result = coap_print_link(r, buf, &buflen, &ofs);
56
57 CU_ASSERT(result == sizeof(teststr) - offset);
58 CU_ASSERT(buflen == sizeof(teststr));
59 CU_ASSERT(memcmp(buf, teststr + offset, sizeof(teststr) - offset) == 0);
60 }
61
62 /* offset points behind teststr */
63 ofs = offset;
64 buflen = sizeof(buf);
65 result = coap_print_link(r, buf, &buflen, &ofs);
66
67 CU_ASSERT(result == 0);
68 CU_ASSERT(buflen == sizeof(teststr));
69
70 /* offset exceeds buffer */
71 buflen = sizeof(buf);
72 ofs = buflen;
73 result = coap_print_link(r, buf, &buflen, &ofs);
74
75 CU_ASSERT(result == 0);
76 CU_ASSERT(buflen == sizeof(teststr));
77 }
78
79 static void
t_wellknown2(void)80 t_wellknown2(void) {
81 coap_print_status_t result;
82 coap_resource_t *r;
83 unsigned char buf[10]; /* smaller than teststr */
84 size_t buflen, offset, ofs;
85
86 char teststr[] = { /* ,</abcd>;if="one";obs (21 chars) */
87 '<', '/', 'a', 'b', 'c', 'd', '>', ';',
88 'i', 'f', '=', '"', 'o', 'n', 'e', '"',
89 ';', 'o', 'b', 's'
90 };
91
92 r = coap_resource_init(coap_make_str_const("abcd"), 0);
93 coap_resource_set_get_observable(r, 1);
94 coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"one\""), 0);
95
96 coap_add_resource(ctx, r);
97
98 for (offset = 0; offset < sizeof(teststr) - sizeof(buf); offset++) {
99 ofs = offset;
100 buflen = sizeof(buf);
101
102 result = coap_print_link(r, buf, &buflen, &ofs);
103
104 CU_ASSERT(result == (COAP_PRINT_STATUS_TRUNC | sizeof(buf)));
105 CU_ASSERT(buflen == sizeof(teststr));
106 CU_ASSERT(ofs == 0);
107 CU_ASSERT(memcmp(buf, teststr + offset, sizeof(buf)) == 0);
108 }
109
110 /* from here on, the resource description fits into buf */
111 for (; offset < sizeof(teststr); offset++) {
112 ofs = offset;
113 buflen = sizeof(buf);
114 result = coap_print_link(r, buf, &buflen, &ofs);
115
116 CU_ASSERT(result == sizeof(teststr) - offset);
117 CU_ASSERT(buflen == sizeof(teststr));
118 CU_ASSERT(ofs == 0);
119 CU_ASSERT(memcmp(buf, teststr + offset,
120 COAP_PRINT_OUTPUT_LENGTH(result)) == 0);
121 }
122
123 /* offset exceeds buffer */
124 buflen = sizeof(buf);
125 ofs = offset;
126 result = coap_print_link(r, buf, &buflen, &ofs);
127 CU_ASSERT(result == 0);
128 CU_ASSERT(buflen == sizeof(teststr));
129 CU_ASSERT(ofs == offset - sizeof(teststr));
130 }
131
132 static void
t_wellknown3(void)133 t_wellknown3(void) {
134 coap_print_status_t result;
135 int j;
136 coap_resource_t *r;
137 static char uris[2 * 1024];
138 unsigned char *uribuf = (unsigned char *)uris;
139 unsigned char buf[40];
140 size_t buflen = sizeof(buf);
141 size_t offset;
142 const uint16_t num_resources = (sizeof(uris) / TEST_URI_LEN) - 1;
143
144 /* ,</0000> (TEST_URI_LEN + 4 chars) */
145 for (j = 0; j < num_resources; j++) {
146 int len = snprintf((char *)uribuf, TEST_URI_LEN + 1,
147 "%0*d", TEST_URI_LEN, j);
148 coap_str_const_t uri_path = {.length = len, .s = uribuf};
149 r = coap_resource_init(&uri_path, 0);
150 coap_add_resource(ctx, r);
151 uribuf += TEST_URI_LEN;
152 }
153
154 /* the following test assumes that the first two resources from
155 * t_wellknown1() and t_wellknown2() need more than buflen
156 * characters. Otherwise, CU_ASSERT(result > 0) will fail.
157 */
158 offset = num_resources * (TEST_URI_LEN + 4);
159 result = coap_print_wellknown(ctx, buf, &buflen, offset, NULL);
160 CU_ASSERT((result & COAP_PRINT_STATUS_ERROR) == 0 );
161 CU_ASSERT(COAP_PRINT_OUTPUT_LENGTH(result) > 0);
162 }
163
164 /* Create wellknown response for request without Block-option. */
165 static void
t_wellknown4(void)166 t_wellknown4(void) {
167 coap_pdu_t *response;
168 coap_block_t block;
169
170 response = coap_wellknown_response(ctx, session, pdu);
171
172 CU_ASSERT_PTR_NOT_NULL(response);
173
174 CU_ASSERT(coap_get_block(response, COAP_OPTION_BLOCK2, &block) != 0);
175
176 CU_ASSERT(block.num == 0);
177 CU_ASSERT(block.m == 1);
178 CU_ASSERT(1 << (block.szx + 4)
179 == response->token + response->used_size - response->data);
180
181 coap_delete_pdu(response);
182 }
183
184 /* Create wellknown response for request with Block2-option and an szx
185 * value smaller than COAP_MAX_BLOCK_SZX.
186 */
187 static void
t_wellknown5(void)188 t_wellknown5(void) {
189 coap_pdu_t *response;
190 coap_block_t inblock = { .num = 1, .m = 0, .szx = 1 };
191 coap_block_t block;
192 unsigned char buf[3];
193
194 if (!coap_add_option(pdu, COAP_OPTION_BLOCK2,
195 coap_encode_var_safe(buf, sizeof(buf),
196 ((inblock.num << 4) |
197 (inblock.m << 3) |
198 inblock.szx)), buf)) {
199 CU_FAIL("cannot add Block2 option");
200 return;
201 }
202
203 response = coap_wellknown_response(ctx, session, pdu);
204
205 CU_ASSERT_PTR_NOT_NULL(response);
206
207 CU_ASSERT(coap_get_block(response, COAP_OPTION_BLOCK2, &block) != 0);
208
209 CU_ASSERT(block.num == inblock.num);
210 CU_ASSERT(block.m == 1);
211 CU_ASSERT(1 << (block.szx + 4)
212 == response->token + response->used_size - response->data);
213
214 coap_delete_pdu(response);
215 }
216
217 static void
t_wellknown6(void)218 t_wellknown6(void) {
219 coap_pdu_t *response;
220 coap_block_t block = { .num = 0, .szx = 6 };
221 unsigned char buf[TEST_PDU_SIZE];
222
223
224 do {
225 coap_pdu_clear(pdu, pdu->max_size); /* clear PDU */
226
227 pdu->type = COAP_MESSAGE_NON;
228 pdu->code = COAP_REQUEST_CODE_GET;
229 pdu->mid = 0x1234;
230
231 CU_ASSERT_PTR_NOT_NULL(pdu);
232
233 if (!pdu || !coap_add_option(pdu, COAP_OPTION_BLOCK2,
234 coap_encode_var_safe(buf, sizeof(buf),
235 ((block.num << 4) | block.szx)), buf)) {
236 CU_FAIL("cannot create request");
237 return;
238 }
239
240 response = coap_wellknown_response(ctx, session, pdu);
241
242 CU_ASSERT_PTR_NOT_NULL(response);
243
244 /* coap_show_pdu(LOG_INFO, response); */
245
246 CU_ASSERT(coap_get_block(response, COAP_OPTION_BLOCK2, &block) != 0);
247
248 block.num++;
249 coap_delete_pdu(response);
250 } while (block.m == 1);
251 }
252
253 static int
t_wkc_tests_create(void)254 t_wkc_tests_create(void) {
255 coap_address_t addr;
256
257 coap_address_init(&addr);
258
259 addr.size = sizeof(struct sockaddr_in6);
260 addr.addr.sin6.sin6_family = AF_INET6;
261 addr.addr.sin6.sin6_addr = in6addr_any;
262 addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT);
263
264 ctx = coap_new_context(&addr);
265
266 addr.addr.sin6.sin6_addr = in6addr_loopback;
267 session = coap_new_client_session(ctx, NULL, &addr, COAP_PROTO_UDP);
268
269 pdu = coap_pdu_init(0, 0, 0, TEST_PDU_SIZE);
270 #if 0
271 /* add resources to coap context */
272 if (ctx && pdu) {
273 coap_resource_t *r;
274 static char _buf[2 * 1024];
275 unsigned char *buf = (unsigned char *)_buf;
276 int i;
277
278 /* </>;title="some attribute";ct=0 (31 chars) */
279 r = coap_resource_init(NULL, 0, 0);
280
281 coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
282 coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"some attribute\""), 0);
283 coap_add_resource(ctx, r);
284
285 /* ,</abcd>;if="one";obs (21 chars) */
286 r = coap_resource_init((const uint8_t *)"abcd", 4, 0);
287 r->observable = 1;
288 coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"one\""), 0);
289
290 coap_add_resource(ctx, r);
291
292 /* ,</0000> (TEST_URI_LEN + 4 chars) */
293 for (i = 0; i < sizeof(_buf) / (TEST_URI_LEN + 4); i++) {
294 int len = snprintf((char *)buf, TEST_URI_LEN + 1,
295 "%0*d", TEST_URI_LEN, i);
296 r = coap_resource_init(buf, len, 0);
297 coap_add_resource(ctx, r);
298 buf += TEST_URI_LEN + 1;
299 }
300
301 }
302 #endif
303 return ctx == NULL || pdu == NULL;
304 }
305
306 static int
t_wkc_tests_remove(void)307 t_wkc_tests_remove(void) {
308 coap_delete_pdu(pdu);
309 coap_free_context(ctx);
310 return 0;
311 }
312
313 CU_pSuite
t_init_wellknown_tests(void)314 t_init_wellknown_tests(void) {
315 CU_pSuite suite;
316
317 suite = CU_add_suite(".well-known/core", t_wkc_tests_create, t_wkc_tests_remove);
318 if (!suite) { /* signal error */
319 fprintf(stderr, "W: cannot add .well-known/core test suite (%s)\n",
320 CU_get_error_msg());
321
322 return NULL;
323 }
324
325 #define WKC_TEST(s,t) \
326 if (!CU_ADD_TEST(s,t)) { \
327 fprintf(stderr, "W: cannot add .well-known/core test (%s)\n", \
328 CU_get_error_msg()); \
329 }
330
331 WKC_TEST(suite, t_wellknown1);
332 WKC_TEST(suite, t_wellknown2);
333 WKC_TEST(suite, t_wellknown3);
334 WKC_TEST(suite, t_wellknown4);
335 WKC_TEST(suite, t_wellknown5);
336 WKC_TEST(suite, t_wellknown6);
337
338 return suite;
339 }
340
341