Imported Upstream version 0.9.1
[platform/upstream/iotivity.git] / resource / csdk / connectivity / lib / libcoap-4.1.1 / examples / rd.c
1 /* coap -- simple implementation of the Constrained Application Protocol (CoAP)
2  *         as defined in draft-ietf-core-coap
3  *
4  * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
5  *
6  * This file is part of the CoAP library libcoap. Please see
7  * README for terms of use.
8  */
9
10 /**
11  * @file rd.c
12  * @brief CoRE resource directory
13  *
14  * @see http://tools.ietf.org/id/draft-shelby-core-resource-directory
15  */
16
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <sys/select.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28 #include <sys/stat.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <signal.h>
32
33 #include "config.h"
34 #include "utlist.h"
35 #include "resource.h"
36 #include "coap.h"
37
38 #define COAP_RESOURCE_CHECK_TIME 2
39
40 #define RD_ROOT_STR   ((unsigned char *)"rd")
41 #define RD_ROOT_SIZE  2
42
43 #ifndef min
44 #define min(a,b) ((a) < (b) ? (a) : (b))
45 #endif
46
47 typedef struct rd_t
48 {
49     UT_hash_handle hh; /**< hash handle (for internal use only) */
50     coap_key_t key; /**< the actual key bytes for this resource */
51
52     size_t etag_len; /**< actual length of @c etag */
53     unsigned char etag[8]; /**< ETag for current description */
54
55     str data; /**< points to the resource description  */
56 } rd_t;
57
58 rd_t *resources = NULL;
59
60 inline rd_t *
61 rd_new()
62 {
63     rd_t *rd;
64     rd = (rd_t *) coap_malloc(sizeof(rd_t));
65     if (rd)
66         memset(rd, 0, sizeof(rd_t));
67
68     return rd;
69 }
70
71 inline void rd_delete(rd_t *rd)
72 {
73     if (rd)
74     {
75         coap_free(rd->data.s);
76         coap_free(rd);
77     }
78 }
79
80 /* temporary storage for dynamic resource representations */
81 static int quit = 0;
82
83 /* SIGINT handler: set quit to 1 for graceful termination */
84 void handle_sigint(int signum)
85 {
86     quit = 1;
87 }
88
89 void hnd_get_resource(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
90         coap_pdu_t *request, str *token, coap_pdu_t *response)
91 {
92     rd_t *rd = NULL;
93     unsigned char buf[3];
94
95     HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
96
97     response->hdr->code = COAP_RESPONSE_CODE(205);
98
99     coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
100             coap_encode_var_bytes(buf, COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf);
101
102     if (rd && rd->etag_len)
103         coap_add_option(response, COAP_OPTION_ETAG, rd->etag_len, rd->etag);
104
105     if (rd && rd->data.s)
106         coap_add_data(response, rd->data.length, rd->data.s);
107 }
108
109 void hnd_put_resource(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
110         coap_pdu_t *request, str *token, coap_pdu_t *response)
111 {
112 #if 1
113     response->hdr->code = COAP_RESPONSE_CODE(501);
114 #else /* FIXME */
115     coap_opt_iterator_t opt_iter;
116     coap_opt_t *token, *etag;
117     coap_pdu_t *response;
118     size_t size = sizeof(coap_hdr_t);
119     int type = (request->hdr->type == COAP_MESSAGE_CON)
120     ? COAP_MESSAGE_ACK : COAP_MESSAGE_NON;
121     rd_t *rd = NULL;
122     unsigned char code; /* result code */
123     unsigned char *data;
124     str tmp;
125
126     HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
127     if (rd)
128     {
129         /* found resource object, now check Etag */
130         etag = coap_check_option(request, COAP_OPTION_ETAG, &opt_iter);
131         if (!etag || (COAP_OPT_LENGTH(etag) != rd->etag_len)
132                 || memcmp(COAP_OPT_VALUE(etag), rd->etag, rd->etag_len) != 0)
133         {
134
135             if (coap_get_data(request, &tmp.length, &data))
136             {
137
138                 tmp.s = (unsigned char *)coap_malloc(tmp.length);
139                 if (!tmp.s)
140                 {
141                     debug("hnd_put_rd: cannot allocate storage for new rd\n");
142                     code = COAP_RESPONSE_CODE(503);
143                     goto finish;
144                 }
145
146                 coap_free(rd->data.s);
147                 rd->data.s = tmp.s;
148                 rd->data.length = tmp.length;
149                 memcpy(rd->data.s, data, rd->data.length);
150             }
151         }
152
153         if (etag)
154         {
155             rd->etag_len = min(COAP_OPT_LENGTH(etag), sizeof(rd->etag));
156             memcpy(rd->etag, COAP_OPT_VALUE(etag), rd->etag_len);
157         }
158
159         code = COAP_RESPONSE_CODE(204);
160         /* FIXME: update lifetime */
161
162     }
163     else
164     {
165
166         code = COAP_RESPONSE_CODE(503);
167     }
168
169     finish:
170     /* FIXME: do not create a new response but use the old one instead */
171     response = coap_pdu_init(type, code, request->hdr->id, size);
172
173     if (!response)
174     {
175         debug("cannot create response for message %d\n", request->hdr->id);
176         return;
177     }
178
179     if (request->hdr->token_length)
180     coap_add_token(response, request->hdr->token_length, request->hdr->token);
181
182     if (coap_send(ctx, peer, response) == COAP_INVALID_TID)
183     {
184         debug("hnd_get_rd: cannot send response for message %d\n",
185                 request->hdr->id);
186     }
187     coap_delete_pdu(response);
188 #endif
189 }
190
191 void hnd_delete_resource(coap_context_t *ctx, struct coap_resource_t *resource,
192         coap_address_t *peer, coap_pdu_t *request, str *token, coap_pdu_t *response)
193 {
194     rd_t *rd = NULL;
195
196     HASH_FIND(hh, resources, resource->key, sizeof(coap_key_t), rd);
197     if (rd)
198     {
199         HASH_DELETE(hh, resources, rd);
200         rd_delete(rd);
201     }
202     /* FIXME: link attributes for resource have been created dynamically
203      * using coap_malloc() and must be released. */
204     coap_delete_resource(ctx, resource->key);
205
206     response->hdr->code = COAP_RESPONSE_CODE(202);
207 }
208
209 void hnd_get_rd(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
210         coap_pdu_t *request, str *token, coap_pdu_t *response)
211 {
212     unsigned char buf[3];
213
214     response->hdr->code = COAP_RESPONSE_CODE(205);
215
216     coap_add_option(response, COAP_OPTION_CONTENT_TYPE,
217             coap_encode_var_bytes(buf, COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf);
218
219     coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_bytes(buf, 0x2ffff), buf);
220 }
221
222 int parse_param(unsigned char *search, size_t search_len, unsigned char *data, size_t data_len,
223         str *result)
224 {
225
226     if (result)
227         memset(result, 0, sizeof(str));
228
229     if (!search_len)
230         return 0;
231
232     while (search_len <= data_len)
233     {
234
235         /* handle parameter if found */
236         if (memcmp(search, data, search_len) == 0)
237         {
238             data += search_len;
239             data_len -= search_len;
240
241             /* key is only valid if we are at end of string or delimiter follows */
242             if (!data_len || *data == '=' || *data == '&')
243             {
244                 while (data_len && *data != '=')
245                 {
246                     ++data;
247                     --data_len;
248                 }
249
250                 if (data_len > 1 && result)
251                 {
252                     /* value begins after '=' */
253
254                     result->s = ++data;
255                     while (--data_len && *data != '&')
256                     {
257                         ++data;
258                         result->length++;
259                     }
260                 }
261
262                 return 1;
263             }
264         }
265
266         /* otherwise proceed to next */
267         while (--data_len && *data++ != '&')
268             ;
269     }
270
271     return 0;
272 }
273
274 void add_source_address(struct coap_resource_t *resource, coap_address_t *peer)
275 {
276 #define BUFSIZE 64
277     char *buf;
278     size_t n = 1;
279
280     buf = (char *) coap_malloc(BUFSIZE);
281     if (!buf)
282         return;
283
284     buf[0] = '"';
285
286     switch (peer->addr.sa.sa_family)
287     {
288
289         case AF_INET:
290             /* FIXME */
291             break;
292
293         case AF_INET6:
294             n += snprintf(buf + n, BUFSIZE - n, "[%02x%02x:%02x%02x:%02x%02x:%02x%02x"
295                     ":%02x%02x:%02x%02x:%02x%02x:%02x%02x]", peer->addr.sin6.sin6_addr.s6_addr[0],
296                     peer->addr.sin6.sin6_addr.s6_addr[1], peer->addr.sin6.sin6_addr.s6_addr[2],
297                     peer->addr.sin6.sin6_addr.s6_addr[3], peer->addr.sin6.sin6_addr.s6_addr[4],
298                     peer->addr.sin6.sin6_addr.s6_addr[5], peer->addr.sin6.sin6_addr.s6_addr[6],
299                     peer->addr.sin6.sin6_addr.s6_addr[7], peer->addr.sin6.sin6_addr.s6_addr[8],
300                     peer->addr.sin6.sin6_addr.s6_addr[9], peer->addr.sin6.sin6_addr.s6_addr[10],
301                     peer->addr.sin6.sin6_addr.s6_addr[11], peer->addr.sin6.sin6_addr.s6_addr[12],
302                     peer->addr.sin6.sin6_addr.s6_addr[13], peer->addr.sin6.sin6_addr.s6_addr[14],
303                     peer->addr.sin6.sin6_addr.s6_addr[15]);
304
305             if (peer->addr.sin6.sin6_port != htons(COAP_DEFAULT_PORT))
306             {
307                 n += snprintf(buf + n, BUFSIZE - n, ":%d", peer->addr.sin6.sin6_port);
308             }
309             break;
310         default:
311             ;
312     }
313
314     if (n < BUFSIZE)
315         buf[n++] = '"';
316
317     coap_add_attr(resource, (unsigned char *) "A", 1, (unsigned char *) buf, n,
318             COAP_ATTR_FLAGS_RELEASE_VALUE);
319 #undef BUFSIZE
320 }
321
322 rd_t *
323 make_rd(coap_address_t *peer, coap_pdu_t *pdu)
324 {
325     rd_t *rd;
326     unsigned char *data;
327     coap_opt_iterator_t opt_iter;
328     coap_opt_t *etag;
329
330     rd = rd_new();
331
332     if (!rd)
333     {
334         debug("hnd_get_rd: cannot allocate storage for rd\n");
335         return NULL;
336     }
337
338     if (coap_get_data(pdu, &rd->data.length, &data))
339     {
340         rd->data.s = (unsigned char *) coap_malloc(rd->data.length);
341         if (!rd->data.s)
342         {
343             debug("hnd_get_rd: cannot allocate storage for rd->data\n");
344             rd_delete(rd);
345             return NULL;
346         }
347         memcpy(rd->data.s, data, rd->data.length);
348     }
349
350     etag = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
351     if (etag)
352     {
353         rd->etag_len = min(COAP_OPT_LENGTH(etag), sizeof(rd->etag));
354         memcpy(rd->etag, COAP_OPT_VALUE(etag), rd->etag_len);
355     }
356
357     return rd;
358 }
359
360 void hnd_post_rd(coap_context_t *ctx, struct coap_resource_t *resource, coap_address_t *peer,
361         coap_pdu_t *request, str *token, coap_pdu_t *response)
362 {
363     coap_resource_t *r;
364     coap_opt_iterator_t opt_iter;
365     coap_opt_t *query;
366 #define LOCSIZE 68
367     unsigned char *loc;
368     size_t loc_size;
369     str h =
370     { 0, NULL }, ins =
371     { 0, NULL }, rt =
372     { 0, NULL }, lt =
373     { 0, NULL }; /* store query parameters */
374     unsigned char *buf;
375
376     loc = (unsigned char *) coap_malloc(LOCSIZE);
377     if (!loc)
378     {
379         response->hdr->code = COAP_RESPONSE_CODE(500);
380         return;
381     }
382     memcpy(loc, RD_ROOT_STR, RD_ROOT_SIZE);
383
384     loc_size = RD_ROOT_SIZE;
385     loc[loc_size++] = '/';
386
387     /* store query parameters for later use */
388     query = coap_check_option(request, COAP_OPTION_URI_QUERY, &opt_iter);
389     if (query)
390     {
391         parse_param((unsigned char *) "h", 1, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &h);
392         parse_param((unsigned char *) "ins", 3, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query),
393                 &ins);
394         parse_param((unsigned char *) "lt", 2, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &lt);
395         parse_param((unsigned char *) "rt", 2, COAP_OPT_VALUE(query), COAP_OPT_LENGTH(query), &rt);
396     }
397
398     if (h.length)
399     { /* client has specified a node name */
400         memcpy(loc + loc_size, h.s, min(h.length, LOCSIZE - loc_size - 1));
401         loc_size += min(h.length, LOCSIZE - loc_size - 1);
402
403         if (ins.length && loc_size > 1)
404         {
405             loc[loc_size++] = '-';
406             memcpy((char *) (loc + loc_size), ins.s, min(ins.length, LOCSIZE - loc_size - 1));
407             loc_size += min(ins.length, LOCSIZE - loc_size - 1);
408         }
409
410     }
411     else
412     { /* generate node identifier */
413         loc_size += snprintf((char *) (loc + loc_size), LOCSIZE - loc_size - 1, "%x",
414                 request->hdr->id);
415
416         if (loc_size > 1)
417         {
418             if (ins.length)
419             {
420                 loc[loc_size++] = '-';
421                 memcpy((char *) (loc + loc_size), ins.s, min(ins.length, LOCSIZE - loc_size - 1));
422                 loc_size += min(ins.length, LOCSIZE - loc_size - 1);
423             }
424             else
425             {
426                 coap_tick_t now;
427                 coap_ticks(&now);
428
429                 loc_size += snprintf((char *) (loc + loc_size), LOCSIZE - loc_size - 1, "-%x", now);
430             }
431         }
432     }
433
434     /* TODO:
435      *   - use lt to check expiration
436      */
437
438     r = coap_resource_init(loc, loc_size, COAP_RESOURCE_FLAGS_RELEASE_URI);
439     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);
440     coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_resource);
441     coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_resource);
442
443     if (ins.s)
444     {
445         buf = (unsigned char *) coap_malloc(ins.length + 2);
446         if (buf)
447         {
448             /* add missing quotes */
449             buf[0] = '"';
450             memcpy(buf + 1, ins.s, ins.length);
451             buf[ins.length + 1] = '"';
452             coap_add_attr(r, (unsigned char *) "ins", 3, buf, ins.length + 2,
453                     COAP_ATTR_FLAGS_RELEASE_VALUE);
454         }
455     }
456
457     if (rt.s)
458     {
459         buf = (unsigned char *) coap_malloc(rt.length + 2);
460         if (buf)
461         {
462             /* add missing quotes */
463             buf[0] = '"';
464             memcpy(buf + 1, rt.s, rt.length);
465             buf[rt.length + 1] = '"';
466             coap_add_attr(r, (unsigned char *) "rt", 2, buf, rt.length + 2,
467                     COAP_ATTR_FLAGS_RELEASE_VALUE);
468         }
469     }
470
471     add_source_address(r, peer);
472
473     {
474         rd_t *rd;
475         rd = make_rd(peer, request);
476         if (rd)
477         {
478             coap_hash_path(loc, loc_size, rd->key);
479             HASH_ADD(hh, resources, key, sizeof(coap_key_t), rd);
480         }
481         else
482         {
483             /* FIXME: send error response and delete r */
484         }
485     }
486
487     coap_add_resource(ctx, r);
488
489     /* create response */
490
491     response->hdr->code = COAP_RESPONSE_CODE(201);
492
493     { /* split path into segments and add Location-Path options */
494         unsigned char _b[LOCSIZE];
495         unsigned char *b = _b;
496         size_t buflen = sizeof(_b);
497         int nseg;
498
499         nseg = coap_split_path(loc, loc_size, b, &buflen);
500         while (nseg--)
501         {
502             coap_add_option(response, COAP_OPTION_LOCATION_PATH, COAP_OPT_LENGTH(b),
503                     COAP_OPT_VALUE(b));
504             b += COAP_OPT_SIZE(b);
505         }
506     }
507 }
508
509 void init_resources(coap_context_t *ctx)
510 {
511     coap_resource_t *r;
512
513     r = coap_resource_init(RD_ROOT_STR, RD_ROOT_SIZE, 0);
514     coap_register_handler(r, COAP_REQUEST_GET, hnd_get_rd);
515     coap_register_handler(r, COAP_REQUEST_POST, hnd_post_rd);
516
517     coap_add_attr(r, (unsigned char *) "ct", 2, (unsigned char *) "40", 2, 0);
518     coap_add_attr(r, (unsigned char *) "rt", 2, (unsigned char *) "\"core-rd\"", 9, 0);
519     coap_add_attr(r, (unsigned char *) "ins", 2, (unsigned char *) "\"default\"", 9, 0);
520
521     coap_add_resource(ctx, r);
522
523 }
524
525 void usage(const char *program, const char *version)
526 {
527     const char *p;
528
529     p = strrchr(program, '/');
530     if (p)
531         program = ++p;
532
533     fprintf(stderr, "%s v%s -- CoRE Resource Directory implementation\n"
534             "(c) 2011-2012 Olaf Bergmann <bergmann@tzi.org>\n\n"
535             "usage: %s [-A address] [-p port]\n\n"
536             "\t-A address\tinterface address to bind to\n"
537             "\t-p port\t\tlisten on specified port\n"
538             "\t-v num\t\tverbosity level (default: 3)\n", program, version, program);
539 }
540
541 coap_context_t *
542 get_context(const char *node, const char *port)
543 {
544     coap_context_t *ctx = NULL;
545     int s;
546     struct addrinfo hints;
547     struct addrinfo *result, *rp;
548
549     memset(&hints, 0, sizeof(struct addrinfo));
550     hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
551     hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
552     hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
553
554     s = getaddrinfo(node, port, &hints, &result);
555     if (s != 0)
556     {
557         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
558         return NULL;
559     }
560
561     /* iterate through results until success */
562     for (rp = result; rp != NULL; rp = rp->ai_next)
563     {
564         coap_address_t addr;
565
566         if (rp->ai_addrlen <= sizeof(addr.addr))
567         {
568             coap_address_init(&addr);
569             addr.size = rp->ai_addrlen;
570             memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
571
572             ctx = coap_new_context(&addr);
573             if (ctx)
574             {
575                 /* TODO: output address:port for successful binding */
576                 goto finish;
577             }
578         }
579     }
580
581     fprintf(stderr, "no context available for interface '%s'\n", node);
582
583     finish: freeaddrinfo(result);
584     return ctx;
585 }
586
587 int join(coap_context_t *ctx, char *group_name)
588 {
589     struct ipv6_mreq mreq;
590     struct addrinfo *reslocal = NULL, *resmulti = NULL, hints, *ainfo;
591     int result = -1;
592
593     /* we have to resolve the link-local interface to get the interface id */
594     memset(&hints, 0, sizeof(hints));
595     hints.ai_family = AF_INET6;
596     hints.ai_socktype = SOCK_DGRAM;
597
598     result = getaddrinfo("::", NULL, &hints, &reslocal);
599     if (result < 0)
600     {
601         perror("join: cannot resolve link-local interface");
602         goto finish;
603     }
604
605     /* get the first suitable interface identifier */
606     for (ainfo = reslocal; ainfo != NULL; ainfo = ainfo->ai_next)
607     {
608         if (ainfo->ai_family == AF_INET6)
609         {
610             mreq.ipv6mr_interface = ((struct sockaddr_in6 *) ainfo->ai_addr)->sin6_scope_id;
611             break;
612         }
613     }
614
615     memset(&hints, 0, sizeof(hints));
616     hints.ai_family = AF_INET6;
617     hints.ai_socktype = SOCK_DGRAM;
618
619     /* resolve the multicast group address */
620     result = getaddrinfo(group_name, NULL, &hints, &resmulti);
621
622     if (result < 0)
623     {
624         perror("join: cannot resolve multicast address");
625         goto finish;
626     }
627
628     for (ainfo = resmulti; ainfo != NULL; ainfo = ainfo->ai_next)
629     {
630         if (ainfo->ai_family == AF_INET6)
631         {
632             mreq.ipv6mr_multiaddr = ((struct sockaddr_in6 *) ainfo->ai_addr)->sin6_addr;
633             break;
634         }
635     }
636
637     result = setsockopt(ctx->sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreq, sizeof(mreq));
638     if (result < 0)
639         perror("join: setsockopt");
640
641     finish: freeaddrinfo(resmulti);
642     freeaddrinfo(reslocal);
643
644     return result;
645 }
646
647 int main(int argc, char **argv)
648 {
649     coap_context_t *ctx;
650     fd_set readfds;
651     struct timeval tv, *timeout;
652     int result;
653     coap_tick_t now;
654     coap_queue_t *nextpdu;
655     char addr_str[NI_MAXHOST] = "::";
656     char port_str[NI_MAXSERV] = "5683";
657     char *group = NULL;
658     int opt;
659     coap_log_t log_level = LOG_WARNING;
660
661     while ((opt = getopt(argc, argv, "A:g:p:v:")) != -1)
662     {
663         switch (opt)
664         {
665             case 'A':
666                 strncpy(addr_str, optarg, NI_MAXHOST - 1);
667                 addr_str[NI_MAXHOST - 1] = '\0';
668                 break;
669             case 'g':
670                 group = optarg;
671                 break;
672             case 'p':
673                 strncpy(port_str, optarg, NI_MAXSERV - 1);
674                 port_str[NI_MAXSERV - 1] = '\0';
675                 break;
676             case 'v':
677                 log_level = strtol(optarg, NULL, 10);
678                 break;
679             default:
680                 usage(argv[0], PACKAGE_VERSION);
681                 exit(1);
682         }
683     }
684
685     coap_set_log_level(log_level);
686
687     ctx = get_context(addr_str, port_str);
688     if (!ctx)
689         return -1;
690
691     if (group)
692         join(ctx, group);
693
694     init_resources(ctx);
695
696     signal(SIGINT, handle_sigint);
697
698     while (!quit)
699     {
700         FD_ZERO(&readfds);
701         FD_SET(ctx->sockfd, &readfds);
702
703         nextpdu = coap_peek_next(ctx);
704
705         coap_ticks(&now);
706         while (nextpdu && nextpdu->t <= now)
707         {
708             coap_retransmit(ctx, coap_pop_next(ctx));
709             nextpdu = coap_peek_next(ctx);
710         }
711
712         if (nextpdu && nextpdu->t <= now + COAP_RESOURCE_CHECK_TIME)
713         {
714             /* set timeout if there is a pdu to send before our automatic timeout occurs */
715             tv.tv_usec = ((nextpdu->t - now) % COAP_TICKS_PER_SECOND) * 1000000
716                     / COAP_TICKS_PER_SECOND;
717             tv.tv_sec = (nextpdu->t - now) / COAP_TICKS_PER_SECOND;
718             timeout = &tv;
719         }
720         else
721         {
722             tv.tv_usec = 0;
723             tv.tv_sec = COAP_RESOURCE_CHECK_TIME;
724             timeout = &tv;
725         }
726         result = select(FD_SETSIZE, &readfds, 0, 0, timeout);
727
728         if (result < 0)
729         { /* error */
730             if (errno != EINTR)
731                 perror("select");
732         }
733         else if (result > 0)
734         { /* read from socket */
735             if (FD_ISSET(ctx->sockfd, &readfds))
736             {
737                 coap_read(ctx); /* read received data */
738                 coap_dispatch(ctx); /* and dispatch PDUs from receivequeue */
739             }
740         }
741         else
742         { /* timeout */
743             /* coap_check_resource_list( ctx ); */
744         }
745     }
746
747     coap_free_context(ctx);
748
749     return 0;
750 }