#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WIN32
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define fileno _fileno
#include "getopt.c"
#if !defined(S_ISDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
static char* strndup(const char* s1, size_t n)
{
char* copy = (char*)malloc(n + 1);
if (copy) {
memcpy(copy, s1, n);
copy[n] = 0;
}
return copy;
};
#else
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <coap3/coap.h>
#define MAX_USER 128
#define MAX_KEY 64
int flags = 0;
static unsigned char _token_data[8];
coap_binary_t base_token = { 0, _token_data };
typedef struct {
coap_binary_t *token;
int observe;
} track_token;
track_token *tracked_tokens = NULL;
size_t tracked_tokens_count = 0;
#define FLAGS_BLOCK 0x01
static coap_optlist_t *optlist = NULL;
static coap_uri_t uri;
static coap_uri_t proxy = { {0, NULL}, 0, {0, NULL}, {0, NULL}, 0 };
static int proxy_scheme_option = 0;
static int uri_host_option = 0;
static unsigned int ping_seconds = 0;
#define REPEAT_DELAY_MS 1000
static size_t repeat_count = 1;
static int ready = 0;
static int doing_getting_block = 0;
static int single_block_requested = 0;
static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP;
static coap_string_t output_file = { 0, NULL };
static FILE *file = NULL;
static coap_string_t payload = { 0, NULL };
static int reliable = 0;
static int add_nl = 0;
static int is_mcast = 0;
static uint32_t csm_max_message_size = 0;
unsigned char msgtype = COAP_MESSAGE_CON;
static char *cert_file = NULL;
static char *key_file = NULL;
static char *pkcs11_pin = NULL;
static char *ca_file = NULL;
static char *root_ca_file = NULL;
static int is_rpk_not_cert = 0;
static uint8_t *cert_mem = NULL;
static uint8_t *key_mem = NULL;
static uint8_t *ca_mem = NULL;
static size_t cert_mem_len = 0;
static size_t key_mem_len = 0;
static size_t ca_mem_len = 0;
static int verify_peer_cert = 1;
typedef struct ih_def_t {
char* hint_match;
coap_bin_const_t *new_identity;
coap_bin_const_t *new_key;
} ih_def_t;
typedef struct valid_ihs_t {
size_t count;
ih_def_t *ih_list;
} valid_ihs_t;
static valid_ihs_t valid_ihs = {0, NULL};
typedef unsigned char method_t;
method_t method = 1;
coap_block_t block = { .num = 0, .m = 0, .szx = 6 };
uint16_t last_block1_mid = 0;
#define DEFAULT_WAIT_TIME 90
unsigned int wait_seconds = DEFAULT_WAIT_TIME;
unsigned int wait_ms = 0;
int obs_started = 0;
unsigned int obs_seconds = 30;
unsigned int obs_ms = 0;
int obs_ms_reset = 0;
int doing_observe = 0;
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
static int quit = 0;
static void
handle_sigint(int signum COAP_UNUSED) {
quit = 1;
}
static int
append_to_output(const uint8_t *data, size_t len) {
size_t written;
if (!file) {
if (!output_file.s || (output_file.length && output_file.s[0] == '-'))
file = stdout;
else {
if (!(file = fopen((char *)output_file.s, "w"))) {
perror("fopen");
return -1;
}
}
}
do {
written = fwrite(data, 1, len, file);
len -= written;
data += written;
} while ( written && len );
fflush(file);
return 0;
}
static void
close_output(void) {
if (file) {
if (!output_file.s)
fwrite("\n", 1, 1, file);
fflush(file);
fclose(file);
}
}
static void
free_xmit_data(coap_session_t *session COAP_UNUSED, void *app_ptr) {
coap_free(app_ptr);
return;
}
static void
track_new_token(size_t tokenlen, uint8_t *token)
{
track_token *new_list = realloc(tracked_tokens,
(tracked_tokens_count + 1) * sizeof(tracked_tokens[0]));
if (!new_list) {
coap_log(LOG_INFO, "Unable to track new token\n");
return;
}
tracked_tokens = new_list;
tracked_tokens[tracked_tokens_count].token = coap_new_binary(tokenlen);
if (!tracked_tokens[tracked_tokens_count].token)
return;
memcpy(tracked_tokens[tracked_tokens_count].token->s, token, tokenlen);
tracked_tokens[tracked_tokens_count].observe = doing_observe;
tracked_tokens_count++;
}
static int
track_check_token(coap_bin_const_t *token)
{
size_t i;
for (i = 0; i < tracked_tokens_count; i++) {
if (coap_binary_equal(token, tracked_tokens[i].token)) {
return 1;
}
}
return 0;
}
static void
track_flush_token(coap_bin_const_t *token)
{
size_t i;
for (i = 0; i < tracked_tokens_count; i++) {
if (coap_binary_equal(token, tracked_tokens[i].token)) {
if (!tracked_tokens[i].observe || !obs_started) {
coap_delete_binary(tracked_tokens[i].token);
if (tracked_tokens_count-i > 1) {
memmove (&tracked_tokens[i],
&tracked_tokens[i+1],
(tracked_tokens_count-i-1) * sizeof (tracked_tokens[0]));
}
tracked_tokens_count--;
}
break;
}
}
}
static coap_pdu_t *
coap_new_request(coap_context_t *ctx,
coap_session_t *session,
method_t m,
coap_optlist_t **options,
unsigned char *data,
size_t length) {
coap_pdu_t *pdu;
uint8_t token[8];
size_t tokenlen;
(void)ctx;
if (!(pdu = coap_new_pdu(msgtype, m, session))) {
free_xmit_data(session, data);
return NULL;
}
coap_session_new_token(session, &tokenlen, token);
track_new_token(tokenlen, token);
if (!coap_add_token(pdu, tokenlen, token)) {
coap_log(LOG_DEBUG, "cannot add token to request\n");
}
if (options)
coap_add_optlist_pdu(pdu, options);
if (length) {
coap_add_data_large_request(session, pdu, length, data,
free_xmit_data, data);
}
return pdu;
}
static int
resolve_address(const coap_str_const_t *server, struct sockaddr *dst) {
struct addrinfo *res, *ainfo;
struct addrinfo hints;
static char addrstr[256];
int error, len=-1;
memset(addrstr, 0, sizeof(addrstr));
if (server->length)
memcpy(addrstr, server->s, server->length);
else
memcpy(addrstr, "localhost", 9);
memset ((char *)&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_UNSPEC;
error = getaddrinfo(addrstr, NULL, &hints, &res);
if (error != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
return error;
}
for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
switch (ainfo->ai_family) {
case AF_INET6:
case AF_INET:
len = (int)ainfo->ai_addrlen;
memcpy(dst, ainfo->ai_addr, len);
goto finish;
default:
;
}
}
finish:
freeaddrinfo(res);
return len;
}
#define HANDLE_BLOCK1(Pdu) \
((method == COAP_REQUEST_PUT || method == COAP_REQUEST_POST) && \
((flags & FLAGS_BLOCK) == 0) && \
((Pdu)->hdr->code == COAP_RESPONSE_CODE_CREATED || \
(Pdu)->hdr->code == COAP_RESPONSE_CODE_CHANGED))
static int
event_handler(coap_session_t *session COAP_UNUSED,
const coap_event_t event) {
switch(event) {
case COAP_EVENT_DTLS_CLOSED:
case COAP_EVENT_TCP_CLOSED:
case COAP_EVENT_SESSION_CLOSED:
quit = 1;
break;
case COAP_EVENT_DTLS_CONNECTED:
case COAP_EVENT_DTLS_RENEGOTIATE:
case COAP_EVENT_DTLS_ERROR:
case COAP_EVENT_TCP_CONNECTED:
case COAP_EVENT_TCP_FAILED:
case COAP_EVENT_SESSION_CONNECTED:
case COAP_EVENT_SESSION_FAILED:
case COAP_EVENT_PARTIAL_BLOCK:
case COAP_EVENT_XMIT_BLOCK_FAIL:
case COAP_EVENT_SERVER_SESSION_NEW:
case COAP_EVENT_SERVER_SESSION_DEL:
default:
break;
}
return 0;
}
static void
nack_handler(coap_session_t *session COAP_UNUSED,
const coap_pdu_t *sent COAP_UNUSED,
const coap_nack_reason_t reason,
const coap_mid_t id COAP_UNUSED) {
switch(reason) {
case COAP_NACK_TOO_MANY_RETRIES:
case COAP_NACK_NOT_DELIVERABLE:
case COAP_NACK_RST:
case COAP_NACK_TLS_FAILED:
coap_log(LOG_ERR, "cannot send CoAP pdu\n");
quit = 1;
break;
case COAP_NACK_ICMP_ISSUE:
default:
;
}
return;
}
static coap_response_t
message_handler(coap_session_t *session COAP_UNUSED,
const coap_pdu_t *sent,
const coap_pdu_t *received,
const coap_mid_t id COAP_UNUSED) {
coap_opt_t *block_opt;
coap_opt_iterator_t opt_iter;
size_t len;
const uint8_t *databuf;
size_t offset;
size_t total;
coap_pdu_code_t rcv_code = coap_pdu_get_code(received);
coap_pdu_type_t rcv_type = coap_pdu_get_type(received);
coap_bin_const_t token = coap_pdu_get_token(received);
coap_log(LOG_DEBUG, "** process incoming %d.%02d response:\n",
COAP_RESPONSE_CLASS(rcv_code), rcv_code & 0x1F);
if (coap_get_log_level() < LOG_DEBUG)
coap_show_pdu(LOG_INFO, received);
if (!track_check_token(&token)) {
if (!sent && (rcv_type == COAP_MESSAGE_CON ||
rcv_type == COAP_MESSAGE_NON)) {
return COAP_RESPONSE_FAIL;
}
return COAP_RESPONSE_OK;
}
if (rcv_type == COAP_MESSAGE_RST) {
coap_log(LOG_INFO, "got RST\n");
return COAP_RESPONSE_OK;
}
if (COAP_RESPONSE_CLASS(rcv_code) == 2) {
if (doing_observe && !obs_started &&
coap_check_option(received, COAP_OPTION_OBSERVE, &opt_iter)) {
coap_log(LOG_DEBUG,
"observation relationship established, set timeout to %d\n",
obs_seconds);
obs_started = 1;
obs_ms = obs_seconds * 1000;
obs_ms_reset = 1;
}
if (coap_get_data_large(received, &len, &databuf, &offset, &total)) {
append_to_output(databuf, len);
if ((len + offset == total) && add_nl)
append_to_output((const uint8_t*)"\n", 1);
}
block_opt = coap_check_option(received, COAP_OPTION_BLOCK2, &opt_iter);
if (!single_block_requested && block_opt) {
if (coap_opt_block_num(block_opt) == 0) {
ready = doing_observe ? coap_check_option(received,
COAP_OPTION_OBSERVE, &opt_iter) == NULL : 1;
}
if(COAP_OPT_BLOCK_MORE(block_opt)) {
doing_getting_block = 1;
}
else {
doing_getting_block = 0;
track_flush_token(&token);
}
return COAP_RESPONSE_OK;
}
} else {
if (COAP_RESPONSE_CLASS(rcv_code) >= 4) {
fprintf(stderr, "%d.%02d", COAP_RESPONSE_CLASS(rcv_code),
rcv_code & 0x1F);
if (coap_get_data_large(received, &len, &databuf, &offset, &total)) {
fprintf(stderr, " ");
while(len--) {
fprintf(stderr, "%c", isprint(*databuf) ? *databuf : '.');
databuf++;
}
}
fprintf(stderr, "\n");
}
}
if (!is_mcast)
track_flush_token(&token);
ready = doing_observe ? coap_check_option(received,
COAP_OPTION_OBSERVE, &opt_iter) == NULL : 1;
return COAP_RESPONSE_OK;
}
static void
usage( const char *program, const char *version) {
const char *p;
char buffer[72];
const char *lib_build = coap_package_build();
p = strrchr( program, '/' );
if ( p )
program = ++p;
fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
"Copyright (C) 2010-2022 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
"Build: %s\n"
"%s\n"
, program, version, lib_build,
coap_string_tls_version(buffer, sizeof(buffer)));
fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer)));
fprintf(stderr, "\n"
"Usage: %s [-a addr] [-b [num,]size] [-e text] [-f file] [-l loss]\n"
"\t\t[-m method] [-o file] [-p port] [-r] [-s duration] [-t type]\n"
"\t\t[-v num] [-w] [-A type] [-B seconds] [G count] [-H hoplimit]\n"
"\t\t[-K interval] [-N] [-O num,text] [-P scheme://address[:port]]\n"
"\t\t[-T token] [-U] [-X size]\n"
"\t\t[[-h match_hint_file] [-k key] [-u user]]\n"
"\t\t[[-c certfile] [-j keyfile] [-n] [-C cafile]\n"
"\t\t[-J pkcs11_pin] [-M raw_pk] [-R trust_casfile]\n"
"\t\t[-S match_pki_sni_file]] URI\n"
"\tURI can be an absolute URI or a URI prefixed with scheme and host\n\n"
"General Options\n"
"\t-a addr\t\tThe local interface address to use\n"
"\t-b [num,]size\tBlock size to be used in GET/PUT/POST requests\n"
"\t \t\t(value must be a multiple of 16 not larger than 1024)\n"
"\t \t\tIf num is present, the request chain will start at\n"
"\t \t\tblock num\n"
"\t-e text\t\tInclude text as payload (use percent-encoding for\n"
"\t \t\tnon-ASCII characters)\n"
"\t-f file\t\tFile to send with PUT/POST (use '-' for STDIN)\n"
"\t-l list\t\tFail to send some datagrams specified by a comma\n"
"\t \t\tseparated list of numbers or number ranges\n"
"\t \t\t(for debugging only)\n"
"\t-l loss%%\tRandomly fail to send datagrams with the specified\n"
"\t \t\tprobability - 100%% all datagrams, 0%% no datagrams\n"
"\t-m method\tRequest method (get|put|post|delete|fetch|patch|ipatch),\n"
"\t \t\tdefault is 'get'\n"
"\t-o file\t\tOutput received data to this file (use '-' for STDOUT)\n"
"\t-p port\t\tListen on specified port\n"
"\t-r \t\tUse reliable protocol (TCP or TLS); requires TCP support\n"
"\t-s duration\tSubscribe to / Observe resource for given duration\n"
"\t \t\tin seconds\n"
"\t-t type\t\tContent format for given resource for PUT/POST\n"
"\t-v num \t\tVerbosity level (default 3, maximum is 9). Above 7,\n"
"\t \t\tthere is increased verbosity in GnuTLS logging\n"
"\t-w \t\tAppend a newline to received data\n"
"\t-A type\t\tAccepted media type\n"
"\t-B seconds\tBreak operation after waiting given seconds\n"
"\t \t\t(default is %d)\n"
"\t-G count\tRepeat the Request 'count' times with a second delay\n"
"\t \t\tbetween each one. Must have a value between 1 and 255\n"
"\t \t\tinclusive. Default is '1'\n"
"\t-H hoplimit\tSet the Hop Limit count to hoplimit for proxies. Must\n"
"\t \t\thave a value between 1 and 255 inclusive.\n"
"\t \t\tDefault is '16'\n"
"\t-K interval\tSend a ping after interval seconds of inactivity\n"
"\t-L value\tSum of one or more COAP_BLOCK_* flag valuess for block\n"
"\t \t\thandling methods. Default is 1 (COAP_BLOCK_USE_LIBCOAP)\n"
"\t \t\t(Sum of one or more of 1,2,4 and 8)\n"
"\t-N \t\tSend NON-confirmable message\n"
"\t-O num,text\tAdd option num with contents text to request. If the\n"
"\t \t\ttext begins with 0x, then the hex text (two [0-9a-f] per\n"
"\t \t\tbyte) is converted to binary data\n"
"\t-P scheme://address[:port]\tScheme, address and optional port to\n"
"\t \t\tdefine how to connect to a CoAP proxy (automatically adds\n"
"\t \t\tProxy-Uri option to request) to forward the request to.\n"
"\t \t\tScheme is one of coap, coaps, coap+tcp and coaps+tcp\n"
"\t-T token\tDefine the initial starting token\n"
"\t-U \t\tNever include Uri-Host or Uri-Port options\n"
"\t-X size\t\tMaximum message size to use for TCP based connections\n"
"\t \t\t(default is 8388864). Maximum value of 2^32 -1\n"
,program, wait_seconds);
fprintf( stderr,
"PSK Options (if supported by underlying (D)TLS library)\n"
"\t-h match_hint_file\n"
"\t \t\tThis is a file that contains one or more lines of\n"
"\t \t\treceived Identity Hints to match to use different\n"
"\t \t\tuser identity and associated pre-shared key (PSK) (comma\n"
"\t \t\tseparated) instead of the '-k key' and '-u user'\n"
"\t \t\toptions. E.g., per line\n"
"\t \t\t hint_to_match,use_user,with_key\n"
"\t \t\tNote: -k and -u still need to be defined for the default\n"
"\t \t\tin case there is no match\n"
"\t-k key \t\tPre-shared key for the specified user identity\n"
"\t-u user\t\tUser identity to send for pre-shared key mode\n"
"PKI Options (if supported by underlying (D)TLS library)\n"
"\tNote: If any one of '-c certfile', '-j keyfile' or '-C cafile' is in\n"
"\tPKCS11 URI naming format (pkcs11: prefix), then any remaining non\n"
"\tPKCS11 URI file definitions have to be in DER, not PEM, format.\n"
"\tOtherwise all of '-c certfile', '-j keyfile' or '-C cafile' are in\n"
"\tPEM format.\n\n"
"\t-c certfile\tPEM file or PKCS11 URI for the certificate. The private\n"
"\t \t\tkey can also be in the PEM file, or has the same PKCS11\n"
"\t \t\tURI. If not, the private key is defined by '-j keyfile'\n"
"\t-j keyfile\tPEM file or PKCS11 URI for the private key for the\n"
"\t \t\tcertificate in '-c certfile' if the parameter is\n"
"\t \t\tdifferent from certfile in '-c certfile'\n"
"\t-n \t\tDisable remote peer certificate checking\n"
"\t-C cafile\tPEM file or PKCS11 URI for the CA certificate that was\n"
"\t \t\tused to sign the server certfile. Ideally the client\n"
"\t \t\tcertificate should be signed by the same CA so that\n"
"\t \t\tmutual authentication can take place. The contents of\n"
"\t \t\tcafile are added to the trusted store of root CAs.\n"
"\t \t\tUsing the -C or -R options will trigger the\n"
"\t \t\tvalidation of the server certificate unless overridden\n"
"\t \t\tby the -n option\n"
"\t-J pkcs11_pin\tThe user pin to unlock access to the PKCS11 token\n"
"\t-M rpk_file\tRaw Public Key (RPK) PEM file or PKCS11 URI that\n"
"\t \t\tcontains both PUBLIC KEY and PRIVATE KEY or just\n"
"\t \t\tEC PRIVATE KEY. (GnuTLS and TinyDTLS(PEM) support only).\n"
"\t \t\t'-C cafile' or '-R trust_casfile' are not required\n"
"\t-R trust_casfile\tPEM file containing the set of trusted root CAs\n"
"\t \t\tthat are to be used to validate the server certificate.\n"
"\t \t\tAlternatively, this can point to a directory containing\n"
"\t \t\ta set of CA PEM files.\n"
"\t \t\tUsing '-R trust_casfile' disables common CA mutual\n"
"\t \t\tauthentication which can only be done by using\n"
"\t \t\t'-C cafile'.\n"
"\t \t\tUsing the -C or -R options will will trigger the\n"
"\t \t\tvalidation of the server certificate unless overridden\n"
"\t \t\tby the -n option\n"
);
fprintf( stderr,
"Examples:\n"
"\tcoap-client -m get coap://[::1]/\n"
"\tcoap-client -m get coap://[::1]/.well-known/core\n"
"\tcoap-client -m get coap+tcp://[::1]/.well-known/core\n"
"\tcoap-client -m get coaps://[::1]/.well-known/core\n"
"\tcoap-client -m get coaps+tcp://[::1]/.well-known/core\n"
"\tcoap-client -m get -T cafe coap://[::1]/time\n"
"\techo -n 1000 | coap-client -m put -T cafe coap://[::1]/time -f -\n"
);
}
typedef struct {
unsigned char code;
const char *media_type;
} content_type_t;
static void
cmdline_content_type(char *arg, uint16_t key) {
static content_type_t content_types[] = {
{ 0, "plain" },
{ 0, "text/plain" },
{ 40, "link" },
{ 40, "link-format" },
{ 40, "application/link-format" },
{ 41, "xml" },
{ 41, "application/xml" },
{ 42, "binary" },
{ 42, "octet-stream" },
{ 42, "application/octet-stream" },
{ 47, "exi" },
{ 47, "application/exi" },
{ 50, "json" },
{ 50, "application/json" },
{ 60, "cbor" },
{ 60, "application/cbor" },
{ 255, NULL }
};
coap_optlist_t *node;
unsigned char i;
uint16_t value;
uint8_t buf[2];
if (isdigit((int)arg[0])) {
value = atoi(arg);
} else {
for (i=0;
content_types[i].media_type &&
strncmp(arg, content_types[i].media_type, strlen(arg)) != 0 ;
++i)
;
if (content_types[i].media_type) {
value = content_types[i].code;
} else {
coap_log(LOG_WARNING, "W: unknown content-format '%s'\n",arg);
return;
}
}
node = coap_new_optlist(key, coap_encode_var_safe(buf, sizeof(buf), value), buf);
if (node) {
coap_insert_optlist(&optlist, node);
}
}
static int
cmdline_hop_limit(char *arg) {
coap_optlist_t *node;
uint32_t value;
uint8_t buf[4];
value = strtol(arg, NULL, 10);
if (value < 1 || value > 255) {
return 0;
}
node = coap_new_optlist(COAP_OPTION_HOP_LIMIT, coap_encode_var_safe(buf, sizeof(buf), value), buf);
if (node) {
coap_insert_optlist(&optlist, node);
}
return 1;
}
static uint16_t
get_default_port(const coap_uri_t *u) {
return coap_uri_scheme_is_secure(u) ? COAPS_DEFAULT_PORT : COAP_DEFAULT_PORT;
}
static int
cmdline_uri(char *arg, int create_uri_opts) {
unsigned char portbuf[2];
#define BUFSIZE 100
unsigned char _buf[BUFSIZE];
unsigned char *buf = _buf;
size_t buflen;
int res;
if (!proxy_scheme_option && proxy.host.length) {
size_t len = strlen(arg);
if (len > 1034) {
coap_log(LOG_ERR, "Absolute URI length must be <= 1034 bytes for a proxy\n");
return -1;
}
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_PROXY_URI,
len,
(unsigned char *)arg));
} else {
if (coap_split_uri((unsigned char *)arg, strlen(arg), &uri) < 0) {
coap_log(LOG_ERR, "invalid CoAP URI\n");
return -1;
}
if (uri.scheme==COAP_URI_SCHEME_COAPS && !reliable && !coap_dtls_is_supported()) {
coap_log(LOG_EMERG,
"coaps URI scheme not supported in this version of libcoap\n");
return -1;
}
if ((uri.scheme==COAP_URI_SCHEME_COAPS_TCP || (uri.scheme==COAP_URI_SCHEME_COAPS && reliable)) && !coap_tls_is_supported()) {
coap_log(LOG_EMERG,
"coaps+tcp URI scheme not supported in this version of libcoap\n");
return -1;
}
if (uri.scheme==COAP_URI_SCHEME_COAP_TCP && !coap_tcp_is_supported()) {
coap_log(LOG_EMERG,
"coap+tcp URI scheme not supported in this version of libcoap\n");
return -1;
}
if (uri.port != get_default_port(&uri) && create_uri_opts) {
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_URI_PORT,
coap_encode_var_safe(portbuf, sizeof(portbuf),
(uri.port & 0xffff)),
portbuf));
}
if (uri.path.length) {
buflen = BUFSIZE;
if (uri.path.length > BUFSIZE)
coap_log(LOG_WARNING, "URI path will be truncated (max buffer %d)\n", BUFSIZE);
res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
while (res--) {
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_URI_PATH,
coap_opt_length(buf),
coap_opt_value(buf)));
buf += coap_opt_size(buf);
}
}
if (uri.query.length) {
buflen = BUFSIZE;
buf = _buf;
res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
while (res--) {
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_URI_QUERY,
coap_opt_length(buf),
coap_opt_value(buf)));
buf += coap_opt_size(buf);
}
}
}
return 0;
}
static int
cmdline_blocksize(char *arg) {
uint16_t size;
again:
size = 0;
while(*arg && *arg != ',')
size = size * 10 + (*arg++ - '0');
if (*arg == ',') {
arg++;
block.num = size;
if (size != 0) {
single_block_requested = 1;
}
goto again;
}
if (size)
block.szx = (coap_fls(size >> 4) - 1) & 0x07;
flags |= FLAGS_BLOCK;
return 1;
}
static void
set_blocksize(void) {
static unsigned char buf[4];
uint16_t opt;
unsigned int opt_length;
if (method != COAP_REQUEST_DELETE) {
if (method == COAP_REQUEST_GET || method == COAP_REQUEST_FETCH) {
opt = COAP_OPTION_BLOCK2;
}
else {
opt = COAP_OPTION_BLOCK1;
}
block.m = (opt == COAP_OPTION_BLOCK1) &&
((1ull << (block.szx + 4)) < payload.length);
opt_length = coap_encode_var_safe(buf, sizeof(buf),
(block.num << 4 | block.m << 3 | block.szx));
coap_insert_optlist(&optlist, coap_new_optlist(opt, opt_length, buf));
}
}
static void
cmdline_subscribe(char *arg) {
uint8_t buf[4];
obs_seconds = atoi(arg);
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_OBSERVE,
coap_encode_var_safe(buf, sizeof(buf),
COAP_OBSERVE_ESTABLISH), buf)
);
doing_observe = 1;
}
static int
cmdline_proxy(char *arg) {
if (coap_split_uri((unsigned char *)arg, strlen(arg), &proxy) < 0 ||
proxy.path.length != 0 || proxy.query.length != 0) {
coap_log(LOG_ERR, "invalid CoAP Proxy definition\n");
return 0;
}
return 1;
}
static inline void
cmdline_token(char *arg) {
base_token.length = min(sizeof(_token_data), strlen(arg));
if (base_token.length > 0) {
memcpy((char *)base_token.s, arg, base_token.length);
}
}
static uint8_t
hex2char(char c) {
assert(isxdigit(c));
if ('a' <= c && c <= 'f')
return c - 'a' + 10;
else if ('A' <= c && c <= 'F')
return c - 'A' + 10;
else
return c - '0';
}
static size_t
convert_hex_string(const char *src, uint8_t *dst) {
uint8_t *p = dst;
while (isxdigit((int)src[0]) && isxdigit((int)src[1])) {
*p++ = (hex2char(src[0]) << 4) + hex2char(src[1]);
src += 2;
}
if (src[0] != '\0') {
coap_log(LOG_WARNING, "invalid hex string in option '%s'\n", src);
}
return p - dst;
}
static void
cmdline_option(char *arg) {
unsigned int num = 0;
while (*arg && *arg != ',') {
num = num * 10 + (*arg - '0');
++arg;
}
if (*arg == ',')
++arg;
if (arg[0] == '0' && arg[1] == 'x') {
size_t len = convert_hex_string(arg + 2, (uint8_t *)arg);
coap_insert_optlist(&optlist,
coap_new_optlist(num, len, (unsigned char *)arg));
} else {
coap_insert_optlist(&optlist,
coap_new_optlist(num, strlen(arg), (unsigned char *)arg));
}
if (num == COAP_OPTION_PROXY_SCHEME) {
proxy_scheme_option = 1;
if (strcasecmp(arg, "coaps+tcp") == 0) {
proxy.scheme = COAP_URI_SCHEME_COAPS_TCP;
proxy.port = COAPS_DEFAULT_PORT;
}
else if (strcasecmp(arg, "coap+tcp") == 0) {
proxy.scheme = COAP_URI_SCHEME_COAP_TCP;
proxy.port = COAP_DEFAULT_PORT;
}
else if (strcasecmp(arg, "coaps") == 0) {
proxy.scheme = COAP_URI_SCHEME_COAPS;
proxy.port = COAPS_DEFAULT_PORT;
}
else if (strcasecmp(arg, "coap") == 0) {
proxy.scheme = COAP_URI_SCHEME_COAP;
proxy.port = COAP_DEFAULT_PORT;
}
else {
coap_log(LOG_WARNING, "%s is not a supported CoAP Proxy-Scheme\n", arg);
}
}
if (num == COAP_OPTION_URI_HOST) {
uri_host_option = 1;
}
}
#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
static void
decode_segment(const uint8_t *seg, size_t length, unsigned char *buf) {
while (length--) {
if (*seg == '%') {
*buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
seg += 2; length -= 2;
} else {
*buf = *seg;
}
++buf; ++seg;
}
}
static int
check_segment(const uint8_t *s, size_t length) {
int n = 0;
while (length) {
if (*s == '%') {
if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
return -1;
s += 2;
length -= 2;
}
++s; ++n; --length;
}
return n;
}
static int
cmdline_input(char *text, coap_string_t *buf) {
int len;
len = check_segment((unsigned char *)text, strlen(text));
if (len < 0)
return 0;
buf->s = (unsigned char *)coap_malloc(len);
if (!buf->s)
return 0;
buf->length = len;
decode_segment((unsigned char *)text, strlen(text), buf->s);
return 1;
}
static int
cmdline_input_from_file(char *filename, coap_string_t *buf) {
FILE *inputfile = NULL;
ssize_t len;
int result = 1;
struct stat statbuf;
if (!filename || !buf)
return 0;
if (filename[0] == '-' && !filename[1]) {
buf->length = 20000;
buf->s = (unsigned char *)coap_malloc(buf->length);
if (!buf->s)
return 0;
inputfile = stdin;
} else {
inputfile = fopen(filename, "r");
if ( !inputfile ) {
perror("cmdline_input_from_file: fopen");
return 0;
}
if (fstat(fileno(inputfile), &statbuf) < 0) {
perror("cmdline_input_from_file: stat");
fclose(inputfile);
return 0;
}
buf->length = statbuf.st_size;
buf->s = (unsigned char *)coap_malloc(buf->length);
if (!buf->s) {
fclose(inputfile);
return 0;
}
}
len = fread(buf->s, 1, buf->length, inputfile);
if (len < 0 || ((size_t)len < buf->length)) {
if (ferror(inputfile) != 0) {
perror("cmdline_input_from_file: fread");
coap_free(buf->s);
buf->length = 0;
buf->s = NULL;
result = 0;
} else {
buf->length = len;
}
}
if (inputfile != stdin)
fclose(inputfile);
return result;
}
static method_t
cmdline_method(char *arg) {
static const char *methods[] =
{ 0, "get", "post", "put", "delete", "fetch", "patch", "ipatch", 0};
unsigned char i;
for (i=1; methods[i] && strcasecmp(arg,methods[i]) != 0 ; ++i)
;
return i;
}
static ssize_t
cmdline_read_user(char *arg, unsigned char **buf, size_t maxlen) {
size_t len = strnlen(arg, maxlen);
if (len) {
*buf = (unsigned char *)arg;
(*buf)[len] = '\000';
}
return len;
}
static ssize_t
cmdline_read_key(char *arg, unsigned char **buf, size_t maxlen) {
size_t len = strnlen(arg, maxlen);
if (len) {
*buf = (unsigned char *)arg;
return len;
}
coap_log( LOG_CRIT, "Invalid Pre-Shared Key specified\n" );
return -1;
}
static int cmdline_read_hint_check(const char *arg) {
FILE *fp = fopen(arg, "r");
static char tmpbuf[256];
if (fp == NULL) {
coap_log(LOG_ERR, "Hint file: %s: Unable to open\n", arg);
return 0;
}
while (fgets(tmpbuf, sizeof(tmpbuf), fp) != NULL) {
char *cp = tmpbuf;
char *tcp = strchr(cp, '\n');
if (tmpbuf[0] == '#')
continue;
if (tcp)
*tcp = '\000';
tcp = strchr(cp, ',');
if (tcp) {
ih_def_t *new_ih_list;
new_ih_list = realloc(valid_ihs.ih_list,
(valid_ihs.count + 1)*sizeof (valid_ihs.ih_list[0]));
if (new_ih_list == NULL) {
break;
}
valid_ihs.ih_list = new_ih_list;
valid_ihs.ih_list[valid_ihs.count].hint_match = strndup(cp, tcp-cp);
cp = tcp+1;
tcp = strchr(cp, ',');
if (tcp) {
valid_ihs.ih_list[valid_ihs.count].new_identity =
coap_new_bin_const((const uint8_t *)cp, tcp-cp);
cp = tcp+1;
valid_ihs.ih_list[valid_ihs.count].new_key =
coap_new_bin_const((const uint8_t *)cp, strlen(cp));
valid_ihs.count++;
}
else {
free(valid_ihs.ih_list[valid_ihs.count].hint_match);
}
}
}
fclose(fp);
return valid_ihs.count > 0;
}
static uint8_t *read_file_mem(const char* filename, size_t *length) {
FILE *f;
uint8_t *buf;
struct stat statbuf;
*length = 0;
if (!filename || !(f = fopen(filename, "r")))
return NULL;
if (fstat(fileno(f), &statbuf) == -1) {
fclose(f);
return NULL;
}
buf = coap_malloc(statbuf.st_size+1);
if (!buf) {
fclose(f);
return NULL;
}
if (fread(buf, 1, statbuf.st_size, f) != (size_t)statbuf.st_size) {
fclose(f);
coap_free(buf);
return NULL;
}
buf[statbuf.st_size] = '\000';
*length = (size_t)(statbuf.st_size + 1);
fclose(f);
return buf;
}
static int
verify_cn_callback(const char *cn,
const uint8_t *asn1_public_cert COAP_UNUSED,
size_t asn1_length COAP_UNUSED,
coap_session_t *session COAP_UNUSED,
unsigned depth,
int validated COAP_UNUSED,
void *arg COAP_UNUSED
) {
coap_log(LOG_INFO, "CN '%s' presented by server (%s)\n",
cn, depth ? "CA" : "Certificate");
return 1;
}
static const coap_dtls_cpsk_info_t *
verify_ih_callback(coap_str_const_t *hint,
coap_session_t *c_session COAP_UNUSED,
void *arg
) {
coap_dtls_cpsk_info_t *psk_info = (coap_dtls_cpsk_info_t *)arg;
char lhint[COAP_DTLS_HINT_LENGTH];
static coap_dtls_cpsk_info_t psk_identity_info;
size_t i;
snprintf(lhint, sizeof(lhint), "%.*s", (int)hint->length, hint->s);
coap_log(LOG_INFO, "Identity Hint '%s' provided\n", lhint);
for (i = 0; i < valid_ihs.count; i++) {
if (strcmp(lhint, valid_ihs.ih_list[i].hint_match) == 0) {
psk_identity_info = *psk_info;
if (valid_ihs.ih_list[i].new_key) {
psk_identity_info.key = *valid_ihs.ih_list[i].new_key;
}
if (valid_ihs.ih_list[i].new_identity) {
psk_identity_info.identity = *valid_ihs.ih_list[i].new_identity;
}
coap_log(LOG_INFO, "Switching to using '%s' identity + '%s' key\n",
psk_identity_info.identity.s, psk_identity_info.key.s);
return &psk_identity_info;
}
}
return psk_info;
}
static coap_dtls_pki_t *
setup_pki(coap_context_t *ctx) {
static coap_dtls_pki_t dtls_pki;
static char client_sni[256];
if (root_ca_file) {
struct stat stbuf;
if ((stat(root_ca_file, &stbuf) == 0) && S_ISDIR(stbuf.st_mode)) {
coap_context_set_pki_root_cas(ctx, NULL, root_ca_file);
} else {
coap_context_set_pki_root_cas(ctx, root_ca_file, NULL);
}
}
memset(client_sni, 0, sizeof(client_sni));
memset (&dtls_pki, 0, sizeof(dtls_pki));
dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
if (ca_file || root_ca_file) {
dtls_pki.verify_peer_cert = verify_peer_cert;
dtls_pki.check_common_ca = !root_ca_file;
dtls_pki.allow_self_signed = 1;
dtls_pki.allow_expired_certs = 1;
dtls_pki.cert_chain_validation = 1;
dtls_pki.cert_chain_verify_depth = 2;
dtls_pki.check_cert_revocation = 1;
dtls_pki.allow_no_crl = 1;
dtls_pki.allow_expired_crl = 1;
}
else if (is_rpk_not_cert) {
dtls_pki.verify_peer_cert = verify_peer_cert;
}
dtls_pki.is_rpk_not_cert = is_rpk_not_cert;
dtls_pki.validate_cn_call_back = verify_cn_callback;
if ((uri.host.length == 3 && memcmp(uri.host.s, "::1", 3) != 0) ||
(uri.host.length == 9 && memcmp(uri.host.s, "127.0.0.1", 9) != 0))
memcpy(client_sni, uri.host.s, min(uri.host.length, sizeof(client_sni)-1));
else
memcpy(client_sni, "localhost", 9);
dtls_pki.client_sni = client_sni;
if ((key_file && strncasecmp (key_file, "pkcs11:", 7) == 0) ||
(cert_file && strncasecmp (cert_file, "pkcs11:", 7) == 0) ||
(ca_file && strncasecmp (ca_file, "pkcs11:", 7) == 0)) {
dtls_pki.pki_key.key_type = COAP_PKI_KEY_PKCS11;
dtls_pki.pki_key.key.pkcs11.public_cert = cert_file;
dtls_pki.pki_key.key.pkcs11.private_key = key_file ?
key_file : cert_file;
dtls_pki.pki_key.key.pkcs11.ca = ca_file;
dtls_pki.pki_key.key.pkcs11.user_pin = pkcs11_pin;
}
else if (!is_rpk_not_cert) {
dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM;
dtls_pki.pki_key.key.pem.public_cert = cert_file;
dtls_pki.pki_key.key.pem.private_key = key_file ? key_file : cert_file;
dtls_pki.pki_key.key.pem.ca_file = ca_file;
}
else {
if (ca_mem == 0 && cert_mem == 0 && key_mem == 0) {
ca_mem = read_file_mem(ca_file, &ca_mem_len);
cert_mem = read_file_mem(cert_file, &cert_mem_len);
key_mem = read_file_mem(key_file, &key_mem_len);
}
dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
dtls_pki.pki_key.key.pem_buf.ca_cert = ca_mem;
dtls_pki.pki_key.key.pem_buf.public_cert = cert_mem;
dtls_pki.pki_key.key.pem_buf.private_key = key_mem ? key_mem : cert_mem;
dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_mem_len;
dtls_pki.pki_key.key.pem_buf.public_cert_len = cert_mem_len;
dtls_pki.pki_key.key.pem_buf.private_key_len = key_mem ?
key_mem_len : cert_mem_len;
}
return &dtls_pki;
}
static coap_dtls_cpsk_t *
setup_psk(
const uint8_t *identity,
size_t identity_len,
const uint8_t *key,
size_t key_len
) {
static coap_dtls_cpsk_t dtls_psk;
static char client_sni[256];
memset(client_sni, 0, sizeof(client_sni));
memset (&dtls_psk, 0, sizeof(dtls_psk));
dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION;
dtls_psk.validate_ih_call_back = verify_ih_callback;
dtls_psk.ih_call_back_arg = &dtls_psk.psk_info;
if (uri.host.length)
memcpy(client_sni, uri.host.s,
min(uri.host.length, sizeof(client_sni) - 1));
else
memcpy(client_sni, "localhost", 9);
dtls_psk.client_sni = client_sni;
dtls_psk.psk_info.identity.s = identity;
dtls_psk.psk_info.identity.length = identity_len;
dtls_psk.psk_info.key.s = key;
dtls_psk.psk_info.key.length = key_len;
return &dtls_psk;
}
#ifdef _WIN32
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
static coap_session_t*
open_session(
coap_context_t *ctx,
coap_proto_t proto,
coap_address_t *bind_addr,
coap_address_t *dst,
const uint8_t *identity,
size_t identity_len,
const uint8_t *key,
size_t key_len
) {
coap_session_t *session;
if (proto == COAP_PROTO_DTLS || proto == COAP_PROTO_TLS) {
if (root_ca_file || ca_file || cert_file) {
coap_dtls_pki_t *dtls_pki = setup_pki(ctx);
session = coap_new_client_session_pki(ctx, bind_addr, dst, proto, dtls_pki);
}
else if (identity || key) {
coap_dtls_cpsk_t *dtls_psk = setup_psk(identity, identity_len,
key, key_len);
session = coap_new_client_session_psk2(ctx, bind_addr, dst, proto,
dtls_psk);
}
else {
coap_dtls_pki_t *dtls_pki = setup_pki(ctx);
session = coap_new_client_session_pki(ctx, bind_addr, dst, proto, dtls_pki);
}
}
else {
session = coap_new_client_session(ctx, bind_addr, dst, proto);
}
return session;
}
static coap_session_t *
get_session(
coap_context_t *ctx,
const char *local_addr,
const char *local_port,
coap_proto_t proto,
coap_address_t *dst,
const uint8_t *identity,
size_t identity_len,
const uint8_t *key,
size_t key_len
) {
coap_session_t *session = NULL;
is_mcast = coap_is_mcast(dst);
if ( local_addr ) {
int s;
struct addrinfo hints;
struct addrinfo *result = NULL, *rp;
memset( &hints, 0, sizeof( struct addrinfo ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = COAP_PROTO_RELIABLE(proto) ? SOCK_STREAM : SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL;
s = getaddrinfo( local_addr, local_port, &hints, &result );
if ( s != 0 ) {
fprintf( stderr, "getaddrinfo: %s\n", gai_strerror( s ) );
return NULL;
}
for ( rp = result; rp != NULL; rp = rp->ai_next ) {
coap_address_t bind_addr;
if ( rp->ai_addrlen <= (socklen_t)sizeof( bind_addr.addr ) ) {
coap_address_init( &bind_addr );
bind_addr.size = (socklen_t)rp->ai_addrlen;
memcpy( &bind_addr.addr, rp->ai_addr, rp->ai_addrlen );
session = open_session(ctx, proto, &bind_addr, dst,
identity, identity_len, key, key_len);
if ( session )
break;
}
}
freeaddrinfo( result );
} else if (local_port) {
coap_address_t bind_addr;
coap_address_init(&bind_addr);
bind_addr.size = dst->size;
bind_addr.addr.sa.sa_family = dst->addr.sa.sa_family;
bind_addr.addr.sin.sin_port = ntohs(atoi(local_port));
session = open_session(ctx, proto, &bind_addr, dst,
identity, identity_len, key, key_len);
} else {
session = open_session(ctx, proto, NULL, dst,
identity, identity_len, key, key_len);
}
return session;
}
int
main(int argc, char **argv) {
coap_context_t *ctx = NULL;
coap_session_t *session = NULL;
coap_address_t dst;
static char addr[INET6_ADDRSTRLEN];
void *addrptr = NULL;
int result = -1;
coap_pdu_t *pdu;
static coap_str_const_t server;
uint16_t port = COAP_DEFAULT_PORT;
char port_str[NI_MAXSERV] = "0";
char node_str[NI_MAXHOST] = "";
int opt, res;
coap_log_t log_level = LOG_WARNING;
unsigned char *user = NULL, *key = NULL;
ssize_t user_length = -1, key_length = 0;
int create_uri_opts = 1;
size_t i;
coap_uri_scheme_t scheme;
uint32_t repeat_ms = REPEAT_DELAY_MS;
uint8_t *data = NULL;
size_t data_len = 0;
#ifndef _WIN32
struct sigaction sa;
#endif
while ((opt = getopt(argc, argv, "a:b:c:e:f:h:j:k:l:m:no:p:rs:t:u:v:wA:B:C:G:H:J:K:L:M:NO:P:R:T:UX:")) != -1) {
switch (opt) {
case 'a':
strncpy(node_str, optarg, NI_MAXHOST - 1);
node_str[NI_MAXHOST - 1] = '\0';
break;
case 'b':
cmdline_blocksize(optarg);
break;
case 'B':
wait_seconds = atoi(optarg);
break;
case 'c':
cert_file = optarg;
break;
case 'C':
ca_file = optarg;
break;
case 'R':
root_ca_file = optarg;
break;
case 'e':
if (!cmdline_input(optarg, &payload))
payload.length = 0;
break;
case 'f':
if (!cmdline_input_from_file(optarg, &payload))
payload.length = 0;
break;
case 'j' :
key_file = optarg;
break;
case 'J' :
pkcs11_pin = optarg;
break;
case 'k':
key_length = cmdline_read_key(optarg, &key, MAX_KEY);
break;
case 'L':
block_mode = strtoul(optarg, NULL, 0);
if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) {
fprintf(stderr, "Block mode must include COAP_BLOCK_USE_LIBCOAP (1)\n");
exit(-1);
}
break;
case 'p':
strncpy(port_str, optarg, NI_MAXSERV - 1);
port_str[NI_MAXSERV - 1] = '\0';
break;
case 'm':
method = cmdline_method(optarg);
break;
case 'w':
add_nl = 1;
break;
case 'N':
msgtype = COAP_MESSAGE_NON;
break;
case 's':
cmdline_subscribe(optarg);
break;
case 'o':
output_file.length = strlen(optarg);
output_file.s = (unsigned char *)coap_malloc(output_file.length + 1);
if (!output_file.s) {
fprintf(stderr, "cannot set output file: insufficient memory\n");
exit(-1);
} else {
memcpy(output_file.s, optarg, output_file.length + 1);
}
break;
case 'A':
cmdline_content_type(optarg, COAP_OPTION_ACCEPT);
break;
case 't':
cmdline_content_type(optarg, COAP_OPTION_CONTENT_TYPE);
break;
case 'M':
cert_file = optarg;
is_rpk_not_cert = 1;
break;
case 'O':
cmdline_option(optarg);
break;
case 'P':
if (!cmdline_proxy(optarg)) {
fprintf(stderr, "error specifying proxy address\n");
exit(-1);
}
break;
case 'T':
cmdline_token(optarg);
break;
case 'u':
user_length = cmdline_read_user(optarg, &user, MAX_USER);
break;
case 'U':
create_uri_opts = 0;
break;
case 'v':
log_level = strtol(optarg, NULL, 10);
break;
case 'l':
if (!coap_debug_set_packet_loss(optarg)) {
usage(argv[0], LIBCOAP_PACKAGE_VERSION);
exit(1);
}
break;
case 'r':
reliable = coap_tcp_is_supported();
break;
case 'K':
ping_seconds = atoi(optarg);
break;
case 'h':
if (!cmdline_read_hint_check(optarg)) {
usage(argv[0], LIBCOAP_PACKAGE_VERSION);
exit(1);
}
break;
case 'H':
if (!cmdline_hop_limit(optarg))
fprintf(stderr, "Hop Limit has to be > 0 and < 256\n");
break;
case 'n':
verify_peer_cert = 0;
break;
case 'G':
repeat_count = atoi(optarg);
if (!repeat_count || repeat_count > 255) {
fprintf(stderr, "'-G count' has to be > 0 and < 256\n");
repeat_count = 1;
}
break;
case 'X':
csm_max_message_size = strtol(optarg, NULL, 10);
break;
default:
usage( argv[0], LIBCOAP_PACKAGE_VERSION );
exit( 1 );
}
}
#ifdef _WIN32
signal(SIGINT, handle_sigint);
#else
memset (&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = handle_sigint;
sa.sa_flags = 0;
sigaction (SIGINT, &sa, NULL);
sigaction (SIGTERM, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction (SIGPIPE, &sa, NULL);
#endif
coap_startup();
coap_dtls_set_log_level(log_level);
coap_set_log_level(log_level);
if (optind < argc) {
if (cmdline_uri(argv[optind], create_uri_opts) < 0) {
exit(1);
}
} else {
usage( argv[0], LIBCOAP_PACKAGE_VERSION );
exit( 1 );
}
if (key_length < 0) {
coap_log( LOG_CRIT, "Invalid pre-shared key specified\n" );
goto finish;
}
if (proxy.host.length) {
server = proxy.host;
port = proxy.port;
scheme = proxy.scheme;
} else {
server = uri.host;
port = proxy_scheme_option ? proxy.port : uri.port;
scheme = proxy_scheme_option ? proxy.scheme : uri.scheme;
}
res = resolve_address(&server, &dst.addr.sa);
if (res < 0) {
fprintf(stderr, "failed to resolve address\n");
exit(-1);
}
ctx = coap_new_context( NULL );
if ( !ctx ) {
coap_log( LOG_EMERG, "cannot create context\n" );
goto finish;
}
coap_context_set_keepalive(ctx, ping_seconds);
coap_context_set_block_mode(ctx, block_mode);
if (csm_max_message_size)
coap_context_set_csm_max_message_size(ctx, csm_max_message_size);
coap_register_response_handler(ctx, message_handler);
coap_register_event_handler(ctx, event_handler);
coap_register_nack_handler(ctx, nack_handler);
dst.size = res;
dst.addr.sin.sin_port = htons( port );
session = get_session(
ctx,
node_str[0] ? node_str : NULL, port_str,
scheme==COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
scheme==COAP_URI_SCHEME_COAPS_TCP ? COAP_PROTO_TLS :
(reliable ?
scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_TLS : COAP_PROTO_TCP
: scheme==COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_UDP),
&dst,
user_length >= 0 ? user : NULL,
user_length >= 0 ? user_length : 0,
key_length > 0 ? key : NULL,
key_length > 0 ? key_length : 0
);
if ( !session ) {
coap_log( LOG_ERR, "cannot create client session\n" );
goto finish;
}
coap_session_init_token(session, base_token.length, base_token.s);
switch (dst.addr.sa.sa_family) {
case AF_INET:
addrptr = &dst.addr.sin.sin_addr;
break;
case AF_INET6:
addrptr = &dst.addr.sin6.sin6_addr;
break;
default:
;
}
if (!uri_host_option && (!proxy.host.length && addrptr
&& (inet_ntop(dst.addr.sa.sa_family, addrptr, addr, sizeof(addr)) != 0)
&& (strlen(addr) != uri.host.length
|| memcmp(addr, uri.host.s, uri.host.length) != 0)
&& create_uri_opts)) {
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_URI_HOST,
uri.host.length,
uri.host.s));
}
if (flags & FLAGS_BLOCK)
set_blocksize();
if (payload.length) {
data = coap_malloc(payload.length);
if (data == NULL)
goto finish;
memcpy(data, payload.s, payload.length);
data_len = payload.length;
}
if (!(pdu = coap_new_request(ctx, session, method, &optlist, data,
data_len))) {
goto finish;
}
if (is_mcast && wait_seconds == DEFAULT_WAIT_TIME)
wait_seconds = coap_session_get_default_leisure(session).integer_part + 1;
wait_ms = wait_seconds * 1000;
coap_log(LOG_DEBUG, "timeout is set to %u seconds\n", wait_seconds);
coap_log(LOG_DEBUG, "sending CoAP request:\n");
if (coap_get_log_level() < LOG_DEBUG)
coap_show_pdu(LOG_INFO, pdu);
if (coap_send(session, pdu) == COAP_INVALID_MID) {
coap_log(LOG_ERR, "cannot send CoAP pdu\n");
quit = 1;
}
repeat_count--;
while(!quit &&
!(ready && !tracked_tokens_count && !is_mcast && !repeat_count &&
coap_can_exit(ctx)) ) {
uint32_t timeout_ms;
if (obs_ms) {
timeout_ms = min(wait_ms, obs_ms);
} else {
timeout_ms = wait_ms;
}
if (repeat_count) {
timeout_ms = min(timeout_ms, repeat_ms);
}
result = coap_io_process(ctx, timeout_ms);
if ( result >= 0 ) {
if (wait_ms > 0) {
if ( (unsigned)result >= wait_ms ) {
coap_log(LOG_INFO, "timeout\n");
break;
} else {
wait_ms -= result;
}
}
if ( obs_ms > 0 && !obs_ms_reset ) {
if ( (unsigned)result >= obs_ms ) {
coap_log(LOG_DEBUG, "clear observation relationship\n" );
for (i = 0; i < tracked_tokens_count; i++) {
if (tracked_tokens[i].observe) {
coap_cancel_observe(session, tracked_tokens[i].token, msgtype);
}
}
doing_observe = 0;
quit = 1;
obs_ms = 0;
obs_seconds = 0;
} else {
obs_ms -= result;
}
}
if (ready && repeat_count) {
if (repeat_ms > (unsigned)result) {
repeat_ms -= (unsigned)result;
} else {
repeat_ms = REPEAT_DELAY_MS;
if (payload.length) {
data = coap_malloc(payload.length);
if (data == NULL)
goto finish;
memcpy(data, payload.s, payload.length);
data_len = payload.length;
}
if (!(pdu = coap_new_request(ctx, session, method, &optlist,
data, data_len))) {
goto finish;
}
coap_log(LOG_DEBUG, "sending CoAP request:\n");
if (coap_get_log_level() < LOG_DEBUG)
coap_show_pdu(LOG_INFO, pdu);
ready = 0;
if (coap_send(session, pdu) == COAP_INVALID_MID) {
coap_log(LOG_ERR, "cannot send CoAP pdu\n");
quit = 1;
}
repeat_count--;
}
}
obs_ms_reset = 0;
}
}
result = 0;
finish:
coap_free(ca_mem);
coap_free(cert_mem);
coap_free(key_mem);
coap_free(payload.s);
for (i = 0; i < valid_ihs.count; i++) {
free(valid_ihs.ih_list[i].hint_match);
coap_delete_bin_const(valid_ihs.ih_list[i].new_identity);
coap_delete_bin_const(valid_ihs.ih_list[i].new_key);
}
if (valid_ihs.count)
free(valid_ihs.ih_list);
for (i = 0; i < tracked_tokens_count; i++) {
coap_delete_binary(tracked_tokens[i].token);
}
free(tracked_tokens);
coap_delete_optlist(optlist);
coap_session_release( session );
coap_free_context( ctx );
coap_cleanup();
close_output();
return result;
}