1 /* coap-client -- simple CoAP client
3 * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
5 * This file is part of the CoAP library libcoap. Please see
6 * README for terms of use.
16 #include <sys/select.h>
17 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
29 static unsigned char _token_data[8];
30 str the_token = { 0, _token_data };
32 #define MOD_NAME ("CoAP-Client")
33 #define FLAGS_BLOCK 0x01
35 static coap_list_t *optlist = NULL;
37 * TODO: associate the resources with transaction id and make it expireable */
38 static coap_uri_t uri;
39 static str proxy = { 0, NULL };
40 static unsigned short proxy_port = COAP_DEFAULT_PORT;
42 /* reading is done when this flag is set */
45 static str output_file = { 0, NULL }; /* output file name */
46 static FILE *file = NULL; /* output file stream */
48 static str payload = { 0, NULL }; /* optional payload to send */
50 unsigned char msgtype = COAP_MESSAGE_CON; /* usually, requests are sent confirmable */
52 typedef unsigned char method_t;
53 method_t method = 1; /* the method we are using in our requests */
55 coap_block_t block = { .num = 0, .m = 0, .szx = 6 };
57 unsigned int wait_seconds = 90; /* default timeout in seconds */
58 coap_tick_t max_wait; /* global timeout (changed by set_timeout()) */
60 unsigned int obs_seconds = 30; /* default observe time */
61 coap_tick_t obs_wait = 0; /* timeout for current subscription */
63 #define min(a,b) ((a) < (b) ? (a) : (b))
66 set_timeout(coap_tick_t *timer, const unsigned int seconds) {
68 *timer += seconds * COAP_TICKS_PER_SECOND;
72 append_to_output(const unsigned char *data, size_t len) {
76 if (!output_file.s || (output_file.length && output_file.s[0] == '-'))
79 if (!(file = fopen((char *)output_file.s, "w"))) {
87 written = fwrite(data, 1, len, file);
90 } while ( written && len );
100 /* add a newline before closing in case were writing to stdout */
101 if (!output_file.s || (output_file.length && output_file.s[0] == '-'))
102 fwrite("\n", 1, 1, file);
110 new_ack( coap_context_t *ctx, coap_queue_t *node ) {
111 coap_pdu_t *pdu = coap_new_pdu();
115 pdu->hdr->type = COAP_MESSAGE_ACK;
117 pdu->hdr->id = node->pdu->hdr->id;
124 new_response( coap_context_t *ctx, coap_queue_t *node, unsigned int code ) {
125 coap_pdu_t *pdu = new_ack(ctx, node);
128 pdu->hdr->code = code;
134 coap_new_request(coap_context_t *ctx, method_t m, coap_list_t *options ) {
138 if ( ! ( pdu = coap_new_pdu() ) )
141 pdu->hdr->type = msgtype;
142 pdu->hdr->id = coap_new_message_id(ctx);
145 pdu->hdr->token_length = the_token.length;
146 if ( !coap_add_token(pdu, the_token.length, the_token.s)) {
147 debug("cannot add token to request\n");
152 for (opt = options; opt; opt = opt->next) {
153 coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *)opt->data),
154 COAP_OPTION_LENGTH(*(coap_option *)opt->data),
155 COAP_OPTION_DATA(*(coap_option *)opt->data));
158 if (payload.length) {
159 if ((flags & FLAGS_BLOCK) == 0)
160 coap_add_data(pdu, payload.length, payload.s);
162 coap_add_block(pdu, payload.length, payload.s, block.num, block.szx);
169 clear_obs(coap_context_t *ctx, const coap_address_t *remote) {
172 coap_tid_t tid = COAP_INVALID_TID;
174 /* create bare PDU w/o any option */
175 pdu = coap_new_request(ctx, COAP_REQUEST_GET, NULL);
178 /* FIXME: add token */
179 /* add URI components from optlist */
180 for (option = optlist; option; option = option->next ) {
181 switch (COAP_OPTION_KEY(*(coap_option *)option->data)) {
182 case COAP_OPTION_URI_HOST :
183 case COAP_OPTION_URI_PORT :
184 case COAP_OPTION_URI_PATH :
185 case COAP_OPTION_URI_QUERY :
186 coap_add_option ( pdu, COAP_OPTION_KEY(*(coap_option *)option->data),
187 COAP_OPTION_LENGTH(*(coap_option *)option->data),
188 COAP_OPTION_DATA(*(coap_option *)option->data) );
191 ; /* skip other options */
195 if (pdu->hdr->type == COAP_MESSAGE_CON)
196 tid = coap_send_confirmed(ctx, remote, pdu);
198 tid = coap_send(ctx, remote, pdu);
200 if (tid == COAP_INVALID_TID) {
201 debug("clear_obs: error sending new request");
202 coap_delete_pdu(pdu);
203 } else if (pdu->hdr->type != COAP_MESSAGE_CON)
204 coap_delete_pdu(pdu);
210 resolve_address(const str *server, struct sockaddr *dst) {
212 struct addrinfo *res, *ainfo;
213 struct addrinfo hints;
214 static char addrstr[256];
218 memset(addrstr, 0, sizeof(addrstr));
220 memcpy(addrstr, server->s, server->length);
222 memcpy(addrstr, "localhost", 9);
224 OC_LOG_V(DEBUG, MOD_NAME, "Line %d, server %s", __LINE__, addrstr);
226 memset ((char *)&hints, 0, sizeof(hints));
227 hints.ai_socktype = SOCK_DGRAM;
228 hints.ai_family = AF_UNSPEC;
230 error = getaddrinfo(addrstr, NULL, &hints, &res);
233 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
237 for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
238 switch (ainfo->ai_family) {
241 len = ainfo->ai_addrlen;
242 memcpy(dst, ainfo->ai_addr, len);
254 static inline coap_opt_t *
255 get_block(coap_pdu_t *pdu, coap_opt_iterator_t *opt_iter) {
260 memset(f, 0, sizeof(coap_opt_filter_t));
261 coap_option_setb(f, COAP_OPTION_BLOCK1);
262 coap_option_setb(f, COAP_OPTION_BLOCK2);
264 coap_option_iterator_init(pdu, opt_iter, f);
265 return coap_option_next(opt_iter);
268 #define HANDLE_BLOCK1(Pdu) \
269 ((method == COAP_REQUEST_PUT || method == COAP_REQUEST_POST) && \
270 ((flags & FLAGS_BLOCK) == 0) && \
271 ((Pdu)->hdr->code == COAP_RESPONSE_CODE(201) || \
272 (Pdu)->hdr->code == COAP_RESPONSE_CODE(204)))
275 check_token(coap_pdu_t *received) {
276 return received->hdr->token_length == the_token.length &&
277 memcmp(received->hdr->token, the_token.s, the_token.length) == 0;
281 message_handler(struct coap_context_t *ctx,
282 const coap_address_t *remote,
284 coap_pdu_t *received,
285 const coap_tid_t id) {
287 coap_pdu_t *pdu = NULL;
288 coap_opt_t *block_opt;
289 coap_opt_iterator_t opt_iter;
290 unsigned char buf[4];
293 unsigned char *databuf;
298 if (LOG_DEBUG <= coap_get_log_level()) {
299 debug("** process incoming %d.%02d response:\n",
300 (received->hdr->code >> 5), received->hdr->code & 0x1F);
301 coap_show_pdu(received);
305 /* check if this is a response to our original request */
306 if (!check_token(received)) {
307 /* drop if this was just some message, or send RST in case of notification */
308 if (!sent && (received->hdr->type == COAP_MESSAGE_CON ||
309 received->hdr->type == COAP_MESSAGE_NON))
310 coap_send_rst(ctx, remote, received);
314 switch (received->hdr->type) {
315 case COAP_MESSAGE_CON:
316 /* acknowledge received response if confirmable (TODO: check Token) */
317 coap_send_ack(ctx, remote, received);
319 case COAP_MESSAGE_RST:
326 /* output the received data, if any */
327 if (received->hdr->code == COAP_RESPONSE_CODE(205)) {
329 /* set obs timer if we have successfully subscribed a resource */
330 if (sent && coap_check_option(received, COAP_OPTION_SUBSCRIPTION, &opt_iter)) {
331 debug("observation relationship established, set timeout to %d\n", obs_seconds);
332 set_timeout(&obs_wait, obs_seconds);
335 /* Got some data, check if block option is set. Behavior is undefined if
336 * both, Block1 and Block2 are present. */
337 block_opt = get_block(received, &opt_iter);
339 /* There is no block option set, just read the data and we are done. */
340 if (coap_get_data(received, &len, &databuf))
341 append_to_output(databuf, len);
343 unsigned short blktype = opt_iter.type;
345 /* TODO: check if we are looking at the correct block number */
346 if (coap_get_data(received, &len, &databuf))
347 append_to_output(databuf, len);
349 if (COAP_OPT_BLOCK_MORE(block_opt)) {
350 /* more bit is set */
351 debug("found the M bit, block size is %u, block nr. %u\n",
352 COAP_OPT_BLOCK_SZX(block_opt), coap_opt_block_num(block_opt));
354 /* create pdu with request for next block */
355 pdu = coap_new_request(ctx, method, NULL); /* first, create bare PDU w/o any option */
357 /* add URI components from optlist */
358 for (option = optlist; option; option = option->next ) {
359 switch (COAP_OPTION_KEY(*(coap_option *)option->data)) {
360 case COAP_OPTION_URI_HOST :
361 case COAP_OPTION_URI_PORT :
362 case COAP_OPTION_URI_PATH :
363 case COAP_OPTION_URI_QUERY :
364 coap_add_option ( pdu, COAP_OPTION_KEY(*(coap_option *)option->data),
365 COAP_OPTION_LENGTH(*(coap_option *)option->data),
366 COAP_OPTION_DATA(*(coap_option *)option->data) );
369 ; /* skip other options */
373 /* finally add updated block option from response, clear M bit */
374 /* blocknr = (blocknr & 0xfffffff7) + 0x10; */
375 debug("query block %d\n", (coap_opt_block_num(block_opt) + 1));
376 coap_add_option(pdu, blktype, coap_encode_var_bytes(buf,
377 ((coap_opt_block_num(block_opt) + 1) << 4) |
378 COAP_OPT_BLOCK_SZX(block_opt)), buf);
380 if (received->hdr->type == COAP_MESSAGE_CON)
381 tid = coap_send_confirmed(ctx, remote, pdu);
383 tid = coap_send(ctx, remote, pdu);
385 if (tid == COAP_INVALID_TID) {
386 debug("message_handler: error sending new request");
387 coap_delete_pdu(pdu);
389 set_timeout(&max_wait, wait_seconds);
390 if (received->hdr->type != COAP_MESSAGE_CON)
391 coap_delete_pdu(pdu);
398 } else { /* no 2.05 */
400 /* check if an error was signaled and output payload if so */
401 if (COAP_RESPONSE_CLASS(received->hdr->code) >= 4) {
402 fprintf(stderr, "%d.%02d",
403 (received->hdr->code >> 5), received->hdr->code & 0x1F);
404 if (coap_get_data(received, &len, &databuf)) {
405 fprintf(stderr, " ");
407 fprintf(stderr, "%c", *databuf++);
409 fprintf(stderr, "\n");
414 /* finally send new request, if needed */
415 if (pdu && coap_send(ctx, remote, pdu) == COAP_INVALID_TID) {
416 debug("message_handler: error sending response");
418 coap_delete_pdu(pdu);
420 /* our job is done, we can exit at any time */
421 ready = coap_check_option(received, COAP_OPTION_SUBSCRIPTION, &opt_iter) == NULL;
425 usage( const char *program, const char *version) {
428 p = strrchr( program, '/' );
432 fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
433 "(c) 2010-2013 Olaf Bergmann <bergmann@tzi.org>\n\n"
434 "usage: %s [-A type...] [-t type] [-b [num,]size] [-B seconds] [-e text]\n"
435 "\t\t[-g group] [-m method] [-N] [-o file] [-P addr[:port]] [-p port]\n"
436 "\t\t[-s duration] [-O num,text] [-T string] [-v num] URI\n\n"
437 "\tURI can be an absolute or relative coap URI,\n"
438 "\t-A type...\taccepted media types as comma-separated list of\n"
439 "\t\t\tsymbolic or numeric values\n"
440 "\t-t type\t\tcontent type for given resource for PUT/POST\n"
441 "\t-b [num,]size\tblock size to be used in GET/PUT/POST requests\n"
442 "\t \t\t(value must be a multiple of 16 not larger than 1024)\n"
443 "\t \t\tIf num is present, the request chain will start at\n"
445 "\t-B seconds\tbreak operation after waiting given seconds\n"
446 "\t\t\t(default is %d)\n"
447 "\t-e text\t\tinclude text as payload (use percent-encoding for\n"
448 "\t\t\tnon-ASCII characters)\n"
449 "\t-f file\t\tfile to send with PUT/POST (use '-' for STDIN)\n"
450 "\t-g group\tjoin the given multicast group\n"
451 "\t-m method\trequest method (get|put|post|delete), default is 'get'\n"
452 "\t-N\t\tsend NON-confirmable message\n"
453 "\t-o file\t\toutput received data to this file (use '-' for STDOUT)\n"
454 "\t-p port\t\tlisten on specified port\n"
455 "\t-s duration\tsubscribe for given duration [s]\n"
456 "\t-v num\t\tverbosity level (default: 3)\n"
457 "\t-O num,text\tadd option num with contents text to request\n"
458 "\t-P addr[:port]\tuse proxy (automatically adds Proxy-Uri option to\n"
460 "\t-T token\tinclude specified token\n"
463 "\tcoap-client -m get coap://[::1]/\n"
464 "\tcoap-client -m get coap://[::1]/.well-known/core\n"
465 "\tcoap-client -m get -T cafe coap://[::1]/time\n"
466 "\techo 1000 | coap-client -m put -T cafe coap://[::1]/time -f -\n"
467 ,program, version, program, wait_seconds);
471 join( coap_context_t *ctx, char *group_name ){
472 struct ipv6_mreq mreq;
473 struct addrinfo *reslocal = NULL, *resmulti = NULL, hints, *ainfo;
476 /* we have to resolve the link-local interface to get the interface id */
477 memset(&hints, 0, sizeof(hints));
478 hints.ai_family = AF_INET6;
479 hints.ai_socktype = SOCK_DGRAM;
481 result = getaddrinfo("::", NULL, &hints, &reslocal);
483 fprintf(stderr, "join: cannot resolve link-local interface: %s\n",
484 gai_strerror(result));
488 /* get the first suitable interface identifier */
489 for (ainfo = reslocal; ainfo != NULL; ainfo = ainfo->ai_next) {
490 if ( ainfo->ai_family == AF_INET6 ) {
491 mreq.ipv6mr_interface =
492 ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_scope_id;
497 memset(&hints, 0, sizeof(hints));
498 hints.ai_family = AF_INET6;
499 hints.ai_socktype = SOCK_DGRAM;
501 /* resolve the multicast group address */
502 result = getaddrinfo(group_name, NULL, &hints, &resmulti);
505 fprintf(stderr, "join: cannot resolve multicast address: %s\n",
506 gai_strerror(result));
510 for (ainfo = resmulti; ainfo != NULL; ainfo = ainfo->ai_next) {
511 if ( ainfo->ai_family == AF_INET6 ) {
512 mreq.ipv6mr_multiaddr =
513 ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_addr;
518 result = setsockopt( ctx->sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
519 (char *)&mreq, sizeof(mreq) );
521 perror("join: setsockopt");
524 freeaddrinfo(resmulti);
525 freeaddrinfo(reslocal);
531 order_opts(void *a, void *b) {
533 return a < b ? -1 : 1;
535 if (COAP_OPTION_KEY(*(coap_option *)a) < COAP_OPTION_KEY(*(coap_option *)b))
538 return COAP_OPTION_KEY(*(coap_option *)a) == COAP_OPTION_KEY(*(coap_option *)b);
543 new_option_node(unsigned short key, unsigned int length, unsigned char *data) {
547 option = coap_malloc(sizeof(coap_option) + length);
551 COAP_OPTION_KEY(*option) = key;
552 COAP_OPTION_LENGTH(*option) = length;
553 memcpy(COAP_OPTION_DATA(*option), data, length);
555 /* we can pass NULL here as delete function since option is released automatically */
556 node = coap_new_listnode(option, NULL);
562 perror("new_option_node: malloc");
573 cmdline_content_type(char *arg, unsigned short key) {
574 static content_type_t content_types[] = {
578 { 40, "link-format" },
579 { 40, "application/link-format" },
582 { 42, "octet-stream" },
583 { 42, "application/octet-stream" },
585 { 47, "application/exi" },
587 { 50, "application/json" },
591 unsigned char i, value[10];
593 unsigned char buf[2];
602 value[valcnt++] = atoi(q);
604 for (i=0; content_types[i].media_type &&
605 strncmp(q,content_types[i].media_type, p ? p-q : strlen(q)) != 0 ;
609 if (content_types[i].media_type) {
610 value[valcnt] = content_types[i].code;
613 warn("W: unknown content-type '%s'\n",arg);
617 if (!p || key == COAP_OPTION_CONTENT_TYPE)
623 for (i = 0; i < valcnt; ++i) {
624 node = new_option_node(key, coap_encode_var_bytes(buf, value[i]), buf);
626 coap_insert( &optlist, node, order_opts );
631 cmdline_uri(char *arg) {
632 unsigned char portbuf[2];
634 unsigned char _buf[BUFSIZE];
635 unsigned char *buf = _buf;
639 if (proxy.length) { /* create Proxy-Uri from argument */
640 size_t len = strlen(arg);
642 coap_insert(&optlist,
643 new_option_node(COAP_OPTION_PROXY_URI,
644 270, (unsigned char *)arg),
650 coap_insert(&optlist,
651 new_option_node(COAP_OPTION_PROXY_URI,
652 len, (unsigned char *)arg),
654 } else { /* split arg into Uri-* options */
655 coap_split_uri((unsigned char *)arg, strlen(arg), &uri );
657 if (uri.port != COAP_DEFAULT_PORT) {
658 coap_insert( &optlist,
659 new_option_node(COAP_OPTION_URI_PORT,
660 coap_encode_var_bytes(portbuf, uri.port),
665 if (uri.path.length) {
667 res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
670 coap_insert(&optlist, new_option_node(COAP_OPTION_URI_PATH,
671 COAP_OPT_LENGTH(buf),
672 COAP_OPT_VALUE(buf)),
675 buf += COAP_OPT_SIZE(buf);
679 if (uri.query.length) {
682 res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
685 coap_insert(&optlist, new_option_node(COAP_OPTION_URI_QUERY,
686 COAP_OPT_LENGTH(buf),
687 COAP_OPT_VALUE(buf)),
690 buf += COAP_OPT_SIZE(buf);
697 cmdline_blocksize(char *arg) {
702 while(*arg && *arg != ',')
703 size = size * 10 + (*arg++ - '0');
712 block.szx = (coap_fls(size >> 4) - 1) & 0x07;
714 flags |= FLAGS_BLOCK;
718 /* Called after processing the options from the commandline to set
719 * Block1 or Block2 depending on method. */
722 static unsigned char buf[4]; /* hack: temporarily take encoded bytes */
725 if (method != COAP_REQUEST_DELETE) {
726 opt = method == COAP_REQUEST_GET ? COAP_OPTION_BLOCK2 : COAP_OPTION_BLOCK1;
728 coap_insert(&optlist, new_option_node(opt,
729 coap_encode_var_bytes(buf, (block.num << 4 | block.szx)), buf),
735 cmdline_subscribe(char *arg) {
737 obs_seconds = atoi(optarg);
738 coap_insert(&optlist, new_option_node(COAP_OPTION_SUBSCRIPTION, 0, NULL),
743 cmdline_proxy(char *arg) {
744 char *proxy_port_str = strrchr((const char *)arg, ':'); /* explicit port ? */
745 if (proxy_port_str) {
746 char *ipv6_delimiter = strrchr((const char *)arg, ']');
747 if (!ipv6_delimiter) {
748 if (proxy_port_str == strchr((const char *)arg, ':')) {
749 /* host:port format - host not in ipv6 hexadecimal string format */
750 *proxy_port_str++ = '\0'; /* split */
751 proxy_port = atoi(proxy_port_str);
754 arg = strchr((const char *)arg, '[');
757 *ipv6_delimiter = '\0'; /* split */
758 if (ipv6_delimiter + 1 == proxy_port_str++) {
759 /* [ipv6 address]:port */
760 proxy_port = atoi(proxy_port_str);
765 proxy.length = strlen(arg);
766 if ( (proxy.s = coap_malloc(proxy.length + 1)) == NULL) {
771 memcpy(proxy.s, arg, proxy.length+1);
776 cmdline_token(char *arg) {
777 strncpy((char *)the_token.s, arg, min(sizeof(_token_data), strlen(arg)));
778 the_token.length = strlen(arg);
782 cmdline_option(char *arg) {
783 unsigned int num = 0;
785 while (*arg && *arg != ',') {
786 num = num * 10 + (*arg - '0');
792 coap_insert( &optlist, new_option_node(num,
794 (unsigned char *)arg), order_opts);
797 extern int check_segment(const unsigned char *s, size_t length);
798 extern void decode_segment(const unsigned char *seg, size_t length, unsigned char *buf);
801 cmdline_input(char *text, str *buf) {
803 len = check_segment((unsigned char *)text, strlen(text));
808 buf->s = (unsigned char *)coap_malloc(len);
813 decode_segment((unsigned char *)text, strlen(text), buf->s);
818 cmdline_input_from_file(char *filename, str *buf) {
819 FILE *inputfile = NULL;
824 if (!filename || !buf)
827 if (filename[0] == '-' && !filename[1]) { /* read from stdin */
829 buf->s = (unsigned char *)coap_malloc(buf->length);
835 /* read from specified input file */
836 if (stat(filename, &statbuf) < 0) {
837 perror("cmdline_input_from_file: stat");
841 buf->length = statbuf.st_size;
842 buf->s = (unsigned char *)coap_malloc(buf->length);
846 inputfile = fopen(filename, "r");
848 perror("cmdline_input_from_file: fopen");
854 len = fread(buf->s, 1, buf->length, inputfile);
856 if (len < buf->length) {
857 if (ferror(inputfile) != 0) {
858 perror("cmdline_input_from_file: fread");
868 if (inputfile != stdin)
875 cmdline_method(char *arg) {
876 static char *methods[] =
877 { 0, "get", "post", "put", "delete", 0};
880 for (i=1; methods[i] && strcasecmp(arg,methods[i]) != 0 ; ++i)
883 return i; /* note that we do not prevent illegal methods */
887 get_context(const char *node, const char *port) {
888 coap_context_t *ctx = NULL;
890 struct addrinfo hints;
891 struct addrinfo *result, *rp;
893 memset(&hints, 0, sizeof(struct addrinfo));
894 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
895 hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
897 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL;
901 OC_LOG_V(DEBUG, MOD_NAME, "Line %d, node %s, port %d", __LINE__, node, port);
902 s = getaddrinfo(node, port, &hints, &result);
904 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
908 /* iterate through results until success */
909 for (rp = result; rp != NULL; rp = rp->ai_next) {
912 if (rp->ai_addrlen <= sizeof(addr.addr)) {
913 coap_address_init(&addr);
914 addr.size = rp->ai_addrlen;
915 memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
917 ctx = coap_new_context(&addr);
919 /* TODO: output address:port for successful binding */
925 fprintf(stderr, "no context available for interface '%s'\n", node);
928 freeaddrinfo(result);
933 main(int argc, char **argv) {
934 coap_context_t *ctx = NULL;
936 static char addr[INET6_ADDRSTRLEN];
937 void *addrptr = NULL;
942 coap_queue_t *nextpdu;
945 unsigned short port = COAP_DEFAULT_PORT;
946 char port_str[NI_MAXSERV] = "0";
949 coap_log_t log_level = LOG_WARNING;
950 coap_tid_t tid = COAP_INVALID_TID;
952 while ((opt = getopt(argc, argv, "Nb:e:f:g:m:p:s:t:o:v:A:B:O:P:T:")) != -1) {
955 cmdline_blocksize(optarg);
958 wait_seconds = atoi(optarg);
961 if (!cmdline_input(optarg,&payload))
965 if (!cmdline_input_from_file(optarg,&payload))
972 strncpy(port_str, optarg, NI_MAXSERV-1);
973 port_str[NI_MAXSERV - 1] = '\0';
976 method = cmdline_method(optarg);
979 msgtype = COAP_MESSAGE_NON;
982 cmdline_subscribe(optarg);
985 output_file.length = strlen(optarg);
986 output_file.s = (unsigned char *)coap_malloc(output_file.length + 1);
988 if (!output_file.s) {
989 fprintf(stderr, "cannot set output file: insufficient memory\n");
992 /* copy filename including trailing zero */
993 memcpy(output_file.s, optarg, output_file.length + 1);
997 cmdline_content_type(optarg,COAP_OPTION_ACCEPT);
1000 cmdline_content_type(optarg,COAP_OPTION_CONTENT_TYPE);
1003 cmdline_option(optarg);
1006 if (!cmdline_proxy(optarg)) {
1007 fprintf(stderr, "error specifying proxy address\n");
1012 cmdline_token(optarg);
1015 log_level = strtol(optarg, NULL, 10);
1018 usage( argv[0], PACKAGE_VERSION );
1023 coap_set_log_level(log_level);
1025 if ( optind < argc )
1026 cmdline_uri( argv[optind] );
1028 usage( argv[0], PACKAGE_VERSION );
1040 /* resolve destination address where server should be sent */
1041 res = resolve_address(&server, &dst.addr.sa);
1044 fprintf(stderr, "failed to resolve address\n");
1049 dst.addr.sin.sin_port = htons(port);
1051 /* add Uri-Host if server address differs from uri.host */
1053 switch (dst.addr.sa.sa_family) {
1055 addrptr = &dst.addr.sin.sin_addr;
1057 /* create context for IPv4 */
1058 ctx = get_context("0.0.0.0", port_str);
1061 addrptr = &dst.addr.sin6.sin6_addr;
1063 /* create context for IPv6 */
1064 ctx = get_context("::", port_str);
1071 coap_log(LOG_EMERG, "cannot create context\n");
1075 coap_register_option(ctx, COAP_OPTION_BLOCK2);
1076 coap_register_response_handler(ctx, message_handler);
1078 /* join multicast group if requested at command line */
1082 /* construct CoAP message */
1084 if (!proxy.length && addrptr
1085 && (inet_ntop(dst.addr.sa.sa_family, addrptr, addr, sizeof(addr)) != 0)
1086 && (strlen(addr) != uri.host.length
1087 || memcmp(addr, uri.host.s, uri.host.length) != 0)) {
1090 coap_insert(&optlist, new_option_node(COAP_OPTION_URI_HOST,
1091 uri.host.length, uri.host.s),
1095 /* set block option if requested at commandline */
1096 if (flags & FLAGS_BLOCK)
1099 if (! (pdu = coap_new_request(ctx, method, optlist)))
1103 if (LOG_DEBUG <= coap_get_log_level()) {
1104 debug("sending CoAP request:\n");
1109 if (pdu->hdr->type == COAP_MESSAGE_CON)
1110 tid = coap_send_confirmed(ctx, &dst, pdu);
1112 tid = coap_send(ctx, &dst, pdu);
1114 if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
1115 coap_delete_pdu(pdu);
1117 set_timeout(&max_wait, wait_seconds);
1118 debug("timeout is set to %d seconds\n", wait_seconds);
1120 while ( !(ready && coap_can_exit(ctx)) ) {
1122 FD_SET( ctx->sockfd, &readfds );
1124 nextpdu = coap_peek_next( ctx );
1127 while (nextpdu && nextpdu->t <= now - ctx->sendqueue_basetime) {
1128 coap_retransmit( ctx, coap_pop_next( ctx ));
1129 nextpdu = coap_peek_next( ctx );
1132 if (nextpdu && nextpdu->t < min(obs_wait ? obs_wait : max_wait, max_wait) - now) {
1133 /* set timeout if there is a pdu to send */
1134 tv.tv_usec = ((nextpdu->t) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1135 tv.tv_sec = (nextpdu->t) / COAP_TICKS_PER_SECOND;
1137 /* check if obs_wait fires before max_wait */
1138 if (obs_wait && obs_wait < max_wait) {
1139 tv.tv_usec = ((obs_wait - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1140 tv.tv_sec = (obs_wait - now) / COAP_TICKS_PER_SECOND;
1142 tv.tv_usec = ((max_wait - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1143 tv.tv_sec = (max_wait - now) / COAP_TICKS_PER_SECOND;
1147 result = select(ctx->sockfd + 1, &readfds, 0, 0, &tv);
1149 if ( result < 0 ) { /* error */
1151 } else if ( result > 0 ) { /* read from socket */
1152 if ( FD_ISSET( ctx->sockfd, &readfds ) ) {
1153 coap_read( ctx, ctx->sockfd ); /* read received data */
1154 coap_dispatch( ctx ); /* and dispatch PDUs from receivequeue */
1156 } else { /* timeout */
1158 if (max_wait <= now) {
1162 if (obs_wait && obs_wait <= now) {
1163 debug("clear observation relationship\n");
1164 clear_obs(ctx, &dst); /* FIXME: handle error case COAP_TID_INVALID */
1166 /* make sure that the obs timer does not fire again */
1175 coap_free_context( ctx );