diff --git a/README.md b/README.md index e22922e852..a5da39438a 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,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 52290101de..76a219a304 100644 --- a/doc/main.md +++ b/doc/main.md @@ -42,6 +42,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 ae68bdd9c9..0ef099b66c 100644 --- a/examples/coap-client.c +++ b/examples/coap-client.c @@ -51,8 +51,8 @@ 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_debug("cannot add token to request\n"); } @@ -585,7 +593,7 @@ usage(const char *program, const char *version) { "\t \t\tconnect to a CoAP proxy (automatically adds Proxy-Uri\n" "\t \t\toption 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-V num \t\tVerbosity level (default 3, maximum is 7) for (D)TLS\n" "\t \t\tlibrary logging\n" @@ -984,9 +992,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); } } @@ -1828,6 +1836,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, @@ -1851,8 +1862,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 6fc32aa796..9c9644f15f 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,7 +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 [-V num]\n" + "\t [-T max_token_size] [-V num]\n" "\t [[-h hint] [-k key]]\n" "\t [[-c certfile] [-C cafile] [-n] [-R trust_casfile]]\n" "General Options\n" @@ -596,6 +597,7 @@ usage(const char *program, const char *version) { "\t-v num \t\tVerbosity level (default 4, maximum is 8) for general\n" "\t \t\tCoAP logging\n" "\t-A address\tInterface address to bind to\n" + "\t-T max_token_length\tSet the maximum token length (8-65804)\n" "\t-V num \t\tVerbosity level (default 3, maximum is 7) for (D)TLS\n" "\t \t\tlibrary logging\n" "PSK Options (if supported by underlying (D)TLS library)\n" @@ -775,6 +777,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_err("Extended Token Length must be 8 or greater\n"); + return 0; + } + else if (extended_token_size > COAP_TOKEN_EXT_MAX) { + coap_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; @@ -790,7 +806,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:V:")) != -1) { + while ((opt = getopt(argc, argv, "A:c:C:g:G:h:k:n:R:p:v:T:V:")) != -1) { switch (opt) { case 'A' : strncpy(addr_str, optarg, NI_MAXHOST-1); @@ -826,13 +842,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; @@ -857,6 +878,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 808252e04d..93c0c1fe2a 100644 --- a/examples/coap-server.c +++ b/examples/coap-server.c @@ -119,6 +119,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); @@ -2151,7 +2152,7 @@ usage( const char *program, const char *version) { "Usage: %s [-d max] [-e] [-g group] [-l loss] [-p port] [-r] [-v num]\n" "\t\t[-A address] [-E oscore_conf_file[,seq_file]] [-G group_if]\n" "\t\t[-L value] [-N] [-P scheme://address[:port],[name1[,name2..]]]\n" - "\t\t[-V num] [-X size]\n" + "\t\t[-T max_token_size] [-V num] [-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" @@ -2207,6 +2208,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-V num \t\tVerbosity level (default 3, maximum is 7) for (D)TLS\n" "\t \t\tlibrary logging\n" "\t-X size\t\tMaximum message size to use for TCP based connections\n" @@ -2680,6 +2682,20 @@ 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_err("Extended Token Length must be 8 or greater\n"); + return 0; + } + else if (extended_token_size > COAP_TOKEN_EXT_MAX) { + coap_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; @@ -2710,7 +2726,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:E:L:M:NP:R:S:V:X:")) != -1) { + while ((opt = getopt(argc, argv, "c:d:eg:G:h:i:j:J:k:l:mnp:rs:u:v:A:C:E:L:M:NP:R:S:T:V:X:")) != -1) { switch (opt) { case 'A' : strncpy(addr_str, optarg, NI_MAXHOST-1); @@ -2816,13 +2832,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; @@ -2881,6 +2900,8 @@ main(int argc, char **argv) { if (get_oscore_conf(ctx) == NULL) goto finish; } + 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 d858db7dfc..cb39b9b860 100644 --- a/include/coap3/coap_block_internal.h +++ b/include/coap3/coap_block_internal.h @@ -122,7 +122,7 @@ struct coap_lg_crcv_t { size_t total_len; /**< Length as indicated by SIZE2 option */ coap_binary_t *body_data; /**< Used for re-assembling entire body */ coap_binary_t *app_token; /**< app requesting PDU token */ - coap_binary_t **obs_token; /**< Tokens used in setting up Observe + coap_bin_const_t **obs_token; /**< Tokens used in setting up Observe (to handle large FETCH) */ size_t obs_token_cnt; /**< number of tokens used to set up Observe */ uint64_t state_token; /**< state token */ @@ -153,8 +153,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 5a6ea31d88..6030b3ef1a 100644 --- a/include/coap3/coap_net_internal.h +++ b/include/coap3/coap_net_internal.h @@ -145,6 +145,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 */ @@ -274,12 +275,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 92c4f2d7d8..3717a7dc72 100644 --- a/include/coap3/coap_pdu_internal.h +++ b/include/coap3/coap_pdu_internal.h @@ -50,6 +50,12 @@ #define OSCORE_CRYPTO_BUFFER_SIZE (COAP_MAX_CHUNK_SIZE+16) #endif /* HAVE_OSCORE */ +/* 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 @@ -117,16 +123,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 d898f34963..8a4b513fbb 100644 --- a/include/coap3/coap_session_internal.h +++ b/include/coap3/coap_session_internal.h @@ -51,6 +51,15 @@ typedef enum { COAP_OSCORE_B_2_STEP_5, } COAP_OSCORE_B_2_STEP; +/** + * 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). @@ -166,6 +175,11 @@ struct coap_session_t { associations */ uint64_t oscore_r2; /**< R2 for RFC8613 Appendix B.2 */ #endif /* HAVE_OSCORE */ + 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 adde584bac..082720922d 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 ceb271c1f7..66d0c670a3 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 33c45890a6..954ca142bb 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 2fe2fd9b05..56e05182d0 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -51,6 +51,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 4f72996ef2..f54473f9a7 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -49,6 +49,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 a1e5c55f50..c5beedb040 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -103,6 +103,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_io.3" > coap_can_exit.3 @echo ".so man3/coap_logging.3" > coap_log_info.3 @echo ".so man3/coap_logging.3" > coap_log_debug.3 diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index 590c8f3e03..986d9217af 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -170,7 +170,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 fbd6c07fc7..eebfe0ed10 100644 --- a/man/coap-rd.txt.in +++ b/man/coap-rd.txt.in @@ -20,7 +20,7 @@ coap-rd-notls SYNOPSIS -------- *coap-rd* [*-g* group] [*-G* group_if] [*-p* port] [*-v* num] [*-A* address] - [*-V* num] + [*-T* max_token_size] [*-V* num] [[*-h* hint] [*-k* key]] [[*-c* certfile] [*-n*] [*-C* cafile] [*-R* trusted_casfile]] @@ -57,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). + *-V* num:: The verbosity level to use (default 3, maximum is 7) for (D)TLS library logging. diff --git a/man/coap-server.txt.in b/man/coap-server.txt.in index e9e5334084..f1ff1e25d8 100644 --- a/man/coap-server.txt.in +++ b/man/coap-server.txt.in @@ -22,8 +22,8 @@ SYNOPSIS *coap-server* [*-d* max] [*-e*] [*-g* group] [*-l* loss] [*-p* port] [-r] [*-v* num] [*-A* address] [*-E* oscore_conf_file[,seq_file]] [*-G* group_if] [*-L* value] [*-N*] - [*-P* scheme://addr[:port],[name1[,name2..]]] [*-V* num] - [*-X* size] + [*-P* scheme://addr[:port],[name1[,name2..]]] + [*-T* max_token_size] [*-V* num] [*-X* size] [[*-h* hint] [*-i* match_identity_file] [*-k* key] [*-s* match_psk_sni_file] [*-u* user]] [[*-c* certfile] [*-j* keyfile] [*-n*] [*-C* cafile] @@ -114,6 +114,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). + *-V* num:: The verbosity level to use (default 3, maximum is 7) for (D)TLS library logging. diff --git a/man/coap.txt.in b/man/coap.txt.in index dca446b61d..ed3775d002 100644 --- a/man/coap.txt.in +++ b/man/coap.txt.in @@ -72,6 +72,8 @@ See "https://rfc-editor.org/rfc/rfc8768[RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option]" +"https://rfc-editor.org/rfc/rfc8974[RFC8974: Extended Tokens and Stateless Clients in the Constrained Application Protocol (CoAP)]" + "https://rfc-editor.org/rfc/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 3ddf97f960..b6bdc44dab 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* @@ -135,6 +139,18 @@ _context_. 0 (the default) means wait forever. 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://rfc-editor.org/rfc/rfc8974[RFC8974] up to _max_token_size_ +bytes, else 8 to disable https://rfc-editor.org/rfc/rfc8974[RFC8974] +(if previously set). + +*NOTE:* For the client, it will send an initial PDU to test the server +supports the requested extended token size as per +"https://rfc-editor.org/rfc/rfc8974.html#section-2.2.2[RFC8794 Section 2.2.2]" + RETURN VALUES ------------- *coap_new_context*() function returns a newly created context or @@ -162,6 +178,8 @@ See "https://rfc-editor.org/rfc/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]" +"https://rfc-editor.org/rfc/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 beaa462d36..804d74fe38 100644 --- a/man/coap_pdu_setup.txt.in +++ b/man/coap_pdu_setup.txt.in @@ -258,6 +258,9 @@ PDU TOKEN FUNCTIONS 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://rfc-editor.org/rfc/rfc8974[RFC8974]). + *Function: coap_session_new_token()* The *coap_session_new_token*() function is used to obtain the next available @@ -266,13 +269,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. @@ -725,6 +733,8 @@ See "https://rfc-editor.org/rfc/rfc8613[RFC8613: Object Security for Constrained RESTful Environments (OSCORE)]" +"https://rfc-editor.org/rfc/rfc8974[RFC8974: Extended Tokens and Stateless Clients in the Constrained Application Protocol (CoAP)]" + for further information. See https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#option-numbers diff --git a/src/block.c b/src/block.c index 7ad526f5ba..ffa9dfb397 100644 --- a/src/block.c +++ b/src/block.c @@ -397,16 +397,16 @@ coap_cancel_observe(coap_session_t *session, coap_binary_t *token, LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) { if (lg_crcv->observe_set) { if ((!token && !lg_crcv->app_token->length) || (token && - full_match(token->s, token->length, lg_crcv->app_token->s, - lg_crcv->app_token->length))) { + coap_binary_equal(token, lg_crcv->app_token))) { uint8_t buf[8]; coap_mid_t mid; size_t size; const uint8_t *data; - coap_binary_t *otoken = lg_crcv->obs_token ? lg_crcv->obs_token[0] ? - lg_crcv->obs_token[0] : - lg_crcv->app_token : - lg_crcv->app_token; + coap_bin_const_t *otoken = lg_crcv->obs_token ? + lg_crcv->obs_token[0] ? + lg_crcv->obs_token[0] : + (coap_bin_const_t *)lg_crcv->app_token : + (coap_bin_const_t *)lg_crcv->app_token; coap_pdu_t * pdu = coap_pdu_duplicate(&lg_crcv->pdu, session, otoken->length, @@ -609,9 +609,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); @@ -656,7 +654,7 @@ coap_add_data_large_internal(coap_session_t *session, avail -= coap_oscore_overhead(session, pdu); #endif /* HAVE_OSCORE */ /* 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; @@ -722,10 +720,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 */ @@ -828,7 +827,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 HAVE_OSCORE avail -= coap_oscore_overhead(session, pdu); #endif /* HAVE_OSCORE */ @@ -1162,9 +1161,14 @@ coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now, #endif /* COAP_SERVER_SUPPORT */ #if COAP_CLIENT_SUPPORT -static void +/* + * If Observe = 0, save the token away and return NULL + * Else If Observe = 1, return the saved token for this block + * Else, return NULL + */ +static coap_bin_const_t * track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv, - uint32_t block_num, uint8_t *buf, size_t len) { + uint32_t block_num, coap_bin_const_t *token) { /* Need to handle Observe for large FETCH */ coap_opt_iterator_t opt_iter; coap_opt_t *opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, @@ -1172,7 +1176,7 @@ track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv, if (opt) { int observe_action = -1; - coap_binary_t **tmp; + coap_bin_const_t **tmp; observe_action = coap_decode_var_bytes(coap_opt_value(opt), coap_opt_length(opt)); @@ -1181,25 +1185,26 @@ track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv, tmp = coap_realloc_type(COAP_STRING, lg_crcv->obs_token, (block_num + 1) * sizeof(lg_crcv->obs_token[0])); if (tmp == NULL) - return; + return NULL; lg_crcv->obs_token = tmp; if (block_num + 1 == lg_crcv->obs_token_cnt) - coap_delete_binary(lg_crcv->obs_token[block_num]); + coap_delete_bin_const(lg_crcv->obs_token[block_num]); lg_crcv->obs_token_cnt = block_num + 1; - lg_crcv->obs_token[block_num] = coap_new_binary(len); + lg_crcv->obs_token[block_num] = coap_new_bin_const(token->s, + token->length); if (lg_crcv->obs_token[block_num] == NULL) - return; - memcpy(lg_crcv->obs_token[block_num]->s, buf, len); + return NULL; } else if (observe_action == COAP_OBSERVE_CANCEL) { /* Use the token in lg_crcv */ if (block_num < lg_crcv->obs_token_cnt) { if (lg_crcv->obs_token[block_num]) { - memcpy(buf, lg_crcv->obs_token[block_num]->s, len); + return lg_crcv->obs_token[block_num]; } } } } + return NULL; } coap_lg_crcv_t * @@ -1244,20 +1249,24 @@ coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu, } /* 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; lg_crcv->state_token = state_token; if (pdu->code == COAP_REQUEST_CODE_FETCH) { - /* Need to handle Observe for large FETCH */ - track_fetch_observe(pdu, lg_crcv, 0, pdu->token, pdu->token_length); + coap_bin_const_t *new_token; + + /* Need to save/restore Observe Token for large FETCH */ + new_token = track_fetch_observe(pdu, lg_crcv, 0, &pdu->actual_token); + if (new_token) + coap_update_token(pdu, new_token->length, new_token->s); } /* In case it is there - must not be in continuing request PDUs */ @@ -1281,7 +1290,7 @@ coap_block_delete_lg_crcv(coap_session_t *session, coap_session_str(session), (void*)lg_crcv); coap_delete_binary(lg_crcv->app_token); for (i = 0; i < lg_crcv->obs_token_cnt; i++) { - coap_delete_binary(lg_crcv->obs_token[i]); + coap_delete_bin_const(lg_crcv->obs_token[i]); } coap_free_type(COAP_STRING, lg_crcv->obs_token); coap_free_type(COAP_LG_CRCV, lg_crcv); @@ -1491,8 +1500,8 @@ coap_handle_request_send_block(coap_session_t *session, memset(&drop_options, 0, sizeof(coap_opt_filter_t)); if (block.num != 0) 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; } @@ -1778,8 +1787,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; @@ -1952,8 +1959,7 @@ check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent, coap_add_data(echo_pdu, data_len, data); } /* Need to track Observe token change if Observe */ - track_fetch_observe(echo_pdu, lg_crcv, 0, echo_pdu->token, - echo_pdu->token_length); + track_fetch_observe(echo_pdu, lg_crcv, 0, &echo_pdu->actual_token); #if HAVE_OSCORE if (session->oscore_encryption && (opt = coap_check_option(echo_pdu, COAP_OPTION_OBSERVE, &opt_iter)) && @@ -2013,8 +2019,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) { @@ -2095,9 +2102,17 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, /* Need to handle Observe for large FETCH */ LL_FOREACH(session->lg_crcv, lg_crcv) { if (coap_binary_equal(p->b.b1.app_token, lg_crcv->app_token)) { - /* In case of observe */ - track_fetch_observe(&p->pdu, lg_crcv, block.num + 1, - buf, len); + coap_bin_const_t *new_token; + coap_bin_const_t ctoken = { len, buf }; + + /* Need to save/restore Observe Token for large FETCH */ + new_token = track_fetch_observe(&p->pdu, lg_crcv, block.num + 1, + &ctoken); + if (new_token) { + assert(len <= sizeof(buf)); + len = new_token->length; + memcpy(buf, new_token->s, len); + } break; } } @@ -2238,8 +2253,9 @@ coap_handle_response_get_block(coap_context_t *context, uint16_t block_opt = 0; size_t offset; int ack_rst_sent = 0; - 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) { @@ -2248,8 +2264,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; } @@ -2448,6 +2463,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_debug("Client app version of updated PDU\n"); + coap_show_pdu(COAP_LOG_DEBUG, rcvd); goto call_app_handler; } /* need to put back original token into rcvd */ @@ -2508,6 +2525,12 @@ coap_handle_response_get_block(coap_context_t *context, p->observe_set = 1; } 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_debug("PDU presented to app.\n"); + coap_show_pdu(COAP_LOG_DEBUG, rcvd); + } /* Expire this entry */ goto expire_lg_crcv; } @@ -2535,8 +2558,7 @@ coap_handle_response_get_block(coap_context_t *context, COAP_TICKS_PER_SECOND; } /* need to put back original token into rcvd */ - if (!full_match(rcvd->token, rcvd->token_length, - p->app_token->s, p->app_token->length)) { + if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { coap_update_token(rcvd, p->app_token->length, p->app_token->s); coap_log_debug("Client app version of updated PDU (3)\n"); coap_show_pdu(COAP_LOG_DEBUG, rcvd); @@ -2592,8 +2614,7 @@ coap_handle_response_get_block(coap_context_t *context, expire_lg_crcv: /* need to put back original token into rcvd */ - if (!full_match(rcvd->token, rcvd->token_length, - p->app_token->s, p->app_token->length)) { + if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { coap_update_token(rcvd, p->app_token->length, p->app_token->s); coap_log_debug("Client app version of updated PDU\n"); coap_show_pdu(COAP_LOG_DEBUG, rcvd); @@ -2641,8 +2662,7 @@ coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu) { if (session->lg_crcv) { LL_FOREACH(session->lg_crcv, lg_crcv) { - if (full_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)) return; if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) { coap_update_token(pdu, lg_crcv->app_token->length, @@ -2655,8 +2675,7 @@ coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu) { } if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) { LL_FOREACH(session->lg_xmit, lg_xmit) { - 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)) return; if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) { coap_update_token(pdu, lg_xmit->b.b1.app_token->length, diff --git a/src/coap_async.c b/src/coap_async.c index 890d5ddc5d..6f68b63a0a 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_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 a18a147144..339ce7b86b 100644 --- a/src/coap_debug.c +++ b/src/coap_debug.c @@ -436,7 +436,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[] = { @@ -599,7 +600,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; @@ -622,10 +624,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, "}"); @@ -645,6 +647,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_oscore.c b/src/coap_oscore.c index b69956adf7..2b5710cdf2 100644 --- a/src/coap_oscore.c +++ b/src/coap_oscore.c @@ -1301,6 +1301,7 @@ coap_oscore_decrypt_pdu(coap_session_t *session, /* Account for 1 byte 'code' used as token */ plain_pdu->token_length = 1; + plain_pdu->actual_token.length = 1; /* Account for the decrypted data */ plain_pdu->used_size = encrypt_len - tag_len; @@ -1390,8 +1391,7 @@ coap_oscore_decrypt_pdu(coap_session_t *session, coap_cancel_all_messages(session->context, session, - pdu->token, - pdu->token_length); + &pdu->actual_token); if (session->con_active) session->con_active--; coap_send_ack(session, pdu); @@ -1540,8 +1540,7 @@ coap_oscore_decrypt_pdu(coap_session_t *session, /* Server is requesting Echo refresh check */ coap_cancel_all_messages(session->context, session, - pdu->token, - pdu->token_length); + &pdu->actual_token); if (session->con_active) session->con_active--; if (sent_pdu) { diff --git a/src/coap_session.c b/src/coap_session.c index f8517fb97e..172bf71d7c 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 */ /* TCP/TLS have no notion of mid */ @@ -507,6 +510,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); @@ -549,10 +558,11 @@ void coap_session_connected(coap_session_t *session) { if (session->state != COAP_SESSION_STATE_ESTABLISHED) { coap_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; @@ -651,7 +661,8 @@ void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reaso /* Make sure that we try a re-transmit later on ICMP error */ if (coap_wait_ack(session->context, session, q) >= 0) { if (session->context->nack_handler) { - coap_bin_const_t token = coap_pdu_get_token(q->pdu); + coap_bin_const_t token = q->pdu->actual_token; + coap_check_update_token(session, q->pdu); session->context->nack_handler(session, q->pdu, reason, q->id); coap_update_token(q->pdu, token.length, token.s); @@ -698,7 +709,8 @@ void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reaso coap_queue_t *q = session->context->sendqueue; while (q) { if (q->session == session) { - coap_bin_const_t token = coap_pdu_get_token(q->pdu); + coap_bin_const_t token = q->pdu->actual_token; + coap_check_update_token(session, q->pdu); session->context->nack_handler(session, q->pdu, reason, q->id); coap_update_token(q->pdu, token.length, token.s); 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 3d357b7d9f..f7ee533ef6 100644 --- a/src/net.c +++ b/src/net.c @@ -396,6 +396,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) { @@ -567,6 +575,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; @@ -800,6 +809,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, @@ -1039,11 +1049,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_debug("Testing for Extended Token support\n"); + /* https://rfc-editor.org/rfc/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) @@ -1095,6 +1139,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); @@ -1104,13 +1156,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_warn( + "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; + } /* A lot of the reliable code assumes type is CON */ if (COAP_PROTO_RELIABLE(session->proto) && pdu->type == COAP_MESSAGE_NON) @@ -1159,14 +1252,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_debug("Token reused - see https://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); } if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter) && (session->block_mode & COAP_BLOCK_NO_PREEMPTIVE_RTAG) == 0 && @@ -1194,14 +1286,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_debug("PDU presented by app\n"); coap_show_pdu(COAP_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]; @@ -1234,9 +1325,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; } } @@ -1558,12 +1647,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) { @@ -1787,14 +1871,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_warn( "** %s: incoming PDU length too large (%zu > %lu)\n", @@ -1816,8 +1903,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 @@ -2202,7 +2289,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; @@ -2215,8 +2302,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_debug("** %s: mid=0x%x: removed (6)\n", coap_session_str(session), q->id); @@ -2320,8 +2406,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_debug("cannot add token to error response\n"); coap_delete_pdu(response); return NULL; @@ -2458,20 +2544,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 */ @@ -2558,6 +2644,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; @@ -2645,14 +2732,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_string_t *query = NULL; coap_opt_t *observe = NULL; coap_string_t *uri_path = NULL; int added_block = 0; coap_lg_srcv_t *free_lg_srcv = NULL; #ifndef WITHOUT_ASYNC - coap_bin_const_t tokenc = { pdu->token_length, pdu->token }; coap_async_t *async; #endif /* WITHOUT_ASYNC */ @@ -2663,7 +2748,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, pdu->actual_token); if (async) { coap_tick_t now; @@ -2933,7 +3018,8 @@ 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, pdu->actual_token.length, + pdu->actual_token.s)) { int observe_action = COAP_OBSERVE_CANCEL; coap_block_b_t block; @@ -2984,12 +3070,12 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu goto skip_handler; } } - subscription = coap_add_observer(resource, session, &token, - pdu); + subscription = coap_add_observer(resource, session, + &pdu->actual_token, pdu); if (subscription) { uint8_t buf[4]; - coap_touch_observer(context, session, &token); + coap_touch_observer(context, session, &pdu->actual_token); coap_add_option_internal(response, COAP_OPTION_OBSERVE, coap_encode_var_safe(buf, sizeof (buf), resource->observe), @@ -2997,7 +3083,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu } } else if (observe_action == COAP_OBSERVE_CANCEL) { - coap_delete_observer(resource, session, &token); + coap_delete_observer(resource, session, &pdu->actual_token); } else { coap_log_info("observe: unexpected action %d\n", observe_action); @@ -3069,7 +3155,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu } if (COAP_RESPONSE_CLASS(response->code) > 2) { if (observe) - coap_delete_observer(resource, session, &token); + coap_delete_observer(resource, session, &pdu->actual_token); if (added_block) coap_remove_option(response, COAP_OPTION_BLOCK1); } @@ -3081,6 +3167,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; } @@ -3160,12 +3247,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)) { @@ -3183,6 +3271,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_debug("Extended Token requested size support not available\n"); + session->max_token_size = COAP_TOKEN_DEFAULT_MAX; + } + else { + coap_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 */ @@ -3227,6 +3332,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), @@ -3235,6 +3343,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) @@ -3265,6 +3383,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://rfc-editor.org/rfc/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_warn( + "coap_dispatch: cannot create error response\n"); + } + else { + /* + * Note - have to leave in oversize token as per + * https://rfc-editor.org/rfc/rfc7252#section-5.3.1 + */ + if (coap_send_internal(session, response) == COAP_INVALID_MID) + coap_log_warn("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) { @@ -3277,6 +3430,7 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, coap_opt_iterator_t opt_iter; coap_pdu_t *dec_pdu = NULL; #endif /* HAVE_OSCORE */ + int is_ext_token_rst; coap_show_pdu(COAP_LOG_DEBUG, pdu); @@ -3383,9 +3537,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 */ @@ -3405,7 +3557,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_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_alert("got RST for mid=0x%x\n", pdu->mid); if (session->con_active) { @@ -3421,14 +3583,14 @@ 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) { coap_check_update_token(sent->session, sent->pdu); 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); } @@ -3443,9 +3605,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; } } @@ -3463,6 +3623,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 */ @@ -3485,6 +3648,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 3bcb2abdac..ec84bcdbd0 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; @@ -271,6 +272,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; @@ -296,8 +303,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) { @@ -305,13 +314,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_warn( + "coap_add_token: Token size too large. Token ignored\n"); + return 0; + } + if (!coap_pdu_check_resize(pdu, len + bias)) { + coap_log_warn( + "coap_add_token: Insufficient space for token. Token ignored\n"); return 0; - 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 = (uint32_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; @@ -320,35 +366,72 @@ 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_warn( + "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_warn("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 && memcmp(pdu->token, data, len) != 0) - 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: + if (memcmp(pdu->token, data, len) != 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; } @@ -880,24 +963,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; @@ -906,6 +1009,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) { @@ -913,23 +1018,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_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_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; @@ -946,8 +1068,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: @@ -957,7 +1082,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: @@ -969,7 +1094,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: @@ -978,7 +1103,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: @@ -1069,7 +1194,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_debug("coap_pdu_parse: invalid Token\n"); return 0; } @@ -1208,6 +1333,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_warn( + "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) { @@ -1217,7 +1355,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); @@ -1238,7 +1376,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) { @@ -1248,7 +1386,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; @@ -1259,7 +1397,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; @@ -1271,7 +1409,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); @@ -1306,11 +1444,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 c9bb8810ec..e292a97e22 100644 --- a/src/resource.c +++ b/src/resource.c @@ -693,16 +693,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; } @@ -729,7 +728,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; @@ -757,8 +756,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; } } @@ -787,8 +785,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(COAP_SUBSCRIPTION, s); @@ -826,7 +824,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) { @@ -839,16 +837,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() >= COAP_LOG_DEBUG ) { + if (s && coap_get_log_level() >= COAP_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_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], @@ -887,7 +890,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; @@ -942,7 +945,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; @@ -953,9 +957,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); /* A lot of the reliable code assumes type is CON */ if (COAP_PROTO_NOT_RELIABLE(obs->session->proto) && @@ -1129,20 +1130,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 */ @@ -1153,7 +1152,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 4e7705b8eb..5088a04d34 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(COAP_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