1 /* coap -- simple implementation of the Constrained Application Protocol (CoAP)
2 * as defined in draft-ietf-core-coap
4 * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
6 * This file is part of the CoAP library libcoap. Please see
7 * README for terms of use.
15 #include <sys/select.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
30 #define COAP_RESOURCE_CHECK_TIME 2
33 #define min(a,b) ((a) < (b) ? (a) : (b))
36 /* temporary storage for dynamic resource representations */
39 /* changeable clock base (see handle_put_time()) */
40 static time_t my_clock_base = 0;
42 struct coap_resource_t *time_resource = NULL;
45 /* This variable is used to mimic long-running tasks that require
46 * asynchronous responses. */
47 static coap_async_state_t *async = NULL;
48 #endif /* WITHOUT_ASYNC */
50 /* SIGINT handler: set quit to 1 for graceful termination */
51 void handle_sigint(int signum)
56 #define INDEX "This is a test server made with libcoap (see http://libcoap.sf.net)\n" \
57 "Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>\n\n"
59 void hnd_get_index(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
60 coap_pdu_t *request, str *token, coap_pdu_t *response)
64 response->hdr->code = COAP_RESPONSE_CODE(205);
66 coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
67 coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
69 coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_bytes(buf, 0x2ffff), buf);
71 coap_add_data(response, strlen(INDEX), (unsigned char *) INDEX);
74 void hnd_get_time(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
75 coap_pdu_t *request, str *token, coap_pdu_t *response)
77 coap_opt_iterator_t opt_iter;
79 unsigned char buf[40];
83 coap_subscription_t *subscription;
85 /* FIXME: return time, e.g. in human-readable by default and ticks
86 * when query ?ticks is given. */
88 /* if my_clock_base was deleted, we pretend to have no such resource */
89 response->hdr->code = my_clock_base ? COAP_RESPONSE_CODE(205) : COAP_RESPONSE_CODE(404);
91 if (request != NULL && coap_check_option(request, COAP_OPTION_OBSERVE, &opt_iter))
93 subscription = coap_add_observer(resource, peer, token);
96 subscription->non = request->hdr->type == COAP_MESSAGE_NON;
97 coap_add_option(response, COAP_OPTION_OBSERVE, 0, NULL);
100 if (resource->dirty == 1)
101 coap_add_option(response, COAP_OPTION_OBSERVE, coap_encode_var_bytes(buf, ctx->observe),
105 coap_add_option(response, COAP_OPTION_CONTENT_FORMAT,
106 coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
108 coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_bytes(buf, 0x01), buf);
113 /* calculate current time */
115 now = my_clock_base + (t / COAP_TICKS_PER_SECOND);
118 && (option = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter))
119 && memcmp(COAP_OPT_VALUE(option), "ticks", min(5, COAP_OPT_LENGTH(option))) == 0)
122 len = snprintf((char *) buf, min(sizeof(buf), response->max_size - response->length),
123 "%u", (unsigned int) now);
124 coap_add_data(response, len, buf);
128 { /* output human-readable time */
131 len = strftime((char *) buf, min(sizeof(buf), response->max_size - response->length),
132 "%b %d %H:%M:%S", tmp);
133 coap_add_data(response, len, buf);
138 void hnd_put_time(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
139 coap_pdu_t *request, str *token, coap_pdu_t *response)
145 /* FIXME: re-set my_clock_base to clock_offset if my_clock_base == 0
146 * and request is empty. When not empty, set to value in request payload
147 * (insist on query ?ticks). Return Created or Ok.
150 /* if my_clock_base was deleted, we pretend to have no such resource */
151 response->hdr->code = my_clock_base ? COAP_RESPONSE_CODE(204) : COAP_RESPONSE_CODE(201);
155 coap_get_data(request, &size, &data);
157 if (size == 0) /* re-init */
158 my_clock_base = clock_offset;
164 my_clock_base = my_clock_base * 10 + *data++;
165 my_clock_base -= t / COAP_TICKS_PER_SECOND;
169 void hnd_delete_time(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
170 coap_pdu_t *request, str *token, coap_pdu_t *response)
172 my_clock_base = 0; /* mark clock as "deleted" */
174 /* type = request->hdr->type == COAP_MESSAGE_CON */
175 /* ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON; */
178 #ifndef WITHOUT_ASYNC
179 void hnd_get_async(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
180 coap_pdu_t *request, str *token, coap_pdu_t *response)
182 coap_opt_iterator_t opt_iter;
184 unsigned long delay = 5;
189 if (async->id != request->hdr->id)
192 coap_option_filter_clear(f);
193 response->hdr->code = COAP_RESPONSE_CODE(503);
198 option = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter);
201 unsigned char *p = COAP_OPT_VALUE(option);
204 for (size = COAP_OPT_LENGTH(option); size; --size, ++p)
205 delay = delay * 10 + (*p - '0');
208 async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM,
209 (void *) (COAP_TICKS_PER_SECOND * delay));
212 void check_async(coap_context_t *ctx, coap_tick_t now)
214 coap_pdu_t *response;
215 coap_async_state_t *tmp;
217 size_t size = sizeof(coap_hdr_t) + 8;
219 if (!async || now < async->created + (unsigned long) async->appdata)
222 response = coap_pdu_init(async->flags & COAP_ASYNC_CONFIRM ? COAP_MESSAGE_CON : COAP_MESSAGE_NON,
223 COAP_RESPONSE_CODE(205), 0, size);
226 debug("check_async: insufficient memory, we'll try later\n");
227 async->appdata = (void *) ((unsigned long) async->appdata + 15 * COAP_TICKS_PER_SECOND);
231 response->hdr->id = coap_new_message_id(ctx);
234 coap_add_token(response, async->tokenlen, async->token);
236 coap_add_data(response, 4, (unsigned char *) "done");
238 if (coap_send(ctx, &async->peer, response) == COAP_INVALID_TID)
240 debug("check_async: cannot send response for message %d\n", response->hdr->id);
242 coap_delete_pdu(response);
243 coap_remove_async(ctx, async->id, &tmp);
244 coap_free_async(async);
247 #endif /* WITHOUT_ASYNC */
249 void init_resources(coap_context_t *ctx)
253 r = coap_resource_init(NULL, 0, 0);
254 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);
256 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
257 coap_add_attr(r, (unsigned char *) "title", 5, (unsigned char *) "\"General Info\"", 14, 0);
258 coap_add_resource(ctx, r);
260 /* store clock base to use in /time */
261 my_clock_base = clock_offset;
263 r = coap_resource_init((unsigned char *) "time", 4, 0);
264 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
265 coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
266 coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
268 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
269 coap_add_attr(r, (unsigned char *) "title", 5, (unsigned char *) "\"Internal Clock\"", 16, 0);
270 coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "\"Ticks\"", 7, 0);
272 coap_add_attr(r, (unsigned char *) "if", 2, (unsigned char *) "\"clock\"", 7, 0);
274 coap_add_resource(ctx, r);
277 #ifndef WITHOUT_ASYNC
278 r = coap_resource_init((unsigned char *) "async", 5, 0);
279 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async);
281 coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "0", 1, 0);
282 coap_add_resource(ctx, r);
283 #endif /* WITHOUT_ASYNC */
286 void usage(const char *program, const char *version)
290 p = strrchr(program, '/');
294 fprintf(stderr, "%s v%s -- a small CoAP implementation\n"
295 "(c) 2010,2011 Olaf Bergmann <bergmann@tzi.org>\n\n"
296 "usage: %s [-A address] [-p port]\n\n"
297 "\t-A address\tinterface address to bind to\n"
298 "\t-p port\t\tlisten on specified port\n"
299 "\t-v num\t\tverbosity level (default: 3)\n", program, version, program);
303 get_context(const char *node, const char *port)
305 coap_context_t *ctx = NULL;
307 struct addrinfo hints;
308 struct addrinfo *result, *rp;
310 memset(&hints, 0, sizeof(struct addrinfo));
311 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
312 hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
313 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
315 s = getaddrinfo(node, port, &hints, &result);
318 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
322 /* iterate through results until success */
323 for (rp = result; rp != NULL; rp = rp->ai_next)
327 if (rp->ai_addrlen <= sizeof(addr.addr))
329 coap_address_init(&addr);
330 addr.size = rp->ai_addrlen;
331 memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
333 ctx = coap_new_context(&addr);
336 /* TODO: output address:port for successful binding */
342 fprintf(stderr, "no context available for interface '%s'\n", node);
344 finish: freeaddrinfo(result);
348 int main(int argc, char **argv)
352 struct timeval tv, *timeout;
355 coap_queue_t *nextpdu;
356 char addr_str[NI_MAXHOST] = "::";
357 char port_str[NI_MAXSERV] = "5683";
359 coap_log_t log_level = LOG_WARNING;
361 while ((opt = getopt(argc, argv, "A:p:v:")) != -1)
366 strncpy(addr_str, optarg, NI_MAXHOST - 1);
367 addr_str[NI_MAXHOST - 1] = '\0';
370 strncpy(port_str, optarg, NI_MAXSERV - 1);
371 port_str[NI_MAXSERV - 1] = '\0';
374 log_level = strtol(optarg, NULL, 10);
377 usage(argv[0], PACKAGE_VERSION);
382 coap_set_log_level(log_level);
384 ctx = get_context(addr_str, port_str);
390 signal(SIGINT, handle_sigint);
395 FD_SET(ctx->sockfd, &readfds);
397 nextpdu = coap_peek_next(ctx);
400 while (nextpdu && nextpdu->t <= now - ctx->sendqueue_basetime)
402 coap_retransmit(ctx, coap_pop_next(ctx));
403 nextpdu = coap_peek_next(ctx);
406 if (nextpdu && nextpdu->t <= COAP_RESOURCE_CHECK_TIME)
408 /* set timeout if there is a pdu to send before our automatic timeout occurs */
409 tv.tv_usec = ((nextpdu->t) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
410 tv.tv_sec = (nextpdu->t) / COAP_TICKS_PER_SECOND;
416 tv.tv_sec = COAP_RESOURCE_CHECK_TIME;
419 result = select(FD_SETSIZE, &readfds, 0, 0, timeout);
427 { /* read from socket */
428 if (FD_ISSET(ctx->sockfd, &readfds))
430 coap_read(ctx); /* read received data */
431 coap_dispatch(ctx); /* and dispatch PDUs from receivequeue */
438 time_resource->dirty = 1;
442 #ifndef WITHOUT_ASYNC
443 /* check if we have to send asynchronous responses */
444 check_async(ctx, now);
445 #endif /* WITHOUT_ASYNC */
447 #ifndef WITHOUT_OBSERVE
448 /* check if we have to send observe notifications */
449 coap_check_notify(ctx);
450 #endif /* WITHOUT_OBSERVE */
453 coap_free_context(ctx);