• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* CoAP server for first ETSI CoAP plugtest, March 2012
2  *
3  * Copyright (C) 2012--2013 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  *
7  * This file is part of the CoAP library libcoap. Please see
8  * README for terms of use.
9  */
10 
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <sys/select.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #include <netdb.h>
22 #include <sys/stat.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <signal.h>
26 
27 #include <coap3/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   unsigned int flags;             /* some flags to control behavior */
43   size_t max_data;                /* maximum size allocated for @p data */
44   uint16_t media_type;            /* media type for this object */
45   size_t length;                  /* length of data */
46   unsigned char data[];           /* the actual contents */
47 } coap_payload_t;
48 
49 /* SIGINT handler: set quit to 1 for graceful termination */
50 static void
handle_sigint(int signum COAP_UNUSED)51 handle_sigint(int signum COAP_UNUSED) {
52   quit = 1;
53 }
54 
55 #define INDEX "libcoap server for ETSI CoAP Plugtest, March 2012, Paris\n" \
56                  "Copyright (C) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
57 
58 static coap_payload_t *
coap_new_payload(size_t size)59 coap_new_payload(size_t size) {
60   coap_payload_t *p;
61   p = (coap_payload_t *)coap_malloc(sizeof(coap_payload_t) + size);
62   if (p) {
63     memset(p, 0, sizeof(coap_payload_t));
64     p->max_data = size;
65   }
66 
67   return p;
68 }
69 
70 static inline coap_payload_t *
coap_find_payload(coap_resource_t * resource)71 coap_find_payload(coap_resource_t *resource) {
72   return coap_resource_get_userdata(resource);
73 }
74 
75 static void
coap_add_payload(coap_resource_t * resource,coap_payload_t * payload)76 coap_add_payload(coap_resource_t *resource, coap_payload_t *payload){
77   assert(payload);
78 
79   coap_resource_set_userdata(resource, payload);
80 }
81 
82 static inline void
coap_delete_payload(coap_resource_t * resource)83 coap_delete_payload(coap_resource_t *resource) {
84   coap_free(coap_resource_get_userdata(resource));
85   coap_resource_set_userdata(resource, NULL);
86 }
87 
88 static void
coap_free_userdata(void * data)89 coap_free_userdata(void *data) {
90   coap_free(data);
91 }
92 
93 #if 0
94 static void
95 hnd_get_index(coap_resource_t *resource COAP_UNUSED,
96               coap_session_t *session COAP_UNUSED,
97               coap_pdu_t *request COAP_UNUSED,
98               coap_string_t *query COAP_UNUSED,
99               coap_pdu_t *response) {
100   unsigned char buf[3];
101 
102   coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
103 
104   coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
105                   coap_encode_var_safe(buf, sizeof(buf),
106                                        COAP_MEDIATYPE_TEXT_PLAIN),
107                   buf);
108 
109   coap_add_option(response, COAP_OPTION_MAXAGE,
110           coap_encode_var_safe(buf, sizeof(buf), 0x2ffff), buf);
111 
112   coap_add_data(response, strlen(INDEX), (const uint8_t *)INDEX);
113 }
114 #endif
115 
116 static void
hnd_get_resource(coap_resource_t * resource,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)117 hnd_get_resource(coap_resource_t *resource,
118                  coap_session_t *session COAP_UNUSED,
119                  const coap_pdu_t *request,
120                  const coap_string_t *query COAP_UNUSED,
121                  coap_pdu_t *response) {
122   coap_payload_t *test_payload;
123 
124   test_payload = coap_find_payload(resource);
125   if (!test_payload) {
126     coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
127 
128     return;
129   }
130 
131   coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
132 
133   coap_add_data_blocked_response(request, response,
134                                  test_payload->media_type, -1,
135                                  test_payload->length,
136                                  test_payload->data);
137   return;
138 }
139 
140 /* DELETE handler for dynamic resources created by POST /test */
141 static void
hnd_delete_resource(coap_resource_t * resource,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request COAP_UNUSED,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)142 hnd_delete_resource(coap_resource_t *resource,
143                     coap_session_t *session COAP_UNUSED,
144                     const coap_pdu_t *request COAP_UNUSED,
145                     const coap_string_t *query COAP_UNUSED,
146                     coap_pdu_t *response) {
147   coap_payload_t *payload;
148 
149   payload = coap_find_payload(resource);
150 
151   if (payload)
152     coap_delete_payload(resource);
153 
154   coap_delete_resource(coap_session_get_context(session), resource);
155 
156   coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED);
157 }
158 
159 static void
hnd_post_test(coap_resource_t * resource COAP_UNUSED,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)160 hnd_post_test(coap_resource_t *resource COAP_UNUSED,
161               coap_session_t *session COAP_UNUSED,
162               const coap_pdu_t *request,
163               const coap_string_t *query COAP_UNUSED,
164               coap_pdu_t *response) {
165   coap_opt_iterator_t opt_iter;
166   coap_opt_t *option;
167   coap_payload_t *test_payload;
168   size_t len;
169   coap_str_const_t *uri;
170   const uint8_t *data;
171 
172 #define BUFSIZE 20
173   int res;
174   unsigned char _buf[BUFSIZE];
175   unsigned char *buf = _buf;
176   size_t buflen = BUFSIZE;
177 
178   coap_get_data(request, &len, &data);
179 
180   /* allocate storage for resource and to hold URI */
181   test_payload = coap_new_payload(len);
182   snprintf((char *)buf, buflen, "test/%p", (void *)test_payload);
183   uri = coap_new_str_const(buf, strlen((char *)buf));
184   if (!(test_payload && uri)) {
185     coap_log(LOG_CRIT, "cannot allocate new resource under /test");
186     coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
187     coap_free(test_payload);
188     coap_free(uri);
189   } else {
190     coap_resource_t *r;
191 
192     test_payload->length = len;
193 
194     memcpy(test_payload->data, data, len);
195 
196     r = coap_resource_init(uri, COAP_RESOURCE_FLAGS_RELEASE_URI);
197     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
198     coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
199 
200     /* set media_type if available */
201     option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
202     if (option) {
203       test_payload->media_type =
204         coap_decode_var_bytes(coap_opt_value(option), coap_opt_length(option));
205     }
206 
207     coap_add_resource(coap_session_get_context(session), r);
208     coap_add_payload(r, test_payload);
209 
210     /* add Location-Path */
211     res = coap_split_path(uri->s, uri->length, buf, &buflen);
212 
213     while (res--) {
214       coap_add_option(response, COAP_OPTION_LOCATION_PATH,
215                       coap_opt_length(buf), coap_opt_value(buf));
216 
217       buf += coap_opt_size(buf);
218     }
219 
220     coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED);
221   }
222 
223 }
224 
225 static void
hnd_put_test(coap_resource_t * resource,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)226 hnd_put_test(coap_resource_t *resource,
227              coap_session_t *session COAP_UNUSED,
228              const coap_pdu_t *request,
229              const coap_string_t *query COAP_UNUSED,
230              coap_pdu_t *response) {
231   coap_opt_iterator_t opt_iter;
232   coap_opt_t *option;
233   coap_payload_t *payload;
234   size_t len;
235   const uint8_t *data;
236 
237   coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED);
238 
239   coap_get_data(request, &len, &data);
240 
241   payload = coap_find_payload(resource);
242   if (payload && payload->max_data < len) { /* need more storage */
243     coap_delete_payload(resource);
244     payload = NULL;
245     /* bug: when subsequent coap_new_payload() fails, our old contents
246        is gone */
247   }
248 
249   if (!payload) {                /* create new payload */
250     payload = coap_new_payload(len);
251     if (!payload)
252       goto error;
253 
254     coap_add_payload(resource, payload);
255   }
256   payload->length = len;
257   memcpy(payload->data, data, len);
258 
259   option = coap_check_option(request, COAP_OPTION_CONTENT_TYPE, &opt_iter);
260   if (option) {
261     /* set media type given in request */
262     payload->media_type =
263       coap_decode_var_bytes(coap_opt_value(option), coap_opt_length(option));
264   } else {
265     /* set default value */
266     payload->media_type = COAP_MEDIATYPE_TEXT_PLAIN;
267   }
268   /* FIXME: need to change attribute ct of resource.
269      To do so, we need dynamic management of the attribute value
270   */
271 
272   return;
273  error:
274   coap_log(LOG_WARNING, "cannot modify resource\n");
275   coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR);
276 }
277 
278 static void
hnd_delete_test(coap_resource_t * resource COAP_UNUSED,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request COAP_UNUSED,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)279 hnd_delete_test(coap_resource_t *resource COAP_UNUSED,
280                 coap_session_t *session COAP_UNUSED,
281                 const coap_pdu_t *request COAP_UNUSED,
282                 const coap_string_t *query COAP_UNUSED,
283                 coap_pdu_t *response) {
284   /* the ETSI validation tool does not like empty resources... */
285 #if 0
286   coap_payload_t *payload;
287   payload = coap_find_payload(resource);
288 
289   if (payload)
290     payload->length = 0;
291 #endif
292 
293   coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED);
294 }
295 
296 static void
hnd_get_query(coap_resource_t * resource COAP_UNUSED,coap_session_t * session COAP_UNUSED,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)297 hnd_get_query(coap_resource_t *resource COAP_UNUSED,
298               coap_session_t *session COAP_UNUSED,
299               const coap_pdu_t *request,
300               const coap_string_t *query COAP_UNUSED,
301               coap_pdu_t *response) {
302   coap_opt_iterator_t opt_iter;
303   coap_opt_filter_t f;
304   coap_opt_t *q;
305   size_t len, L;
306   unsigned char buf[70];
307 
308   coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
309 
310   coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
311                   coap_encode_var_safe(buf, sizeof(buf),
312                                        COAP_MEDIATYPE_TEXT_PLAIN),
313                   buf);
314 
315   coap_option_filter_clear(&f);
316   coap_option_filter_set(&f, COAP_OPTION_URI_QUERY);
317 
318   coap_option_iterator_init(request, &opt_iter, &f);
319 
320   len = 0;
321   while ((len < sizeof(buf)) && (q = coap_option_next(&opt_iter))) {
322     L = min(sizeof(buf) - len, 11);
323     memcpy(buf + len, "Uri-Query: ", L);
324     len += L;
325 
326     L = min(sizeof(buf) - len, coap_opt_length(q));
327     memcpy(buf + len, coap_opt_value(q), L);
328     len += L;
329 
330     if (len < sizeof(buf))
331       buf[len++] = '\n';
332   }
333 
334   coap_add_data(response, len, buf);
335 }
336 
337 /* handler for TD_COAP_CORE_16 */
338 static void
hnd_get_separate(coap_resource_t * resource COAP_UNUSED,coap_session_t * session,const coap_pdu_t * request,const coap_string_t * query COAP_UNUSED,coap_pdu_t * response)339 hnd_get_separate(coap_resource_t *resource COAP_UNUSED,
340                  coap_session_t *session,
341                  const coap_pdu_t *request,
342                  const coap_string_t *query COAP_UNUSED,
343                  coap_pdu_t *response) {
344   coap_opt_iterator_t opt_iter;
345   coap_opt_t *option;
346   coap_opt_filter_t f;
347   unsigned long delay = 5;
348 
349   if (request) {
350     coap_async_t *async;
351     coap_bin_const_t token = coap_pdu_get_token(request);
352 
353     async = coap_find_async(session, token);
354 
355     if (!async) {
356       /* Set up an async request to trigger delay in the future */
357 
358       /* search for option delay in query list */
359       coap_option_filter_clear(&f);
360       coap_option_filter_set(&f, COAP_OPTION_URI_QUERY);
361 
362       coap_option_iterator_init(request, &opt_iter, &f);
363 
364       while ((option = coap_option_next(&opt_iter))) {
365         if (strncmp("delay=", (const char *)coap_opt_value(option), 6) == 0) {
366           unsigned int i;
367           unsigned long d = 0;
368 
369           for (i = 6; i < coap_opt_length(option); ++i)
370             d = d * 10 + coap_opt_value(option)[i] - '0';
371 
372           /* don't allow delay to be less than COAP_RESOURCE_CHECK_TIME*/
373           delay = d < COAP_RESOURCE_CHECK_TIME_SEC
374             ? COAP_RESOURCE_CHECK_TIME_SEC
375             : d;
376           coap_log(LOG_DEBUG, "set delay to %lu\n", delay);
377           break;
378         }
379       }
380       async = coap_register_async(session,
381                                   request,
382                                   COAP_TICKS_PER_SECOND * delay);
383       if (async == NULL) {
384         coap_pdu_set_code(response, COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE);
385         return;
386       }
387       /* Not setting response code will cause empty ACK to be sent
388          if Confirmable */
389       return;
390     }
391   }
392 
393   /* no request (observe) or async set up, so this is the delayed request */
394   coap_add_data(response, 4, (const uint8_t *)"done");
395   coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
396 
397   /* async is automatically removed by libcoap */
398 }
399 
400 static coap_payload_t *
make_large(const char * filename)401 make_large(const char *filename) {
402   coap_payload_t *payload;
403   FILE *inputfile = NULL;
404   struct stat statbuf;
405 
406   if (!filename)
407     return NULL;
408 
409   /* read from specified input file */
410   if (stat(filename, &statbuf) < 0) {
411     coap_log(LOG_WARNING, "cannot stat file %s\n", filename);
412     return NULL;
413   }
414 
415   payload = coap_new_payload(statbuf.st_size);
416   if (!payload)
417     return NULL;
418 
419   inputfile = fopen(filename, "r");
420   if ( !inputfile ) {
421     coap_log(LOG_WARNING, "cannot read file %s\n", filename);
422     coap_free(payload);
423     return NULL;
424   }
425 
426   payload->length = fread(payload->data, 1, statbuf.st_size, inputfile);
427   payload->media_type = 41;
428 
429   fclose(inputfile);
430 
431   return payload;
432 }
433 
434 static void
init_resources(coap_context_t * ctx)435 init_resources(coap_context_t *ctx) {
436   coap_resource_t *r;
437   coap_payload_t *test_payload;
438 
439   test_payload = coap_new_payload(200);
440   if (!test_payload)
441     coap_log(LOG_CRIT, "cannot allocate resource /test");
442   else {
443     test_payload->length = 13;
444     memcpy(test_payload->data, "put data here", test_payload->length);
445     /* test_payload->media_type is 0 anyway */
446 
447     r = coap_resource_init(coap_make_str_const("test"), 0);
448     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
449     coap_register_handler(r, COAP_REQUEST_POST, hnd_post_test);
450     coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_test);
451     coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_test);
452 
453     coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
454     coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("test"), 0);
455     coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("core#b"), 0);
456 #if 0
457     coap_add_attr(r, coap_make_str_const("obs"), NULL, 0);
458 #endif
459     coap_add_resource(ctx, r);
460     coap_resource_release_userdata_handler(ctx, coap_free_userdata);
461     coap_add_payload(r, test_payload);
462   }
463 
464   /* TD_COAP_BLOCK_01
465    * TD_COAP_BLOCK_02 */
466   test_payload = make_large("etsi_iot_01_largedata.txt");
467   if (!test_payload)
468     coap_log(LOG_CRIT, "cannot allocate resource /large\n");
469   else {
470     r = coap_resource_init(coap_make_str_const("large"), 0);
471     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
472 
473     coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("41"), 0);
474     coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("large"), 0);
475     coap_add_resource(ctx, r);
476 
477     test_payload->flags |= REQUIRE_ETAG;
478 
479     coap_add_payload(r, test_payload);
480   }
481 
482   /* For TD_COAP_CORE_12 */
483   test_payload = coap_new_payload(20);
484   if (!test_payload)
485     coap_log(LOG_CRIT, "cannot allocate resource /seg1/seg2/seg3\n");
486   else {
487     test_payload->length = 10;
488     memcpy(test_payload->data, "segsegseg!", test_payload->length);
489     /* test_payload->media_type is 0 anyway */
490 
491     r = coap_resource_init(coap_make_str_const("seg1/seg2/seg3"), 0);
492     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
493 
494     coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
495     coap_add_resource(ctx, r);
496 
497     coap_add_payload(r, test_payload);
498   }
499 
500   /* For TD_COAP_CORE_13 */
501   r = coap_resource_init(coap_make_str_const("query"), 0);
502   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_query);
503 
504   coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
505   coap_add_resource(ctx, r);
506 
507   /* For TD_COAP_CORE_16 */
508   r = coap_resource_init(coap_make_str_const("separate"), 0);
509   coap_register_handler(r, COAP_REQUEST_GET, hnd_get_separate);
510 
511   coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
512   coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("seperate"), 0);
513   coap_add_resource(ctx, r);
514 }
515 
516 static void
usage(const char * program,const char * version)517 usage( const char *program, const char *version) {
518   const char *p;
519 
520   p = strrchr( program, '/' );
521   if ( p )
522     program = ++p;
523 
524   fprintf( stderr, "%s v%s -- ETSI CoAP plugtest server\n"
525            "(c) 2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
526            "usage: %s [-A address] [-p port]\n\n"
527            "\t-A address\tinterface address to bind to\n"
528            "\t-p port\t\tlisten on specified port\n"
529            "\t-v num\t\tverbosity level (default: 3)\n",
530            program, version, program );
531 }
532 
533 static coap_context_t *
get_context(const char * node,const char * port)534 get_context(const char *node, const char *port) {
535   coap_context_t *ctx = NULL;
536   int s;
537   struct addrinfo hints;
538   struct addrinfo *result, *rp;
539 
540   memset(&hints, 0, sizeof(struct addrinfo));
541   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
542   hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
543   hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
544 
545   s = getaddrinfo(node, port, &hints, &result);
546   if ( s != 0 ) {
547     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
548     return NULL;
549   }
550 
551   /* iterate through results until success */
552   for (rp = result; rp != NULL; rp = rp->ai_next) {
553     coap_address_t addr;
554 
555     if (rp->ai_addrlen <= (socklen_t)sizeof(addr.addr)) {
556       coap_address_init(&addr);
557       addr.size = rp->ai_addrlen;
558       memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
559 
560       ctx = coap_new_context(&addr);
561       if (ctx) {
562         /* TODO: output address:port for successful binding */
563         goto finish;
564       }
565     }
566   }
567 
568   fprintf(stderr, "no context available for interface '%s'\n", node);
569 
570  finish:
571   freeaddrinfo(result);
572   return ctx;
573 }
574 
575 int
main(int argc,char ** argv)576 main(int argc, char **argv) {
577   coap_context_t  *ctx;
578   int result;
579   char addr_str[NI_MAXHOST] = "::";
580   char port_str[NI_MAXSERV] = "5683";
581   int opt;
582   coap_log_t log_level = LOG_WARNING;
583   struct sigaction sa;
584 
585   while ((opt = getopt(argc, argv, "A:p:v:")) != -1) {
586     switch (opt) {
587     case 'A' :
588       strncpy(addr_str, optarg, NI_MAXHOST-1);
589       addr_str[NI_MAXHOST - 1] = '\0';
590       break;
591     case 'p' :
592       strncpy(port_str, optarg, NI_MAXSERV-1);
593       port_str[NI_MAXSERV - 1] = '\0';
594       break;
595     case 'v' :
596       log_level = strtol(optarg, NULL, 10);
597       break;
598     default:
599       usage( argv[0], LIBCOAP_PACKAGE_VERSION );
600       exit( 1 );
601     }
602   }
603 
604   coap_set_log_level(log_level);
605 
606   ctx = get_context(addr_str, port_str);
607   if (!ctx)
608     return -1;
609 
610   init_resources(ctx);
611 
612   memset (&sa, 0, sizeof(sa));
613   sigemptyset(&sa.sa_mask);
614   sa.sa_handler = handle_sigint;
615   sa.sa_flags = 0;
616   sigaction (SIGINT, &sa, NULL);
617   sigaction (SIGTERM, &sa, NULL);
618   /* So we do not exit on a SIGPIPE */
619   sa.sa_handler = SIG_IGN;
620   sigaction (SIGPIPE, &sa, NULL);
621 
622   while ( !quit ) {
623     result = coap_io_process( ctx, COAP_RESOURCE_CHECK_TIME * 1000 );
624     if ( result >= 0 ) {
625       /* coap_check_resource_list( ctx ); */
626     }
627   }
628 
629   coap_free_context( ctx );
630   coap_cleanup();
631 
632   return 0;
633 }
634