1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 * -*- */
2
3 /* coap -- simple implementation of the Constrained Application Protocol (CoAP)
4 * as defined in RFC 7252
5 *
6 * Copyright (C) 2010--2016 Olaf Bergmann <bergmann@tzi.org>
7 *
8 * This file is part of the CoAP library libcoap. Please see README for terms
9 * of use.
10 */
11
12 #include <string.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <sys/select.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <netdb.h>
23 #include <sys/stat.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <signal.h>
27
28 #include "coap_config.h"
29 #include "resource.h"
30 #include "coap.h"
31
32 #define COAP_RESOURCE_CHECK_TIME 2
33
34 #ifndef min
35 #define min(a,b) ((a) < (b) ? (a) : (b))
36 #endif
37
38 /* temporary storage for dynamic resource representations */
39 static int quit = 0;
40
41 /* changeable clock base (see handle_put_time()) */
42 static time_t clock_offset;
43 static time_t my_clock_base = 0;
44
45 struct coap_resource_t *time_resource = NULL;
46
47 #ifndef WITHOUT_ASYNC
48 /* This variable is used to mimic long-running tasks that require
49 * asynchronous responses. */
50 static coap_async_state_t *async = NULL;
51 #endif /* WITHOUT_ASYNC */
52
53 #ifdef __GNUC__
54 #define UNUSED_PARAM __attribute__ ((unused))
55 #else /* not a GCC */
56 #define UNUSED_PARAM
57 #endif /* GCC */
58
59 /* SIGINT handler: set quit to 1 for graceful termination */
60 static void
handle_sigint(int signum UNUSED_PARAM)61 handle_sigint(int signum UNUSED_PARAM) {
62 quit = 1;
63 }
64
65 #define INDEX "This is a test server made with libcoap (see https://libcoap.net)\n" \
66 "Copyright (C) 2010--2016 Olaf Bergmann <bergmann@tzi.org>\n\n"
67
68 static void
hnd_get_index(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource UNUSED_PARAM,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,str * token UNUSED_PARAM,coap_pdu_t * response)69 hnd_get_index(coap_context_t *ctx UNUSED_PARAM,
70 struct coap_resource_t *resource UNUSED_PARAM,
71 const coap_endpoint_t *local_interface UNUSED_PARAM,
72 coap_address_t *peer UNUSED_PARAM,
73 coap_pdu_t *request UNUSED_PARAM,
74 str *token UNUSED_PARAM,
75 coap_pdu_t *response) {
76 unsigned char buf[3];
77
78 response->hdr->code = COAP_RESPONSE_CODE(205);
79
80 coap_add_option(response,
81 COAP_OPTION_CONTENT_TYPE,
82 coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
83
84 coap_add_option(response,
85 COAP_OPTION_MAXAGE,
86 coap_encode_var_bytes(buf, 0x2ffff), buf);
87
88 coap_add_data(response, strlen(INDEX), (unsigned char *)INDEX);
89 }
90
91 static void
hnd_get_time(coap_context_t * ctx,struct coap_resource_t * resource,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)92 hnd_get_time(coap_context_t *ctx,
93 struct coap_resource_t *resource,
94 const coap_endpoint_t *local_interface UNUSED_PARAM,
95 coap_address_t *peer,
96 coap_pdu_t *request,
97 str *token,
98 coap_pdu_t *response) {
99 coap_opt_iterator_t opt_iter;
100 coap_opt_t *option;
101 unsigned char buf[40];
102 size_t len;
103 time_t now;
104 coap_tick_t t;
105
106 /* FIXME: return time, e.g. in human-readable by default and ticks
107 * when query ?ticks is given. */
108
109 /* if my_clock_base was deleted, we pretend to have no such resource */
110 response->hdr->code =
111 my_clock_base ? COAP_RESPONSE_CODE(205) : COAP_RESPONSE_CODE(404);
112
113 if (coap_find_observer(resource, peer, token)) {
114 /* FIXME: need to check for resource->dirty? */
115 coap_add_option(response,
116 COAP_OPTION_OBSERVE,
117 coap_encode_var_bytes(buf, ctx->observe), buf);
118 }
119
120 if (my_clock_base)
121 coap_add_option(response,
122 COAP_OPTION_CONTENT_FORMAT,
123 coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
124
125 coap_add_option(response,
126 COAP_OPTION_MAXAGE,
127 coap_encode_var_bytes(buf, 0x01), buf);
128
129 if (my_clock_base) {
130
131 /* calculate current time */
132 coap_ticks(&t);
133 now = my_clock_base + (t / COAP_TICKS_PER_SECOND);
134
135 if (request != NULL
136 && (option = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter))
137 && memcmp(COAP_OPT_VALUE(option), "ticks",
138 min(5, COAP_OPT_LENGTH(option))) == 0) {
139 /* output ticks */
140 len = snprintf((char *)buf,
141 min(sizeof(buf),
142 response->max_size - response->length),
143 "%u", (unsigned int)now);
144 coap_add_data(response, len, buf);
145
146 } else { /* output human-readable time */
147 struct tm *tmp;
148 tmp = gmtime(&now);
149 len = strftime((char *)buf,
150 min(sizeof(buf),
151 response->max_size - response->length),
152 "%b %d %H:%M:%S", tmp);
153 coap_add_data(response, len, buf);
154 }
155 }
156 }
157
158 static void
hnd_put_time(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource UNUSED_PARAM,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer UNUSED_PARAM,coap_pdu_t * request,str * token UNUSED_PARAM,coap_pdu_t * response)159 hnd_put_time(coap_context_t *ctx UNUSED_PARAM,
160 struct coap_resource_t *resource UNUSED_PARAM,
161 const coap_endpoint_t *local_interface UNUSED_PARAM,
162 coap_address_t *peer UNUSED_PARAM,
163 coap_pdu_t *request,
164 str *token UNUSED_PARAM,
165 coap_pdu_t *response) {
166 coap_tick_t t;
167 size_t size;
168 unsigned char *data;
169
170 /* FIXME: re-set my_clock_base to clock_offset if my_clock_base == 0
171 * and request is empty. When not empty, set to value in request payload
172 * (insist on query ?ticks). Return Created or Ok.
173 */
174
175 /* if my_clock_base was deleted, we pretend to have no such resource */
176 response->hdr->code =
177 my_clock_base ? COAP_RESPONSE_CODE(204) : COAP_RESPONSE_CODE(201);
178
179 resource->dirty = 1;
180
181 coap_get_data(request, &size, &data);
182
183 if (size == 0) /* re-init */
184 my_clock_base = clock_offset;
185 else {
186 my_clock_base = 0;
187 coap_ticks(&t);
188 while(size--)
189 my_clock_base = my_clock_base * 10 + *data++;
190 my_clock_base -= t / COAP_TICKS_PER_SECOND;
191 }
192 }
193
194 static void
hnd_delete_time(coap_context_t * ctx UNUSED_PARAM,struct coap_resource_t * resource UNUSED_PARAM,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer UNUSED_PARAM,coap_pdu_t * request UNUSED_PARAM,str * token UNUSED_PARAM,coap_pdu_t * response UNUSED_PARAM)195 hnd_delete_time(coap_context_t *ctx UNUSED_PARAM,
196 struct coap_resource_t *resource UNUSED_PARAM,
197 const coap_endpoint_t *local_interface UNUSED_PARAM,
198 coap_address_t *peer UNUSED_PARAM,
199 coap_pdu_t *request UNUSED_PARAM,
200 str *token UNUSED_PARAM,
201 coap_pdu_t *response UNUSED_PARAM) {
202 my_clock_base = 0; /* mark clock as "deleted" */
203
204 /* type = request->hdr->type == COAP_MESSAGE_CON */
205 /* ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON; */
206 }
207
208 #ifndef WITHOUT_ASYNC
209 static void
hnd_get_async(coap_context_t * ctx,struct coap_resource_t * resource UNUSED_PARAM,const coap_endpoint_t * local_interface UNUSED_PARAM,coap_address_t * peer,coap_pdu_t * request,str * token UNUSED_PARAM,coap_pdu_t * response)210 hnd_get_async(coap_context_t *ctx,
211 struct coap_resource_t *resource UNUSED_PARAM,
212 const coap_endpoint_t *local_interface UNUSED_PARAM,
213 coap_address_t *peer,
214 coap_pdu_t *request,
215 str *token UNUSED_PARAM,
216 coap_pdu_t *response) {
217 coap_opt_iterator_t opt_iter;
218 coap_opt_t *option;
219 unsigned long delay = 5;
220 size_t size;
221
222 if (async) {
223 if (async->id != request->hdr->id) {
224 coap_opt_filter_t f;
225 coap_option_filter_clear(f);
226 response->hdr->code = COAP_RESPONSE_CODE(503);
227 }
228 return;
229 }
230
231 option = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter);
232 if (option) {
233 unsigned char *p = COAP_OPT_VALUE(option);
234
235 delay = 0;
236 for (size = COAP_OPT_LENGTH(option); size; --size, ++p)
237 delay = delay * 10 + (*p - '0');
238 }
239
240 async = coap_register_async(ctx,
241 peer,
242 request,
243 COAP_ASYNC_SEPARATE | COAP_ASYNC_CONFIRM,
244 (void *)(COAP_TICKS_PER_SECOND * delay));
245 }
246
247 static void
check_async(coap_context_t * ctx,const coap_endpoint_t * local_if,coap_tick_t now)248 check_async(coap_context_t *ctx,
249 const coap_endpoint_t *local_if,
250 coap_tick_t now) {
251 coap_pdu_t *response;
252 coap_async_state_t *tmp;
253
254 size_t size = sizeof(coap_hdr_t) + 13;
255
256 if (!async || now < async->created + (unsigned long)async->appdata)
257 return;
258
259 response = coap_pdu_init(async->flags & COAP_ASYNC_CONFIRM
260 ? COAP_MESSAGE_CON
261 : COAP_MESSAGE_NON,
262 COAP_RESPONSE_CODE(205), 0, size);
263 if (!response) {
264 debug("check_async: insufficient memory, we'll try later\n");
265 async->appdata =
266 (void *)((unsigned long)async->appdata + 15 * COAP_TICKS_PER_SECOND);
267 return;
268 }
269
270 response->hdr->id = coap_new_message_id(ctx);
271
272 if (async->tokenlen)
273 coap_add_token(response, async->tokenlen, async->token);
274
275 coap_add_data(response, 4, (unsigned char *)"done");
276
277 if (coap_send(ctx, local_if, &async->peer, response) == COAP_INVALID_TID) {
278 debug("check_async: cannot send response for message %d\n",
279 response->hdr->id);
280 }
281 coap_delete_pdu(response);
282 coap_remove_async(ctx, async->id, &tmp);
283 coap_free_async(async);
284 async = NULL;
285 }
286 #endif /* WITHOUT_ASYNC */
287
288 static void
init_resources(coap_context_t * ctx)289 init_resources(coap_context_t *ctx) {
290 coap_resource_t *r;
291
292 r = coap_resource_init(NULL, 0, 0);
293 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);
294
295 coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"0", 1, 0);
296 coap_add_attr(r, (unsigned char *)"title", 5, (unsigned char *)"\"General Info\"", 14, 0);
297 coap_add_resource(ctx, r);
298
299 /* store clock base to use in /time */
300 my_clock_base = clock_offset;
301
302 r = coap_resource_init((unsigned char *)"time", 4, COAP_RESOURCE_FLAGS_NOTIFY_CON);
303 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
304 coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
305 coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
306
307 coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"0", 1, 0);
308 coap_add_attr(r, (unsigned char *)"title", 5, (unsigned char *)"\"Internal Clock\"", 16, 0);
309 coap_add_attr(r, (unsigned char *)"rt", 2, (unsigned char *)"\"Ticks\"", 7, 0);
310 r->observable = 1;
311 coap_add_attr(r, (unsigned char *)"if", 2, (unsigned char *)"\"clock\"", 7, 0);
312
313 coap_add_resource(ctx, r);
314 time_resource = r;
315
316 #ifndef WITHOUT_ASYNC
317 r = coap_resource_init((unsigned char *)"async", 5, 0);
318 coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async);
319
320 coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"0", 1, 0);
321 coap_add_resource(ctx, r);
322 #endif /* WITHOUT_ASYNC */
323 }
324
325 static void
usage(const char * program,const char * version)326 usage( const char *program, const char *version) {
327 const char *p;
328
329 p = strrchr( program, '/' );
330 if ( p )
331 program = ++p;
332
333 fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
334 "(c) 2010,2011,2015 Olaf Bergmann <bergmann@tzi.org>\n\n"
335 "usage: %s [-A address] [-p port]\n\n"
336 "\t-A address\tinterface address to bind to\n"
337 "\t-g group\tjoin the given multicast group\n"
338 "\t-p port\t\tlisten on specified port\n"
339 "\t-v num\t\tverbosity level (default: 3)\n",
340 program, version, program );
341 }
342
343 static coap_context_t *
get_context(const char * node,const char * port)344 get_context(const char *node, const char *port) {
345 coap_context_t *ctx = NULL;
346 int s;
347 struct addrinfo hints;
348 struct addrinfo *result, *rp;
349
350 memset(&hints, 0, sizeof(struct addrinfo));
351 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
352 hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
353 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
354
355 s = getaddrinfo(node, port, &hints, &result);
356 if ( s != 0 ) {
357 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
358 return NULL;
359 }
360
361 /* iterate through results until success */
362 for (rp = result; rp != NULL; rp = rp->ai_next) {
363 coap_address_t addr;
364
365 if (rp->ai_addrlen <= sizeof(addr.addr)) {
366 coap_address_init(&addr);
367 addr.size = rp->ai_addrlen;
368 memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
369
370 ctx = coap_new_context(&addr);
371 if (ctx) {
372 /* TODO: output address:port for successful binding */
373 goto finish;
374 }
375 }
376 }
377
378 fprintf(stderr, "no context available for interface '%s'\n", node);
379
380 finish:
381 freeaddrinfo(result);
382 return ctx;
383 }
384
385 static int
join(coap_context_t * ctx,char * group_name)386 join(coap_context_t *ctx, char *group_name){
387 struct ipv6_mreq mreq;
388 struct addrinfo *reslocal = NULL, *resmulti = NULL, hints, *ainfo;
389 int result = -1;
390
391 /* we have to resolve the link-local interface to get the interface id */
392 memset(&hints, 0, sizeof(hints));
393 hints.ai_family = AF_INET6;
394 hints.ai_socktype = SOCK_DGRAM;
395
396 result = getaddrinfo("::", NULL, &hints, &reslocal);
397 if (result < 0) {
398 fprintf(stderr, "join: cannot resolve link-local interface: %s\n",
399 gai_strerror(result));
400 goto finish;
401 }
402
403 /* get the first suitable interface identifier */
404 for (ainfo = reslocal; ainfo != NULL; ainfo = ainfo->ai_next) {
405 if (ainfo->ai_family == AF_INET6) {
406 mreq.ipv6mr_interface =
407 ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_scope_id;
408 break;
409 }
410 }
411
412 memset(&hints, 0, sizeof(hints));
413 hints.ai_family = AF_INET6;
414 hints.ai_socktype = SOCK_DGRAM;
415
416 /* resolve the multicast group address */
417 result = getaddrinfo(group_name, NULL, &hints, &resmulti);
418
419 if (result < 0) {
420 fprintf(stderr, "join: cannot resolve multicast address: %s\n",
421 gai_strerror(result));
422 goto finish;
423 }
424
425 for (ainfo = resmulti; ainfo != NULL; ainfo = ainfo->ai_next) {
426 if (ainfo->ai_family == AF_INET6) {
427 mreq.ipv6mr_multiaddr =
428 ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_addr;
429 break;
430 }
431 }
432
433 result = setsockopt(ctx->sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
434 (char *)&mreq, sizeof(mreq));
435 if (result < 0)
436 perror("join: setsockopt");
437
438 finish:
439 freeaddrinfo(resmulti);
440 freeaddrinfo(reslocal);
441
442 return result;
443 }
444
445 int
main(int argc,char ** argv)446 main(int argc, char **argv) {
447 coap_context_t *ctx;
448 char *group = NULL;
449 fd_set readfds;
450 struct timeval tv, *timeout;
451 int result;
452 coap_tick_t now;
453 coap_queue_t *nextpdu;
454 char addr_str[NI_MAXHOST] = "::";
455 char port_str[NI_MAXSERV] = "5683";
456 int opt;
457 coap_log_t log_level = LOG_WARNING;
458
459 clock_offset = time(NULL);
460
461 while ((opt = getopt(argc, argv, "A:g:p:v:")) != -1) {
462 switch (opt) {
463 case 'A' :
464 strncpy(addr_str, optarg, NI_MAXHOST-1);
465 addr_str[NI_MAXHOST - 1] = '\0';
466 break;
467 case 'g' :
468 group = optarg;
469 break;
470 case 'p' :
471 strncpy(port_str, optarg, NI_MAXSERV-1);
472 port_str[NI_MAXSERV - 1] = '\0';
473 break;
474 case 'v' :
475 log_level = strtol(optarg, NULL, 10);
476 break;
477 default:
478 usage( argv[0], PACKAGE_VERSION );
479 exit( 1 );
480 }
481 }
482
483 coap_set_log_level(log_level);
484
485 ctx = get_context(addr_str, port_str);
486 if (!ctx)
487 return -1;
488
489 init_resources(ctx);
490
491 /* join multicast group if requested at command line */
492 if (group)
493 join(ctx, group);
494
495 signal(SIGINT, handle_sigint);
496
497 while ( !quit ) {
498 FD_ZERO(&readfds);
499 FD_SET( ctx->sockfd, &readfds );
500
501 nextpdu = coap_peek_next( ctx );
502
503 coap_ticks(&now);
504 while (nextpdu && nextpdu->t <= now - ctx->sendqueue_basetime) {
505 coap_retransmit( ctx, coap_pop_next( ctx ) );
506 nextpdu = coap_peek_next( ctx );
507 }
508
509 if ( nextpdu && nextpdu->t <= COAP_RESOURCE_CHECK_TIME ) {
510 /* set timeout if there is a pdu to send before our automatic timeout occurs */
511 tv.tv_usec = ((nextpdu->t) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
512 tv.tv_sec = (nextpdu->t) / COAP_TICKS_PER_SECOND;
513 timeout = &tv;
514 } else {
515 tv.tv_usec = 0;
516 tv.tv_sec = COAP_RESOURCE_CHECK_TIME;
517 timeout = &tv;
518 }
519 result = select( FD_SETSIZE, &readfds, 0, 0, timeout );
520
521 if ( result < 0 ) { /* error */
522 if (errno != EINTR)
523 perror("select");
524 } else if ( result > 0 ) { /* read from socket */
525 if ( FD_ISSET( ctx->sockfd, &readfds ) ) {
526 coap_read( ctx ); /* read received data */
527 /* coap_dispatch( ctx ); /\* and dispatch PDUs from receivequeue *\/ */
528 }
529 } else { /* timeout */
530 if (time_resource) {
531 time_resource->dirty = 1;
532 }
533 }
534
535 #ifndef WITHOUT_ASYNC
536 /* check if we have to send asynchronous responses */
537 check_async(ctx, ctx->endpoint, now);
538 #endif /* WITHOUT_ASYNC */
539
540 #ifndef WITHOUT_OBSERVE
541 /* check if we have to send observe notifications */
542 coap_check_notify(ctx);
543 #endif /* WITHOUT_OBSERVE */
544 }
545
546 coap_free_context(ctx);
547
548 return 0;
549 }
550