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