From 710eee6353eca2e35e3d4f13903c631e7bded5fd Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Mon, 10 Oct 2022 08:54:34 +0100 Subject: [PATCH] RFC8974: Add in support for Extended Tokens Add in new function coap_context_set_max_token_size() to define the maximum supported token size. Track changes to PDU to support extended tokens. Update documentation. --- README.md | 2 + doc/main.md | 2 + examples/coap-client.c | 34 ++- examples/coap-rd.c | 32 ++- examples/coap-server.c | 30 ++- include/coap3/coap_block_internal.h | 2 - include/coap3/coap_net_internal.h | 5 +- include/coap3/coap_pdu_internal.h | 13 +- include/coap3/coap_session_internal.h | 16 +- include/coap3/coap_subscribe_internal.h | 10 +- include/coap3/net.h | 10 + include/coap3/pdu.h | 8 + libcoap-3.map | 1 + libcoap-3.sym | 1 + man/Makefile.am | 1 + man/coap-client.txt.in | 2 +- man/coap-rd.txt.in | 4 + man/coap-server.txt.in | 6 +- man/coap.txt.in | 2 + man/coap_context.txt.in | 46 +++- man/coap_pdu_setup.txt.in | 14 +- src/block.c | 55 ++--- src/coap_async.c | 19 +- src/coap_debug.c | 11 +- src/coap_session.c | 16 +- src/encode.c | 2 +- src/net.c | 287 +++++++++++++++++++----- src/pdu.c | 220 ++++++++++++++---- src/resource.c | 51 +++-- tests/test_pdu.c | 18 +- 30 files changed, 709 insertions(+), 211 deletions(-) diff --git a/README.md b/README.md index faab2727e4..33d4a6ab61 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ The following RFCs are supported * RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option +* RFC8974: Extended Tokens and Stateless Clients in the Constrained Application Protocol (CoAP) + * RFC9175: CoAP: Echo, Request-Tag, and Token Processing There is (D)TLS support for the following libraries diff --git a/doc/main.md b/doc/main.md index abd6a134c3..208afbf4d0 100644 --- a/doc/main.md +++ b/doc/main.md @@ -40,6 +40,8 @@ The following RFCs are supported * RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option +* RFC8974: Extended Tokens and Stateless Clients in the Constrained Application Protocol (CoAP) + * RFC9175: CoAP: Echo, Request-Tag, and Token Processing There is (D)TLS support for the following libraries diff --git a/examples/coap-client.c b/examples/coap-client.c index 263dd31dad..10a1224619 100644 --- a/examples/coap-client.c +++ b/examples/coap-client.c @@ -51,8 +51,8 @@ static char* strndup(const char* s1, size_t n) int flags = 0; -static unsigned char _token_data[8]; -coap_binary_t base_token = { 0, _token_data }; +static unsigned char _token_data[24]; /* With support for RFC8974 */ +coap_binary_t the_token = { 0, _token_data }; typedef struct { coap_binary_t *token; @@ -271,11 +271,19 @@ coap_new_request(coap_context_t *ctx, /* * Create unique token for this request for handling unsolicited / - * delayed responses + * delayed responses. + * Note that only up to 8 bytes are returned */ - coap_session_new_token(session, &tokenlen, token); - track_new_token(tokenlen, token); - if (!coap_add_token(pdu, tokenlen, token)) { + if (the_token.length > COAP_TOKEN_DEFAULT_MAX) { + coap_session_new_token(session, &tokenlen, token); + /* Update the last part 8 bytes of the large token */ + memcpy(&the_token.s[the_token.length - tokenlen], token, tokenlen); + } + else { + coap_session_new_token(session, &the_token.length, the_token.s); + } + track_new_token(the_token.length, the_token.s); + if (!coap_add_token(pdu, the_token.length, the_token.s)) { coap_log(LOG_DEBUG, "cannot add token to request\n"); } @@ -567,7 +575,7 @@ usage( const char *program, const char *version) { "\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-T token\tDefine the initial starting token (up to 24 characters)\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" @@ -847,9 +855,9 @@ cmdline_proxy(char *arg) { 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); + the_token.length = min(sizeof(_token_data), strlen(arg)); + if (the_token.length > 0) { + memcpy((char *)the_token.s, arg, the_token.length); } } @@ -1700,6 +1708,9 @@ main(int argc, char **argv) { dst.size = res; dst.addr.sin.sin_port = htons( port ); + if (the_token.length > COAP_TOKEN_DEFAULT_MAX) + coap_context_set_max_token_size(ctx, the_token.length); + session = get_session( ctx, node_str[0] ? node_str : NULL, port_str, @@ -1723,8 +1734,9 @@ main(int argc, char **argv) { * Prime the base token value, which coap_session_new_token() will increment * every time it is called to get an unique token. * [Option '-T token' is used to seed a different value] + * Note that only the first 8 bytes of the token are used as the prime. */ - coap_session_init_token(session, base_token.length, base_token.s); + coap_session_init_token(session, the_token.length, the_token.s); /* add Uri-Host if server address differs from uri.host */ diff --git a/examples/coap-rd.c b/examples/coap-rd.c index 33ad395795..d954c8abc1 100644 --- a/examples/coap-rd.c +++ b/examples/coap-rd.c @@ -59,6 +59,7 @@ static uint8_t key[MAX_KEY]; static ssize_t key_length = 0; static int key_defined = 0; static const char *hint = "CoAP"; +static size_t extended_token_size = COAP_TOKEN_DEFAULT_MAX; #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) @@ -583,6 +584,7 @@ usage( const char *program, const char *version) { fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer))); fprintf(stderr, "\n" "Usage: %s [-g group] [-G group_if] [-p port] [-v num] [-A address]\n" + "\t [-T max_token_size]\n" "\t [[-h hint] [-k key]]\n" "\t [[-c certfile] [-C cafile] [-n] [-R trust_casfile]]\n" "General Options\n" @@ -595,6 +597,7 @@ usage( const char *program, const char *version) { "\t-v num \t\tVerbosity level (default 3, maximum is 9). Above 7,\n" "\t \t\tthere is increased verbosity in GnuTLS and OpenSSL logging\n" "\t-A address\tInterface address to bind to\n" + "\t-T max_token_length\tSet the maximum token length (8-65804)\n" "PSK Options (if supported by underlying (D)TLS library)\n" "\t-h hint\t\tIdentity Hint. Default is CoAP. Zero length is no hint\n" "\t-k key \t\tPre-Shared Key. This argument requires (D)TLS with PSK\n" @@ -772,6 +775,20 @@ get_context(const char *node, const char *port) { return ctx; } +static int +cmdline_read_extended_token_size(char *arg) { + extended_token_size = strtoul(arg, NULL, 0); + if (extended_token_size < COAP_TOKEN_DEFAULT_MAX) { + coap_log(LOG_ERR, "Extended Token Length must be 8 or greater\n"); + return 0; + } + else if (extended_token_size > COAP_TOKEN_EXT_MAX) { + coap_log(LOG_ERR, "Extended Token Length must be 65804 or less\n"); + return 0; + } + return 1; +} + int main(int argc, char **argv) { coap_context_t *ctx; @@ -786,7 +803,7 @@ main(int argc, char **argv) { struct sigaction sa; #endif - while ((opt = getopt(argc, argv, "A:c:C:g:G:h:k:n:R:p:v:")) != -1) { + while ((opt = getopt(argc, argv, "A:c:C:g:G:h:k:n:p:R:T:v:")) != -1) { switch (opt) { case 'A' : strncpy(addr_str, optarg, NI_MAXHOST-1); @@ -822,13 +839,18 @@ main(int argc, char **argv) { case 'n': verify_peer_cert = 0; break; - case 'R' : - root_ca_file = optarg; - break; case 'p' : strncpy(port_str, optarg, NI_MAXSERV-1); port_str[NI_MAXSERV - 1] = '\0'; break; + case 'R' : + root_ca_file = optarg; + break; + case 'T': + if (!cmdline_read_extended_token_size(optarg)) { + exit(1); + } + break; case 'v' : log_level = strtol(optarg, NULL, 10); break; @@ -850,6 +872,8 @@ main(int argc, char **argv) { coap_join_mcast_group_intf(ctx, group, group_if); init_resources(ctx); + if (extended_token_size > COAP_TOKEN_DEFAULT_MAX) + coap_context_set_max_token_size(ctx, extended_token_size); #ifdef _WIN32 signal(SIGINT, handle_sigint); diff --git a/examples/coap-server.c b/examples/coap-server.c index f35cf09aef..db15792740 100644 --- a/examples/coap-server.c +++ b/examples/coap-server.c @@ -116,6 +116,7 @@ static int support_dynamic = 0; static uint32_t block_mode = COAP_BLOCK_USE_LIBCOAP; static int echo_back = 0; static uint32_t csm_max_message_size = 0; +static size_t extended_token_size = COAP_TOKEN_DEFAULT_MAX; static coap_dtls_pki_t * setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *sni); @@ -2156,7 +2157,8 @@ usage( const char *program, const char *version) { fprintf(stderr, "\n" "Usage: %s [-d max] [-e] [-g group] [-G group_if] [-l loss] [-p port]\n" "\t\t[-r] [-v num] [-A address] [-L value] [-N]\n" - "\t\t[-P scheme://address[:port],[name1[,name2..]]] [-X size]\n" + "\t\t[-P scheme://address[:port],[name1[,name2..]]]\n" + "\t\t[-T max_token_size] [-X size]\n" "\t\t[[-h hint] [-i match_identity_file] [-k key]\n" "\t\t[-s match_psk_sni_file] [-u user]]\n" "\t\t[[-c certfile] [-j keyfile] [-m] [-n] [-C cafile]\n" @@ -2208,6 +2210,7 @@ usage( const char *program, const char *version) { "\t \t\tdefined before the leading , (comma) of the first name,\n" "\t \t\tthen the ongoing connection will be a direct connection.\n" "\t \t\tScheme is one of coap, coaps, coap+tcp and coaps+tcp\n" + "\t-T max_token_length\tSet the maximum token length (8-65804)\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" "PSK Options (if supported by underlying (D)TLS library)\n" @@ -2604,6 +2607,20 @@ static int cmdline_read_pki_sni_check(char *arg) { return valid_pki_snis.count > 0; } +static int +cmdline_read_extended_token_size(char *arg) { + extended_token_size = strtoul(arg, NULL, 0); + if (extended_token_size < COAP_TOKEN_DEFAULT_MAX) { + coap_log(LOG_ERR, "Extended Token Length must be 8 or greater\n"); + return 0; + } + else if (extended_token_size > COAP_TOKEN_EXT_MAX) { + coap_log(LOG_ERR, "Extended Token Length must be 65804 or less\n"); + return 0; + } + return 1; +} + int main(int argc, char **argv) { coap_context_t *ctx; @@ -2633,7 +2650,7 @@ main(int argc, char **argv) { clock_offset = time(NULL); - while ((opt = getopt(argc, argv, "c:d:eg:G:h:i:j:J:k:l:mnp:rs:u:v:A:C:L:M:NP:R:S:X:")) != -1) { + while ((opt = getopt(argc, argv, "c:d:eg:G:h:i:j:J:k:l:mnp:rs:u:v:A:C:L:M:NP:R:S:T:X:")) != -1) { switch (opt) { case 'A' : strncpy(addr_str, optarg, NI_MAXHOST-1); @@ -2733,13 +2750,16 @@ main(int argc, char **argv) { break; case 's': if (!cmdline_read_psk_sni_check(optarg)) { - usage(argv[0], LIBCOAP_PACKAGE_VERSION); exit(1); } break; case 'S': if (!cmdline_read_pki_sni_check(optarg)) { - usage(argv[0], LIBCOAP_PACKAGE_VERSION); + exit(1); + } + break; + case 'T': + if (!cmdline_read_extended_token_size(optarg)) { exit(1); } break; @@ -2791,6 +2811,8 @@ main(int argc, char **argv) { 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); + if (extended_token_size > COAP_TOKEN_DEFAULT_MAX) + coap_context_set_max_token_size(ctx, extended_token_size); /* Define the options to ignore when setting up cache-keys */ coap_cache_ignore_options(ctx, cache_ignore_options, diff --git a/include/coap3/coap_block_internal.h b/include/coap3/coap_block_internal.h index fe5c7d3a71..9fc21da10d 100644 --- a/include/coap3/coap_block_internal.h +++ b/include/coap3/coap_block_internal.h @@ -148,8 +148,6 @@ struct coap_lg_srcv_t { coap_resource_t *resource; /**< associated resource */ coap_str_const_t *uri_path; /** set to uri_path if unknown resource */ coap_rblock_t rec_blocks; /** < list of received blocks */ - uint8_t last_token[8]; /**< last used token */ - size_t last_token_length; /**< length of token */ coap_mid_t last_mid; /**< Last received mid for this set of packets */ coap_tick_t last_used; /**< Last time data sent or 0 */ uint16_t block_option; /**< Block option in use */ diff --git a/include/coap3/coap_net_internal.h b/include/coap3/coap_net_internal.h index c43917053e..06de2e4895 100644 --- a/include/coap3/coap_net_internal.h +++ b/include/coap3/coap_net_internal.h @@ -142,6 +142,7 @@ struct coap_context_t { when creating a cache-key */ #endif /* COAP_SERVER_SUPPORT */ void *app; /**< application-specific data */ + uint32_t max_token_size; /**< Largest token size supported RFC8974 */ #ifdef COAP_EPOLL_SUPPORT int epfd; /**< External FD for epoll */ int eptimerfd; /**< Internal FD for timeout */ @@ -265,12 +266,10 @@ coap_wait_ack( coap_context_t *context, coap_session_t *session, * @param context The context in use. * @param session Session of the messages to remove. * @param token Message token. - * @param token_length Actual length of @p token. */ void coap_cancel_all_messages(coap_context_t *context, coap_session_t *session, - const uint8_t *token, - size_t token_length); + coap_bin_const_t *token); /** * Cancels all outstanding messages for session @p session. diff --git a/include/coap3/coap_pdu_internal.h b/include/coap3/coap_pdu_internal.h index 67d20ce6e7..3162681ea0 100644 --- a/include/coap3/coap_pdu_internal.h +++ b/include/coap3/coap_pdu_internal.h @@ -45,6 +45,12 @@ #define COAP_MAX_MESSAGE_SIZE_TCP16 (COAP_MESSAGE_SIZE_OFFSET_TCP32-1) /* 65804 */ #define COAP_MAX_MESSAGE_SIZE_TCP32 (COAP_MESSAGE_SIZE_OFFSET_TCP32+0xFFFFFFFF) +/* Extended Token constants */ +#define COAP_TOKEN_EXT_1B_TKL 13 +#define COAP_TOKEN_EXT_2B_TKL 14 +#define COAP_TOKEN_EXT_1B_BIAS 13 +#define COAP_TOKEN_EXT_2B_BIAS 269 /* 13 + 256 */ + #ifndef COAP_DEBUG_BUF_SIZE #if defined(WITH_CONTIKI) || defined(WITH_LWIP) #define COAP_DEBUG_BUF_SIZE 128 @@ -112,16 +118,19 @@ struct coap_pdu_t { uint8_t max_hdr_size; /**< space reserved for protocol-specific header */ uint8_t hdr_size; /**< actual size used for protocol-specific header (0 until header is encoded) */ - uint8_t token_length; /**< length of Token */ uint8_t crit_opt; /**< Set if unknown critical option for proxy */ uint16_t max_opt; /**< highest option number in PDU */ + uint32_t token_length; /**< length of Token space (includes leading + extended bytes */ + coap_bin_const_t actual_token; /**< Actual token in pdu */ size_t alloc_size; /**< allocated storage for token, options and payload */ size_t used_size; /**< used bytes of storage for token, options and payload */ size_t max_size; /**< maximum size for token, options and payload, or zero for variable size pdu */ - uint8_t *token; /**< first byte of token, if any, or options */ + uint8_t *token; /**< first byte of token (or extended length bytes + prefix), if any, or options */ uint8_t *data; /**< first byte of payload, if any */ #ifdef WITH_LWIP struct pbuf *pbuf; /**< lwIP PBUF. The package data will always reside diff --git a/include/coap3/coap_session_internal.h b/include/coap3/coap_session_internal.h index 476239636b..5cb6250a74 100644 --- a/include/coap3/coap_session_internal.h +++ b/include/coap3/coap_session_internal.h @@ -42,6 +42,15 @@ struct coap_addr_hash_t { coap_proto_t proto; /**< CoAP protocol */ }; +/** + * coap_ext_token_check_t values + */ +typedef enum coap_ext_token_check_t { + COAP_EXT_T_NOT_CHECKED = 0, /**< Not checked */ + COAP_EXT_T_CHECKED, /**< Token size valid */ + COAP_EXT_T_CHECKING, /**< Token size check request sent */ +} coap_ext_token_check_t; + /** * Abstraction of virtual session that can be attached to coap_context_t * (client) or coap_endpoint_t (server). @@ -139,6 +148,7 @@ struct coap_session_t { unsigned int dtls_timeout_count; /**< dtls setup retry counter */ int dtls_event; /**< Tracking any (D)TLS events on this sesison */ + uint32_t tx_rtag; /**< Next Request-Tag number to use */ uint8_t csm_bert_rem_support; /**< CSM TCP BERT blocks supported (remote) */ uint8_t csm_bert_loc_support; /**< CSM TCP BERT blocks supported (local) */ uint8_t block_mode; /**< Zero or more COAP_BLOCK_ or'd options */ @@ -147,7 +157,11 @@ struct coap_session_t { uint8_t delay_recursive; /**< Set if in coap_client_delay_first() */ uint8_t no_observe_cancel; /**< Set if do not cancel observe on session close */ - uint32_t tx_rtag; /**< Next Request-Tag number to use */ + volatile uint8_t max_token_checked; /**< Check for max token size + coap_ext_token_check_t */ + uint16_t max_token_mid; /**< mid used for checking ext token + support */ + uint32_t max_token_size; /**< Largest token size supported RFC8974 */ uint64_t tx_token; /**< Next token number to use */ coap_bin_const_t *last_token; /** last token used to make a request */ coap_bin_const_t *echo; /**< Echo value to send with next request */ diff --git a/include/coap3/coap_subscribe_internal.h b/include/coap3/coap_subscribe_internal.h index 9fe548064f..860dd5c934 100644 --- a/include/coap3/coap_subscribe_internal.h +++ b/include/coap3/coap_subscribe_internal.h @@ -79,7 +79,7 @@ void coap_subscription_init(coap_subscription_t *); void coap_handle_failed_notify(coap_context_t *context, coap_session_t *session, - const coap_binary_t *token); + const coap_bin_const_t *token); /** * Checks all known resources to see if they are dirty and then notifies @@ -105,7 +105,7 @@ void coap_check_notify(coap_context_t *context); */ coap_subscription_t *coap_add_observer(coap_resource_t *resource, coap_session_t *session, - const coap_binary_t *token, + const coap_bin_const_t *token, const coap_pdu_t *pdu); /** @@ -119,7 +119,7 @@ coap_subscription_t *coap_add_observer(coap_resource_t *resource, */ coap_subscription_t *coap_find_observer(coap_resource_t *resource, coap_session_t *session, - const coap_binary_t *token); + const coap_bin_const_t *token); /** * Flags that data is ready to be sent to observers. @@ -131,7 +131,7 @@ coap_subscription_t *coap_find_observer(coap_resource_t *resource, */ void coap_touch_observer(coap_context_t *context, coap_session_t *session, - const coap_binary_t *token); + const coap_bin_const_t *token); /** * Removes any subscription for @p observer from @p resource and releases the @@ -146,7 +146,7 @@ void coap_touch_observer(coap_context_t *context, */ int coap_delete_observer(coap_resource_t *resource, coap_session_t *session, - const coap_binary_t *token); + const coap_bin_const_t *token); /** * Removes any subscription for @p session and releases the allocated storage. diff --git a/include/coap3/net.h b/include/coap3/net.h index d4a3f07f6e..874677d1cc 100644 --- a/include/coap3/net.h +++ b/include/coap3/net.h @@ -233,6 +233,16 @@ coap_context_set_pki_root_cas(coap_context_t *context, */ void coap_context_set_keepalive(coap_context_t *context, unsigned int seconds); +/** + * Set the maximum token size (RFC8974). + * + * @param context The coap_context_t object. + * @param max_token_size The maximum token size. A value between 8 and 65804 + * inclusive. + */ +void coap_context_set_max_token_size(coap_context_t *context, + size_t max_token_size); + /** * Get the libcoap internal file descriptor for using in an application's * select() or returned as an event in an application's epoll_wait() call. diff --git a/include/coap3/pdu.h b/include/coap3/pdu.h index 29331b3358..dc2b258fbe 100644 --- a/include/coap3/pdu.h +++ b/include/coap3/pdu.h @@ -52,6 +52,10 @@ /** well-known resources URI */ #define COAP_DEFAULT_URI_WELLKNOWN ".well-known/core" +/* Extended Token constants */ +#define COAP_TOKEN_DEFAULT_MAX 8 +#define COAP_TOKEN_EXT_MAX 65804 /* 13 + 256 + 65535 */ + /* CoAP message types */ /** @@ -182,11 +186,15 @@ typedef enum coap_pdu_signaling_proto_t { /* Applies to COAP_SIGNALING_CSM */ #define COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE 2 #define COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER 4 +#define COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH 6 + /* Applies to COAP_SIGNALING_PING / COAP_SIGNALING_PONG */ #define COAP_SIGNALING_OPTION_CUSTODY 2 + /* Applies to COAP_SIGNALING_RELEASE */ #define COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS 2 #define COAP_SIGNALING_OPTION_HOLD_OFF 4 + /* Applies to COAP_SIGNALING_ABORT */ #define COAP_SIGNALING_OPTION_BAD_CSM_OPTION 2 diff --git a/libcoap-3.map b/libcoap-3.map index b899285d16..d38546f8ac 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -50,6 +50,7 @@ global: coap_context_set_keepalive; coap_context_set_max_handshake_sessions; coap_context_set_max_idle_sessions; + coap_context_set_max_token_size; coap_context_set_pki; coap_context_set_pki_root_cas; coap_context_set_psk; diff --git a/libcoap-3.sym b/libcoap-3.sym index 322a4f1e16..4d080e20bd 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -48,6 +48,7 @@ coap_context_set_csm_timeout coap_context_set_keepalive coap_context_set_max_handshake_sessions coap_context_set_max_idle_sessions +coap_context_set_max_token_size coap_context_set_pki coap_context_set_pki_root_cas coap_context_set_psk diff --git a/man/Makefile.am b/man/Makefile.am index 411f81ee60..eef891d00c 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -101,6 +101,7 @@ install-man: install-man3 install-man5 install-man7 @echo ".so man3/coap_context.3" > coap_context_get_session_timeout.3 @echo ".so man3/coap_context.3" > coap_context_set_csm_timeout.3 @echo ".so man3/coap_context.3" > coap_context_get_csm_timeout.3 + @echo ".so man3/coap_context.3" > coap_context_set_max_token_size.3 @echo ".so man3/coap_logging.3" > coap_show_pdu.3 @echo ".so man3/coap_logging.3" > coap_endpoint_str.3 @echo ".so man3/coap_logging.3" > coap_session_str.3 diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index a031b854ed..94c07c5e9e 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -161,7 +161,7 @@ OPTIONS - General Scheme is one of coap, coaps, coap+tcp and coaps+tcp. *-T* token:: - Define the initial starting 'token' for the request. + Define the initial starting 'token' for the request (up to 24 characters). *-U* :: Never include Uri-Host or Uri-Port options. diff --git a/man/coap-rd.txt.in b/man/coap-rd.txt.in index 14dc151554..a5cbd04b54 100644 --- a/man/coap-rd.txt.in +++ b/man/coap-rd.txt.in @@ -20,6 +20,7 @@ coap-rd-notls SYNOPSIS -------- *coap-rd* [*-g* group] [*-G* group_if] [*-p* port] [*-v* num] [*-A* address] + [*-T* max_token_size] [[*-h* hint] [*-k* key]] [[*-c* certfile] [*-n*] [*-C* cafile] [*-R* trusted_casfile]] @@ -56,6 +57,9 @@ OPTIONS *-A* address:: The local address of the interface which the server has to listen on. +*-T* max_token_size:: + Set the maximum token length (8-65804). + OPTIONS - PSK ------------- (If supported by underlying (D)TLS library) diff --git a/man/coap-server.txt.in b/man/coap-server.txt.in index 27726690b3..0c259bd4c0 100644 --- a/man/coap-server.txt.in +++ b/man/coap-server.txt.in @@ -21,7 +21,8 @@ SYNOPSIS -------- *coap-server* [*-d* max] [*-e*] [*-g* group] [*-G* group_if] [*-l* loss] [*-p* port] [-r] [*-v* num] [*-A* address] [*-L* value] [*-N*] - [*-P* scheme://addr[:port],[name1[,name2..]]] [*-X* size] + [*-P* scheme://addr[:port],[name1[,name2..]]] + [*-T* max_token_size] [*-X* size] [[*-h* hint] [*-i* match_identity_file] [*-k* key] [*-s* match_psk_sni_file] [*-u* user]] [[*-c* certfile] [*-j* keyfile] [*-n*] [*-C* cafile] @@ -105,6 +106,9 @@ OPTIONS - General first name, then the ongoing connection will be a direct connection. Scheme is one of coap, coaps, coap+tcp and coaps+tcp. +*-T* max_token_size:: + Set the maximum token length (8-65804). + *-X* size:: Maximum message size to use for TCP based connections (default is 8388864). Maximum value of 2^32 -1. diff --git a/man/coap.txt.in b/man/coap.txt.in index 40e7891b67..d3d0138622 100644 --- a/man/coap.txt.in +++ b/man/coap.txt.in @@ -72,6 +72,8 @@ See "https://tools.ietf.org/html/rfc8768[RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option]" +"https://tools.ietf.org/html/rfc8974[RFC8974: Extended Tokens and Stateless Clients in the Constrained Application Protocol (CoAP)]" + "https://tools.ietf.org/html/rfc9175[RFC9175: CoAP: Echo, Request-Tag, and Token Processing]" for further information. diff --git a/man/coap_context.txt.in b/man/coap_context.txt.in index 53579d292f..40edcafcae 100644 --- a/man/coap_context.txt.in +++ b/man/coap_context.txt.in @@ -20,7 +20,8 @@ coap_context_get_max_handshake_sessions, coap_context_set_session_timeout, coap_context_get_session_timeout, coap_context_set_csm_timeout, -coap_context_get_csm_timeout +coap_context_get_csm_timeout, +coap_context_set_max_token_size - Work with CoAP contexts SYNOPSIS @@ -54,6 +55,9 @@ unsigned int _csm_timeout_);* *unsigned int coap_context_get_csm_timeout(const coap_context_t *_context_);* +*void coap_context_set_max_token_size(coap_context_t *_context_, +size_t _max_token_size_);* + For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls* @@ -70,48 +74,79 @@ Resources, Endpoints and Sessions are associated with this context object. There can be more than one coap_context_t object per application, it is up to the application to manage each one accordingly. +FUNCTIONS +--------- + +*Function: coap_new_context()* + The *coap_new_context*() function creates a new Context that is then used to keep all the CoAP Resources, Endpoints and Sessions information. The optional _listen_addr_ parameter, if set for a CoAP server, creates an Endpoint that is added to the _context_ that is listening for un-encrypted traffic on the IP address and port number defined by _listen_addr_. +*Function: coap_free_context()* + The *coap_free_context*() function must be used to release the CoAP stack _context_. It clears all entries from the receive queue and send queue and deletes the Resources that have been registered with _context_, and frees the attached Sessions and Endpoints. +*Function: coap_context_set_max_idle_sessions()* + The *coap_context_set_max_idle_sessions*() function sets the maximum number of idle server sessions to _max_idle_sessions_ for _context_. If this number is exceeded, the least recently used server session is completely removed. 0 (the initial default) means that the number of idle sessions is not monitored. +*Function: coap_context_get_max_idle_sessions()* + The *coap_context_get_max_idle_sessions*() function returns the maximum number of idle server sessions for _context_. +*Function: coap_context_set_max_handshake_sessions()* + The *coap_context_set_max_handshake_sessions*() function sets the maximum number of outstanding server sessions in (D)TLS handshake to _max_handshake_sessions_ for _context_. If this number is exceeded, the least recently used server session in handshake is completely removed. 0 (the default) means that the number of handshakes is not monitored. +*Function: coap_context_get_max_handshake_sessions()* + The *coap_context_get_max_handshake_sessions*() function returns the maximum number of outstanding server sessions in (D)TLS handshake for _context_. +*Function: coap_context_set_session_timeout()* + The *coap_context_set_session_timeout*() function sets the number of seconds of inactivity to _session_timeout_ for _context_ before an idle server session is removed. 0 (the default) means wait for the default of 300 seconds. +*Function: coap_context_get_session_timeout()* + The *coap_context_get_session_timeout*() function returns the seconds to wait before timing out an idle server session for _context_. +*Function: coap_context_set_csm_timeout()* + The *coap_context_set_csm_timeout*() function sets the number of seconds to wait for a (TCP) CSM negotiation response from the peer to _csm_timeout_ for _context_. 0 (the default) means wait forever. +*Function: coap_context_get_csm_timeout()* + The *coap_context_get_csm_timeout*() function returns the seconds to wait for a (TCP) CSM negotiation response from the peer for _context_, +*Function: coap_context_set_max_token_size()* + +The *coap_context_set_max_token_size*() function sets the _max_token_size_ +for _context_. _max_token_size_ must be greater than 8 to indicate +support for https://tools.ietf.org/html/rfc8974[RFC8974] up to _max_token_size_ +bytes, else 8 to disable https://tools.ietf.org/html/rfc8974[RFC8974] +(if previously set). + RETURN VALUES ------------- *coap_new_context*() function returns a newly created context or @@ -135,8 +170,13 @@ SEE ALSO FURTHER INFORMATION ------------------- -See "RFC7252: The Constrained Application Protocol (CoAP)" for further -information. +See + +"https://tools.ietf.org/html/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]" + +"https://tools.ietf.org/html/rfc8974[RFC8974: Extended Tokens and Stateless Clients in the Constrained Application Protocol (CoAP)]" + +for further information. BUGS ---- diff --git a/man/coap_pdu_setup.txt.in b/man/coap_pdu_setup.txt.in index 33aa2e2675..0b6ccd4677 100644 --- a/man/coap_pdu_setup.txt.in +++ b/man/coap_pdu_setup.txt.in @@ -247,6 +247,9 @@ PDU TOKEN The *coap_session_init_token*() function is used to initialize the starting _token_ of _length_ for the _session_. +*NOTE:* this only takes the first 8 bytes of the token if extended tokens +are being used (https://tools.ietf.org/html/rfc8974[RFC8974]). + *Function: coap_session_new_token()* The *coap_session_new_token*() function is used to obtain the next available @@ -255,13 +258,18 @@ for doing an observe cancellation that was used for doing the observe registration. Otherwise tokens should be unique for each request/response so that they can be correctly matched. +*NOTE:* Only a token of up to 8 bytes is returned. + *Function: coap_add_token()* The *coap_add_token*() function adds in the specified token's _data_ of length -_length_ to the PDU _pdu_. The maximum length of the token is 8 bytes. +_length_ to the PDU _pdu_. The maximum (but impractical due to PDU sizes) +length of the token is 65804 bytes. If anything more than 8, the remote peer +needs to support extended tokens for this to work. Adding the token must be done before any options or data are added. This function must only be called once per _pdu_, and must not be called -in the appropriate request handler. +in the appropriate request handler as the token has already been added into +the skeletal response PDU. If a token is not added, then the token in the PDU is zero length, but still a valid token which is used for matching. The exception is an empty ACK packet. @@ -703,6 +711,8 @@ See "https://tools.ietf.org/html/rfc7959[RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)]" +"https://tools.ietf.org/html/rfc8974[RFC8974: Extended Tokens and Stateless Clients in the Constrained Application Protocol (CoAP)]" + "https://tools.ietf.org/html/rfc9175[RFC9175: CoAP: Echo, Request-Tag, and Token Processing]" for further information. diff --git a/src/block.c b/src/block.c index 377563468f..9504a76145 100644 --- a/src/block.c +++ b/src/block.c @@ -395,8 +395,7 @@ coap_cancel_observe(coap_session_t *session, coap_binary_t *token, LL_FOREACH(session->lg_crcv, cq) { if (cq->observe_set) { if ((!token && !cq->app_token->length) || (token && - full_match(token->s, token->length, cq->app_token->s, - cq->app_token->length))) { + coap_binary_equal(token, cq->app_token))) { uint8_t buf[8]; coap_mid_t mid; size_t size; @@ -490,9 +489,7 @@ coap_add_data_large_internal(coap_session_t *session, /* See if this token is already in use for large bodies (unlikely) */ LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { - if (full_match(pdu->token, pdu->token_length, - lg_xmit->b.b1.app_token->s, - lg_xmit->b.b1.app_token->length)) { + if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { /* Unfortunately need to free this off as potential size change */ LL_DELETE(session->lg_xmit, lg_xmit); coap_block_delete_lg_xmit(session, lg_xmit); @@ -530,7 +527,7 @@ coap_add_data_large_internal(coap_session_t *session, /* There may be a response with Echo option */ avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); /* May need token of length 8, so account for this */ - avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0; + avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0; blk_size = coap_flsll((long long)avail) - 4 - 1; if (blk_size > 6) blk_size = 6; @@ -595,10 +592,11 @@ coap_add_data_large_internal(coap_session_t *session, coap_ticks(&lg_xmit->last_sent); if (COAP_PDU_IS_REQUEST(pdu)) { /* Need to keep original token for updating response PDUs */ - lg_xmit->b.b1.app_token = coap_new_binary(pdu->token_length); + lg_xmit->b.b1.app_token = coap_new_binary(pdu->actual_token.length); if (!lg_xmit->b.b1.app_token) goto fail; - memcpy(lg_xmit->b.b1.app_token->s, pdu->token, pdu->token_length); + memcpy(lg_xmit->b.b1.app_token->s, pdu->actual_token.s, + pdu->actual_token.length); /* * Need to set up new token for use during transmits */ @@ -691,7 +689,7 @@ coap_add_data_large_internal(coap_session_t *session, /* There may be a response with Echo option */ avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); /* May need token of length 8, so account for this */ - avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0; + avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0; if (avail < (ssize_t)chunk) { /* chunk size change down */ if (avail < 16) { @@ -1052,12 +1050,12 @@ coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) { lg_crcv->pdu.max_size = lg_crcv->pdu.used_size + 9; /* Need to keep original token for updating response PDUs */ - lg_crcv->app_token = coap_new_binary(pdu->token_length); + lg_crcv->app_token = coap_new_binary(pdu->actual_token.length); if (!lg_crcv->app_token) { coap_block_delete_lg_crcv(session, lg_crcv); return NULL; } - memcpy(lg_crcv->app_token->s, pdu->token, pdu->token_length); + memcpy(lg_crcv->app_token->s, pdu->actual_token.s, pdu->actual_token.length); /* Need to set up a base token for actual communications if retries needed */ lg_crcv->retry_counter = 1; @@ -1294,8 +1292,8 @@ coap_handle_request_send_block(coap_session_t *session, memset(&drop_options, 0, sizeof(coap_opt_filter_t)); coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); - out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->token_length, - pdu->token, &drop_options); + out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->actual_token.length, + pdu->actual_token.s, &drop_options); if (!out_pdu) { goto internal_issue; } @@ -1574,8 +1572,6 @@ coap_handle_request_put_block(coap_context_t *context, } p->last_mid = pdu->mid; p->last_type = pdu->type; - memcpy(p->last_token, pdu->token, pdu->token_length); - p->last_token_length = pdu->token_length; if ((session->block_mode & COAP_BLOCK_SINGLE_BODY) || block.bert) { size_t chunk = (size_t)1 << (block.szx + 4); int update_data = 0; @@ -1820,8 +1816,9 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd) { coap_lg_xmit_t *p; coap_lg_xmit_t *q; - uint64_t token_match = STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->token, - rcvd->token_length)); + uint64_t token_match = + STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->actual_token.s, + rcvd->actual_token.length)); coap_lg_crcv_t *lg_crcv = NULL; LL_FOREACH_SAFE(session->lg_xmit, p, q) { @@ -2034,8 +2031,9 @@ coap_handle_response_get_block(coap_context_t *context, int have_block = 0; uint16_t block_opt = 0; size_t offset; - uint64_t token_match = STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->token, - rcvd->token_length)); + uint64_t token_match = + STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->actual_token.s, + rcvd->actual_token.length)); memset(&block, 0, sizeof(block)); LL_FOREACH(session->lg_crcv, p) { @@ -2044,8 +2042,7 @@ coap_handle_response_get_block(coap_context_t *context, coap_opt_iterator_t opt_iter; if (token_match != STATE_TOKEN_BASE(p->state_token) && - !full_match(rcvd->token, rcvd->token_length, - p->app_token->s, p->app_token->length)) { + !coap_binary_equal(&rcvd->actual_token, p->app_token)) { /* try out the next one */ continue; } @@ -2175,11 +2172,11 @@ coap_handle_response_get_block(coap_context_t *context, p->observe_set = 1; /* Need to keep observe response token for later cancellation */ coap_delete_binary(p->obs_token); - p->obs_token = coap_new_binary(rcvd->token_length); + p->obs_token = coap_new_binary(rcvd->actual_token.length); if (!p->obs_token) { goto fail_resp; } - memcpy(p->obs_token->s, rcvd->token, rcvd->token_length); + memcpy(p->obs_token->s, rcvd->actual_token.s, rcvd->actual_token.length); } else { p->observe_set = 0; @@ -2250,6 +2247,8 @@ coap_handle_response_get_block(coap_context_t *context, coap_update_token(rcvd, p->app_token->length, p->app_token->s); rcvd->body_offset = saved_offset; rcvd->body_total = size2; + coap_log(LOG_DEBUG, "Client app version of updated PDU\n"); + coap_show_pdu(LOG_DEBUG, rcvd); goto call_app_handler; } /* need to put back original token into rcvd */ @@ -2323,14 +2322,20 @@ coap_handle_response_get_block(coap_context_t *context, p->observe_set = 1; /* Need to keep observe response token for later cancellation */ coap_delete_binary(p->obs_token); - p->obs_token = coap_new_binary(rcvd->token_length); + p->obs_token = coap_new_binary(rcvd->actual_token.length); if (!p->obs_token) { goto fail_resp; } - memcpy(p->obs_token->s, rcvd->token, rcvd->token_length); + memcpy(p->obs_token->s, rcvd->actual_token.s, rcvd->actual_token.length); } else { p->observe_set = 0; + if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { + /* need to put back original token into rcvd */ + coap_update_token(rcvd, p->app_token->length, p->app_token->s); + coap_log(LOG_DEBUG, "PDU presented to app.\n"); + coap_show_pdu(LOG_DEBUG, rcvd); + } /* Expire this entry */ goto expire_lg_crcv; } diff --git a/src/coap_async.c b/src/coap_async.c index 5c7cce4d18..bbbe33a6db 100644 --- a/src/coap_async.c +++ b/src/coap_async.c @@ -47,8 +47,8 @@ coap_register_async(coap_session_t *session, SEARCH_PAIR(session->context->async_state, s, session, session, - pdu->token_length, request->token_length, - pdu->token, request->token); + pdu->actual_token.length, request->actual_token.length, + pdu->actual_token.s, request->actual_token.s); if (s != NULL) { size_t i; @@ -56,7 +56,8 @@ coap_register_async(coap_session_t *session, size_t outbuflen; outbuf[0] = '\000'; - for (i = 0; i < request->token_length; i++) { + for (i = 0; i < request->actual_token.length; i++) { + /* Output maybe truncated */ outbuflen = strlen(outbuf); snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "%02x", request->token[i]); @@ -73,11 +74,12 @@ coap_register_async(coap_session_t *session, return NULL; } + LL_PREPEND(session->context->async_state, s); memset(s, 0, sizeof(coap_async_t)); /* Note that this generates a new MID */ - s->pdu = coap_pdu_duplicate(request, session, request->token_length, - request->token, NULL); + s->pdu = coap_pdu_duplicate(request, session, request->actual_token.length, + request->actual_token.s, NULL); if (s->pdu == NULL) { coap_free_async(session, s); coap_log(LOG_CRIT, "coap_register_async: insufficient memory\n"); @@ -92,8 +94,6 @@ coap_register_async(coap_session_t *session, coap_async_set_delay(s, delay); - LL_PREPEND(session->context->async_state, s); - return s; } @@ -138,10 +138,11 @@ coap_async_set_delay(coap_async_t *async, coap_tick_t delay) { coap_async_t * coap_find_async(coap_session_t *session, coap_bin_const_t token) { coap_async_t *tmp; + SEARCH_PAIR(session->context->async_state, tmp, session, session, - pdu->token_length, token.length, - pdu->token, token.s); + pdu->actual_token.length, token.length, + pdu->actual_token.s, token.s); return tmp; } diff --git a/src/coap_debug.c b/src/coap_debug.c index dda64b1e7f..75ae700bf8 100644 --- a/src/coap_debug.c +++ b/src/coap_debug.c @@ -371,7 +371,8 @@ msg_option_string(uint8_t code, uint16_t option_type) { static struct option_desc_t options_csm[] = { { COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size" }, - { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-Wise-Transfer" } + { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-Wise-Transfer" }, + { COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH, "Extended-Token-Length" } }; static struct option_desc_t options_pingpong[] = { @@ -532,7 +533,8 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { char outbuf[COAP_DEBUG_BUF_SIZE]; #endif /* ! COAP_CONSTRAINED_STACK */ size_t buf_len = 0; /* takes the number of bytes written to buf */ - int encode = 0, have_options = 0, i; + int encode = 0, have_options = 0; + uint32_t i; coap_opt_iterator_t opt_iter; coap_opt_t *option; int content_format = -1; @@ -554,10 +556,10 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { COAP_DEFAULT_VERSION, msg_type_string(pdu->type), msg_code_string(pdu->code), pdu->mid); - for (i = 0; i < pdu->token_length; i++) { + for (i = 0; i < pdu->actual_token.length; i++) { outbuflen = strlen(outbuf); snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, - "%02x", pdu->token[i]); + "%02x", pdu->actual_token.s[i]); } outbuflen = strlen(outbuf); snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "}"); @@ -577,6 +579,7 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { } if (pdu->code == COAP_SIGNALING_CODE_CSM) switch(opt_iter.number) { + case COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH: case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE: buf_len = snprintf((char *)buf, sizeof(buf), "%u", coap_decode_var_bytes(coap_opt_value(option), diff --git a/src/coap_session.c b/src/coap_session.c index f5949b1a6a..a1907a477a 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -217,6 +217,9 @@ coap_make_session(coap_proto_t proto, coap_session_type_t type, session->last_ping_mid = COAP_INVALID_MID; session->last_ack_mid = COAP_INVALID_MID; session->last_con_mid = COAP_INVALID_MID; + session->max_token_size = context->max_token_size; /* RFC8974 */ + if (session->type != COAP_SESSION_TYPE_CLIENT) + session->max_token_checked = COAP_EXT_T_CHECKED; /* Randomly initialize */ coap_prng((unsigned char *)&session->tx_mid, sizeof(session->tx_mid)); @@ -493,6 +496,12 @@ void coap_session_send_csm(coap_session_t *session) { || coap_add_option_internal(pdu, COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, coap_encode_var_safe(buf, sizeof(buf), 0), buf) == 0 + || (session->max_token_size > COAP_TOKEN_DEFAULT_MAX && + coap_add_option_internal(pdu, + COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH, + coap_encode_var_safe(buf, sizeof(buf), + session->max_token_size), + buf) == 0) || coap_pdu_encode_header(pdu, session->proto) == 0 ) { coap_session_disconnected(session, COAP_NACK_NOT_DELIVERABLE); @@ -535,10 +544,11 @@ void coap_session_connected(coap_session_t *session) { if (session->state != COAP_SESSION_STATE_ESTABLISHED) { coap_log(LOG_DEBUG, "***%s: session connected\n", coap_session_str(session)); - if (session->state == COAP_SESSION_STATE_CSM) + if (session->state == COAP_SESSION_STATE_CSM) { coap_handle_event(session->context, COAP_EVENT_SESSION_CONNECTED, session); - if (session->doing_first) - session->doing_first = 0; + if (session->doing_first) + session->doing_first = 0; + } } session->state = COAP_SESSION_STATE_ESTABLISHED; diff --git a/src/encode.c b/src/encode.c index 8705a78315..cff9f674be 100644 --- a/src/encode.c +++ b/src/encode.c @@ -65,7 +65,7 @@ uint64_t coap_decode_var_bytes8(const uint8_t *buf, size_t len) { unsigned int i; uint64_t n = 0; - for (i = 0; i < len; ++i) + for (i = 0; i < len && i < sizeof(uint64_t); ++i) n = (n << 8) + buf[i]; return n; diff --git a/src/net.c b/src/net.c index a191139da0..77358a2d02 100644 --- a/src/net.c +++ b/src/net.c @@ -411,6 +411,14 @@ void coap_context_set_keepalive(coap_context_t *context, unsigned int seconds) { context->ping_timeout = seconds; } +void +coap_context_set_max_token_size(coap_context_t *context, + size_t max_token_size) { + assert(max_token_size >= COAP_TOKEN_DEFAULT_MAX && + max_token_size <= COAP_TOKEN_EXT_MAX); + context->max_token_size = (uint32_t)max_token_size; +} + void coap_context_set_max_idle_sessions(coap_context_t *context, unsigned int max_idle_sessions) { @@ -582,6 +590,7 @@ coap_new_context( etimer_set(&the_coap_context.retransmit_timer, 0xFFFF); PROCESS_CONTEXT_END(&coap_retransmit_process); #endif /* WITH_CONTIKI */ + c->max_token_size = COAP_TOKEN_DEFAULT_MAX; /* RFC8974 */ return c; @@ -773,6 +782,7 @@ ssize_t coap_session_send_pdu(coap_session_t *session, coap_pdu_t *pdu) { ssize_t bytes_written = -1; assert(pdu->hdr_size > 0); + switch(session->proto) { case COAP_PROTO_UDP: bytes_written = coap_session_send(session, pdu->token - pdu->hdr_size, @@ -1041,11 +1051,45 @@ coap_wait_ack(coap_context_t *context, coap_session_t *session, return node->id; } -COAP_STATIC_INLINE int -token_match(const uint8_t *a, size_t alen, - const uint8_t *b, size_t blen) { - return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0); +#if COAP_CLIENT_SUPPORT +/* + * Sent out a test PDU for Extended Token + */ +static coap_mid_t +coap_send_test_extended_token(coap_session_t *session) { + coap_pdu_t *pdu; + coap_mid_t mid = COAP_INVALID_MID; + size_t i; + coap_binary_t *token; + + coap_log(LOG_DEBUG, "Testing for Extended Token support\n"); + /* https://datatracker.ietf.org/doc/html/rfc8974#section-2.2.2 */ + pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, + coap_new_message_id(session), + coap_session_max_pdu_size(session)); + if (!pdu) + return COAP_INVALID_MID; + + token = coap_new_binary(session->max_token_size); + if (token == NULL) { + coap_delete_pdu(pdu); + return COAP_INVALID_MID; + } + for (i = 0; i < session->max_token_size; i++) { + token->s[i] = (uint8_t)(i + 1); + } + coap_add_token(pdu, session->max_token_size, token->s); + coap_delete_binary(token); + + coap_insert_option(pdu, COAP_OPTION_IF_NONE_MATCH, 0 , NULL); + + session->max_token_checked = COAP_EXT_T_CHECKING; /* Checking out this one */ + if ((mid = coap_send_internal(session, pdu)) == COAP_INVALID_MID) + return COAP_INVALID_MID; + session->max_token_mid = mid; + return mid; } +#endif /* COAP_CLIENT_SUPPORT */ int coap_client_delay_first(coap_session_t *session) @@ -1097,6 +1141,14 @@ coap_client_delay_first(coap_session_t *session) coap_mid_t coap_send(coap_session_t *session, coap_pdu_t *pdu) { coap_mid_t mid = COAP_INVALID_MID; +#if COAP_CLIENT_SUPPORT + coap_lg_crcv_t *lg_crcv = NULL; + coap_opt_iterator_t opt_iter; + coap_block_b_t block; + int observe_action = -1; + int have_block1 = 0; + coap_opt_t *opt; +#endif /* COAP_CLIENT_SUPPORT */ assert(pdu); @@ -1106,13 +1158,54 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { coap_delete_pdu(pdu); return COAP_INVALID_MID; } + /* + * If this is not the first client request and are waiting for a response + * to the first client request, then delay sending out this next request + * untill all is properly established. + */ + if (!coap_client_delay_first(session)) + return COAP_INVALID_MID; + #if COAP_CLIENT_SUPPORT - coap_lg_crcv_t *lg_crcv = NULL; - coap_opt_iterator_t opt_iter; - coap_block_b_t block; - int observe_action = -1; - int have_block1 = 0; - coap_opt_t *opt; + assert(pdu); + + /* Indicate support for Extended Tokens if appropriate */ + if (session->max_token_checked == COAP_EXT_T_NOT_CHECKED && + session->max_token_size > COAP_TOKEN_DEFAULT_MAX && + session->type == COAP_SESSION_TYPE_CLIENT && + COAP_PDU_IS_REQUEST(pdu)) { + if (COAP_PROTO_NOT_RELIABLE(session->proto)) { + /* + * When the pass / fail response for Extended Token is received, this PDU + * will get transmitted. + */ + if (coap_send_test_extended_token(session) == COAP_INVALID_MID) { + coap_delete_pdu(pdu); + return COAP_INVALID_MID; + } + } + /* + * For reliable protocols, this will get cleared after CSM exchanged + * in coap_session_connected() + */ + session->doing_first = 1; + } + + if (coap_client_delay_first(session) == 0) { + return COAP_INVALID_MID; + } + + /* + * Check validity of token length + */ + if (COAP_PDU_IS_REQUEST(pdu) && + pdu->actual_token.length > session->max_token_size) { + coap_log(LOG_WARNING, + "coap_send: PDU dropped as token too long (%ld > %d)\n", + pdu->actual_token.length, session->max_token_size); + coap_delete_pdu(pdu); + return COAP_INVALID_MID; + } if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { return coap_send_internal(session, pdu); @@ -1131,14 +1224,13 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { have_block1 = 1; if (observe_action != COAP_OBSERVE_CANCEL) { /* Warn about re-use of tokens */ - coap_bin_const_t token = coap_pdu_get_token(pdu); - if (session->last_token && - coap_binary_equal(&token, session->last_token)) { + coap_binary_equal(&pdu->actual_token, session->last_token)) { coap_log(LOG_DEBUG, "Token reused - see https://www.rfc-editor.org/rfc/rfc9175.html#section-4.2\n"); } coap_delete_bin_const(session->last_token); - session->last_token = coap_new_bin_const(token.s, token.length); + session->last_token = coap_new_bin_const(pdu->actual_token.s, + pdu->actual_token.length); } } else { memset(&block, 0, sizeof(block)); @@ -1154,14 +1246,13 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { COAP_PDU_IS_REQUEST(pdu) && pdu->code != COAP_REQUEST_CODE_DELETE)) { coap_lg_xmit_t *lg_xmit = NULL; - if (!session->lg_xmit) { + if (!session->lg_xmit && have_block1) { coap_log(LOG_DEBUG, "PDU presented by app\n"); coap_show_pdu(LOG_DEBUG, pdu); } /* See if this token is already in use for large body responses */ LL_FOREACH(session->lg_crcv, lg_crcv) { - if (token_match(pdu->token, pdu->token_length, - lg_crcv->app_token->s, lg_crcv->app_token->length)) { + if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) { if (observe_action == COAP_OBSERVE_CANCEL) { uint8_t buf[8]; @@ -1189,9 +1280,7 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { LL_FOREACH(session->lg_xmit, lg_xmit) { if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu) && lg_xmit->b.b1.app_token && - token_match(pdu->token, pdu->token_length, - lg_xmit->b.b1.app_token->s, - lg_xmit->b.b1.app_token->length)) { + coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { break; } } @@ -1489,12 +1578,7 @@ coap_retransmit(coap_context_t *context, coap_queue_t *node) { /* Check if subscriptions exist that should be canceled after COAP_OBS_MAX_FAIL */ if (COAP_RESPONSE_CLASS(node->pdu->code) >= 2) { - coap_binary_t token = { 0, NULL }; - - token.length = node->pdu->token_length; - token.s = node->pdu->token; - - coap_handle_failed_notify(context, node->session, &token); + coap_handle_failed_notify(context, node->session, &node->pdu->actual_token); } #endif /* COAP_SERVER_SUPPORT */ if (node->session->con_active) { @@ -1717,14 +1801,17 @@ coap_read_session(coap_context_t *ctx, coap_session_t *session, coap_tick_t now) } else if (session->partial_read > 0) { size_t hdr_size = coap_pdu_parse_header_size(session->proto, session->read_header); - size_t len = hdr_size - session->partial_read; + size_t tkl = session->read_header[0] & 0x0f; + size_t tok_ext_bytes = tkl == COAP_TOKEN_EXT_1B_TKL ? 1 : + tkl == COAP_TOKEN_EXT_2B_TKL ? 2 : 0; + size_t len = hdr_size + tok_ext_bytes - session->partial_read; size_t n = min(len, (size_t)bytes_read); memcpy(session->read_header + session->partial_read, p, n); p += n; bytes_read -= n; if (n == len) { size_t size = coap_pdu_parse_size(session->proto, session->read_header, - hdr_size); + hdr_size + tok_ext_bytes); if (size > COAP_DEFAULT_MAX_PDU_RX_SIZE) { coap_log(LOG_WARNING, "** %s: incoming PDU length too large (%zu > %lu)\n", @@ -1746,8 +1833,8 @@ coap_read_session(coap_context_t *ctx, coap_session_t *session, coap_tick_t now) } session->partial_pdu->hdr_size = (uint8_t)hdr_size; session->partial_pdu->used_size = size; - memcpy(session->partial_pdu->token - hdr_size, session->read_header, hdr_size); - session->partial_read = hdr_size; + memcpy(session->partial_pdu->token - hdr_size, session->read_header, hdr_size + tok_ext_bytes); + session->partial_read = hdr_size + tok_ext_bytes; if (size == 0) { if (coap_pdu_parse_header(session->partial_pdu, session->proto)) { #if COAP_CONSTRAINED_STACK @@ -2022,7 +2109,7 @@ coap_handle_dgram(coap_context_t *ctx, coap_session_t *session, } /* Need max space incase PDU is updated with updated token etc. */ - pdu = coap_pdu_init(0, 0, 0, coap_session_max_pdu_size(session)); + pdu = coap_pdu_init(0, 0, 0, coap_session_max_pdu_rcv_size(session)); if (!pdu) goto error; @@ -2129,7 +2216,7 @@ coap_cancel_session_messages(coap_context_t *context, coap_session_t *session, void coap_cancel_all_messages(coap_context_t *context, coap_session_t *session, - const uint8_t *token, size_t token_length) { + coap_bin_const_t *token) { /* cancel all messages in sendqueue that belong to session * and use the specified token */ coap_queue_t **p, *q; @@ -2142,8 +2229,7 @@ coap_cancel_all_messages(coap_context_t *context, coap_session_t *session, while (q) { if (q->session == session && - token_match(token, token_length, - q->pdu->token, q->pdu->token_length)) { + coap_binary_equal(&q->pdu->actual_token, token)) { *p = q->next; coap_log(LOG_DEBUG, "** %s: mid=0x%x: removed 6\n", coap_session_str(session), q->id); @@ -2247,8 +2333,8 @@ coap_new_error_response(const coap_pdu_t *request, coap_pdu_code_t code, response = coap_pdu_init(type, code, request->mid, size); if (response) { /* copy token */ - if (!coap_add_token(response, request->token_length, - request->token)) { + if (!coap_add_token(response, request->actual_token.length, + request->actual_token.s)) { coap_log(LOG_DEBUG, "cannot add token to error response\n"); coap_delete_pdu(response); return NULL; @@ -2385,20 +2471,20 @@ hnd_get_wellknown(coap_resource_t *resource, */ static int coap_cancel(coap_context_t *context, const coap_queue_t *sent) { - coap_binary_t token = { 0, NULL }; int num_cancelled = 0; /* the number of observers cancelled */ +#ifndef COAP_SERVER_SUPPORT + (void)sent; +#endif /* ! COAP_SERVER_SUPPORT */ (void)context; - /* remove observer for this resource, if any - * get token from sent and try to find a matching resource. Uh! - */ - - COAP_SET_STR(&token, sent->pdu->token_length, sent->pdu->token); #if COAP_SERVER_SUPPORT + /* remove observer for this resource, if any + * Use token from sent and try to find a matching resource. Uh! + */ RESOURCES_ITER(context->resources, r) { - coap_cancel_all_messages(context, sent->session, token.s, token.length); - num_cancelled += coap_delete_observer(r, sent->session, &token); + coap_cancel_all_messages(context, sent->session, &sent->pdu->actual_token); + num_cancelled += coap_delete_observer(r, sent->session, &sent->pdu->actual_token); } #endif /* COAP_SERVER_SUPPORT */ @@ -2485,6 +2571,7 @@ no_response(coap_pdu_t *request, coap_pdu_t *response, /* Still need to ACK the request */ response->code = 0; /* Remove token/data from piggybacked acknowledgment PDU */ + response->actual_token.length = 0; response->token_length = 0; response->used_size = 0; return RESPONSE_SEND; @@ -2572,13 +2659,12 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu int skip_hop_limit_check = 0; int resp; int send_early_empty_ack = 0; - coap_binary_t token = { pdu->token_length, pdu->token }; + coap_bin_const_t token = coap_pdu_get_token(pdu); coap_string_t *query = NULL; coap_opt_t *observe = NULL; coap_string_t *uri_path = NULL; int added_block = 0; #ifndef WITHOUT_ASYNC - coap_bin_const_t tokenc = { pdu->token_length, pdu->token }; coap_async_t *async; #endif /* WITHOUT_ASYNC */ @@ -2589,7 +2675,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu } } #ifndef WITHOUT_ASYNC - async = coap_find_async(session, tokenc); + async = coap_find_async(session, token); if (async) { coap_tick_t now; @@ -2827,7 +2913,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu /* Implementation detail: coap_add_token() immediately returns 0 if response == NULL */ - if (coap_add_token(response, pdu->token_length, pdu->token)) { + if (coap_add_token(response, token.length, token.s)) { int observe_action = COAP_OBSERVE_CANCEL; coap_block_b_t block; @@ -2946,6 +3032,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu if ((response->type == COAP_MESSAGE_ACK) && (response->code == 0)) { /* Remove token from otherwise-empty acknowledgment PDU */ + response->actual_token.length = 0; response->token_length = 0; response->used_size = 0; } @@ -3025,12 +3112,13 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu static void handle_response(coap_context_t *context, coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd) { + /* In a lossy context, the ACK of a separate response may have * been lost, so we need to stop retransmitting requests with the - * same token. + * same token. Matching on token potentially containing ext length bytes. */ if (rcvd->type != COAP_MESSAGE_ACK) - coap_cancel_all_messages(context, session, rcvd->token, rcvd->token_length); + coap_cancel_all_messages(context, session, &rcvd->actual_token); /* Check for message duplication */ if (COAP_PROTO_NOT_RELIABLE(session->proto)) { @@ -3048,6 +3136,23 @@ handle_response(coap_context_t *context, coap_session_t *session, session->last_ack_mid = rcvd->mid; } } + /* Check to see if checking out extended token support */ + if (session->max_token_checked == COAP_EXT_T_CHECKING && + session->max_token_mid == rcvd->mid) { + + if (rcvd->actual_token.length != session->max_token_size || + rcvd->code == COAP_RESPONSE_CODE(400) || + rcvd->code == COAP_RESPONSE_CODE(503)) { + coap_log(LOG_DEBUG, "Extended Token requested size support not available\n"); + session->max_token_size = COAP_TOKEN_DEFAULT_MAX; + } + else { + coap_log(LOG_DEBUG, "Extended Token support available\n"); + } + session->max_token_checked = COAP_EXT_T_CHECKED; + session->doing_first = 0; + return; + } if (session->block_mode & COAP_BLOCK_USE_LIBCOAP) { /* See if need to send next block to server */ @@ -3065,6 +3170,8 @@ handle_response(coap_context_t *context, coap_session_t *session, return; } } + if (session->doing_first) + session->doing_first = 0; /* Call application-specific response handler when available. */ if (context->response_handler) { @@ -3091,6 +3198,9 @@ handle_signaling(coap_context_t *context, coap_session_t *session, coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); if (pdu->code == COAP_SIGNALING_CODE_CSM) { + if (session->max_token_checked == COAP_EXT_T_NOT_CHECKED) { + session->max_token_size = COAP_TOKEN_DEFAULT_MAX; + } while ((option = coap_option_next(&opt_iter))) { if (opt_iter.number == COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE) { coap_session_set_mtu(session, coap_decode_var_bytes(coap_opt_value(option), @@ -3099,6 +3209,16 @@ handle_signaling(coap_context_t *context, coap_session_t *session, } else if (opt_iter.number == COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER) { session->csm_block_supported = 1; } + else if (opt_iter.number == COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH) { + session->max_token_size = + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option)); + if (session->max_token_size < COAP_TOKEN_DEFAULT_MAX) + session->max_token_size = COAP_TOKEN_DEFAULT_MAX; + else if (session->max_token_size > COAP_TOKEN_EXT_MAX) + session->max_token_size = COAP_TOKEN_EXT_MAX; + session->max_token_checked = COAP_EXT_T_CHECKED; + } } if (set_mtu) { if (session->mtu > COAP_BERT_BASE && session->csm_block_supported) @@ -3129,6 +3249,41 @@ handle_signaling(coap_context_t *context, coap_session_t *session, } #endif /* !COAP_DISABLE_TCP */ +static int check_token_size(coap_session_t *session, const coap_pdu_t *pdu) { + if (COAP_PDU_IS_REQUEST(pdu) && + pdu->actual_token.length > + (session->type == COAP_SESSION_TYPE_CLIENT ? + session->max_token_size : session->context->max_token_size)) { + /* https://datatracker.ietf.org/doc/html/rfc8974#section-2.2.2 */ + if (session->max_token_size > COAP_TOKEN_DEFAULT_MAX) { + coap_opt_filter_t opt_filter; + coap_pdu_t *response; + + memset(&opt_filter, 0, sizeof(coap_opt_filter_t)); + response = coap_new_error_response(pdu, COAP_RESPONSE_CODE(400), + &opt_filter); + if (!response) { + coap_log(LOG_WARNING, + "coap_dispatch: cannot create error response\n"); + } + else { + /* + * Note - have to leave in oversize token as per + * https://datatracker.ietf.org/doc/html/rfc7252#section-5.3.1 + */ + if (coap_send_internal(session, response) == COAP_INVALID_MID) + coap_log(LOG_WARNING, "coap_dispatch: error sending response\n"); + } + } + else { + /* Indicate no extended token support */ + coap_send_rst(session, pdu); + } + return 0; + } + return 1; +} + void coap_dispatch(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu) { @@ -3137,6 +3292,7 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, coap_opt_filter_t opt_filter; int is_ping_rst; int packet_is_bad = 0; + int is_ext_token_rst; if (LOG_DEBUG <= coap_get_log_level()) { /* FIXME: get debug to work again ** @@ -3173,9 +3329,7 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, * notification. Then, we must flag the observer to be alive * by setting obs->fail_cnt = 0. */ if (sent && COAP_RESPONSE_CLASS(sent->pdu->code) == 2) { - const coap_binary_t token = - { sent->pdu->token_length, sent->pdu->token }; - coap_touch_observer(context, sent->session, &token); + coap_touch_observer(context, sent->session, &sent->pdu->actual_token); } #endif /* COAP_SERVER_SUPPORT */ @@ -3195,7 +3349,17 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, context->ping_timeout && session->last_ping > 0) is_ping_rst = 1; - if (!is_ping_rst) + /* Check to see if checking out extended token support */ + is_ext_token_rst = 0; + if (session->max_token_checked == COAP_EXT_T_CHECKING && + session->max_token_mid == pdu->mid) { + coap_log(LOG_DEBUG, "Extended Token support not available\n"); + session->max_token_size = COAP_TOKEN_DEFAULT_MAX; + session->max_token_checked = COAP_EXT_T_CHECKED; + session->doing_first = 0; + is_ext_token_rst = 1; + } + if (!is_ping_rst && !is_ext_token_rst) coap_log(LOG_ALERT, "got RST for mid=0x%x\n", pdu->mid); if (session->con_active) { @@ -3211,12 +3375,12 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, if (sent) { coap_cancel(context, sent); - if (!is_ping_rst) { + if (!is_ping_rst && !is_ext_token_rst) { if(sent->pdu->type==COAP_MESSAGE_CON && context->nack_handler) context->nack_handler(sent->session, sent->pdu, COAP_NACK_RST, sent->id); } - else { + else if (is_ping_rst) { if (context->pong_handler) { context->pong_handler(session, pdu, pdu->mid); } @@ -3231,9 +3395,7 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, coap_subscription_t *obs, *tmp; LL_FOREACH_SAFE(r->subscribers, obs, tmp) { if (obs->pdu->mid == pdu->mid && obs->session == session) { - coap_binary_t token = { 0, NULL }; - COAP_SET_STR(&token, obs->pdu->token_length, obs->pdu->token); - coap_delete_observer(r, session, &token); + coap_delete_observer(r, session, &obs->pdu->actual_token); goto cleanup; } } @@ -3251,6 +3413,9 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, coap_send_rst(session, pdu); goto cleanup; } + if (!check_token_size(session, pdu)) { + goto cleanup; + } break; case COAP_MESSAGE_CON: /* check for unknown critical options */ @@ -3274,6 +3439,10 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, goto cleanup; } + if (!check_token_size(session, pdu)) { + goto cleanup; + } + break; default: break; } diff --git a/src/pdu.c b/src/pdu.c index c8a9f6ed9d..deeff4ed10 100644 --- a/src/pdu.c +++ b/src/pdu.c @@ -54,6 +54,7 @@ coap_pdu_clear(coap_pdu_t *pdu, size_t size) { pdu->type = 0; pdu->code = 0; pdu->hdr_size = 0; + pdu->actual_token.length = 0; pdu->token_length = 0; pdu->crit_opt = 0; pdu->mid = 0; @@ -179,10 +180,15 @@ coap_pdu_duplicate(const coap_pdu_t *old_pdu, size_t token_length, const uint8_t *token, coap_opt_filter_t *drop_options) { - coap_pdu_t *pdu = coap_pdu_init(old_pdu->type, - old_pdu->code, - coap_new_message_id(session), - coap_session_max_pdu_size(session)); + uint8_t doing_first = session->doing_first; + coap_pdu_t *pdu; + + session->doing_first = 0; + pdu = coap_pdu_init(old_pdu->type, + old_pdu->code, + coap_new_message_id(session), + coap_session_max_pdu_size(session)); + session->doing_first = doing_first; if (pdu == NULL) return NULL; @@ -258,6 +264,12 @@ coap_pdu_resize(coap_pdu_t *pdu, size_t new_size) { pdu->data = pdu->token + offset; else pdu->data = NULL; + if (pdu->actual_token.length < COAP_TOKEN_EXT_1B_BIAS) + pdu->actual_token.s = &pdu->token[0]; + else if (pdu->actual_token.length < COAP_TOKEN_EXT_2B_BIAS) + pdu->actual_token.s = &pdu->token[1]; + else + pdu->actual_token.s = &pdu->token[2]; #endif } pdu->alloc_size = new_size; @@ -283,8 +295,10 @@ coap_pdu_check_resize(coap_pdu_t *pdu, size_t size) { int coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) { + size_t bias = 0; + /* must allow for pdu == NULL as callers may rely on this */ - if (!pdu || len > 8) + if (!pdu) return 0; if (pdu->used_size) { @@ -292,13 +306,50 @@ coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) { "coap_add_token: The token must defined first. Token ignored\n"); return 0; } - if (!coap_pdu_check_resize(pdu, len)) + pdu->actual_token.length = len; + if (len < COAP_TOKEN_EXT_1B_BIAS) { + bias = 0; + } + else if (len < COAP_TOKEN_EXT_2B_BIAS) { + bias = 1; + } + else if (len <= COAP_TOKEN_EXT_MAX) { + bias = 2; + } + else { + coap_log(LOG_WARNING, + "coap_add_token: Token size too large. Token ignored\n"); return 0; - pdu->token_length = (uint8_t)len; - if (len) - memcpy(pdu->token, data, len); + } + if (!coap_pdu_check_resize(pdu, len + bias)) { + coap_log(LOG_WARNING, + "coap_add_token: Insufficient space for token. Token ignored\n"); + return 0; + } + + pdu->actual_token.length = len; + pdu->actual_token.s = &pdu->token[bias]; + pdu->token_length = (uint8_t)(len + bias); + if (len) { + switch (bias) { + case 0: + memcpy(pdu->token, data, len); + break; + case 1: + pdu->token[0] = (uint8_t)(len - COAP_TOKEN_EXT_1B_BIAS); + memcpy(&pdu->token[1], data, len); + break; + case 2: + pdu->token[0] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) >> 8); + pdu->token[1] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) & 0xff); + memcpy(&pdu->token[2], data, len); + break; + default: + break; + } + } pdu->max_opt = 0; - pdu->used_size = len; + pdu->used_size = len + bias; pdu->data = NULL; return 1; @@ -307,35 +358,71 @@ coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) { /* It is assumed that coap_encode_var_safe8() has been called to reduce data */ int coap_update_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) { + size_t bias = 0; + /* must allow for pdu == NULL as callers may rely on this */ - if (!pdu || len > 8) + if (!pdu) return 0; if (pdu->used_size == 0) { return coap_add_token(pdu, len, data); } - if (len == pdu->token_length) { + if (len < COAP_TOKEN_EXT_1B_BIAS) { + bias = 0; + } + else if (len < COAP_TOKEN_EXT_2B_BIAS) { + bias = 1; + } + else if (len <= COAP_TOKEN_EXT_MAX) { + bias = 2; + } + else { + coap_log(LOG_WARNING, + "coap_add_token: Token size too large. Token ignored\n"); + return 0; + } + if ((len + bias) == pdu->token_length) { /* Easy case - just data has changed */ } - else if (len > pdu->token_length) { - if (!coap_pdu_check_resize(pdu, pdu->used_size + len - pdu->token_length)) { + else if ((len + bias) > pdu->token_length) { + if (!coap_pdu_check_resize(pdu, + pdu->used_size + (len + bias) - pdu->token_length)) { coap_log(LOG_WARNING, "Failed to update token\n"); return 0; } - memmove(&pdu->token[len - pdu->token_length], pdu->token, pdu->used_size); - pdu->used_size += len - pdu->token_length; + memmove(&pdu->token[(len + bias) - pdu->token_length], + pdu->token, pdu->used_size); + pdu->used_size += len + bias - pdu->token_length; } else { - pdu->used_size -= pdu->token_length - len; - memmove(pdu->token, &pdu->token[pdu->token_length - len], pdu->used_size); + pdu->used_size -= pdu->token_length - (len + bias); + memmove(pdu->token, &pdu->token[pdu->token_length - (len + bias)], pdu->used_size); } if (pdu->data) { - pdu->data += len - pdu->token_length; + pdu->data += (len + bias) - pdu->token_length; } - pdu->token_length = (uint8_t)len; - if (len) - memcpy(pdu->token, data, len); + pdu->actual_token.length = len; + pdu->actual_token.s = &pdu->token[bias]; + pdu->token_length = (uint8_t)(len + bias); + if (len) { + switch (bias) { + case 0: + memcpy(pdu->token, data, len); + break; + case 1: + pdu->token[0] = (uint8_t)(len - COAP_TOKEN_EXT_1B_BIAS); + memcpy(&pdu->token[1], data, len); + break; + case 2: + pdu->token[0] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) >> 8); + pdu->token[1] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) & 0xff); + memcpy(&pdu->token[2], data, len); + break; + default: + break; + } + } return 1; } @@ -867,24 +954,44 @@ coap_pdu_parse_size(coap_proto_t proto, assert(coap_pdu_parse_header_size(proto, data) <= length ); size_t size = 0; + const uint8_t *token_start = NULL; if ((proto == COAP_PROTO_TCP || proto==COAP_PROTO_TLS) && length >= 1) { uint8_t len = *data >> 4; + uint8_t tkl = *data & 0x0f; + if (len < 13) { size = len; + token_start = &data[2]; } else if (length >= 2) { if (len==13) { size = (size_t)data[1] + COAP_MESSAGE_SIZE_OFFSET_TCP8; + token_start = &data[3]; } else if (length >= 3) { if (len==14) { size = ((size_t)data[1] << 8) + data[2] + COAP_MESSAGE_SIZE_OFFSET_TCP16; + token_start = &data[4]; } else if (length >= 5) { size = ((size_t)data[1] << 24) + ((size_t)data[2] << 16) + ((size_t)data[3] << 8) + data[4] + COAP_MESSAGE_SIZE_OFFSET_TCP32; + token_start = &data[6]; } } } - size += data[0] & 0x0f; + /* account for the token length */ + if (tkl < COAP_TOKEN_EXT_1B_TKL) { + size += tkl; + } + else if (tkl == COAP_TOKEN_EXT_1B_TKL) { + size += token_start[0] + COAP_TOKEN_EXT_1B_BIAS + 1; + } + else if (tkl == COAP_TOKEN_EXT_2B_TKL) { + size += ((uint16_t)token_start[0] << 8) + token_start[1] + + COAP_TOKEN_EXT_2B_BIAS + 2; + } + else { + /* Invalid at this point - caught later as undersized */ + } } return size; @@ -893,6 +1000,8 @@ coap_pdu_parse_size(coap_proto_t proto, int coap_pdu_parse_header(coap_pdu_t *pdu, coap_proto_t proto) { uint8_t *hdr = pdu->token - pdu->hdr_size; + uint8_t e_token_length; + if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) { assert(pdu->hdr_size == 4); if ((hdr[0] >> 6) != COAP_DEFAULT_VERSION) { @@ -900,23 +1009,40 @@ coap_pdu_parse_header(coap_pdu_t *pdu, coap_proto_t proto) { return 0; } pdu->type = (hdr[0] >> 4) & 0x03; - pdu->token_length = hdr[0] & 0x0f; pdu->code = hdr[1]; pdu->mid = (uint16_t)hdr[2] << 8 | hdr[3]; } else if (proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) { assert(pdu->hdr_size >= 2 && pdu->hdr_size <= 6); pdu->type = COAP_MESSAGE_CON; - pdu->token_length = hdr[0] & 0x0f; pdu->code = hdr[pdu->hdr_size-1]; pdu->mid = 0; } else { coap_log(LOG_DEBUG, "coap_pdu_parse: unsupported protocol\n"); return 0; } - if (pdu->token_length > pdu->alloc_size) { + + e_token_length = hdr[0] & 0x0f; + if (e_token_length < COAP_TOKEN_EXT_1B_TKL) { + pdu->token_length = e_token_length; + pdu->actual_token.length = pdu->token_length; + pdu->actual_token.s = &pdu->token[0]; + } + else if (e_token_length == COAP_TOKEN_EXT_1B_TKL) { + pdu->token_length = pdu->token[0] + COAP_TOKEN_EXT_1B_BIAS + 1; + pdu->actual_token.length = pdu->token_length - 1; + pdu->actual_token.s = &pdu->token[1]; + } + else if (e_token_length == COAP_TOKEN_EXT_2B_TKL) { + pdu->token_length = ((uint16_t)pdu->token[0] << 8) + pdu->token[1] + + COAP_TOKEN_EXT_2B_BIAS + 2; + pdu->actual_token.length = pdu->token_length - 2; + pdu->actual_token.s = &pdu->token[2]; + } + if (pdu->token_length > pdu->alloc_size || e_token_length == 15) { /* Invalid PDU provided - not wise to assert here though */ coap_log(LOG_DEBUG, "coap_pdu_parse: PDU header token size broken\n"); pdu->token_length = (uint8_t)pdu->alloc_size; + pdu->actual_token.length = 0; return 0; } return 1; @@ -933,8 +1059,11 @@ coap_pdu_parse_opt_csm(coap_pdu_t *pdu, uint16_t len) { case COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER: if (len > 0) goto bad; break; + case COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH: + if (len > 3) goto bad; + break; default: - ; + if (pdu->max_opt & 0x01) goto bad; /* Critical */ } break; case COAP_SIGNALING_PING: @@ -944,7 +1073,7 @@ coap_pdu_parse_opt_csm(coap_pdu_t *pdu, uint16_t len) { if (len > 0) goto bad; break; default: - ; + if (pdu->max_opt & 0x01) goto bad; /* Critical */ } break; case COAP_SIGNALING_RELEASE: @@ -956,7 +1085,7 @@ coap_pdu_parse_opt_csm(coap_pdu_t *pdu, uint16_t len) { if (len > 3) goto bad; break; default: - ; + if (pdu->max_opt & 0x01) goto bad; /* Critical */ } break; case COAP_SIGNALING_ABORT: @@ -965,7 +1094,7 @@ coap_pdu_parse_opt_csm(coap_pdu_t *pdu, uint16_t len) { if (len > 2) goto bad; break; default: - ; + if (pdu->max_opt & 0x01) goto bad; /* Critical */ } break; default: @@ -1056,7 +1185,7 @@ coap_pdu_parse_opt(coap_pdu_t *pdu) { } } - if (pdu->token_length > pdu->used_size || pdu->token_length > 8) { + if (pdu->token_length > pdu->used_size) { coap_log(LOG_DEBUG, "coap_pdu_parse: invalid Token\n"); return 0; } @@ -1196,6 +1325,19 @@ coap_pdu_parse(coap_proto_t proto, size_t coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) { + uint8_t e_token_length; + + if (pdu->actual_token.length < COAP_TOKEN_EXT_1B_BIAS) { + e_token_length = (uint8_t)pdu->actual_token.length; + } else if (pdu->actual_token.length < COAP_TOKEN_EXT_2B_BIAS) { + e_token_length = COAP_TOKEN_EXT_1B_TKL; + } else if (pdu->actual_token.length <= COAP_TOKEN_EXT_MAX) { + e_token_length = COAP_TOKEN_EXT_2B_TKL; + } else { + coap_log(LOG_WARNING, + "coap_add_token: Token size too large. PDU ignored\n"); + return 0; + } if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) { assert(pdu->max_hdr_size >= 4); if (pdu->max_hdr_size < 4) { @@ -1205,7 +1347,7 @@ coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) { } pdu->token[-4] = COAP_DEFAULT_VERSION << 6 | pdu->type << 4 - | pdu->token_length; + | e_token_length; pdu->token[-3] = pdu->code; pdu->token[-2] = (uint8_t)(pdu->mid >> 8); pdu->token[-1] = (uint8_t)(pdu->mid); @@ -1226,7 +1368,7 @@ coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) { return 0; } pdu->token[-2] = (uint8_t)len << 4 - | pdu->token_length; + | e_token_length; pdu->token[-1] = pdu->code; pdu->hdr_size = 2; } else if (len <= COAP_MAX_MESSAGE_SIZE_TCP8) { @@ -1236,7 +1378,7 @@ coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) { "coap_pdu_encode_header: not enough space for TCP8 header\n"); return 0; } - pdu->token[-3] = 13 << 4 | pdu->token_length; + pdu->token[-3] = 13 << 4 | e_token_length; pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP8); pdu->token[-1] = pdu->code; pdu->hdr_size = 3; @@ -1247,7 +1389,7 @@ coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) { "coap_pdu_encode_header: not enough space for TCP16 header\n"); return 0; } - pdu->token[-4] = 14 << 4 | pdu->token_length; + pdu->token[-4] = 14 << 4 | e_token_length; pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP16) >> 8); pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP16); pdu->token[-1] = pdu->code; @@ -1259,7 +1401,7 @@ coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) { "coap_pdu_encode_header: not enough space for TCP32 header\n"); return 0; } - pdu->token[-6] = 15 << 4 | pdu->token_length; + pdu->token[-6] = 15 << 4 | e_token_length; pdu->token[-5] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 24); pdu->token[-4] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 16); pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 8); @@ -1294,11 +1436,7 @@ void coap_pdu_set_type(coap_pdu_t *pdu, coap_pdu_type_t type) { } coap_bin_const_t coap_pdu_get_token(const coap_pdu_t *pdu) { - coap_bin_const_t token; - - token.length = pdu->token_length; - token.s = pdu->token; - return token; + return pdu->actual_token; } coap_mid_t coap_pdu_get_mid(const coap_pdu_t *pdu) { diff --git a/src/resource.c b/src/resource.c index c6fef70317..cd1787007f 100644 --- a/src/resource.c +++ b/src/resource.c @@ -708,16 +708,15 @@ coap_register_request_handler(coap_resource_t *resource, coap_subscription_t * coap_find_observer(coap_resource_t *resource, coap_session_t *session, - const coap_binary_t *token) { + const coap_bin_const_t *token) { coap_subscription_t *s; assert(resource); assert(session); LL_FOREACH(resource->subscribers, s) { - if (s->session == session - && (!token || (token->length == s->pdu->token_length - && memcmp(token->s, s->pdu->token, token->length) == 0))) + if (s->session == session && + (!token || coap_binary_equal(token, &s->pdu->actual_token))) return s; } @@ -744,7 +743,7 @@ coap_find_observer_cache_key(coap_resource_t *resource, coap_session_t *session, coap_subscription_t * coap_add_observer(coap_resource_t *resource, coap_session_t *session, - const coap_binary_t *token, + const coap_bin_const_t *token, const coap_pdu_t *request) { coap_subscription_t *s; coap_cache_key_t *cache_key = NULL; @@ -771,8 +770,7 @@ static const uint16_t cache_ignore_options[] = { COAP_OPTION_ETAG }; s = coap_find_observer_cache_key(resource, session, cache_key); if (s) { /* Delete old entry with old token */ - coap_binary_t tmp_token = { s->pdu->token_length, s->pdu->token }; - coap_delete_observer(resource, session, &tmp_token); + coap_delete_observer(resource, session, &s->pdu->actual_token); s = NULL; } } @@ -792,8 +790,8 @@ static const uint16_t cache_ignore_options[] = { COAP_OPTION_ETAG }; } coap_subscription_init(s); - s->pdu = coap_pdu_duplicate(request, session, request->token_length, - request->token, NULL); + s->pdu = coap_pdu_duplicate(request, session, token->length, + token->s, NULL); if (s->pdu == NULL) { coap_delete_cache_key(cache_key); COAP_FREE_TYPE(subscription, s); @@ -829,7 +827,7 @@ static const uint16_t cache_ignore_options[] = { COAP_OPTION_ETAG }; void coap_touch_observer(coap_context_t *context, coap_session_t *session, - const coap_binary_t *token) { + const coap_bin_const_t *token) { coap_subscription_t *s; RESOURCES_ITER(context->resources, r) { @@ -842,16 +840,21 @@ coap_touch_observer(coap_context_t *context, coap_session_t *session, int coap_delete_observer(coap_resource_t *resource, coap_session_t *session, - const coap_binary_t *token) { + const coap_bin_const_t *token) { coap_subscription_t *s; s = coap_find_observer(resource, session, token); - if ( s && coap_get_log_level() >= LOG_DEBUG ) { + if (s && coap_get_log_level() >= LOG_DEBUG) { char outbuf[2 * 8 + 1] = ""; unsigned int i; - for ( i = 0; i < s->pdu->token_length; i++ ) - snprintf( &outbuf[2 * i], 3, "%02x", s->pdu->token[i] ); + + for ( i = 0; i < s->pdu->actual_token.length; i++ ) { + size_t size = strlen(outbuf); + + snprintf(&outbuf[size], sizeof(outbuf)-size, "%02x", + s->pdu->actual_token.s[i]); + } coap_log(LOG_DEBUG, "removed subscription %p with token '%s' key 0x%02x%02x%02x%02x\n", (void*)s, outbuf, s->cache_key->key[0], s->cache_key->key[1], @@ -890,7 +893,7 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, coap_deleting_resource_t deleting) { coap_method_handler_t h; coap_subscription_t *obs, *otmp; - coap_binary_t token; + coap_bin_const_t token; coap_pdu_t *response; uint8_t buf[4]; coap_string_t *query; @@ -943,7 +946,8 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, continue; } - if (!coap_add_token(response, obs->pdu->token_length, obs->pdu->token)) { + if (!coap_add_token(response, obs->pdu->actual_token.length, + obs->pdu->actual_token.s)) { obs->dirty = 1; r->partiallydirty = 1; context->observe_pending = 1; @@ -954,9 +958,6 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, continue; } - token.length = obs->pdu->token_length; - token.s = obs->pdu->token; - obs->pdu->mid = response->mid = coap_new_message_id(obs->session); if ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_CON) == 0 && ((r->flags & COAP_RESOURCE_FLAGS_NOTIFY_NON_ALWAYS) || @@ -1126,20 +1127,18 @@ static void coap_remove_failed_observers(coap_context_t *context, coap_resource_t *resource, coap_session_t *session, - const coap_binary_t *token) { + const coap_bin_const_t *token) { coap_subscription_t *obs, *otmp; LL_FOREACH_SAFE(resource->subscribers, obs, otmp) { - if ( obs->session == session && - token->length == obs->pdu->token_length && - memcmp(token->s, obs->pdu->token, token->length) == 0) { - + if (obs->session == session && + coap_binary_equal(token, &obs->pdu->actual_token)) { /* count failed notifies and remove when * COAP_OBS_MAX_FAIL is reached */ obs->fail_cnt++; if (obs->fail_cnt >= COAP_OBS_MAX_FAIL) { coap_cancel_all_messages(context, obs->session, - obs->pdu->token, obs->pdu->token_length); + &obs->pdu->actual_token); coap_delete_observer(resource, session, token); } break; /* break loop if observer was found */ @@ -1150,7 +1149,7 @@ coap_remove_failed_observers(coap_context_t *context, void coap_handle_failed_notify(coap_context_t *context, coap_session_t *session, - const coap_binary_t *token) { + const coap_bin_const_t *token) { RESOURCES_ITER(context->resources, r) { coap_remove_failed_observers(context, r, session, token); diff --git a/tests/test_pdu.c b/tests/test_pdu.c index 86d71909f2..7e318313c5 100644 --- a/tests/test_pdu.c +++ b/tests/test_pdu.c @@ -66,14 +66,15 @@ t_parse_pdu3(void) { static void t_parse_pdu4(void) { - /* illegal token length */ + /* illegal token length (token only 8 bytes) */ uint8_t teststr[] = { 0x59, 0x69, 0x12, 0x34, - 't', 'o', 'k', 'e', 'n', '1', '2', '3', '4' }; + 't', 'o', 'k', 'e', 'n', '1', '2', '3' }; int result; result = coap_pdu_parse(COAP_PROTO_UDP, teststr, sizeof(teststr), pdu); CU_ASSERT(result == 0); + /* illegal token length */ teststr[0] = 0x5f; result = coap_pdu_parse(COAP_PROTO_UDP, teststr, sizeof(teststr), pdu); @@ -350,6 +351,7 @@ t_encode_pdu1(void) { static void t_encode_pdu2(void) { + coap_log_t level = coap_get_log_level(); size_t old_max = pdu->max_size; int result; @@ -359,7 +361,9 @@ t_encode_pdu2(void) { pdu->code = COAP_REQUEST_CODE_GET; pdu->mid = 0x1234; + coap_set_log_level(LOG_CRIT); result = coap_add_token(pdu, 5, (const uint8_t *)"token"); + coap_set_log_level(level); CU_ASSERT(result == 0); @@ -369,10 +373,16 @@ t_encode_pdu2(void) { static void t_encode_pdu3(void) { int result; + coap_bin_const_t check_token; - result = coap_add_token(pdu, 9, (const uint8_t *)"123456789"); + result = coap_add_token(pdu, 15, (const uint8_t *)"123456789012345"); - CU_ASSERT(result == 0); + /* length of 15 triggers extension */ + CU_ASSERT(result == 1 && pdu->actual_token.length == 15 && + pdu->token_length == 16 && pdu->token[0] == 2); + + check_token = coap_pdu_get_token(pdu); + CU_ASSERT(check_token.length == 15); } static void