1 /* CoAP server for first ETSI CoAP plugtest, March 2012
3 * Copyright (C) 2012--2013 Olaf Bergmann <bergmann@tzi.org>
5 * This file is part of the CoAP library libcoap. Please see
6 * README for terms of use.
14 #include <sys/select.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
29 #define COAP_RESOURCE_CHECK_TIME_SEC 1
32 #define min(a,b) ((a) < (b) ? (a) : (b))
35 /* temporary storage for dynamic resource representations */
38 #define COAP_OPT_BLOCK_SZX_MAX 6 /**< allowed maximum for block szx value */
40 #define REQUIRE_ETAG 0x01 /* flag for coap_payload_t: require ETag option */
44 coap_key_t resource_key; /* foreign key that points into resource space */
45 unsigned int flags; /* some flags to control behavior */
46 size_t max_data; /* maximum size allocated for @p data */
47 uint16_t media_type; /* media type for this object */
48 size_t length; /* length of data */
49 unsigned char data[]; /* the actual contents */
52 coap_payload_t *test_resources = NULL;
55 * This structure is used to store URIs for dynamically allocated
56 * resources, usually by POST or PUT.
61 coap_key_t resource_key; /* foreign key that points into resource space */
62 size_t length; /* length of data */
63 unsigned char data[]; /* the actual contents */
66 coap_dynamic_uri_t *test_dynamic_uris = NULL;
68 /* This variable is used to mimic long-running tasks that require
69 * asynchronous responses. */
70 static coap_async_state_t *async = NULL;
72 /* SIGINT handler: set quit to 1 for graceful termination */
73 void handle_sigint(int signum)
78 #define INDEX "libcoap server for ETSI CoAP Plugtest, March 2012, Paris\n" \
79 "Copyright (C) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
82 coap_new_payload(size_t size)
85 p = (coap_payload_t *) coap_malloc(sizeof(coap_payload_t) + size);
88 memset(p, 0, sizeof(coap_payload_t));
95 static inline coap_payload_t *
96 coap_find_payload(const coap_key_t key)
99 HASH_FIND(hh, test_resources, key, sizeof(coap_key_t), p);
103 static inline void coap_add_payload(const coap_key_t key, coap_payload_t *payload,
104 coap_dynamic_uri_t *uri)
108 memcpy(payload->resource_key, key, sizeof(coap_key_t));
109 HASH_ADD(hh, test_resources, resource_key, sizeof(coap_key_t), payload);
113 memcpy(uri->resource_key, key, sizeof(coap_key_t));
114 HASH_ADD(hh, test_dynamic_uris, resource_key, sizeof(coap_key_t), uri);
118 static inline void coap_delete_payload(coap_payload_t *payload)
122 coap_dynamic_uri_t *uri;
123 HASH_FIND(hh, test_dynamic_uris, payload->resource_key, sizeof(coap_key_t), uri);
126 HASH_DELETE(hh, test_dynamic_uris, uri);
131 HASH_DELETE(hh, test_resources, payload);
135 void hnd_get_index(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
136 coap_pdu_t *request, str *token, coap_pdu_t *response)
138 unsigned char buf[3];
140 response->hdr->code = COAP_RESPONSE_CODE(205);
142 coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
143 coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
145 coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_bytes(buf, 0x2ffff), buf);
147 coap_add_data(response, strlen(INDEX), (unsigned char *) INDEX);
150 void hnd_get_resource(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
151 coap_pdu_t *request, str *token, coap_pdu_t *response)
154 unsigned char buf[2];
155 coap_payload_t *test_payload;
158 test_payload = coap_find_payload(resource->key);
161 response->hdr->code = COAP_RESPONSE_CODE(500);
166 response->hdr->code = COAP_RESPONSE_CODE(205);
168 coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
169 coap_encode_var_bytes(buf, test_payload->media_type), buf);
171 /* add etag for the resource */
172 if (test_payload->flags & REQUIRE_ETAG)
174 memset(etag, 0, sizeof(etag));
175 coap_hash(test_payload->data, test_payload->length, etag);
176 coap_add_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
183 if (coap_get_block(request, COAP_OPTION_BLOCK2, &block))
185 res = coap_write_block_opt(&block, COAP_OPTION_BLOCK2, response, test_payload->length);
189 case -2: /* illegal block */
190 response->hdr->code = COAP_RESPONSE_CODE(400);
192 case -1: /* should really not happen */
194 /* fall through if assert is a no-op */
195 case -3: /* cannot handle request */
196 response->hdr->code = COAP_RESPONSE_CODE(500);
198 default: /* everything is good */
202 coap_add_block(response, test_payload->length, test_payload->data, block.num,
207 if (!coap_add_data(response, test_payload->length, test_payload->data))
209 /* set initial block size, will be lowered by
210 * coap_write_block_opt) automatically */
212 coap_write_block_opt(&block, COAP_OPTION_BLOCK2, response, test_payload->length);
214 coap_add_block(response, test_payload->length, test_payload->data, block.num,
220 { /* this is a notification, block is 0 */
221 /* FIXME: need to store block size with subscription */
226 error: coap_add_data(response, strlen(coap_response_phrase(response->hdr->code)),
227 (unsigned char *) coap_response_phrase(response->hdr->code));
230 /* DELETE handler for dynamic resources created by POST /test */
231 void hnd_delete_resource(coap_context_t *ctx, struct coap_resource_t *resource,
232 coap_address_t *peer, coap_pdu_t *request, str *token, coap_pdu_t *response)
234 coap_payload_t *payload;
236 payload = coap_find_payload(resource->key);
239 coap_delete_payload(payload);
241 coap_delete_resource(ctx, resource->key);
243 response->hdr->code = COAP_RESPONSE_CODE(202);
246 void hnd_post_test(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
247 coap_pdu_t *request, str *token, coap_pdu_t *response)
249 coap_opt_iterator_t opt_iter;
251 coap_payload_t *test_payload;
253 size_t l = 6 + sizeof(void *);
254 coap_dynamic_uri_t *uri;
259 unsigned char _buf[BUFSIZE];
260 unsigned char *buf = _buf;
261 size_t buflen = BUFSIZE;
263 coap_get_data(request, &len, &data);
265 /* allocate storage for resource and to hold URI */
266 test_payload = coap_new_payload(len);
267 uri = (coap_dynamic_uri_t *) coap_malloc(sizeof(coap_dynamic_uri_t) + l);
268 if (!(test_payload && uri))
270 coap_log(LOG_CRIT, "cannot allocate new resource under /test");
271 response->hdr->code = COAP_RESPONSE_CODE(500);
272 coap_free(test_payload);
279 memset(uri, 0, sizeof(coap_dynamic_uri_t));
280 uri->length = min(l, snprintf((char *)uri->data, l, "test/%p", test_payload));
281 test_payload->length = len;
283 memcpy(test_payload->data, data, len);
285 r = coap_resource_init(uri->data, uri->length, 0);
286 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
287 coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
289 /* set media_type if available */
290 option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
293 test_payload->media_type = coap_decode_var_bytes(COAP_OPT_VALUE(option),
294 COAP_OPT_LENGTH(option));
297 coap_add_resource(ctx, r);
298 coap_add_payload(r->key, test_payload, uri);
300 /* add Location-Path */
301 res = coap_split_path(uri->data, uri->length, buf, &buflen);
305 coap_add_option(response, COAP_OPTION_LOCATION_PATH, COAP_OPT_LENGTH(buf),
306 COAP_OPT_VALUE(buf));
308 buf += COAP_OPT_SIZE(buf);
311 response->hdr->code = COAP_RESPONSE_CODE(201);
316 void hnd_put_test(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
317 coap_pdu_t *request, str *token, coap_pdu_t *response)
319 coap_opt_iterator_t opt_iter;
321 coap_payload_t *payload;
325 response->hdr->code = COAP_RESPONSE_CODE(204);
327 coap_get_data(request, &len, &data);
329 payload = coap_find_payload(resource->key);
330 if (payload && payload->max_data < len)
331 { /* need more storage */
332 coap_delete_payload(payload);
334 /* bug: when subsequent coap_new_payload() fails, our old contents
339 { /* create new payload */
340 payload = coap_new_payload(len);
344 coap_add_payload(resource->key, payload, NULL);
346 payload->length = len;
347 memcpy(payload->data, data, len);
349 option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
352 /* set media type given in request */
353 payload->media_type = coap_decode_var_bytes(COAP_OPT_VALUE(option),
354 COAP_OPT_LENGTH(option));
358 /* set default value */
359 payload->media_type = COAP_MEDIATYPE_TEXT_PLAIN;
361 /* FIXME: need to change attribute ct of resource.
362 To do so, we need dynamic management of the attribute value
367 warn("cannot modify resource\n");
368 response->hdr->code = COAP_RESPONSE_CODE(500);
371 void hnd_delete_test(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
372 coap_pdu_t *request, str *token, coap_pdu_t *response)
374 /* the ETSI validation tool does not like empty resources... */
376 coap_payload_t *payload;
377 payload = coap_find_payload(resource->key);
383 response->hdr->code = COAP_RESPONSE_CODE(202);
386 void hnd_get_query(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
387 coap_pdu_t *request, str *token, coap_pdu_t *response)
389 coap_opt_iterator_t opt_iter;
393 unsigned char buf[70];
395 response->hdr->code = COAP_RESPONSE_CODE(205);
397 coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
398 coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
400 coap_option_filter_clear(f);
401 coap_option_setb(f, COAP_OPTION_URI_QUERY);
403 coap_option_iterator_init(request, &opt_iter, f);
406 while ((len < sizeof(buf)) && (q = coap_option_next(&opt_iter)))
408 L = min(sizeof(buf) - len, 11);
409 memcpy(buf + len, "Uri-Query: ", L);
412 L = min(sizeof(buf) - len, COAP_OPT_LENGTH(q));
413 memcpy(buf + len, COAP_OPT_VALUE(q), L);
416 if (len < sizeof(buf))
420 coap_add_data(response, len, buf);
423 /* handler for TD_COAP_CORE_16 */
424 void hnd_get_separate(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
425 coap_pdu_t *request, str *token, coap_pdu_t *response)
427 coap_opt_iterator_t opt_iter;
430 unsigned long delay = 5;
434 if (async->id != request->hdr->id)
437 coap_option_filter_clear(f);
438 response->hdr->code = COAP_RESPONSE_CODE(503);
443 /* search for option delay in query list */
444 coap_option_filter_clear(f);
445 coap_option_setb(f, COAP_OPTION_URI_QUERY);
447 coap_option_iterator_init(request, &opt_iter, f);
449 while ((option = coap_option_next(&opt_iter)))
451 if (strncmp("delay=", (char *) COAP_OPT_VALUE(option), 6) == 0)
456 for (i = 6; i < COAP_OPT_LENGTH(option); ++i)
457 d = d * 10 + COAP_OPT_VALUE(option)[i] - '0';
459 /* don't allow delay to be less than COAP_RESOURCE_CHECK_TIME*/
460 delay = d < COAP_RESOURCE_CHECK_TIME_SEC ? COAP_RESOURCE_CHECK_TIME_SEC : d;
461 debug("set delay to %lu\n", delay);
466 async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE,
467 (void *) (COAP_TICKS_PER_SECOND * delay));
470 void check_async(coap_context_t *ctx, coap_tick_t now)
472 coap_pdu_t *response;
473 coap_async_state_t *tmp;
474 unsigned char buf[2];
475 size_t size = sizeof(coap_hdr_t) + 8;
477 if (!async || now < async->created + (unsigned long) async->appdata)
480 size += async->tokenlen;
482 response = coap_pdu_init(async->flags & COAP_ASYNC_CONFIRM ? COAP_MESSAGE_CON : COAP_MESSAGE_NON,
483 COAP_RESPONSE_CODE(205), 0, size);
486 debug("check_async: insufficient memory, we'll try later\n");
487 async->appdata = (void *) ((unsigned long) async->appdata + 15 * COAP_TICKS_PER_SECOND);
491 response->hdr->id = coap_new_message_id(ctx);
494 coap_add_token(response, async->tokenlen, async->token);
496 coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
497 coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
499 coap_add_data(response, 4, (unsigned char *) "done");
501 if (coap_send(ctx, &async->peer, response) == COAP_INVALID_TID)
503 debug("check_async: cannot send response for message %d\n", response->hdr->id);
505 coap_delete_pdu(response);
507 coap_remove_async(ctx, async->id, &tmp);
508 coap_free_async(async);
513 make_large(char *filename)
515 coap_payload_t *payload;
516 FILE *inputfile = NULL;
522 /* read from specified input file */
523 if (stat(filename, &statbuf) < 0)
525 warn("cannot stat file %s\n", filename);
529 payload = coap_new_payload(statbuf.st_size);
533 inputfile = fopen(filename, "r");
536 warn("cannot read file %s\n", filename);
541 payload->length = fread(payload->data, 1, statbuf.st_size, inputfile);
542 payload->media_type = 41;
549 void init_resources(coap_context_t *ctx)
552 coap_payload_t *test_payload;
554 test_payload = coap_new_payload(200);
556 coap_log(LOG_CRIT, "cannot allocate resource /test");
559 test_payload->length = 13;
560 memcpy(test_payload->data, "put data here", test_payload->length);
561 /* test_payload->media_type is 0 anyway */
563 r = coap_resource_init((unsigned char *) "test", 4, 0);
564 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
565 coap_register_handler(r, COAP_REQUEST_POST, hnd_post_test);
566 coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_test);
567 coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_test);
569 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
570 coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "test", 4, 0);
571 coap_add_attr(r, (unsigned char *) "if", 2, (unsigned char *) "core#b", 6, 0);
573 coap_add_attr(r, (unsigned char *)"obs", 3, NULL, 0, 0);
575 coap_add_resource(ctx, r);
576 coap_add_payload(r->key, test_payload, NULL);
580 * TD_COAP_BLOCK_02 */
581 test_payload = make_large("etsi_iot_01_largedata.txt");
583 coap_log(LOG_CRIT, "cannot allocate resource /large\n");
586 r = coap_resource_init((unsigned char *) "large", 5, 0);
587 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
589 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "41", 2, 0);
590 coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "large", 5, 0);
591 coap_add_resource(ctx, r);
593 test_payload->flags |= REQUIRE_ETAG;
595 coap_add_payload(r->key, test_payload, NULL);
598 /* For TD_COAP_CORE_12 */
599 test_payload = coap_new_payload(20);
601 coap_log(LOG_CRIT, "cannot allocate resource /seg1/seg2/seg3\n");
604 test_payload->length = 10;
605 memcpy(test_payload->data, "segsegseg!", test_payload->length);
606 /* test_payload->media_type is 0 anyway */
608 r = coap_resource_init((unsigned char *) "seg1/seg2/seg3", 14, 0);
609 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
611 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
612 coap_add_resource(ctx, r);
614 coap_add_payload(r->key, test_payload, NULL);
617 /* For TD_COAP_CORE_13 */
618 r = coap_resource_init((unsigned char *) "query", 5, 0);
619 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_query);
621 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
622 coap_add_resource(ctx, r);
624 /* For TD_COAP_CORE_16 */
625 r = coap_resource_init((unsigned char *) "separate", 8, 0);
626 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_separate);
628 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
629 coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "separate", 8, 0);
630 coap_add_resource(ctx, r);
633 void usage(const char *program, const char *version)
637 p = strrchr(program, '/');
641 fprintf(stderr, "%s v%s -- ETSI CoAP plugtest server\n"
642 "(c) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
643 "usage: %s [-A address] [-p port]\n\n"
644 "\t-A address\tinterface address to bind to\n"
645 "\t-p port\t\tlisten on specified port\n"
646 "\t-v num\t\tverbosity level (default: 3)\n", program, version, program);
650 get_context(const char *node, const char *port)
652 coap_context_t *ctx = NULL;
654 struct addrinfo hints;
655 struct addrinfo *result, *rp;
657 memset(&hints, 0, sizeof(struct addrinfo));
658 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
659 hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
660 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
662 s = getaddrinfo(node, port, &hints, &result);
665 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
669 /* iterate through results until success */
670 for (rp = result; rp != NULL; rp = rp->ai_next)
674 if (rp->ai_addrlen <= sizeof(addr.addr))
676 coap_address_init(&addr);
677 addr.size = rp->ai_addrlen;
678 memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
680 ctx = coap_new_context(&addr);
683 /* TODO: output address:port for successful binding */
689 fprintf(stderr, "no context available for interface '%s'\n", node);
691 finish: freeaddrinfo(result);
695 int main(int argc, char **argv)
699 struct timeval tv, *timeout;
702 coap_queue_t *nextpdu;
703 char addr_str[NI_MAXHOST] = "::";
704 char port_str[NI_MAXSERV] = "5683";
706 coap_log_t log_level = LOG_WARNING;
708 while ((opt = getopt(argc, argv, "A:p:v:")) != -1)
713 strncpy(addr_str, optarg, NI_MAXHOST - 1);
714 addr_str[NI_MAXHOST - 1] = '\0';
717 strncpy(port_str, optarg, NI_MAXSERV - 1);
718 port_str[NI_MAXSERV - 1] = '\0';
721 log_level = strtol(optarg, NULL, 10);
724 usage(argv[0], PACKAGE_VERSION);
729 coap_set_log_level(log_level);
731 ctx = get_context(addr_str, port_str);
735 coap_register_option(ctx, COAP_OPTION_BLOCK2);
739 signal(SIGINT, handle_sigint);
744 FD_SET(ctx->sockfd, &readfds);
746 nextpdu = coap_peek_next(ctx);
749 while (nextpdu && nextpdu->t <= now)
751 coap_retransmit(ctx, coap_pop_next(ctx));
752 nextpdu = coap_peek_next(ctx);
755 if (nextpdu && nextpdu->t <= now + COAP_RESOURCE_CHECK_TIME_SEC)
757 /* set timeout if there is a pdu to send before our automatic timeout occurs */
758 tv.tv_usec = ((nextpdu->t - now) % COAP_TICKS_PER_SECOND) * 1000000
759 / COAP_TICKS_PER_SECOND;
760 tv.tv_sec = (nextpdu->t - now) / COAP_TICKS_PER_SECOND;
766 tv.tv_sec = COAP_RESOURCE_CHECK_TIME_SEC;
769 result = select(FD_SETSIZE, &readfds, 0, 0, timeout);
777 { /* read from socket */
778 if (FD_ISSET(ctx->sockfd, &readfds))
780 coap_read(ctx); /* read received data */
781 coap_dispatch(ctx); /* and dispatch PDUs from receivequeue */
786 /* coap_check_resource_list( ctx ); */
789 /* check if we have to send asynchronous responses */
790 check_async(ctx, now);
793 coap_free_context(ctx);