1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 * -*- */
2
3 /* coap-client -- simple CoAP client
4 *
5 * Copyright (C) 2010--2015 Olaf Bergmann <bergmann@tzi.org>
6 *
7 * This file is part of the CoAP library libcoap. Please see README for terms of
8 * use.
9 */
10
11 #include "coap_config.h"
12
13 #include <string.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <ctype.h>
18 #include <sys/select.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <netdb.h>
25
26 #include "coap.h"
27 #include "coap_list.h"
28
29 int flags = 0;
30
31 static unsigned char _token_data[8];
32 str the_token = { 0, _token_data };
33
34 #define FLAGS_BLOCK 0x01
35
36 static coap_list_t *optlist = NULL;
37 /* Request URI.
38 * TODO: associate the resources with transaction id and make it expireable */
39 static coap_uri_t uri;
40 static str proxy = { 0, NULL };
41 static unsigned short proxy_port = COAP_DEFAULT_PORT;
42
43 /* reading is done when this flag is set */
44 static int ready = 0;
45
46 static str output_file = { 0, NULL }; /* output file name */
47 static FILE *file = NULL; /* output file stream */
48
49 static str payload = { 0, NULL }; /* optional payload to send */
50
51 unsigned char msgtype = COAP_MESSAGE_CON; /* usually, requests are sent confirmable */
52
53 typedef unsigned char method_t;
54 method_t method = 1; /* the method we are using in our requests */
55
56 coap_block_t block = { .num = 0, .m = 0, .szx = 6 };
57
58 unsigned int wait_seconds = 90; /* default timeout in seconds */
59 coap_tick_t max_wait; /* global timeout (changed by set_timeout()) */
60
61 unsigned int obs_seconds = 30; /* default observe time */
62 coap_tick_t obs_wait = 0; /* timeout for current subscription */
63
64 #define min(a,b) ((a) < (b) ? (a) : (b))
65
66 #ifdef __GNUC__
67 #define UNUSED_PARAM __attribute__ ((unused))
68 #else /* not a GCC */
69 #define UNUSED_PARAM
70 #endif /* GCC */
71
72 static inline void
set_timeout(coap_tick_t * timer,const unsigned int seconds)73 set_timeout(coap_tick_t *timer, const unsigned int seconds) {
74 coap_ticks(timer);
75 *timer += seconds * COAP_TICKS_PER_SECOND;
76 }
77
78 static int
append_to_output(const unsigned char * data,size_t len)79 append_to_output(const unsigned char *data, size_t len) {
80 size_t written;
81
82 if (!file) {
83 if (!output_file.s || (output_file.length && output_file.s[0] == '-'))
84 file = stdout;
85 else {
86 if (!(file = fopen((char *)output_file.s, "w"))) {
87 perror("fopen");
88 return -1;
89 }
90 }
91 }
92
93 do {
94 written = fwrite(data, 1, len, file);
95 len -= written;
96 data += written;
97 } while ( written && len );
98 fflush(file);
99
100 return 0;
101 }
102
103 static void
close_output(void)104 close_output(void) {
105 if (file) {
106
107 /* add a newline before closing in case were writing to stdout */
108 if (!output_file.s || (output_file.length && output_file.s[0] == '-'))
109 fwrite("\n", 1, 1, file);
110
111 fflush(file);
112 fclose(file);
113 }
114 }
115
116 static int
order_opts(void * a,void * b)117 order_opts(void *a, void *b) {
118 coap_option *o1, *o2;
119
120 if (!a || !b)
121 return a < b ? -1 : 1;
122
123 o1 = (coap_option *)(((coap_list_t *)a)->data);
124 o2 = (coap_option *)(((coap_list_t *)b)->data);
125
126 return (COAP_OPTION_KEY(*o1) < COAP_OPTION_KEY(*o2))
127 ? -1
128 : (COAP_OPTION_KEY(*o1) != COAP_OPTION_KEY(*o2));
129 }
130
131 static coap_pdu_t *
coap_new_request(coap_context_t * ctx,method_t m,coap_list_t ** options,unsigned char * data,size_t length)132 coap_new_request(coap_context_t *ctx,
133 method_t m,
134 coap_list_t **options,
135 unsigned char *data,
136 size_t length) {
137 coap_pdu_t *pdu;
138 coap_list_t *opt;
139
140 if ( ! ( pdu = coap_new_pdu() ) )
141 return NULL;
142
143 pdu->hdr->type = msgtype;
144 pdu->hdr->id = coap_new_message_id(ctx);
145 pdu->hdr->code = m;
146
147 pdu->hdr->token_length = the_token.length;
148 if ( !coap_add_token(pdu, the_token.length, the_token.s)) {
149 debug("cannot add token to request\n");
150 }
151
152 coap_show_pdu(pdu);
153
154 if (options) {
155 /* sort options for delta encoding */
156 LL_SORT((*options), order_opts);
157
158 LL_FOREACH((*options), opt) {
159 coap_option *o = (coap_option *)(opt->data);
160 coap_add_option(pdu,
161 COAP_OPTION_KEY(*o),
162 COAP_OPTION_LENGTH(*o),
163 COAP_OPTION_DATA(*o));
164 }
165 }
166
167 if (length) {
168 if ((flags & FLAGS_BLOCK) == 0)
169 coap_add_data(pdu, length, data);
170 else
171 coap_add_block(pdu, length, data, block.num, block.szx);
172 }
173
174 return pdu;
175 }
176
177 static coap_tid_t
clear_obs(coap_context_t * ctx,const coap_endpoint_t * local_interface,const coap_address_t * remote)178 clear_obs(coap_context_t *ctx,
179 const coap_endpoint_t *local_interface,
180 const coap_address_t *remote) {
181 coap_pdu_t *pdu;
182 coap_list_t *option;
183 coap_tid_t tid = COAP_INVALID_TID;
184 unsigned char buf[2];
185
186 /* create bare PDU w/o any option */
187 pdu = coap_pdu_init(msgtype,
188 COAP_REQUEST_GET,
189 coap_new_message_id(ctx),
190 COAP_MAX_PDU_SIZE);
191
192 if (!pdu) {
193 return tid;
194 }
195
196 if (!coap_add_token(pdu, the_token.length, the_token.s)) {
197 coap_log(LOG_CRIT, "cannot add token");
198 goto error;
199 }
200
201 for (option = optlist; option; option = option->next ) {
202 coap_option *o = (coap_option *)(option->data);
203 if (COAP_OPTION_KEY(*o) == COAP_OPTION_URI_HOST) {
204 if (!coap_add_option(pdu,
205 COAP_OPTION_KEY(*o),
206 COAP_OPTION_LENGTH(*o),
207 COAP_OPTION_DATA(*o))) {
208 goto error;
209 }
210 break;
211 }
212 }
213
214 if (!coap_add_option(pdu,
215 COAP_OPTION_OBSERVE,
216 coap_encode_var_bytes(buf, COAP_OBSERVE_CANCEL),
217 buf)) {
218 coap_log(LOG_CRIT, "cannot add option Observe: %u", COAP_OBSERVE_CANCEL);
219 goto error;
220 }
221
222 for (option = optlist; option; option = option->next ) {
223 coap_option *o = (coap_option *)(option->data);
224 switch (COAP_OPTION_KEY(*o)) {
225 case COAP_OPTION_URI_PORT :
226 case COAP_OPTION_URI_PATH :
227 case COAP_OPTION_URI_QUERY :
228 if (!coap_add_option (pdu,
229 COAP_OPTION_KEY(*o),
230 COAP_OPTION_LENGTH(*o),
231 COAP_OPTION_DATA(*o))) {
232 goto error;
233 }
234 break;
235 default:
236 ;
237 }
238 }
239
240 coap_show_pdu(pdu);
241
242 if (pdu->hdr->type == COAP_MESSAGE_CON)
243 tid = coap_send_confirmed(ctx, local_interface, remote, pdu);
244 else
245 tid = coap_send(ctx, local_interface, remote, pdu);
246
247 if (tid == COAP_INVALID_TID) {
248 debug("clear_obs: error sending new request");
249 coap_delete_pdu(pdu);
250 } else if (pdu->hdr->type != COAP_MESSAGE_CON)
251 coap_delete_pdu(pdu);
252
253 return tid;
254 error:
255
256 coap_delete_pdu(pdu);
257 return tid;
258 }
259
260 static int
resolve_address(const str * server,struct sockaddr * dst)261 resolve_address(const str *server, struct sockaddr *dst) {
262
263 struct addrinfo *res, *ainfo;
264 struct addrinfo hints;
265 static char addrstr[256];
266 int error, len=-1;
267
268 memset(addrstr, 0, sizeof(addrstr));
269 if (server->length)
270 memcpy(addrstr, server->s, server->length);
271 else
272 memcpy(addrstr, "localhost", 9);
273
274 memset ((char *)&hints, 0, sizeof(hints));
275 hints.ai_socktype = SOCK_DGRAM;
276 hints.ai_family = AF_UNSPEC;
277
278 error = getaddrinfo(addrstr, NULL, &hints, &res);
279
280 if (error != 0) {
281 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
282 return error;
283 }
284
285 for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
286 switch (ainfo->ai_family) {
287 case AF_INET6:
288 case AF_INET:
289 len = ainfo->ai_addrlen;
290 memcpy(dst, ainfo->ai_addr, len);
291 goto finish;
292 default:
293 ;
294 }
295 }
296
297 finish:
298 freeaddrinfo(res);
299 return len;
300 }
301
302 #define HANDLE_BLOCK1(Pdu) \
303 ((method == COAP_REQUEST_PUT || method == COAP_REQUEST_POST) && \
304 ((flags & FLAGS_BLOCK) == 0) && \
305 ((Pdu)->hdr->code == COAP_RESPONSE_CODE(201) || \
306 (Pdu)->hdr->code == COAP_RESPONSE_CODE(204)))
307
308 static inline int
check_token(coap_pdu_t * received)309 check_token(coap_pdu_t *received) {
310 return received->hdr->token_length == the_token.length &&
311 memcmp(received->hdr->token, the_token.s, the_token.length) == 0;
312 }
313
314 static void
message_handler(struct coap_context_t * ctx,const coap_endpoint_t * local_interface,const coap_address_t * remote,coap_pdu_t * sent,coap_pdu_t * received,const coap_tid_t id UNUSED_PARAM)315 message_handler(struct coap_context_t *ctx,
316 const coap_endpoint_t *local_interface,
317 const coap_address_t *remote,
318 coap_pdu_t *sent,
319 coap_pdu_t *received,
320 const coap_tid_t id UNUSED_PARAM) {
321
322 coap_pdu_t *pdu = NULL;
323 coap_opt_t *block_opt;
324 coap_opt_iterator_t opt_iter;
325 unsigned char buf[4];
326 coap_list_t *option;
327 size_t len;
328 unsigned char *databuf;
329 coap_tid_t tid;
330
331 #ifndef NDEBUG
332 if (LOG_DEBUG <= coap_get_log_level()) {
333 debug("** process incoming %d.%02d response:\n",
334 (received->hdr->code >> 5), received->hdr->code & 0x1F);
335 coap_show_pdu(received);
336 }
337 #endif
338
339 /* check if this is a response to our original request */
340 if (!check_token(received)) {
341 /* drop if this was just some message, or send RST in case of notification */
342 if (!sent && (received->hdr->type == COAP_MESSAGE_CON ||
343 received->hdr->type == COAP_MESSAGE_NON))
344 coap_send_rst(ctx, local_interface, remote, received);
345 return;
346 }
347
348 if (received->hdr->type == COAP_MESSAGE_RST) {
349 info("got RST\n");
350 return;
351 }
352
353 /* output the received data, if any */
354 if (COAP_RESPONSE_CLASS(received->hdr->code) == 2) {
355
356 /* set obs timer if we have successfully subscribed a resource */
357 if (sent && coap_check_option(received, COAP_OPTION_SUBSCRIPTION, &opt_iter)) {
358 debug("observation relationship established, set timeout to %d\n", obs_seconds);
359 set_timeout(&obs_wait, obs_seconds);
360 }
361
362 /* Got some data, check if block option is set. Behavior is undefined if
363 * both, Block1 and Block2 are present. */
364 block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter);
365 if (block_opt) { /* handle Block2 */
366 unsigned short blktype = opt_iter.type;
367
368 /* TODO: check if we are looking at the correct block number */
369 if (coap_get_data(received, &len, &databuf))
370 append_to_output(databuf, len);
371
372 if(COAP_OPT_BLOCK_MORE(block_opt)) {
373 /* more bit is set */
374 debug("found the M bit, block size is %u, block nr. %u\n",
375 COAP_OPT_BLOCK_SZX(block_opt),
376 coap_opt_block_num(block_opt));
377
378 /* create pdu with request for next block */
379 pdu = coap_new_request(ctx, method, NULL, NULL, 0); /* first, create bare PDU w/o any option */
380 if ( pdu ) {
381 /* add URI components from optlist */
382 for (option = optlist; option; option = option->next ) {
383 coap_option *o = (coap_option *)(option->data);
384 switch (COAP_OPTION_KEY(*o)) {
385 case COAP_OPTION_URI_HOST :
386 case COAP_OPTION_URI_PORT :
387 case COAP_OPTION_URI_PATH :
388 case COAP_OPTION_URI_QUERY :
389 coap_add_option (pdu,
390 COAP_OPTION_KEY(*o),
391 COAP_OPTION_LENGTH(*o),
392 COAP_OPTION_DATA(*o));
393 break;
394 default:
395 ; /* skip other options */
396 }
397 }
398
399 /* finally add updated block option from response, clear M bit */
400 /* blocknr = (blocknr & 0xfffffff7) + 0x10; */
401 debug("query block %d\n", (coap_opt_block_num(block_opt) + 1));
402 coap_add_option(pdu,
403 blktype,
404 coap_encode_var_bytes(buf,
405 ((coap_opt_block_num(block_opt) + 1) << 4) |
406 COAP_OPT_BLOCK_SZX(block_opt)), buf);
407
408 if (pdu->hdr->type == COAP_MESSAGE_CON)
409 tid = coap_send_confirmed(ctx, local_interface, remote, pdu);
410 else
411 tid = coap_send(ctx, local_interface, remote, pdu);
412
413 if (tid == COAP_INVALID_TID) {
414 debug("message_handler: error sending new request");
415 coap_delete_pdu(pdu);
416 } else {
417 set_timeout(&max_wait, wait_seconds);
418 if (pdu->hdr->type != COAP_MESSAGE_CON)
419 coap_delete_pdu(pdu);
420 }
421
422 return;
423 }
424 }
425 } else { /* no Block2 option */
426 block_opt = coap_check_option(received, COAP_OPTION_BLOCK1, &opt_iter);
427
428 if (block_opt) { /* handle Block1 */
429 block.szx = COAP_OPT_BLOCK_SZX(block_opt);
430 block.num = coap_opt_block_num(block_opt);
431
432 debug("found Block1, block size is %u, block nr. %u\n",
433 block.szx, block.num);
434
435 if (payload.length <= (block.num+1) * (1 << (block.szx + 4))) {
436 debug("upload ready\n");
437 ready = 1;
438 return;
439 }
440
441 /* create pdu with request for next block */
442 pdu = coap_new_request(ctx, method, NULL, NULL, 0); /* first, create bare PDU w/o any option */
443 if (pdu) {
444
445 /* add URI components from optlist */
446 for (option = optlist; option; option = option->next ) {
447 coap_option *o = (coap_option *)(option->data);
448 switch (COAP_OPTION_KEY(*o)) {
449 case COAP_OPTION_URI_HOST :
450 case COAP_OPTION_URI_PORT :
451 case COAP_OPTION_URI_PATH :
452 case COAP_OPTION_CONTENT_FORMAT :
453 case COAP_OPTION_URI_QUERY :
454 coap_add_option (pdu,
455 COAP_OPTION_KEY(*o),
456 COAP_OPTION_LENGTH(*o),
457 COAP_OPTION_DATA(*o));
458 break;
459 default:
460 ; /* skip other options */
461 }
462 }
463
464 /* finally add updated block option from response, clear M bit */
465 /* blocknr = (blocknr & 0xfffffff7) + 0x10; */
466 block.num++;
467 block.m = ((block.num+1) * (1 << (block.szx + 4)) < payload.length);
468
469 debug("send block %d\n", block.num);
470 coap_add_option(pdu,
471 COAP_OPTION_BLOCK1,
472 coap_encode_var_bytes(buf,
473 (block.num << 4) | (block.m << 3) | block.szx), buf);
474
475 coap_add_block(pdu,
476 payload.length,
477 payload.s,
478 block.num,
479 block.szx);
480 coap_show_pdu(pdu);
481 if (pdu->hdr->type == COAP_MESSAGE_CON)
482 tid = coap_send_confirmed(ctx, local_interface, remote, pdu);
483 else
484 tid = coap_send(ctx, local_interface, remote, pdu);
485
486 if (tid == COAP_INVALID_TID) {
487 debug("message_handler: error sending new request");
488 coap_delete_pdu(pdu);
489 } else {
490 set_timeout(&max_wait, wait_seconds);
491 if (pdu->hdr->type != COAP_MESSAGE_CON)
492 coap_delete_pdu(pdu);
493 }
494
495 return;
496 }
497 } else {
498 /* There is no block option set, just read the data and we are done. */
499 if (coap_get_data(received, &len, &databuf))
500 append_to_output(databuf, len);
501 }
502 }
503 } else { /* no 2.05 */
504
505 /* check if an error was signaled and output payload if so */
506 if (COAP_RESPONSE_CLASS(received->hdr->code) >= 4) {
507 fprintf(stderr, "%d.%02d",
508 (received->hdr->code >> 5), received->hdr->code & 0x1F);
509 if (coap_get_data(received, &len, &databuf)) {
510 fprintf(stderr, " ");
511 while(len--)
512 fprintf(stderr, "%c", *databuf++);
513 }
514 fprintf(stderr, "\n");
515 }
516
517 }
518
519 /* finally send new request, if needed */
520 if (pdu && coap_send(ctx, local_interface, remote, pdu) == COAP_INVALID_TID) {
521 debug("message_handler: error sending response");
522 }
523 coap_delete_pdu(pdu);
524
525 /* our job is done, we can exit at any time */
526 ready = coap_check_option(received, COAP_OPTION_SUBSCRIPTION, &opt_iter) == NULL;
527 }
528
529 static void
usage(const char * program,const char * version)530 usage( const char *program, const char *version) {
531 const char *p;
532
533 p = strrchr( program, '/' );
534 if ( p )
535 program = ++p;
536
537 fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
538 "(c) 2010-2015 Olaf Bergmann <bergmann@tzi.org>\n\n"
539 "usage: %s [-A type...] [-t type] [-b [num,]size] [-B seconds] [-e text]\n"
540 "\t\t[-m method] [-N] [-o file] [-P addr[:port]] [-p port]\n"
541 "\t\t[-s duration] [-O num,text] [-T string] [-v num] [-a addr] URI\n\n"
542 "\tURI can be an absolute or relative coap URI,\n"
543 "\t-a addr\tthe local interface address to use\n"
544 "\t-A type...\taccepted media types as comma-separated list of\n"
545 "\t\t\tsymbolic or numeric values\n"
546 "\t-t type\t\tcontent format for given resource for PUT/POST\n"
547 "\t-b [num,]size\tblock size to be used in GET/PUT/POST requests\n"
548 "\t \t\t(value must be a multiple of 16 not larger than 1024)\n"
549 "\t \t\tIf num is present, the request chain will start at\n"
550 "\t \t\tblock num\n"
551 "\t-B seconds\tbreak operation after waiting given seconds\n"
552 "\t\t\t(default is %d)\n"
553 "\t-e text\t\tinclude text as payload (use percent-encoding for\n"
554 "\t\t\tnon-ASCII characters)\n"
555 "\t-f file\t\tfile to send with PUT/POST (use '-' for STDIN)\n"
556 "\t-m method\trequest method (get|put|post|delete), default is 'get'\n"
557 "\t-N\t\tsend NON-confirmable message\n"
558 "\t-o file\t\toutput received data to this file (use '-' for STDOUT)\n"
559 "\t-p port\t\tlisten on specified port\n"
560 "\t-s duration\tsubscribe for given duration [s]\n"
561 "\t-v num\t\tverbosity level (default: 3)\n"
562 "\t-O num,text\tadd option num with contents text to request\n"
563 "\t-P addr[:port]\tuse proxy (automatically adds Proxy-Uri option to\n"
564 "\t\t\trequest)\n"
565 "\t-T token\tinclude specified token\n"
566 "\n"
567 "examples:\n"
568 "\tcoap-client -m get coap://[::1]/\n"
569 "\tcoap-client -m get coap://[::1]/.well-known/core\n"
570 "\tcoap-client -m get -T cafe coap://[::1]/time\n"
571 "\techo 1000 | coap-client -m put -T cafe coap://[::1]/time -f -\n"
572 ,program, version, program, wait_seconds);
573 }
574
575 static coap_list_t *
new_option_node(unsigned short key,unsigned int length,unsigned char * data)576 new_option_node(unsigned short key, unsigned int length, unsigned char *data) {
577 coap_list_t *node;
578
579 node = coap_malloc(sizeof(coap_list_t) + sizeof(coap_option) + length);
580
581 if (node) {
582 coap_option *option;
583 option = (coap_option *)(node->data);
584 COAP_OPTION_KEY(*option) = key;
585 COAP_OPTION_LENGTH(*option) = length;
586 memcpy(COAP_OPTION_DATA(*option), data, length);
587 } else {
588 coap_log(LOG_DEBUG, "new_option_node: malloc\n");
589 }
590
591 return node;
592 }
593
594 typedef struct {
595 unsigned char code;
596 char *media_type;
597 } content_type_t;
598
599 static void
cmdline_content_type(char * arg,unsigned short key)600 cmdline_content_type(char *arg, unsigned short key) {
601 static content_type_t content_types[] = {
602 { 0, "plain" },
603 { 0, "text/plain" },
604 { 40, "link" },
605 { 40, "link-format" },
606 { 40, "application/link-format" },
607 { 41, "xml" },
608 { 41, "application/xml" },
609 { 42, "binary" },
610 { 42, "octet-stream" },
611 { 42, "application/octet-stream" },
612 { 47, "exi" },
613 { 47, "application/exi" },
614 { 50, "json" },
615 { 50, "application/json" },
616 { 60, "cbor" },
617 { 60, "application/cbor" },
618 { 255, NULL }
619 };
620 coap_list_t *node;
621 unsigned char i, value[10];
622 int valcnt = 0;
623 unsigned char buf[2];
624 char *p, *q = arg;
625
626 while (q && *q) {
627 p = strchr(q, ',');
628
629 if (isdigit(*q)) {
630 if (p)
631 *p = '\0';
632 value[valcnt++] = atoi(q);
633 } else {
634 for (i=0;
635 content_types[i].media_type &&
636 strncmp(q, content_types[i].media_type, p ? (size_t)(p-q) : strlen(q)) != 0 ;
637 ++i)
638 ;
639
640 if (content_types[i].media_type) {
641 value[valcnt] = content_types[i].code;
642 valcnt++;
643 } else {
644 warn("W: unknown content-format '%s'\n",arg);
645 }
646 }
647
648 if (!p || key == COAP_OPTION_CONTENT_TYPE)
649 break;
650
651 q = p+1;
652 }
653
654 for (i = 0; i < valcnt; ++i) {
655 node = new_option_node(key, coap_encode_var_bytes(buf, value[i]), buf);
656 if (node) {
657 LL_PREPEND(optlist, node);
658 }
659 }
660 }
661
662 static void
cmdline_uri(char * arg)663 cmdline_uri(char *arg) {
664 unsigned char portbuf[2];
665 #define BUFSIZE 40
666 unsigned char _buf[BUFSIZE];
667 unsigned char *buf = _buf;
668 size_t buflen;
669 int res;
670
671 if (proxy.length) { /* create Proxy-Uri from argument */
672 size_t len = strlen(arg);
673 while (len > 270) {
674 coap_insert(&optlist,
675 new_option_node(COAP_OPTION_PROXY_URI,
676 270,
677 (unsigned char *)arg));
678
679 len -= 270;
680 arg += 270;
681 }
682
683 coap_insert(&optlist,
684 new_option_node(COAP_OPTION_PROXY_URI,
685 len,
686 (unsigned char *)arg));
687
688 } else { /* split arg into Uri-* options */
689 coap_split_uri((unsigned char *)arg, strlen(arg), &uri );
690
691 if (uri.port != COAP_DEFAULT_PORT) {
692 coap_insert(&optlist,
693 new_option_node(COAP_OPTION_URI_PORT,
694 coap_encode_var_bytes(portbuf, uri.port),
695 portbuf));
696 }
697
698 if (uri.path.length) {
699 buflen = BUFSIZE;
700 res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
701
702 while (res--) {
703 coap_insert(&optlist,
704 new_option_node(COAP_OPTION_URI_PATH,
705 COAP_OPT_LENGTH(buf),
706 COAP_OPT_VALUE(buf)));
707
708 buf += COAP_OPT_SIZE(buf);
709 }
710 }
711
712 if (uri.query.length) {
713 buflen = BUFSIZE;
714 buf = _buf;
715 res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
716
717 while (res--) {
718 coap_insert(&optlist,
719 new_option_node(COAP_OPTION_URI_QUERY,
720 COAP_OPT_LENGTH(buf),
721 COAP_OPT_VALUE(buf)));
722
723 buf += COAP_OPT_SIZE(buf);
724 }
725 }
726 }
727 }
728
729 static int
cmdline_blocksize(char * arg)730 cmdline_blocksize(char *arg) {
731 unsigned short size;
732
733 again:
734 size = 0;
735 while(*arg && *arg != ',')
736 size = size * 10 + (*arg++ - '0');
737
738 if (*arg == ',') {
739 arg++;
740 block.num = size;
741 goto again;
742 }
743
744 if (size)
745 block.szx = (coap_fls(size >> 4) - 1) & 0x07;
746
747 flags |= FLAGS_BLOCK;
748 return 1;
749 }
750
751 /* Called after processing the options from the commandline to set
752 * Block1 or Block2 depending on method. */
753 static void
set_blocksize(void)754 set_blocksize(void) {
755 static unsigned char buf[4]; /* hack: temporarily take encoded bytes */
756 unsigned short opt;
757 unsigned int opt_length;
758
759 if (method != COAP_REQUEST_DELETE) {
760 opt = method == COAP_REQUEST_GET ? COAP_OPTION_BLOCK2 : COAP_OPTION_BLOCK1;
761
762 block.m = (opt == COAP_OPTION_BLOCK1) &&
763 ((1u << (block.szx + 4)) < payload.length);
764
765 opt_length = coap_encode_var_bytes(buf,
766 (block.num << 4 | block.m << 3 | block.szx));
767
768 coap_insert(&optlist, new_option_node(opt, opt_length, buf));
769 }
770 }
771
772 static void
cmdline_subscribe(char * arg UNUSED_PARAM)773 cmdline_subscribe(char *arg UNUSED_PARAM) {
774 obs_seconds = atoi(optarg);
775 coap_insert(&optlist, new_option_node(COAP_OPTION_SUBSCRIPTION, 0, NULL));
776 }
777
778 static int
cmdline_proxy(char * arg)779 cmdline_proxy(char *arg) {
780 char *proxy_port_str = strrchr((const char *)arg, ':'); /* explicit port ? */
781 if (proxy_port_str) {
782 char *ipv6_delimiter = strrchr((const char *)arg, ']');
783 if (!ipv6_delimiter) {
784 if (proxy_port_str == strchr((const char *)arg, ':')) {
785 /* host:port format - host not in ipv6 hexadecimal string format */
786 *proxy_port_str++ = '\0'; /* split */
787 proxy_port = atoi(proxy_port_str);
788 }
789 } else {
790 arg = strchr((const char *)arg, '[');
791 if (!arg) return 0;
792 arg++;
793 *ipv6_delimiter = '\0'; /* split */
794 if (ipv6_delimiter + 1 == proxy_port_str++) {
795 /* [ipv6 address]:port */
796 proxy_port = atoi(proxy_port_str);
797 }
798 }
799 }
800
801 proxy.length = strlen(arg);
802 if ( (proxy.s = coap_malloc(proxy.length + 1)) == NULL) {
803 proxy.length = 0;
804 return 0;
805 }
806
807 memcpy(proxy.s, arg, proxy.length+1);
808 return 1;
809 }
810
811 static inline void
cmdline_token(char * arg)812 cmdline_token(char *arg) {
813 strncpy((char *)the_token.s, arg, min(sizeof(_token_data), strlen(arg)));
814 the_token.length = strlen(arg);
815 }
816
817 static void
cmdline_option(char * arg)818 cmdline_option(char *arg) {
819 unsigned int num = 0;
820
821 while (*arg && *arg != ',') {
822 num = num * 10 + (*arg - '0');
823 ++arg;
824 }
825 if (*arg == ',')
826 ++arg;
827
828 coap_insert(&optlist,
829 new_option_node(num, strlen(arg), (unsigned char *)arg));
830 }
831
832 /**
833 * Calculates decimal value from hexadecimal ASCII character given in
834 * @p c. The caller must ensure that @p c actually represents a valid
835 * heaxdecimal character, e.g. with isxdigit(3).
836 *
837 * @hideinitializer
838 */
839 #define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
840
841 /**
842 * Decodes percent-encoded characters while copying the string @p seg
843 * of size @p length to @p buf. The caller of this function must
844 * ensure that the percent-encodings are correct (i.e. the character
845 * '%' is always followed by two hex digits. and that @p buf provides
846 * sufficient space to hold the result. This function is supposed to
847 * be called by make_decoded_option() only.
848 *
849 * @param seg The segment to decode and copy.
850 * @param length Length of @p seg.
851 * @param buf The result buffer.
852 */
853 static void
decode_segment(const unsigned char * seg,size_t length,unsigned char * buf)854 decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) {
855
856 while (length--) {
857
858 if (*seg == '%') {
859 *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
860
861 seg += 2; length -= 2;
862 } else {
863 *buf = *seg;
864 }
865
866 ++buf; ++seg;
867 }
868 }
869
870 /**
871 * Runs through the given path (or query) segment and checks if
872 * percent-encodings are correct. This function returns @c -1 on error
873 * or the length of @p s when decoded.
874 */
875 static int
check_segment(const unsigned char * s,size_t length)876 check_segment(const unsigned char *s, size_t length) {
877
878 size_t n = 0;
879
880 while (length) {
881 if (*s == '%') {
882 if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
883 return -1;
884
885 s += 2;
886 length -= 2;
887 }
888
889 ++s; ++n; --length;
890 }
891
892 return n;
893 }
894
895 static int
cmdline_input(char * text,str * buf)896 cmdline_input(char *text, str *buf) {
897 int len;
898 len = check_segment((unsigned char *)text, strlen(text));
899
900 if (len < 0)
901 return 0;
902
903 buf->s = (unsigned char *)coap_malloc(len);
904 if (!buf->s)
905 return 0;
906
907 buf->length = len;
908 decode_segment((unsigned char *)text, strlen(text), buf->s);
909 return 1;
910 }
911
912 static int
cmdline_input_from_file(char * filename,str * buf)913 cmdline_input_from_file(char *filename, str *buf) {
914 FILE *inputfile = NULL;
915 ssize_t len;
916 int result = 1;
917 struct stat statbuf;
918
919 if (!filename || !buf)
920 return 0;
921
922 if (filename[0] == '-' && !filename[1]) { /* read from stdin */
923 buf->length = 20000;
924 buf->s = (unsigned char *)coap_malloc(buf->length);
925 if (!buf->s)
926 return 0;
927
928 inputfile = stdin;
929 } else {
930 /* read from specified input file */
931 if (stat(filename, &statbuf) < 0) {
932 perror("cmdline_input_from_file: stat");
933 return 0;
934 }
935
936 buf->length = statbuf.st_size;
937 buf->s = (unsigned char *)coap_malloc(buf->length);
938 if (!buf->s)
939 return 0;
940
941 inputfile = fopen(filename, "r");
942 if ( !inputfile ) {
943 perror("cmdline_input_from_file: fopen");
944 coap_free(buf->s);
945 return 0;
946 }
947 }
948
949 len = fread(buf->s, 1, buf->length, inputfile);
950
951 if (len < 0 || ((size_t)len < buf->length)) {
952 if (ferror(inputfile) != 0) {
953 perror("cmdline_input_from_file: fread");
954 coap_free(buf->s);
955 buf->length = 0;
956 buf->s = NULL;
957 result = 0;
958 } else {
959 buf->length = len;
960 }
961 }
962
963 if (inputfile != stdin)
964 fclose(inputfile);
965
966 return result;
967 }
968
969 static method_t
cmdline_method(char * arg)970 cmdline_method(char *arg) {
971 static char *methods[] =
972 { 0, "get", "post", "put", "delete", 0};
973 unsigned char i;
974
975 for (i=1; methods[i] && strcasecmp(arg,methods[i]) != 0 ; ++i)
976 ;
977
978 return i; /* note that we do not prevent illegal methods */
979 }
980
981 static coap_context_t *
get_context(const char * node,const char * port)982 get_context(const char *node, const char *port) {
983 coap_context_t *ctx = NULL;
984 int s;
985 struct addrinfo hints;
986 struct addrinfo *result, *rp;
987
988 memset(&hints, 0, sizeof(struct addrinfo));
989 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
990 hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
991 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL;
992
993 s = getaddrinfo(node, port, &hints, &result);
994 if ( s != 0 ) {
995 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
996 return NULL;
997 }
998
999 /* iterate through results until success */
1000 for (rp = result; rp != NULL; rp = rp->ai_next) {
1001 coap_address_t addr;
1002
1003 if (rp->ai_addrlen <= sizeof(addr.addr)) {
1004 coap_address_init(&addr);
1005 addr.size = rp->ai_addrlen;
1006 memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
1007
1008 ctx = coap_new_context(&addr);
1009 if (ctx) {
1010 /* TODO: output address:port for successful binding */
1011 goto finish;
1012 }
1013 }
1014 }
1015
1016 fprintf(stderr, "no context available for interface '%s'\n", node);
1017
1018 finish:
1019 freeaddrinfo(result);
1020 return ctx;
1021 }
1022
1023 int
main(int argc,char ** argv)1024 main(int argc, char **argv) {
1025 coap_context_t *ctx = NULL;
1026 coap_address_t dst;
1027 static char addr[INET6_ADDRSTRLEN];
1028 void *addrptr = NULL;
1029 fd_set readfds;
1030 struct timeval tv;
1031 int result;
1032 coap_tick_t now;
1033 coap_queue_t *nextpdu;
1034 coap_pdu_t *pdu;
1035 static str server;
1036 unsigned short port = COAP_DEFAULT_PORT;
1037 char port_str[NI_MAXSERV] = "0";
1038 char node_str[NI_MAXHOST] = "";
1039 int opt, res;
1040 coap_log_t log_level = LOG_WARNING;
1041 coap_tid_t tid = COAP_INVALID_TID;
1042
1043 while ((opt = getopt(argc, argv, "Na:b:e:f:g:m:p:s:t:o:v:A:B:O:P:T:")) != -1) {
1044 switch (opt) {
1045 case 'a' :
1046 strncpy(node_str, optarg, NI_MAXHOST-1);
1047 node_str[NI_MAXHOST - 1] = '\0';
1048 break;
1049 case 'b' :
1050 cmdline_blocksize(optarg);
1051 break;
1052 case 'B' :
1053 wait_seconds = atoi(optarg);
1054 break;
1055 case 'e' :
1056 if (!cmdline_input(optarg,&payload))
1057 payload.length = 0;
1058 break;
1059 case 'f' :
1060 if (!cmdline_input_from_file(optarg,&payload))
1061 payload.length = 0;
1062 break;
1063 case 'p' :
1064 strncpy(port_str, optarg, NI_MAXSERV-1);
1065 port_str[NI_MAXSERV - 1] = '\0';
1066 break;
1067 case 'm' :
1068 method = cmdline_method(optarg);
1069 break;
1070 case 'N' :
1071 msgtype = COAP_MESSAGE_NON;
1072 break;
1073 case 's' :
1074 cmdline_subscribe(optarg);
1075 break;
1076 case 'o' :
1077 output_file.length = strlen(optarg);
1078 output_file.s = (unsigned char *)coap_malloc(output_file.length + 1);
1079
1080 if (!output_file.s) {
1081 fprintf(stderr, "cannot set output file: insufficient memory\n");
1082 exit(-1);
1083 } else {
1084 /* copy filename including trailing zero */
1085 memcpy(output_file.s, optarg, output_file.length + 1);
1086 }
1087 break;
1088 case 'A' :
1089 cmdline_content_type(optarg,COAP_OPTION_ACCEPT);
1090 break;
1091 case 't' :
1092 cmdline_content_type(optarg,COAP_OPTION_CONTENT_TYPE);
1093 break;
1094 case 'O' :
1095 cmdline_option(optarg);
1096 break;
1097 case 'P' :
1098 if (!cmdline_proxy(optarg)) {
1099 fprintf(stderr, "error specifying proxy address\n");
1100 exit(-1);
1101 }
1102 break;
1103 case 'T' :
1104 cmdline_token(optarg);
1105 break;
1106 case 'v' :
1107 log_level = strtol(optarg, NULL, 10);
1108 break;
1109 default:
1110 usage( argv[0], PACKAGE_VERSION );
1111 exit( 1 );
1112 }
1113 }
1114
1115 coap_set_log_level(log_level);
1116
1117 if ( optind < argc )
1118 cmdline_uri( argv[optind] );
1119 else {
1120 usage( argv[0], PACKAGE_VERSION );
1121 exit( 1 );
1122 }
1123
1124 if (proxy.length) {
1125 server = proxy;
1126 port = proxy_port;
1127 } else {
1128 server = uri.host;
1129 port = uri.port;
1130 }
1131
1132 /* resolve destination address where server should be sent */
1133 res = resolve_address(&server, &dst.addr.sa);
1134
1135 if (res < 0) {
1136 fprintf(stderr, "failed to resolve address\n");
1137 exit(-1);
1138 }
1139
1140 dst.size = res;
1141 dst.addr.sin.sin_port = htons(port);
1142
1143 /* add Uri-Host if server address differs from uri.host */
1144
1145 switch (dst.addr.sa.sa_family) {
1146 case AF_INET:
1147 addrptr = &dst.addr.sin.sin_addr;
1148
1149 /* create context for IPv4 */
1150 ctx = get_context(node_str[0] == 0 ? "0.0.0.0" : node_str, port_str);
1151 break;
1152 case AF_INET6:
1153 addrptr = &dst.addr.sin6.sin6_addr;
1154
1155 /* create context for IPv6 */
1156 ctx = get_context(node_str[0] == 0 ? "::" : node_str, port_str);
1157 break;
1158 default:
1159 ;
1160 }
1161
1162 if (!ctx) {
1163 coap_log(LOG_EMERG, "cannot create context\n");
1164 return -1;
1165 }
1166
1167 coap_register_option(ctx, COAP_OPTION_BLOCK2);
1168 coap_register_response_handler(ctx, message_handler);
1169
1170 /* construct CoAP message */
1171
1172 if (!proxy.length && addrptr
1173 && (inet_ntop(dst.addr.sa.sa_family, addrptr, addr, sizeof(addr)) != 0)
1174 && (strlen(addr) != uri.host.length
1175 || memcmp(addr, uri.host.s, uri.host.length) != 0)) {
1176 /* add Uri-Host */
1177
1178 coap_insert(&optlist,
1179 new_option_node(COAP_OPTION_URI_HOST,
1180 uri.host.length,
1181 uri.host.s));
1182 }
1183
1184 /* set block option if requested at commandline */
1185 if (flags & FLAGS_BLOCK)
1186 set_blocksize();
1187
1188 if (! (pdu = coap_new_request(ctx, method, &optlist, payload.s, payload.length)))
1189 return -1;
1190
1191 #ifndef NDEBUG
1192 if (LOG_DEBUG <= coap_get_log_level()) {
1193 debug("sending CoAP request:\n");
1194 coap_show_pdu(pdu);
1195 }
1196 #endif
1197
1198 if (pdu->hdr->type == COAP_MESSAGE_CON)
1199 tid = coap_send_confirmed(ctx, ctx->endpoint, &dst, pdu);
1200 else
1201 tid = coap_send(ctx, ctx->endpoint, &dst, pdu);
1202
1203 if (pdu->hdr->type != COAP_MESSAGE_CON || tid == COAP_INVALID_TID)
1204 coap_delete_pdu(pdu);
1205
1206 set_timeout(&max_wait, wait_seconds);
1207 debug("timeout is set to %d seconds\n", wait_seconds);
1208
1209 while ( !(ready && coap_can_exit(ctx)) ) {
1210 FD_ZERO(&readfds);
1211 FD_SET( ctx->sockfd, &readfds );
1212
1213 nextpdu = coap_peek_next( ctx );
1214
1215 coap_ticks(&now);
1216 while (nextpdu && nextpdu->t <= now - ctx->sendqueue_basetime) {
1217 coap_retransmit( ctx, coap_pop_next( ctx ));
1218 nextpdu = coap_peek_next( ctx );
1219 }
1220
1221 if (nextpdu && nextpdu->t < min(obs_wait ? obs_wait : max_wait, max_wait) - now) {
1222 /* set timeout if there is a pdu to send */
1223 tv.tv_usec = ((nextpdu->t) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1224 tv.tv_sec = (nextpdu->t) / COAP_TICKS_PER_SECOND;
1225 } else {
1226 /* check if obs_wait fires before max_wait */
1227 if (obs_wait && obs_wait < max_wait) {
1228 tv.tv_usec = ((obs_wait - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1229 tv.tv_sec = (obs_wait - now) / COAP_TICKS_PER_SECOND;
1230 } else {
1231 tv.tv_usec = ((max_wait - now) % COAP_TICKS_PER_SECOND) * 1000000 / COAP_TICKS_PER_SECOND;
1232 tv.tv_sec = (max_wait - now) / COAP_TICKS_PER_SECOND;
1233 }
1234 }
1235
1236 result = select(ctx->sockfd + 1, &readfds, 0, 0, &tv);
1237
1238 if ( result < 0 ) { /* error */
1239 perror("select");
1240 } else if ( result > 0 ) { /* read from socket */
1241 if ( FD_ISSET( ctx->sockfd, &readfds ) ) {
1242 coap_read( ctx ); /* read received data */
1243 /* coap_dispatch( ctx ); /\* and dispatch PDUs from receivequeue *\/ */
1244 }
1245 } else { /* timeout */
1246 coap_ticks(&now);
1247 if (max_wait <= now) {
1248 info("timeout\n");
1249 break;
1250 }
1251 if (obs_wait && obs_wait <= now) {
1252 debug("clear observation relationship\n");
1253 clear_obs(ctx, ctx->endpoint, &dst); /* FIXME: handle error case COAP_TID_INVALID */
1254
1255 /* make sure that the obs timer does not fire again */
1256 obs_wait = 0;
1257 obs_seconds = 0;
1258 }
1259 }
1260 }
1261
1262 close_output();
1263
1264 coap_delete_list(optlist);
1265 coap_free_context( ctx );
1266
1267 return 0;
1268 }
1269