1 /* CoAP server for first ETSI CoAP plugtest, March 2012
2  *
3  * Copyright (C) 2012--2013 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8 
9 #include <string.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <ctype.h>
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>
19 #include <netdb.h>
20 #include <sys/stat.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <signal.h>
24 
25 #include "config.h"
26 #include "uthash.h"
27 #include "coap.h"
28 
29 #define COAP_RESOURCE_CHECK_TIME_SEC  1
30 
31 #ifndef min
32 #define min(a,b) ((a) < (b) ? (a) : (b))
33 #endif
34 
35 /* temporary storage for dynamic resource representations */
36 static int quit = 0;
37 
38 #define COAP_OPT_BLOCK_SZX_MAX 6 /**< allowed maximum for block szx value */
39 
40 #define REQUIRE_ETAG 0x01 	/* flag for coap_payload_t: require ETag option  */
41 typedef struct {
42   UT_hash_handle hh;
43   coap_key_t resource_key;	/* foreign key that points into resource space */
44   unsigned int flags;		/* some flags to control behavior */
45   size_t max_data;		/* maximum size allocated for @p data */
46   uint16_t media_type;		/* media type for this object */
47   size_t length;		/* length of data */
48   unsigned char data[];		/* the actual contents */
49 } coap_payload_t;
50 
51 coap_payload_t *test_resources = NULL;
52 
53 /**
54  * This structure is used to store URIs for dynamically allocated
55  * resources, usually by POST or PUT.
56  */
57 typedef struct {
58   UT_hash_handle hh;
59   coap_key_t resource_key;	/* foreign key that points into resource space */
60   size_t length;		/* length of data */
61   unsigned char data[];		/* the actual contents */
62 } coap_dynamic_uri_t;
63 
64 coap_dynamic_uri_t *test_dynamic_uris = NULL;
65 
66 /* This variable is used to mimic long-running tasks that require
67  * asynchronous responses. */
68 static coap_async_state_t *async = NULL;
69 
70 /* SIGINT handler: set quit to 1 for graceful termination */
71 void
handle_sigint(int signum)72 handle_sigint(int signum) {
73   quit = 1;
74 }
75 
76 #define INDEX "libcoap server for ETSI CoAP Plugtest, March 2012, Paris\n" \
77    	      "Copyright (C) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
78 
79 coap_payload_t *
coap_new_payload(size_t size)80 coap_new_payload(size_t size) {
81   coap_payload_t *p;
82   p = (coap_payload_t *)coap_malloc(sizeof(coap_payload_t) + size);
83   if (p) {
84     memset(p, 0, sizeof(coap_payload_t));
85     p->max_data = size;
86   }
87 
88   return p;
89 }
90 
91 static inline coap_payload_t *
coap_find_payload(const coap_key_t key)92 coap_find_payload(const coap_key_t key) {
93   coap_payload_t *p;
94   HASH_FIND(hh, test_resources, key, sizeof(coap_key_t), p);
95   return p;
96 }
97 
98 static inline void
coap_add_payload(const coap_key_t key,coap_payload_t * payload,coap_dynamic_uri_t * uri)99 coap_add_payload(const coap_key_t key, coap_payload_t *payload,
100 		 coap_dynamic_uri_t *uri) {
101   assert(payload);
102 
103   memcpy(payload->resource_key, key, sizeof(coap_key_t));
104   HASH_ADD(hh, test_resources, resource_key, sizeof(coap_key_t), payload);
105 
106   if (uri) {
107     memcpy(uri->resource_key, key, sizeof(coap_key_t));
108     HASH_ADD(hh, test_dynamic_uris, resource_key, sizeof(coap_key_t), uri);
109   }
110 }
111 
112 static inline void
coap_delete_payload(coap_payload_t * payload)113 coap_delete_payload(coap_payload_t *payload) {
114   if (payload) {
115     coap_dynamic_uri_t *uri;
116     HASH_FIND(hh, test_dynamic_uris,
117 	      payload->resource_key, sizeof(coap_key_t), uri);
118     if (uri) {
119       HASH_DELETE(hh, test_dynamic_uris, uri);
120       coap_free(uri);
121     }
122   }
123 
124   HASH_DELETE(hh, test_resources, payload);
125   coap_free(payload);
126 }
127 
128 void
hnd_get_index(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)129 hnd_get_index(coap_context_t  *ctx, struct coap_resource_t *resource,
130 	      coap_address_t *peer, coap_pdu_t *request, str *token,
131 	      coap_pdu_t *response) {
132   unsigned char buf[3];
133 
134   response->hdr->code = COAP_RESPONSE_CODE(205);
135 
136   coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
137 	  coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
138 
139   coap_add_option(response, COAP_OPTION_MAXAGE,
140 	  coap_encode_var_bytes(buf, 0x2ffff), buf);
141 
142   coap_add_data(response, strlen(INDEX), (unsigned char *)INDEX);
143 }
144 
145 
146 void
hnd_get_resource(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)147 hnd_get_resource(coap_context_t  *ctx, struct coap_resource_t *resource,
148 		 coap_address_t *peer, coap_pdu_t *request, str *token,
149 		 coap_pdu_t *response) {
150   coap_key_t etag;
151   unsigned char buf[2];
152   coap_payload_t *test_payload;
153   coap_block_t block;
154 
155   test_payload = coap_find_payload(resource->key);
156   if (!test_payload) {
157     response->hdr->code = COAP_RESPONSE_CODE(500);
158 
159     return;
160   }
161 
162   response->hdr->code = COAP_RESPONSE_CODE(205);
163 
164   coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
165 	  coap_encode_var_bytes(buf, test_payload->media_type), buf);
166 
167   /* add etag for the resource */
168   if (test_payload->flags & REQUIRE_ETAG) {
169     memset(etag, 0, sizeof(etag));
170     coap_hash(test_payload->data, test_payload->length, etag);
171     coap_add_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
172   }
173 
174   if (request) {
175     int res;
176 
177     if (coap_get_block(request, COAP_OPTION_BLOCK2, &block)) {
178       res = coap_write_block_opt(&block, COAP_OPTION_BLOCK2, response,
179 				 test_payload->length);
180 
181       switch (res) {
182       case -2:			/* illegal block */
183 	response->hdr->code = COAP_RESPONSE_CODE(400);
184 	goto error;
185       case -1:			/* should really not happen */
186 	assert(0);
187 	/* fall through if assert is a no-op */
188       case -3:			/* cannot handle request */
189 	response->hdr->code = COAP_RESPONSE_CODE(500);
190 	goto error;
191       default:			/* everything is good */
192 	;
193       }
194 
195       coap_add_block(response, test_payload->length, test_payload->data,
196 		     block.num, block.szx);
197     } else {
198       if (!coap_add_data(response, test_payload->length, test_payload->data)) {
199 	/* set initial block size, will be lowered by
200 	 * coap_write_block_opt) automatically */
201 	block.szx = 6;
202 	coap_write_block_opt(&block, COAP_OPTION_BLOCK2, response,
203 			     test_payload->length);
204 
205 	coap_add_block(response, test_payload->length, test_payload->data,
206 		       block.num, block.szx);
207       }
208     }
209   } else {		      /* this is a notification, block is 0 */
210     /* FIXME: need to store block size with subscription */
211   }
212 
213   return;
214 
215  error:
216   coap_add_data(response,
217 		strlen(coap_response_phrase(response->hdr->code)),
218 		(unsigned char *)coap_response_phrase(response->hdr->code));
219 }
220 
221 /* DELETE handler for dynamic resources created by POST /test */
222 void
hnd_delete_resource(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)223 hnd_delete_resource(coap_context_t  *ctx, struct coap_resource_t *resource,
224 		coap_address_t *peer, coap_pdu_t *request, str *token,
225 		coap_pdu_t *response) {
226   coap_payload_t *payload;
227 
228   payload = coap_find_payload(resource->key);
229 
230   if (payload)
231     coap_delete_payload(payload);
232 
233   coap_delete_resource(ctx, resource->key);
234 
235   response->hdr->code = COAP_RESPONSE_CODE(202);
236 }
237 
238 void
hnd_post_test(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)239 hnd_post_test(coap_context_t  *ctx, struct coap_resource_t *resource,
240 	      coap_address_t *peer, coap_pdu_t *request, str *token,
241 	      coap_pdu_t *response) {
242   coap_opt_iterator_t opt_iter;
243   coap_opt_t *option;
244   coap_payload_t *test_payload;
245   size_t len;
246   size_t l = 6 + sizeof(void *);
247   coap_dynamic_uri_t *uri;
248   unsigned char *data;
249 
250 #define BUFSIZE 20
251   int res;
252   unsigned char _buf[BUFSIZE];
253   unsigned char *buf = _buf;
254   size_t buflen = BUFSIZE;
255 
256   coap_get_data(request, &len, &data);
257 
258   /* allocate storage for resource and to hold URI */
259   test_payload = coap_new_payload(len);
260   uri = (coap_dynamic_uri_t *)coap_malloc(sizeof(coap_dynamic_uri_t) + l);
261   if (!(test_payload && uri)) {
262     coap_log(LOG_CRIT, "cannot allocate new resource under /test");
263     response->hdr->code = COAP_RESPONSE_CODE(500);
264     coap_free(test_payload);
265     coap_free(uri);
266   } else {
267     coap_resource_t *r;
268 
269     memset(uri, 0, sizeof(coap_dynamic_uri_t));
270     uri->length = min(l, snprintf((char *)uri->data, l, "test/%p", test_payload));
271     test_payload->length = len;
272 
273     memcpy(test_payload->data, data, len);
274 
275     r = coap_resource_init(uri->data, uri->length, 0);
276     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
277     coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
278 
279     /* set media_type if available */
280     option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
281     if (option) {
282       test_payload->media_type =
283 	coap_decode_var_bytes(COAP_OPT_VALUE(option), COAP_OPT_LENGTH(option));
284     }
285 
286     coap_add_resource(ctx, r);
287     coap_add_payload(r->key, test_payload, uri);
288 
289     /* add Location-Path */
290     res = coap_split_path(uri->data, uri->length, buf, &buflen);
291 
292     while (res--) {
293       coap_add_option(response, COAP_OPTION_LOCATION_PATH,
294 		      COAP_OPT_LENGTH(buf), COAP_OPT_VALUE(buf));
295 
296       buf += COAP_OPT_SIZE(buf);
297     }
298 
299     response->hdr->code = COAP_RESPONSE_CODE(201);
300   }
301 
302 }
303 
304 void
hnd_put_test(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)305 hnd_put_test(coap_context_t  *ctx, struct coap_resource_t *resource,
306 	      coap_address_t *peer, coap_pdu_t *request, str *token,
307 	      coap_pdu_t *response) {
308   coap_opt_iterator_t opt_iter;
309   coap_opt_t *option;
310   coap_payload_t *payload;
311   size_t len;
312   unsigned char *data;
313 
314   response->hdr->code = COAP_RESPONSE_CODE(204);
315 
316   coap_get_data(request, &len, &data);
317 
318   payload = coap_find_payload(resource->key);
319   if (payload && payload->max_data < len) { /* need more storage */
320     coap_delete_payload(payload);
321     payload = NULL;
322     /* bug: when subsequent coap_new_payload() fails, our old contents
323        is gone */
324   }
325 
326   if (!payload) {		/* create new payload */
327     payload = coap_new_payload(len);
328     if (!payload)
329       goto error;
330 
331     coap_add_payload(resource->key, payload, NULL);
332   }
333   payload->length = len;
334   memcpy(payload->data, data, len);
335 
336   option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
337   if (option) {
338     /* set media type given in request */
339     payload->media_type =
340       coap_decode_var_bytes(COAP_OPT_VALUE(option), COAP_OPT_LENGTH(option));
341   } else {
342     /* set default value */
343     payload->media_type = COAP_MEDIATYPE_TEXT_PLAIN;
344   }
345   /* FIXME: need to change attribute ct of resource.
346      To do so, we need dynamic management of the attribute value
347   */
348 
349   return;
350  error:
351   warn("cannot modify resource\n");
352   response->hdr->code = COAP_RESPONSE_CODE(500);
353 }
354 
355 void
hnd_delete_test(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)356 hnd_delete_test(coap_context_t  *ctx, struct coap_resource_t *resource,
357 		coap_address_t *peer, coap_pdu_t *request, str *token,
358 		coap_pdu_t *response) {
359   /* the ETSI validation tool does not like empty resources... */
360 #if 0
361   coap_payload_t *payload;
362   payload = coap_find_payload(resource->key);
363 
364   if (payload)
365     payload->length = 0;
366 #endif
367 
368   response->hdr->code = COAP_RESPONSE_CODE(202);
369 }
370 
371 void
hnd_get_query(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)372 hnd_get_query(coap_context_t  *ctx, struct coap_resource_t *resource,
373 	      coap_address_t *peer, coap_pdu_t *request, str *token,
374 	      coap_pdu_t *response) {
375   coap_opt_iterator_t opt_iter;
376   coap_opt_filter_t f;
377   coap_opt_t *q;
378   size_t len, L;
379   unsigned char buf[70];
380 
381   response->hdr->code = COAP_RESPONSE_CODE(205);
382 
383   coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
384 	  coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
385 
386   coap_option_filter_clear(f);
387   coap_option_setb(f, COAP_OPTION_URI_QUERY);
388 
389   coap_option_iterator_init(request, &opt_iter, f);
390 
391   len = 0;
392   while ((len < sizeof(buf)) && (q = coap_option_next(&opt_iter))) {
393     L = min(sizeof(buf) - len, 11);
394     memcpy(buf + len, "Uri-Query: ", L);
395     len += L;
396 
397     L = min(sizeof(buf) - len, COAP_OPT_LENGTH(q));
398     memcpy(buf + len, COAP_OPT_VALUE(q), L);
399     len += L;
400 
401     if (len < sizeof(buf))
402       buf[len++] = '\n';
403   }
404 
405   coap_add_data(response, len, buf);
406 }
407 
408 /* handler for TD_COAP_CORE_16 */
409 void
hnd_get_separate(coap_context_t * ctx,struct coap_resource_t * resource,coap_address_t * peer,coap_pdu_t * request,str * token,coap_pdu_t * response)410 hnd_get_separate(coap_context_t  *ctx, struct coap_resource_t *resource,
411 		 coap_address_t *peer, coap_pdu_t *request, str *token,
412 		 coap_pdu_t *response) {
413   coap_opt_iterator_t opt_iter;
414   coap_opt_t *option;
415   coap_opt_filter_t f;
416   unsigned long delay = 5;
417 
418   if (async) {
419     if (async->id != request->hdr->id) {
420       coap_opt_filter_t f;
421       coap_option_filter_clear(f);
422       response->hdr->code = COAP_RESPONSE_CODE(503);
423     }
424     return;
425   }
426 
427   /* search for option delay in query list */
428   coap_option_filter_clear(f);
429   coap_option_setb(f, COAP_OPTION_URI_QUERY);
430 
431   coap_option_iterator_init(request, &opt_iter, f);
432 
433   while ((option = coap_option_next(&opt_iter))) {
434     if (strncmp("delay=", (char *)COAP_OPT_VALUE(option), 6) == 0) {
435       int i;
436       unsigned long d = 0;
437 
438       for (i = 6; i < COAP_OPT_LENGTH(option); ++i)
439 	d = d * 10 + COAP_OPT_VALUE(option)[i] - '0';
440 
441       /* don't allow delay to be less than COAP_RESOURCE_CHECK_TIME*/
442       delay = d < COAP_RESOURCE_CHECK_TIME_SEC
443 	? COAP_RESOURCE_CHECK_TIME_SEC
444 	: d;
445       debug("set delay to %lu\n", delay);
446       break;
447     }
448   }
449 
450   async = coap_register_async(ctx, peer, request, COAP_ASYNC_SEPARATE,
451 			      (void *)(COAP_TICKS_PER_SECOND * delay));
452 }
453 
454 void
check_async(coap_context_t * ctx,coap_tick_t now)455 check_async(coap_context_t  *ctx, coap_tick_t now) {
456   coap_pdu_t *response;
457   coap_async_state_t *tmp;
458   unsigned char buf[2];
459   size_t size = sizeof(coap_hdr_t) + 8;
460 
461   if (!async || now < async->created + (unsigned long)async->appdata)
462     return;
463 
464   size += async->tokenlen;
465 
466   response = coap_pdu_init(async->flags & COAP_ASYNC_CONFIRM
467 			   ? COAP_MESSAGE_CON
468 			   : COAP_MESSAGE_NON,
469 			   COAP_RESPONSE_CODE(205), 0, size);
470   if (!response) {
471     debug("check_async: insufficient memory, we'll try later\n");
472     async->appdata =
473       (void *)((unsigned long)async->appdata + 15 * COAP_TICKS_PER_SECOND);
474     return;
475   }
476 
477   response->hdr->id = coap_new_message_id(ctx);
478 
479   if (async->tokenlen)
480     coap_add_token(response, async->tokenlen, async->token);
481 
482   coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
483 		  coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
484 
485   coap_add_data(response, 4, (unsigned char *)"done");
486 
487   if (coap_send(ctx, &async->peer, response) == COAP_INVALID_TID) {
488     debug("check_async: cannot send response for message %d\n",
489 	  response->hdr->id);
490   }
491   coap_delete_pdu(response);
492 
493   coap_remove_async(ctx, async->id, &tmp);
494   coap_free_async(async);
495   async = NULL;
496 }
497 
498 coap_payload_t *
make_large(char * filename)499 make_large(char *filename) {
500   coap_payload_t *payload;
501   FILE *inputfile = NULL;
502   struct stat statbuf;
503 
504   if (!filename)
505     return NULL;
506 
507   /* read from specified input file */
508   if (stat(filename, &statbuf) < 0) {
509     warn("cannot stat file %s\n", filename);
510     return NULL;
511   }
512 
513   payload = coap_new_payload(statbuf.st_size);
514   if (!payload)
515     return NULL;
516 
517   inputfile = fopen(filename, "r");
518   if ( !inputfile ) {
519     warn("cannot read file %s\n", filename);
520     coap_free(payload);
521     return NULL;
522   }
523 
524   payload->length = fread(payload->data, 1, statbuf.st_size, inputfile);
525   payload->media_type = 41;
526 
527   fclose(inputfile);
528 
529   return payload;
530 }
531 
532 void
init_resources(coap_context_t * ctx)533 init_resources(coap_context_t *ctx) {
534   coap_resource_t *r;
535   coap_payload_t *test_payload;
536 
537   test_payload = coap_new_payload(200);
538   if (!test_payload)
539     coap_log(LOG_CRIT, "cannot allocate resource /test");
540   else {
541     test_payload->length = 13;
542     memcpy(test_payload->data, "put data here", test_payload->length);
543     /* test_payload->media_type is 0 anyway */
544 
545     r = coap_resource_init((unsigned char *)"test", 4, 0);
546     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
547     coap_register_handler(r, COAP_REQUEST_POST, hnd_post_test);
548     coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_test);
549     coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_test);
550 
551     coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"0", 1, 0);
552     coap_add_attr(r, (unsigned char *)"rt", 2, (unsigned char *)"test", 4, 0);
553     coap_add_attr(r, (unsigned char *)"if", 2, (unsigned char *)"core#b", 6, 0);
554 #if 0
555     coap_add_attr(r, (unsigned char *)"obs", 3, NULL, 0, 0);
556 #endif
557     coap_add_resource(ctx, r);
558     coap_add_payload(r->key, test_payload, NULL);
559   }
560 
561   /* TD_COAP_BLOCK_01
562    * TD_COAP_BLOCK_02 */
563   test_payload = make_large("etsi_iot_01_largedata.txt");
564   if (!test_payload)
565     coap_log(LOG_CRIT, "cannot allocate resource /large\n");
566   else {
567     r = coap_resource_init((unsigned char *)"large", 5, 0);
568     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
569 
570     coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"41", 2, 0);
571     coap_add_attr(r, (unsigned char *)"rt", 2, (unsigned char *)"large", 5, 0);
572     coap_add_resource(ctx, r);
573 
574     test_payload->flags |= REQUIRE_ETAG;
575 
576     coap_add_payload(r->key, test_payload, NULL);
577   }
578 
579   /* For TD_COAP_CORE_12 */
580   test_payload = coap_new_payload(20);
581   if (!test_payload)
582     coap_log(LOG_CRIT, "cannot allocate resource /seg1/seg2/seg3\n");
583   else {
584     test_payload->length = 10;
585     memcpy(test_payload->data, "segsegseg!", test_payload->length);
586     /* test_payload->media_type is 0 anyway */
587 
588     r = coap_resource_init((unsigned char *)"seg1/seg2/seg3", 14, 0);
589     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
590 
591     coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"0", 1, 0);
592     coap_add_resource(ctx, r);
593 
594     coap_add_payload(r->key, test_payload, NULL);
595   }
596 
597   /* For TD_COAP_CORE_13 */
598   r = coap_resource_init((unsigned char *)"query", 5, 0);
599   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_query);
600 
601   coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"0", 1, 0);
602   coap_add_resource(ctx, r);
603 
604   /* For TD_COAP_CORE_16 */
605   r = coap_resource_init((unsigned char *)"separate", 8, 0);
606   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_separate);
607 
608   coap_add_attr(r, (unsigned char *)"ct", 2, (unsigned char *)"0", 1, 0);
609   coap_add_attr(r, (unsigned char *)"rt", 2, (unsigned char *)"separate", 8, 0);
610   coap_add_resource(ctx, r);
611 }
612 
613 void
usage(const char * program,const char * version)614 usage( const char *program, const char *version) {
615   const char *p;
616 
617   p = strrchr( program, '/' );
618   if ( p )
619     program = ++p;
620 
621   fprintf( stderr, "%s v%s -- ETSI CoAP plugtest server\n"
622 	   "(c) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
623 	   "usage: %s [-A address] [-p port]\n\n"
624 	   "\t-A address\tinterface address to bind to\n"
625 	   "\t-p port\t\tlisten on specified port\n"
626 	   "\t-v num\t\tverbosity level (default: 3)\n",
627 	   program, version, program );
628 }
629 
630 coap_context_t *
get_context(const char * node,const char * port)631 get_context(const char *node, const char *port) {
632   coap_context_t *ctx = NULL;
633   int s;
634   struct addrinfo hints;
635   struct addrinfo *result, *rp;
636 
637   memset(&hints, 0, sizeof(struct addrinfo));
638   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
639   hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
640   hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
641 
642   s = getaddrinfo(node, port, &hints, &result);
643   if ( s != 0 ) {
644     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
645     return NULL;
646   }
647 
648   /* iterate through results until success */
649   for (rp = result; rp != NULL; rp = rp->ai_next) {
650     coap_address_t addr;
651 
652     if (rp->ai_addrlen <= sizeof(addr.addr)) {
653       coap_address_init(&addr);
654       addr.size = rp->ai_addrlen;
655       memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
656 
657       ctx = coap_new_context(&addr);
658       if (ctx) {
659 	/* TODO: output address:port for successful binding */
660 	goto finish;
661       }
662     }
663   }
664 
665   fprintf(stderr, "no context available for interface '%s'\n", node);
666 
667  finish:
668   freeaddrinfo(result);
669   return ctx;
670 }
671 
672 int
main(int argc,char ** argv)673 main(int argc, char **argv) {
674   coap_context_t  *ctx;
675   fd_set readfds;
676   struct timeval tv, *timeout;
677   int result;
678   coap_tick_t now;
679   coap_queue_t *nextpdu;
680   char addr_str[NI_MAXHOST] = "::";
681   char port_str[NI_MAXSERV] = "5683";
682   int opt;
683   coap_log_t log_level = LOG_WARNING;
684 
685   while ((opt = getopt(argc, argv, "A:p:v:")) != -1) {
686     switch (opt) {
687     case 'A' :
688       strncpy(addr_str, optarg, NI_MAXHOST-1);
689       addr_str[NI_MAXHOST - 1] = '\0';
690       break;
691     case 'p' :
692       strncpy(port_str, optarg, NI_MAXSERV-1);
693       port_str[NI_MAXSERV - 1] = '\0';
694       break;
695     case 'v' :
696       log_level = strtol(optarg, NULL, 10);
697       break;
698     default:
699       usage( argv[0], PACKAGE_VERSION );
700       exit( 1 );
701     }
702   }
703 
704   coap_set_log_level(log_level);
705 
706   ctx = get_context(addr_str, port_str);
707   if (!ctx)
708     return -1;
709 
710   coap_register_option(ctx, COAP_OPTION_BLOCK2);
711 
712   init_resources(ctx);
713 
714   signal(SIGINT, handle_sigint);
715 
716   while ( !quit ) {
717     FD_ZERO(&readfds);
718     FD_SET( ctx->sockfd, &readfds );
719 
720     nextpdu = coap_peek_next( ctx );
721 
722     coap_ticks(&now);
723     while ( nextpdu && nextpdu->t <= now ) {
724       coap_retransmit( ctx, coap_pop_next( ctx ) );
725       nextpdu = coap_peek_next( ctx );
726     }
727 
728     if ( nextpdu && nextpdu->t <= now + COAP_RESOURCE_CHECK_TIME_SEC ) {
729       /* set timeout if there is a pdu to send before our automatic timeout occurs */
730       tv.tv_usec = ((nextpdu->t - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
731       tv.tv_sec = (nextpdu->t - now) / COAP_TICKS_PER_SECOND;
732       timeout = &tv;
733     } else {
734       tv.tv_usec = 0;
735       tv.tv_sec = COAP_RESOURCE_CHECK_TIME_SEC;
736       timeout = &tv;
737     }
738     result = select( FD_SETSIZE, &readfds, 0, 0, timeout );
739 
740     if ( result < 0 ) {		/* error */
741       if (errno != EINTR)
742 	perror("select");
743     } else if ( result > 0 ) {	/* read from socket */
744       if ( FD_ISSET( ctx->sockfd, &readfds ) ) {
745 	coap_read( ctx );	/* read received data */
746 	coap_dispatch( ctx );	/* and dispatch PDUs from receivequeue */
747       }
748     } else {			/* timeout */
749       /* coap_check_resource_list( ctx ); */
750     }
751 
752     /* check if we have to send asynchronous responses */
753     check_async(ctx, now);
754   }
755 
756   coap_free_context( ctx );
757 
758   return 0;
759 }
760