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