From eb33a92564f0e611d79be34b6e010cfe73f7b5df Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Sat, 27 Sep 2025 18:48:09 +0100 Subject: [PATCH] Uri-Path-Abbrev: Initial IETF Draft support https://datatracker.ietf.org/doc/html/draft-ietf-core-uri-path-abbrev-02 --- README.md | 4 + configure.ac | 5 +- doc/main.md | 4 + examples/coap-client.c | 38 +++++-- examples/coap-server.c | 8 ++ include/coap3/coap_net_internal.h | 4 +- include/coap3/coap_pdu.h | 6 ++ include/coap3/coap_pdu_internal.h | 4 +- include/coap3/coap_proxy_internal.h | 10 ++ include/coap3/coap_session_internal.h | 1 + include/coap3/coap_uri.h | 75 ++++++++++++++ include/coap3/coap_uri_internal.h | 24 +++++ libcoap-3.map | 4 + libcoap-3.sym | 4 + man/Makefile.am | 3 + man/coap-client.txt.in | 5 +- man/coap_pdu_setup.txt.in | 16 ++- man/coap_uri.txt.in | 50 +++++++++- src/coap_async.c | 2 +- src/coap_block.c | 102 ++++++++++++++++--- src/coap_debug.c | 17 ++++ src/coap_net.c | 32 +++++- src/coap_oscore.c | 3 +- src/coap_pdu.c | 48 +++++++-- src/coap_proxy.c | 11 ++- src/coap_resource.c | 2 +- src/coap_uri.c | 136 +++++++++++++++++++++++++- src/oscore/oscore_context.c | 3 +- 28 files changed, 563 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 72b3491c8d..509d8a5add 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ The following RFCs are supported * [RFC9177: Constrained Application Protocol (CoAP) Block-Wise Transfer Options Supporting Robust Transmission](https://rfc-editor.org/rfc/rfc9177) +The following Internet Draft is supported + +* [I-D URI-Path abbreviation in CoAP](https://datatracker.ietf.org/doc/html/draft-ietf-core-uri-path-abbrev-02) + There is (D)TLS support for the following libraries * [OpenSSL](https://www.openssl.org) (Minimum version 1.1.0) [PKI, PSK and PKCS11] diff --git a/configure.ac b/configure.ac index 8a5e37f768..e0d79d18cf 100644 --- a/configure.ac +++ b/configure.ac @@ -243,7 +243,7 @@ WARNING_CFLAGS="\ # check whether or not the compiler supports -Wlogical-op (clang does not...) AX_CHECK_COMPILE_FLAG([-Wlogical-op], [WARNING_CFLAGS="$WARNING_CFLAGS -Wlogical-op"],,[-Werror]) AX_CHECK_COMPILE_FLAG([-fdiagnostics-color], [CFLAGS="$CFLAGS -fdiagnostics-color"],,[-Werror]) -AX_CHECK_COMPILE_FLAG([-Wunused-result], [WARNING_CFLAGS="$WARNING_CFLAGS -Wunused-result"]) +AX_CHECK_COMPILE_FLAG([-Wunused-result], [WARNING_CFLAGS="$WARNING_CFLAGS -Wunused-result"],,[-Werror]) # clang 14+ generates dwarf-5, which is not currently supported by valgrind if test "x$CC" = "xclang"; then @@ -1409,7 +1409,8 @@ libcoap Configuration Summary: libcoap ABI version : "$LIBCOAP_ABI_VERSION" libcoap libtool SO version : "$LIBCOAP_SO_VERSION" libcoap DTLS lib extn : "$LIBCOAP_DTLS_LIB_EXTENSION_NAME" - host system : "$host"]); + host system : "$host" + compiler : "$CC"]); if test "x$enable_server_mode" = "xyes"; then AC_MSG_RESULT([ build with server support : "yes"]) else diff --git a/doc/main.md b/doc/main.md index df20fdde85..d98ad7a908 100644 --- a/doc/main.md +++ b/doc/main.md @@ -48,6 +48,10 @@ The following RFCs are supported * [RFC9177: Constrained Application Protocol (CoAP) Block-Wise Transfer Options Supporting Robust Transmission](https://rfc-editor.org/rfc/rfc9177) +The following Internet Draft is supported + +* [I-D URI-Path abbreviation in CoAP](https://datatracker.ietf.org/doc/html/draft-ietf-core-uri-path-abbrev-02) + There is (D)TLS support for the following libraries * [OpenSSL](https://www.openssl.org) (Minimum version 1.1.0) [PKI, PSK and PKCS11] diff --git a/examples/coap-client.c b/examples/coap-client.c index 3b317850c9..b1ba8e3a1b 100644 --- a/examples/coap-client.c +++ b/examples/coap-client.c @@ -59,6 +59,11 @@ int flags = 0; static coap_session_t *global_session; static unsigned char _token_data[24]; /* With support for RFC8974 */ static coap_binary_t the_token = { 0, _token_data }; +static coap_upa_abbrev_t abbrev_mappings[] = { + { 0, ".well-known/core" }, + { 1, ".well-known/rd" } +}; + typedef struct { coap_binary_t *token; @@ -587,7 +592,7 @@ usage(const char *program, const char *version) { coap_string_tls_version(buffer, sizeof(buffer))); fprintf(stderr, "%s\n", coap_string_tls_support(buffer, sizeof(buffer))); fprintf(stderr, "\n" - "Usage: %s [-a addr] [-b [num,]size] [-e text] [-f file] [-g file]\n" + "Usage: %s [-a addr] [-b [num,]size] [-e text] [-f file] [-g file] [-i]\n" "\t\t[-l loss] [-m method] [-o file] [-p port] [-q tls_engine_conf_file]\n" "\t\t[-r] [-s duration] [-t type] [-v num] [-w] [-x] [-y rec_secs]\n" "\t\t[-z] [-A type] [-B seconds]\n" @@ -610,6 +615,7 @@ usage(const char *program, const char *version) { "\t-f file\t\tFile to send with PUT/POST (use '-' for STDIN)\n" "\t-g file\t\tFile to send with PUT/POST using individual block\n" "\t \t\trequest call-back\n" + "\t-i \t\tTest using Uri-Path-Abbrev path mapping\n" "\t-l list\t\tFail to send some datagrams specified by a comma\n" "\t \t\tseparated list of numbers or number ranges\n" "\t \t\t(for debugging only)\n" @@ -665,9 +671,9 @@ usage(const char *program, const char *version) { "\t \t\tcoap+ws, and coaps+ws\n" "\t-S \t\tUse Proxy-Scheme instead of Proxy-Uri option if -P\n" "\t \t\toption used\n" - "\t-T token\tDefine the initial starting token (up to 24 characters)\n" ,program, wait_seconds); fprintf(stderr, + "\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" @@ -1935,6 +1941,7 @@ main(int argc, char **argv) { uint8_t cid_every = 0; int report_ind_blocks = 0; coap_pdu_t *resp_pdu; + int use_abbrev = 0; #ifndef _WIN32 struct sigaction sa; #endif @@ -1950,7 +1957,7 @@ main(int argc, char **argv) { coap_startup(); while ((opt = getopt(argc, argv, - "a:b:c:d:e:f:g:h:j:k:l:m:no:p:q:rs:t:u:v:wxy:zA:B:C:E:G:H:J:K:L:M:NO:P:R:ST:UV:X:Y23")) != -1) { + "a:b:c:d:e:f:g:h:ij:k:l:m:no:p:q:rs:t:u:v:wxy:zA:B:C:E:G:H:J:K:L:M:NO:P:R:ST:UV:X:Y23")) != -1) { switch (opt) { case 'a': strncpy(node_str, optarg, NI_MAXHOST - 1); @@ -1991,6 +1998,9 @@ main(int argc, char **argv) { if (!cmdline_input_from_file_f(optarg)) payload.length = 0; break; + case 'i' : + use_abbrev = 1; + break; case 'j' : key_file = optarg; break; @@ -2259,12 +2269,22 @@ main(int argc, char **argv) { coap_session_init_token(session, the_token.length, the_token.s); /* Convert provided uri into CoAP options */ - if (!coap_uri_into_optlist((proxy.host.length && !use_proxy_scheme) ? - &proxy : &uri, !uri_host_option ? - &dst : NULL, - &optlist, create_uri_opts)) { - coap_log_err("Failed to create options for URI\n"); - goto failed; + if (use_abbrev) { + if (!coap_uri_into_optlist_abbrev((proxy.host.length && !use_proxy_scheme) ? + &proxy : &uri, !uri_host_option ? + &dst : NULL, + &optlist, create_uri_opts, abbrev_mappings, sizeof(abbrev_mappings)/sizeof(abbrev_mappings[0]))) { + coap_log_err("Failed to create options for URI\n"); + goto failed; + } + } else { + if (!coap_uri_into_optlist((proxy.host.length && !use_proxy_scheme) ? + &proxy : &uri, !uri_host_option ? + &dst : NULL, + &optlist, create_uri_opts)) { + coap_log_err("Failed to create options for URI\n"); + goto failed; + } } /* set block option if requested at commandline */ diff --git a/examples/coap-server.c b/examples/coap-server.c index 942a65dbbe..839cdae14b 100644 --- a/examples/coap-server.c +++ b/examples/coap-server.c @@ -147,6 +147,12 @@ static int call_home = 0; static const char *proxy_add_resource = NULL; static const char *proxy_add_check = NULL; +static coap_upa_abbrev_t abbrev_mappings[] = { + { 0, ".well-known/core" }, + { 1000, "example_data" }, + { 1001, "time" } +}; + static coap_dtls_pki_t *setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *sni); typedef struct psk_sni_def_t { @@ -2883,6 +2889,8 @@ main(int argc, char **argv) { coap_context_set_max_block_size(ctx, max_block_size); coap_context_set_session_reconnect_time2(ctx, reconnect_secs, 10); coap_context_set_keepalive(ctx, reconnect_secs ? reconnect_secs : 30); + coap_upa_server_mapping(abbrev_mappings, sizeof(abbrev_mappings)/sizeof(abbrev_mappings[0])); + if (report_each_block) coap_register_block_data_handler(ctx, individual_blocks); if (csm_max_message_size) diff --git a/include/coap3/coap_net_internal.h b/include/coap3/coap_net_internal.h index f42ceebb20..2aa2fa1012 100644 --- a/include/coap3/coap_net_internal.h +++ b/include/coap3/coap_net_internal.h @@ -224,8 +224,8 @@ struct coap_context_t { #endif /* COAP_CLIENT_SUPPORT */ uint32_t block_mode; /**< Zero or more COAP_BLOCK_ or'd options */ coap_resource_dynamic_create_t dyn_create_handler; /**< Dynamc resource create handler */ - uint32_t dynamic_cur; /* Current number of dynamic resources */ - uint32_t dynamic_max; /* Max number of dynamic resources or 0 is unlimited */ + uint32_t dynamic_cur; /**< Current number of dynamic resources */ + uint32_t dynamic_max; /**< Max number of dynamic resources or 0 is unlimited */ }; /** diff --git a/include/coap3/coap_pdu.h b/include/coap3/coap_pdu.h index f387a1f41c..d1f112b487 100644 --- a/include/coap3/coap_pdu.h +++ b/include/coap3/coap_pdu.h @@ -130,6 +130,7 @@ typedef enum coap_request_t { #define COAP_OPTION_OSCORE 9 /* C_____U, *, 0-255 B, RFC8613 */ #define COAP_OPTION_URI_PATH 11 /* CU-RE__, String, 0-255 B, RFC7252 */ #define COAP_OPTION_CONTENT_FORMAT 12 /* ____E__, uint, 0-2 B, RFC7252 */ +#define COAP_OPTION_URI_PATH_ABB 13 /* C___E__, uint, 0-4 B, RFC TBD */ #define COAP_OPTION_CONTENT_TYPE COAP_OPTION_CONTENT_FORMAT /* COAP_OPTION_MAXAGE default 60 seconds if not set */ #define COAP_OPTION_MAXAGE 14 /* _U-_E_U, uint, 0-4 B, RFC7252 */ @@ -375,6 +376,11 @@ typedef enum coap_pdu_code_t { COAP_SIGNALING_CODE_ABORT = COAP_SIGNALING_ABORT } coap_pdu_code_t; +typedef enum { + COAP_BOOL_FALSE, + COAP_BOOL_TRUE +} coap_bool_t; + /** * Creates a new CoAP PDU with at least enough storage space for the given * @p size maximum message size. The function returns a pointer to the diff --git a/include/coap3/coap_pdu_internal.h b/include/coap3/coap_pdu_internal.h index d6cc8c0911..e651846657 100644 --- a/include/coap3/coap_pdu_internal.h +++ b/include/coap3/coap_pdu_internal.h @@ -440,6 +440,7 @@ coap_pdu_release_lkd(coap_pdu_t *pdu) { * @param token The token to use in this duplicated PDU. * @param drop_options A list of options not to copy into the duplicated PDU. * If @c NULL, then all options are copied across. + * @param expand_opt_abb If COAP_BOOL_TRUE, try to expand out Uri-Path-Abbrev. * * @return The duplicated PDU or @c NULL if failure. */ @@ -447,7 +448,8 @@ coap_pdu_t *coap_pdu_duplicate_lkd(const coap_pdu_t *old_pdu, coap_session_t *session, size_t token_length, const uint8_t *token, - coap_opt_filter_t *drop_options); + coap_opt_filter_t *drop_options, + coap_bool_t expand_opt_abb); /** * Increment reference counter on a pdu to stop it prematurely getting freed off diff --git a/include/coap3/coap_proxy_internal.h b/include/coap3/coap_proxy_internal.h index 6771f771e4..90b8f990cc 100644 --- a/include/coap3/coap_proxy_internal.h +++ b/include/coap3/coap_proxy_internal.h @@ -270,6 +270,16 @@ void coap_proxy_del_req(coap_proxy_entry_t *proxy_entry, coap_proxy_req_t *prox void coap_delete_proxy_subscriber(coap_session_t *session, coap_bin_const_t *token, coap_mid_t mid, coap_proxy_subs_delete_t type); +/** + * coap_proxy_log_entry() is used to log a proxy status + * + * @param incoming The incoming proxy session. + * @param pdu The request PDU. + * @param upstream_token The token used for the ongoing session. + * @param type The change update type. + */ +void coap_proxy_log_entry(coap_session_t *incoming, const coap_pdu_t *pdu, + coap_bin_const_t *upstream_token, const char *type); /** @} */ #define PROXY_CACHE_ADD(e, obj) \ diff --git a/include/coap3/coap_session_internal.h b/include/coap3/coap_session_internal.h index 4e7e5a6dcb..bc6eddcae8 100644 --- a/include/coap3/coap_session_internal.h +++ b/include/coap3/coap_session_internal.h @@ -227,6 +227,7 @@ struct coap_session_t { coap_tick_t doing_first_timeout; /**< If doing_first, when to timeout */ coap_pdu_t *doing_first_pdu; /**< If doing fist, PDU to retry sending */ #endif /* COAP_CLIENT_SUPPORT */ + uint8_t no_path_abbrev; /**< Set is remote does not support Uri-Path-Abbrev */ coap_mid_t remote_test_mid; /**< mid used for checking remote support */ uint32_t max_token_size; /**< Largest token size supported RFC8974 */ diff --git a/include/coap3/coap_uri.h b/include/coap3/coap_uri.h index 8b6faadc67..4c261e7e0f 100644 --- a/include/coap3/coap_uri.h +++ b/include/coap3/coap_uri.h @@ -84,6 +84,12 @@ typedef struct { enum coap_uri_scheme_t scheme; } coap_uri_t; +typedef struct { + uint32_t upa_value; /**< The Uri-Path-Abbrev option value */ + const char *upa_path; /**< The Uri-Path-Abbrev option path representation + (withouot the leading '/') */ +} coap_upa_abbrev_t; + static inline int coap_uri_scheme_is_secure(const coap_uri_t *uri) { return uri && ((uri->scheme & COAP_URI_SCHEME_SECURE_MASK) != 0); @@ -222,6 +228,32 @@ int coap_uri_into_options(const coap_uri_t *uri, const coap_address_t *dst, int coap_uri_into_optlist(const coap_uri_t *uri, const coap_address_t *dst, coap_optlist_t **optlist_chain, int create_port_host_opt); +/** + * Takes a coap_uri_t and then adds CoAP options into the @p optlist_chain. + * If the port is not the default port and create_port_host_opt is not 0, then + * the Uri-Port option is added to the @p optlist_chain. + * If the dst defines an address that does not match the host in uri->host and + * is not 0, then the Uri-Host option is added to the @p optlist_chain. + * Any path or query are broken down into the individual segment Uri-Path + * or Uri-Path-Abbrev (if mapping match) or Uri-Query options and added to + * the @p optlist_chain. + * + * @param uri The coap_uri_t object. + * @param dst The destination, or NULL if URI_HOST not to be added. + * @param optlist_chain Where to store the chain of options. + * @param create_port_host_opt @c 1 if port/host option to be added + * (if non-default) else @c 0. + * @param mapping A path mapping to value for Uri-Path-Abbrev table or NULL. + * (path does not have the leading '/'). + * @param count Number of Uri-Path-Abbrev table entries. + * + * @return @c 1 on success, @c 0 if error. + * + */ +int coap_uri_into_optlist_abbrev(const coap_uri_t *uri, const coap_address_t *dst, + coap_optlist_t **optlist_chain, + int create_port_host_opt, + coap_upa_abbrev_t *mapping, uint32_t count); /** * Splits the given URI path into segments. Each segment is preceded @@ -261,6 +293,28 @@ int coap_split_path(const uint8_t *path, int coap_path_into_optlist(const uint8_t *path, size_t length, coap_option_num_t optnum, coap_optlist_t **optlist_chain); +/** + * Splits the given URI path into '/' separate segments, and then adds + * the Uri-Path / Location-Path option for each segment to the @p optlist_chain. + * + * Note: any segments that are just '.' or '..' are stripped out. + * + * @param path The path string to split. + * @param length The actual length of @p path. + * @param optnum The CoAP option (COAP_OPTION_URI_PATH or + * COAP_OPTION_LOCATION_PATH) + * @param optlist_chain The chain of optlists to add to. optlist_chain + * parent is to be NULL or a previous set of optlists. + * @param mapping A path mapping to value for Uri-Path-Abbrev table or NULL. + * (path does not have the leading '/'). + * @param count Number of Uri-Path-Abbrev table entries. + * + * @return @c 1 on success else @c 0 if error. + */ +int coap_path_into_optlist_abbrev(const uint8_t *path, size_t length, + coap_option_num_t optnum, + coap_optlist_t **optlist_chain, + coap_upa_abbrev_t *mapping, uint32_t count); /** * Splits the given URI query into segments. Each segment is preceded @@ -319,6 +373,27 @@ coap_string_t *coap_get_query(const coap_pdu_t *request); */ coap_string_t *coap_get_uri_path(const coap_pdu_t *request); +/** + * Define a Path to use if an Uri-Path-Abbrev option fails and the client + * is to retry the request using Uri-Path instread of Uri-Path-Abbrev. + * + * @param mapping A path mapping to value for Uri-Path-Abbrev table or NULL + if the previous mapping is to be cleared. + * (path does not have the leading '/'). + * @param count Number of Uri-Path-Abbrev table entries. + */ +void coap_upa_client_fallback(coap_upa_abbrev_t *mapping, uint32_t count); + +/** + * Define a Path to use on receipt of an Uri-Path-Abbrev option value. + * + * @param mapping A path mapping to value for Uri-Path-Abbrev table or NULL + if the previous mapping is to be cleared. + * (path does not have the leading '/'). + * @param count Number of Uri-Path-Abbrev table entries. + */ +void coap_upa_server_mapping(coap_upa_abbrev_t *mapping, uint32_t count); + /** @} */ #ifdef __cplusplus diff --git a/include/coap3/coap_uri_internal.h b/include/coap3/coap_uri_internal.h index 2d22f8d18b..aa20bf1650 100644 --- a/include/coap3/coap_uri_internal.h +++ b/include/coap3/coap_uri_internal.h @@ -37,7 +37,16 @@ typedef struct { coap_uri_scheme_t scheme; /**< scheme */ } coap_uri_info_t; +typedef struct coap_upa_chain_t { + struct coap_upa_chain_t *next; /**< Next entry in the chain */ + uint32_t upa_value; /**< The Uri-Path-Abbrev option value */ + char *upa_path; /**< The Uri-Path-Abbrev option path representation + (withouot the leading '/') */ +} coap_upa_chain_t; + extern coap_uri_info_t coap_uri_scheme[COAP_URI_SCHEME_LAST]; +extern coap_upa_chain_t *coap_upa_client_fallback_chain; +extern coap_upa_chain_t *coap_upa_server_mapping_chain; /** * replace any % hex definitions with the actual character. @@ -47,6 +56,21 @@ extern coap_uri_info_t coap_uri_scheme[COAP_URI_SCHEME_LAST]; */ void coap_replace_percents(coap_optlist_t *optlist); +/** + * Determine the expanded Uri-Path-Abbrev option value. + * + * @param chain Chain holding the information. + * @param value The Uri-Path-Abbrev numeric value + * + * @return The expanded textual path or @c NULL if not found. + */ +const char *coap_map_abbrev_uri_path(coap_upa_chain_t *chain, u_int value); + +int coap_map_uri_path_abbrev(coap_upa_chain_t *chain, const char *path, size_t length, + u_int *value); + +void coap_delete_upa_chain(coap_upa_chain_t *chain); + /** @} */ #ifdef __cplusplus diff --git a/libcoap-3.map b/libcoap-3.map index 4d15252388..8c492a1dde 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -206,6 +206,7 @@ global: coap_package_name; coap_package_version; coap_path_into_optlist; + coap_path_into_optlist_abbrev; coap_pdu_duplicate; coap_pdu_get_code; coap_pdu_get_mid; @@ -348,8 +349,11 @@ global: coap_tls_engine_configure; coap_tls_engine_remove; coap_tls_is_supported; + coap_upa_client_fallback; + coap_upa_server_mapping; coap_uri_into_options; coap_uri_into_optlist; + coap_uri_into_optlist_abbrev; coap_verify_proxy_scheme_supported; coap_write_block_b_opt; coap_write_block_opt; diff --git a/libcoap-3.sym b/libcoap-3.sym index 3c41ac2147..83f9692177 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -204,6 +204,7 @@ coap_package_build coap_package_name coap_package_version coap_path_into_optlist +coap_path_into_optlist_abbrev coap_pdu_duplicate coap_pdu_get_code coap_pdu_get_mid @@ -346,8 +347,11 @@ coap_ticks_to_rt_us coap_tls_engine_configure coap_tls_engine_remove coap_tls_is_supported +coap_upa_client_fallback +coap_upa_server_mapping coap_uri_into_options coap_uri_into_optlist +coap_uri_into_optlist_abbrev coap_verify_proxy_scheme_supported coap_write_block_b_opt coap_write_block_opt diff --git a/man/Makefile.am b/man/Makefile.am index 738838f157..78b6abd77e 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -198,6 +198,7 @@ install-man: install-man3 install-man5 install-man7 @echo ".so man3/coap_pdu_setup.3" > coap_add_data.3 @echo ".so man3/coap_pdu_setup.3" > coap_add_data_blocked_response.3 @echo ".so man3/coap_pdu_setup.3" > coap_path_into_optlist.3 + @echo ".so man3/coap_pdu_setup.3" > coap_path_into_optlist_abbrev.3 @echo ".so man3/coap_pdu_setup.3" > coap_split_path.3 @echo ".so man3/coap_pdu_setup.3" > coap_query_into_optlist.3 @echo ".so man3/coap_pdu_setup.3" > coap_split_query.3 @@ -255,6 +256,8 @@ install-man: install-man3 install-man5 install-man7 @echo ".so man3/coap_supported.3" > coap_tls_is_supported.3 @echo ".so man3/coap_supported.3" > coap_ws_is_supported.3 @echo ".so man3/coap_supported.3" > coap_wss_is_supported.3 + @echo ".so man3/coap_uri.3" > coap_upa_client_fallback.3 + @echo ".so man3/coap_uri.3" > coap_upa_server_mapping.3 $(INSTALL_DATA) $(A2X_EXTRA_PAGES_3) "$(DESTDIR)$(man3dir)" $(INSTALL_DATA) $(A2X_EXTRA_PAGES_5) "$(DESTDIR)$(man5dir)" diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index 44dc31c1a3..aeddda7d6b 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -20,7 +20,7 @@ coap-client-notls SYNOPSIS -------- *coap-client* [*-a* addr] [*-b* [num,]size] [*-e* text] [*-f* file] [*-g* file] - [*-l* loss] + [*-i*] [*-l* loss] [*-m* method] [*-o* file] [*-p* port] [*-q* tls_engine_conf_file] [*-r*] [*-s duration*] [*-t* type] [*-v* num] [*-w*] [*-x*] [*-y* rec_secs] [*-z*] [*-A* type] [*-B* seconds] @@ -82,6 +82,9 @@ OPTIONS - General *-g* file:: File to send with PUT/POST using individual block request call-back. +*-i* :: + Test using Uri-Path-Abbrev path mapping. + *-l* list:: Fail to send some datagrams specified by a comma separated list of numbers or number ranges (debugging only). diff --git a/man/coap_pdu_setup.txt.in b/man/coap_pdu_setup.txt.in index 2684ea8924..3e80d2dd6b 100644 --- a/man/coap_pdu_setup.txt.in +++ b/man/coap_pdu_setup.txt.in @@ -27,6 +27,7 @@ coap_add_option, coap_add_data, coap_add_data_blocked_response, coap_path_into_optlist, +coap_path_into_optlist_abbrev, coap_split_path, coap_query_into_optlist, coap_split_query, @@ -84,6 +85,10 @@ const uint8_t *_data_);* *int coap_path_into_optlist(const uint8_t *_path_, size_t _length_, coap_option_num_t _optnum_, coap_optlist_t **_optlist_chain_);* +*int coap_path_into_optlist_abbrev(const uint8_t *_path_, size_t _length_, +coap_option_num_t _optnum_, coap_optlist_t **_optlist_chain_, +coap_upa_abbrev_t *_mapping_, uint32_t _count_);* + *int coap_split_path(const uint8_t *_path_, size_t _length_, uint8_t *_buffer_, size_t *_buflen_);* @@ -427,6 +432,13 @@ segment (if just added by *coap_path_into_optlist*()) to be removed from _optlist_chain_. Any % definitions are replaced by the actual byte. _optnum_ should be one of COAP_OPTION_URI_PATH or COAP_OPTION_LOCATION_PATH. +*Function: coap_path_into_optlist_abbrev()* + +The *coap_path_into_optlist_abbrev*() function is the same as +*coap_path_into_optlist*() but supports mapping of _path_ into a +Uri-Path-Abbrev option based on the provided mapping of paths into values +using the provided _mapping_ table which has _count_ entries. + *Function: coap_split_path()* The *coap_split_path*() function splits up _path_ of length _length_ into @@ -582,8 +594,8 @@ encoded (which can be 0 when encoding 0) or 0 on failure. *coap_add_option*() returns the size of option added, or 0 on failure. -*coap_path_into_optlist*() and *coap_query_into_optlist*() return 1 on -success or 0 on failure. +*coap_path_into_optlist*(), *coap_path_into_optlist_abbrev*() and +*coap_query_into_optlist*() return 1 on success or 0 on failure. *coap_split_path*() and *coap_split_query*() return the number of components found. diff --git a/man/coap_uri.txt.in b/man/coap_uri.txt.in index 1696c992bc..78c559401c 100644 --- a/man/coap_uri.txt.in +++ b/man/coap_uri.txt.in @@ -17,7 +17,10 @@ coap_new_uri, coap_clone_uri, coap_delete_uri, coap_uri_into_optlist, -coap_uri_into_options +coap_uri_into_optlist_abbrev, +coap_uri_into_options, +coap_upa_client_fallback, +coap_upa_server_mapping - Work with CoAP URIs SYNOPSIS @@ -40,10 +43,18 @@ coap_uri_t *_uri_);* const coap_address_t *_dst_, coap_optlist_t **_optlist_chain_, int _create_port_host_opt_);* +*int coap_uri_into_optlist_abbrev(const coap_uri_t *_uri_, +const coap_address_t *_dst_, coap_optlist_t **_optlist_chain_, +int _create_port_host_opt_, coap_upa_abbrev_t *_mapping_, uint32_t _count_);* + *int coap_uri_into_options(const coap_uri_t *_uri_, const coap_address_t *_dst_, coap_optlist_t **_optlist_chain_, int _create_port_host_opt_, uint8_t *_buf_, size_t _buflen_);* +*void coap_upa_client_fallback(coap_upa_abbrev_t *_mapping_, uint32_t _count_);* + +*void coap_upa_server_mapping(coap_upa_abbrev_t *_mapping_, uint32_t _count_);* + 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*, @@ -86,6 +97,15 @@ typedef struct { } coap_uri_t; ---- +[source, c] +---- +typedef struct { + u_int upa_value; /* The Uri-Path-Abbrev option value. */ + const char *upa_path; /* The Uri-Path-Abbrev option path representation + (withouot the leading '/') */ +} coap_upa_abbrev_t; +---- + FUNCTIONS --------- @@ -144,18 +164,44 @@ then added to _optlist_chain_. *NOTE:* It is the responsibility of the application to free off the entries added to _optlist_chain_ using *coap_delete_optlist*(3). +*Function: coap_uri_into_optlist_abbrev()* + +The *coap_uri_into_optlist_abbrev*() is the same as *coap_uri_into_optlist*() +but supports mapping of the path component into a Uri-Path-Abbrev option based +on the provided mapping of paths into values using the provided _mapping_ +table which has _count_ entries. If no _mapping_match_, then Uri-Path options +are created as per *coap_uri_into_optlist*(). + *Function: coap_uri_into_options()* The *coap_uri_into_options*() function has the same functionality as *coap_uri_into_optlist*() except that _buf_ and _buflen_ are ignored, but the return values are different. +*Function: coap_upa_client_fallback()* + +The *coap_upa_client_fallback*() function defines a _mapping_ of _count_ entries +that are to be checked against if there is a Uri-Path-Abbrev option request +failure, and if a upa_value match, then the upa_path is to be converted into +Uri-Path options replacing the Uri-Path-Abbrev option and the request retransmitted. +Any existing mapping is overwritten. If _mapping_ is NULL, then the previous +mapping is cleared out. + +*Function: coap_upa_server_mapping()* + +The *coap_upa_server_mapping*() function defines a _mapping_ of _count_ entries +that are to be checked against if there is a Uri-Path-Abbrev option request, +and if a upa_value match, then the option is converted into a path using upa_path. +Any existing mapping is overwritten. If _mapping_ is NULL, then the previous +mapping is cleared out. + RETURN VALUES ------------- *coap_split_uri*(), *coap_split_proxy_uri*(), and *coap_uri_into_options*() return 0 on success, else < 0 on failure. -*coap_uri_into_optlist*() returns 1 on success, 0 on failure. +*coap_uri_into_optlist*() and *coap_uri_into_optlist_abbrev*() return 1 on +success, 0 on failure. *coap_new_uri*() and *coap_clone_uri*() return a newly allocated coap_uri_t structure or NULL on failure. diff --git a/src/coap_async.c b/src/coap_async.c index c7b81edc80..8d166cbd06 100644 --- a/src/coap_async.c +++ b/src/coap_async.c @@ -90,7 +90,7 @@ coap_register_async_lkd(coap_session_t *session, /* Note that this generates a new MID */ s->pdu = coap_pdu_duplicate_lkd(request, session, request->actual_token.length, - request->actual_token.s, NULL); + request->actual_token.s, NULL, COAP_BOOL_FALSE); if (s->pdu == NULL) { coap_free_async_lkd(session, s); coap_log_crit("coap_register_async: insufficient memory\n"); diff --git a/src/coap_block.c b/src/coap_block.c index 57938abc2e..85412deebe 100644 --- a/src/coap_block.c +++ b/src/coap_block.c @@ -588,7 +588,8 @@ coap_cancel_observe_lkd(coap_session_t *session, coap_binary_t *token, session, otoken->length, otoken->s, - NULL); + NULL, + COAP_BOOL_FALSE); lg_crcv->observe_set = 0; if (pdu == NULL) @@ -700,7 +701,7 @@ coap_retransmit_oscore_pdu(coap_session_t *session, ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); /* There could be a Block option in pdu */ resend_pdu = coap_pdu_duplicate_lkd(pdu, session, ltoken_len, - ltoken, NULL); + ltoken, NULL, COAP_BOOL_FALSE); if (!resend_pdu) goto error; if (echo) { @@ -1579,7 +1580,7 @@ coap_build_missing_pdu(coap_session_t *session, coap_lg_crcv_t *lg_crcv) { coap_option_filter_set(&drop_options, COAP_OPTION_Q_BLOCK2); coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, - &drop_options); + &drop_options, COAP_BOOL_FALSE); if (!pdu) return NULL; pdu->type = lg_crcv->last_type; @@ -2112,7 +2113,7 @@ coap_send_q_blocks(coap_session_t *session, coap_option_filter_set(&drop_options, lg_xmit->option); block_pdu = coap_pdu_duplicate_lkd(pdu, session, ltoken_length, - ptoken, &drop_options); + ptoken, &drop_options, COAP_BOOL_FALSE); if (block_pdu->type == COAP_MESSAGE_ACK) block_pdu->type = COAP_MESSAGE_CON; } @@ -2154,7 +2155,8 @@ coap_send_q_blocks(coap_session_t *session, ptoken = ltoken; } t_pdu = coap_pdu_duplicate_lkd(block_pdu, session, - ltoken_length, ptoken, &drop_options); + ltoken_length, ptoken, &drop_options, + COAP_BOOL_FALSE); } if (!coap_update_option(block_pdu, lg_xmit->option, coap_encode_var_safe(buf, @@ -2926,10 +2928,13 @@ coap_handle_request_send_block(coap_session_t *session, if (out_blocks[i].is_continue) { out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, lg_xmit->sent_pdu->actual_token.length, - lg_xmit->sent_pdu->actual_token.s, &drop_options); + lg_xmit->sent_pdu->actual_token.s, + &drop_options, COAP_BOOL_FALSE); } else { - out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, pdu->actual_token.length, - pdu->actual_token.s, &drop_options); + out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, + pdu->actual_token.length, + pdu->actual_token.s, + &drop_options, COAP_BOOL_FALSE); } if (!out_pdu) { goto internal_issue; @@ -3441,7 +3446,7 @@ coap_handle_request_put_block(coap_context_t *context, session, response->actual_token.length, response->actual_token.s, - NULL); + NULL, COAP_BOOL_FALSE); if (tmp_pdu) { tmp_pdu->code = COAP_RESPONSE_CODE(231); if (coap_send_internal(session, tmp_pdu, NULL) == COAP_INVALID_MID) { @@ -3620,7 +3625,8 @@ check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent, ++lg_crcv->retry_counter); } ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); - echo_pdu = coap_pdu_duplicate_lkd(sent, session, ltoken_len, ltoken, NULL); + echo_pdu = coap_pdu_duplicate_lkd(sent, session, ltoken_len, ltoken, + NULL, COAP_BOOL_FALSE); if (!echo_pdu) return 0; if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO, @@ -3781,7 +3787,8 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, } } } - pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, len, buf, NULL); + pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, + len, buf, NULL, COAP_BOOL_FALSE); if (!pdu) goto fail_body; @@ -3936,7 +3943,7 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count); ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token); pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, ltoken_length, - ltoken, NULL); + ltoken, NULL, COAP_BOOL_FALSE); if (!pdu) goto fail_body; @@ -4079,6 +4086,7 @@ coap_handle_response_get_block(coap_context_t *context, uint16_t block_opt = 0; size_t offset; int ack_rst_sent = 0; + coap_opt_iterator_t opt_iter; coap_lock_check_locked(); memset(&block, 0, sizeof(block)); @@ -4089,7 +4097,6 @@ coap_handle_response_get_block(coap_context_t *context, if (lg_crcv) { size_t chunk = 0; uint8_t buf[8]; - coap_opt_iterator_t opt_iter; if (COAP_RESPONSE_CLASS(rcvd->code) == 2) { size_t length; @@ -4218,7 +4225,8 @@ coap_handle_response_get_block(coap_context_t *context, memset(&drop_options, 0, sizeof(coap_opt_filter_t)); coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); coap_option_filter_set(&drop_options, COAP_OPTION_BLOCK1); - pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, &drop_options); + pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, + &drop_options, COAP_BOOL_FALSE); if (!pdu) goto fail_resp; @@ -4364,7 +4372,8 @@ coap_handle_response_get_block(coap_context_t *context, len = coap_encode_var_safe8(buf, sizeof(token), token); memset(&drop_options, 0, sizeof(coap_opt_filter_t)); coap_option_filter_set(&drop_options, COAP_OPTION_BLOCK1); - pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, &drop_options); + pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, + &drop_options, COAP_BOOL_FALSE); if (!pdu) goto fail_resp; @@ -4509,8 +4518,52 @@ coap_handle_response_get_block(coap_context_t *context, #endif /* !COAP_OSCORE_SUPPORT */ goto skip_app_handler; goto expire_lg_crcv; + } else if (rcvd->code == COAP_RESPONSE_CODE(402)) { + coap_opt_t *abb_opt = coap_check_option(sent, + COAP_OPTION_URI_PATH_ABB, + &opt_iter); + if (abb_opt) { + /* Send the request again with the Uri-Path-Abbrev expanded out */ + coap_pdu_t *pdu; + size_t data_len; + const uint8_t *data; + uint64_t token; + uint8_t ltoken[8]; + size_t ltoken_len; + + session->no_path_abbrev = 1; + token = STATE_TOKEN_FULL(lg_crcv->state_token, + ++lg_crcv->retry_counter); + ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); + pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, ltoken_len, + ltoken, NULL, COAP_BOOL_TRUE); + if (pdu) { + if (coap_get_data(lg_crcv->sent_pdu, &data_len, &data)) { + coap_add_data(pdu, data_len, data); + } +#if COAP_PROXY_SUPPORT + coap_proxy_req_t *proxy_req = session->context->proxy_list_count ? + coap_proxy_map_outgoing_request(session, rcvd, NULL) : NULL; + + if (proxy_req) { + coap_bin_const_t *new = coap_new_bin_const(ltoken, ltoken_len); + if (new) { + coap_delete_bin_const(proxy_req->token_used); + proxy_req->token_used = new; + coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used, "upd"); + } + } +#endif /* COAP_PROXY_SUPPORT */ + coap_log_debug("* Retransmitting PDU with Uri-Path-Abbrev replaced (1)\n"); + coap_delete_pdu_lkd(lg_crcv->sent_pdu); + lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu); + coap_send_internal(session, pdu, NULL); + goto skip_app_handler; + } + } + goto expire_lg_crcv; } else { - /* Not 2.xx or 4.01 - assume it is a failure of some sort */ + /* Not 2.xx, 4.01 or 4.02 - assume it is a failure of some sort */ goto expire_lg_crcv; } if (!block.m && !lg_crcv->observe_set) { @@ -4589,6 +4642,23 @@ coap_handle_response_get_block(coap_context_t *context, return coap_handle_response_get_block(context, session, sent, rcvd, COAP_RECURSE_NO); } + } else if (rcvd->code == COAP_RESPONSE_CODE(402)) { + coap_opt_t *abb_opt = coap_check_option(sent, + COAP_OPTION_URI_PATH_ABB, + &opt_iter); + if (abb_opt) { + /* + * Send the request again with the Uri-Path-Abbrev expanded out, but need + lg_crcv in place to handle the token update. + */ + lg_crcv = coap_block_new_lg_crcv(session, sent, NULL); + + if (lg_crcv) { + LL_PREPEND(session->lg_crcv, lg_crcv); + return coap_handle_response_get_block(context, session, sent, rcvd, + COAP_RECURSE_NO); + } + } } } return 0; diff --git a/src/coap_debug.c b/src/coap_debug.c index 991a5d9b02..48066cd512 100644 --- a/src/coap_debug.c +++ b/src/coap_debug.c @@ -610,6 +610,7 @@ msg_option_string(uint8_t code, uint16_t option_type) { { COAP_OPTION_OSCORE, "Oscore" }, { COAP_OPTION_URI_PATH, "Uri-Path" }, { COAP_OPTION_CONTENT_FORMAT, "Content-Format" }, + { COAP_OPTION_URI_PATH_ABB, "Uri-Path-Abbrev" }, { COAP_OPTION_MAXAGE, "Max-Age" }, { COAP_OPTION_URI_QUERY, "Uri-Query" }, { COAP_OPTION_HOP_LIMIT, "Hop-Limit" }, @@ -813,6 +814,8 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { const uint8_t *opt_val; size_t outbuflen = 0; int is_oscore_payload = 0; + const char *exp; + u_int value; /* Save time if not needed */ if (level > coap_get_log_level()) @@ -1031,6 +1034,20 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { } buf_len = strlen((char *)buf); break; + case COAP_OPTION_URI_PATH_ABB: + value = coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option)); + exp = coap_map_abbrev_uri_path(coap_upa_server_mapping_chain, value); + if (!exp) { + exp = coap_map_abbrev_uri_path(coap_upa_client_fallback_chain, value); + } + if (exp) { + snprintf((char *)&buf, sizeof(buf), "%s", exp); + } else { + snprintf((char *)&buf, sizeof(buf), "(%u)", value); + } + buf_len = strlen((char *)buf); + break; default: /* generic output function for all other option types */ if (opt_iter.number == COAP_OPTION_URI_PATH || diff --git a/src/coap_net.c b/src/coap_net.c index f915ca30ea..dcf01d6cda 100644 --- a/src/coap_net.c +++ b/src/coap_net.c @@ -975,6 +975,7 @@ coap_option_check_critical(coap_session_t *session, case COAP_OPTION_IF_NONE_MATCH: case COAP_OPTION_URI_PORT: case COAP_OPTION_URI_PATH: + case COAP_OPTION_URI_PATH_ABB: case COAP_OPTION_URI_QUERY: case COAP_OPTION_ACCEPT: case COAP_OPTION_PROXY_URI: @@ -1614,6 +1615,27 @@ coap_send_lkd(coap_session_t *session, coap_pdu_t *pdu) { return coap_send_internal(session, pdu, NULL); } + if (session->no_path_abbrev) { + opt = coap_check_option(pdu, COAP_OPTION_URI_PATH_ABB, &opt_iter); + if (opt) { + /* Server cannot handle Uri-Path-Abbrev */ + coap_pdu_t *new; + size_t data_len; + const uint8_t *data; + + new = coap_pdu_duplicate_lkd(pdu, session, pdu->actual_token.length, + pdu->actual_token.s, NULL, COAP_BOOL_TRUE); + if (new) { + if (coap_get_data(pdu, &data_len, &data)) { + coap_add_data(pdu, data_len, data); + } + coap_log_debug("* Retransmitting PDU with Uri-Path-Abbrev replaced (3)\n"); + coap_delete_pdu_lkd(pdu); + pdu = new; + } + } + } + if (COAP_PDU_IS_REQUEST(pdu)) { uint8_t buf[4]; @@ -3699,8 +3721,10 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu } uri_path = coap_get_uri_path(pdu); - if (!uri_path) - return; + if (!uri_path) { + resp = 402; + goto fail_response; + } if (!is_proxy_uri && !is_proxy_scheme) { /* try to find the resource from the request URI */ @@ -5236,6 +5260,10 @@ coap_cleanup(void) { #endif /* WITH_LWIP */ coap_dtls_shutdown(); + coap_delete_upa_chain(coap_upa_client_fallback_chain); + coap_upa_client_fallback_chain = NULL; + coap_delete_upa_chain(coap_upa_server_mapping_chain); + coap_upa_server_mapping_chain = NULL; #if COAP_THREAD_SAFE coap_mutex_destroy(&m_show_pdu); coap_mutex_destroy(&m_log_impl); diff --git a/src/coap_oscore.c b/src/coap_oscore.c index 1e5b4da9a2..c678378414 100644 --- a/src/coap_oscore.c +++ b/src/coap_oscore.c @@ -794,7 +794,8 @@ coap_oscore_new_pdu_encrypted_lkd(coap_session_t *session, association->sent_pdu = coap_pdu_duplicate_lkd(pdu, session, pdu_token.length, - pdu_token.s, NULL); + pdu_token.s, NULL, + COAP_BOOL_FALSE); if (association->sent_pdu == NULL) goto error; if (coap_get_data(pdu, &size, &data)) { diff --git a/src/coap_pdu.c b/src/coap_pdu.c index 94641cc7b1..5f8395f2d0 100644 --- a/src/coap_pdu.c +++ b/src/coap_pdu.c @@ -221,7 +221,7 @@ coap_pdu_duplicate(const coap_pdu_t *old_pdu, session, token_length, token, - drop_options); + drop_options, COAP_BOOL_FALSE); coap_lock_unlock(); return new_pdu; } @@ -235,7 +235,8 @@ coap_pdu_duplicate_lkd(const coap_pdu_t *old_pdu, coap_session_t *session, size_t token_length, const uint8_t *token, - coap_opt_filter_t *drop_options) { + coap_opt_filter_t *drop_options, + coap_bool_t expand_opt_abb) { #if COAP_CLIENT_SUPPORT uint8_t doing_first = session->doing_first; #endif /* COAP_CLIENT_SUPPORT */ @@ -265,7 +266,7 @@ coap_pdu_duplicate_lkd(const coap_pdu_t *old_pdu, coap_add_token(pdu, token_length, token); pdu->lg_xmit = old_pdu->lg_xmit; - if (drop_options == NULL) { + if (drop_options == NULL && expand_opt_abb == COAP_BOOL_FALSE) { /* Drop COAP_PAYLOAD_START as well if data */ size_t length = old_pdu->used_size - old_pdu->e_token_length - (old_pdu->data ? @@ -284,12 +285,41 @@ coap_pdu_duplicate_lkd(const coap_pdu_t *old_pdu, coap_option_iterator_init(old_pdu, &opt_iter, COAP_OPT_ALL); while ((option = coap_option_next(&opt_iter))) { - if (drop_options && coap_option_filter_get(drop_options, opt_iter.number)) - continue; - if (!coap_add_option_internal(pdu, opt_iter.number, - coap_opt_length(option), - coap_opt_value(option))) - goto fail; + if (opt_iter.number == COAP_OPTION_URI_PATH_ABB && expand_opt_abb == COAP_BOOL_TRUE) { + u_int value; + const char *exp; + + value = coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option)); + exp = coap_map_abbrev_uri_path(coap_upa_server_mapping_chain, value); + if (exp) { + while (exp) { + const char *next = strchr(exp, '/'); + + if (!coap_insert_option(pdu, COAP_OPTION_URI_PATH, + next ? (int)(next - exp) : (int)strlen(exp), + (const uint8_t *)exp)) + goto fail; + if (next) + exp = next + 1; + else + exp = NULL; + } + } else { + coap_delete_pdu_lkd(pdu); + coap_log_info("coap_pdu_duplicate: Uri-Path-Abbrev value %u not known for fallback\n", value); + return NULL; + } + } else { + if (drop_options) { + if (coap_option_filter_get(drop_options, opt_iter.number)) + continue; + } + if (!coap_insert_option(pdu, opt_iter.number, + coap_opt_length(option), + coap_opt_value(option))) + goto fail; + } } } return pdu; diff --git a/src/coap_proxy.c b/src/coap_proxy.c index 544f3e3931..0e1188facf 100644 --- a/src/coap_proxy.c +++ b/src/coap_proxy.c @@ -38,7 +38,7 @@ coap_proxy_is_supported(void) { return 1; } -static void +void coap_proxy_log_entry(coap_session_t *incoming, const coap_pdu_t *pdu, coap_bin_const_t *upstream_token, const char *type) { if (coap_get_log_level() >= COAP_LOG_DEBUG) { @@ -805,11 +805,11 @@ coap_proxy_call_response_handler(coap_session_t *session, const coap_pdu_t *sent coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); /* Correct the token */ resp_pdu = coap_pdu_duplicate_lkd(rcvd, session, token->length, token->s, - &drop_options); + &drop_options, COAP_BOOL_FALSE); } else { /* Correct the token */ resp_pdu = coap_pdu_duplicate_lkd(rcvd, session, token->length, token->s, - NULL); + NULL, COAP_BOOL_FALSE); } if (!resp_pdu) return COAP_RESPONSE_FAIL; @@ -1153,7 +1153,8 @@ coap_proxy_forward_request_lkd(coap_session_t *session, /* * Duplicate request PDU for onward transmission (with new token). */ - pdu = coap_pdu_duplicate_lkd(request, proxy_entry->ongoing, token_len, token, NULL); + pdu = coap_pdu_duplicate_lkd(request, proxy_entry->ongoing, token_len, + token, NULL, COAP_BOOL_FALSE); if (!pdu) { coap_log_debug("proxy: PDU generation error\n"); goto failed; @@ -1479,7 +1480,7 @@ coap_proxy_process_incoming(coap_session_t *session, session, rcvd->actual_token.length, rcvd->actual_token.s, - NULL); + NULL, COAP_BOOL_FALSE); if (proxy_cache->rsp_pdu) { if (coap_get_data(rcvd, &data_len, &data)) { coap_binary_t *copy; diff --git a/src/coap_resource.c b/src/coap_resource.c index 25013bd547..45db60ba03 100644 --- a/src/coap_resource.c +++ b/src/coap_resource.c @@ -873,7 +873,7 @@ coap_add_observer(coap_resource_t *resource, coap_subscription_init(s); s->pdu = coap_pdu_duplicate_lkd(request, session, token->length, - token->s, NULL); + token->s, NULL, COAP_BOOL_FALSE); if (s->pdu == NULL) { coap_delete_cache_key(cache_key); coap_free_type(COAP_SUBSCRIPTION, s); diff --git a/src/coap_uri.c b/src/coap_uri.c index 46879bb959..f1e93112eb 100644 --- a/src/coap_uri.c +++ b/src/coap_uri.c @@ -29,6 +29,9 @@ #define strncasecmp _strnicmp #endif +coap_upa_chain_t *coap_upa_client_fallback_chain = NULL; +coap_upa_chain_t *coap_upa_server_mapping_chain = NULL; + /** * A length-safe version of strchr(). This function returns a pointer * to the first occurrence of @p c in @p s, or @c NULL if not found. @@ -321,6 +324,13 @@ coap_uri_into_options(const coap_uri_t *uri, const coap_address_t *dst, int coap_uri_into_optlist(const coap_uri_t *uri, const coap_address_t *dst, coap_optlist_t **optlist_chain, int create_port_host_opt) { + return coap_uri_into_optlist_abbrev(uri, dst, optlist_chain, create_port_host_opt, NULL, 0); +} + +int +coap_uri_into_optlist_abbrev(const coap_uri_t *uri, const coap_address_t *dst, + coap_optlist_t **optlist_chain, int create_port_host_opt, + coap_upa_abbrev_t *mapping, uint32_t count) { if (create_port_host_opt && !coap_host_is_unix_domain(&uri->host)) { int add_option = 0; @@ -390,8 +400,8 @@ coap_uri_into_optlist(const coap_uri_t *uri, const coap_address_t *dst, } if (uri->path.length) { - if (!coap_path_into_optlist(uri->path.s, uri->path.length, COAP_OPTION_URI_PATH, - optlist_chain)) + if (!coap_path_into_optlist_abbrev(uri->path.s, uri->path.length, COAP_OPTION_URI_PATH, + optlist_chain, mapping, count)) return 0; } @@ -754,6 +764,13 @@ backup_optlist(coap_optlist_t **optlist_begin) { int coap_path_into_optlist(const uint8_t *s, size_t length, coap_option_num_t optnum, coap_optlist_t **optlist_chain) { + return coap_path_into_optlist_abbrev(s, length, optnum, optlist_chain, NULL, 0); +} + +int +coap_path_into_optlist_abbrev(const uint8_t *s, size_t length, coap_option_num_t optnum, + coap_optlist_t **optlist_chain, coap_upa_abbrev_t *mapping, + uint32_t count) { const uint8_t *p = s; coap_optlist_t *optlist; int num_dots; @@ -766,6 +783,23 @@ coap_path_into_optlist(const uint8_t *s, size_t length, coap_option_num_t optnum optlist_start = optlist_chain; } + if (length > 0 && mapping) { + uint32_t i; + uint8_t buf[4]; + + for (i = 0; i < count; i++) { + if (strlen(mapping[i].upa_path) == length && + memcmp(mapping[i].upa_path, s, length) == 0) { + /* add in as Uri_path-Abbrev */ + optlist = coap_new_optlist(COAP_OPTION_URI_PATH_ABB, + coap_encode_var_safe(buf, sizeof(buf), mapping[i].upa_value), buf); + if (!coap_insert_optlist(optlist_chain, optlist)) { + return 0; + } + return 1; + } + } + } while (length > 0 && !strnchr((const uint8_t *)"?#", 2, *s)) { if (*s == '/') { /* start of new path element */ /* start new segment */ @@ -1016,6 +1050,33 @@ coap_get_query(const coap_pdu_t *request) { return query; } +const char * +coap_map_abbrev_uri_path(coap_upa_chain_t *chain, u_int value) { + while (chain) { + coap_upa_chain_t *next = chain->next; + + if (chain->upa_value == value) { + return chain->upa_path; + } + chain = next; + } + return NULL; +} + +int +coap_map_uri_path_abbrev(coap_upa_chain_t *chain, const char *path, size_t length, u_int *value) { + while (chain) { + coap_upa_chain_t *next = chain->next; + + if (strlen(chain->upa_path) == length && memcmp(chain->upa_path, path, length) == 0) { + *value = chain->upa_value; + return 1; + } + chain = next; + } + return 0; +} + coap_string_t * coap_get_uri_path(const coap_pdu_t *request) { coap_opt_iterator_t opt_iter; @@ -1025,6 +1086,25 @@ coap_get_uri_path(const coap_pdu_t *request) { size_t length = 0; static const uint8_t hex[] = "0123456789ABCDEF"; + q = coap_check_option(request, COAP_OPTION_URI_PATH_ABB, &opt_iter); + if (q) { + u_int value; + const char *exp; + if (coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter) || + coap_check_option(request, COAP_OPTION_URI_PATH, &opt_iter)) { + return NULL; + } + value = coap_decode_var_bytes(coap_opt_value(q), coap_opt_length(q)); + exp = coap_map_abbrev_uri_path(coap_upa_server_mapping_chain, value); + if (exp) { + uri_path = coap_new_string(strlen(exp)); + if (uri_path) { + memcpy(uri_path->s, exp, strlen(exp)); + } + return uri_path; + } + return NULL; + } q = coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter); if (q) { coap_uri_t uri; @@ -1045,7 +1125,7 @@ coap_get_uri_path(const coap_pdu_t *request) { coap_option_iterator_init(request, &opt_iter, &f); while ((q = coap_option_next(&opt_iter))) { uint16_t seg_len = coap_opt_length(q), i; - const uint8_t *seg= coap_opt_value(q); + const uint8_t *seg = coap_opt_value(q); for (i = 0; i < seg_len; i++) { if (is_unescaped_in_path(seg[i])) length += 1; @@ -1085,3 +1165,53 @@ coap_get_uri_path(const coap_pdu_t *request) { } return uri_path; } + +void +coap_delete_upa_chain(coap_upa_chain_t *chain) { + while (chain) { + coap_upa_chain_t *next = chain->next; + + coap_free_type(COAP_STRING, chain); + chain = next; + } +} + +static coap_upa_chain_t * +coap_build_upa_chain(coap_upa_abbrev_t *list, uint32_t count) { + uint32_t i; + coap_upa_chain_t *chain = NULL; + coap_upa_chain_t *next; + + for (i = 0; i < count; i++) { + next = coap_malloc_type(COAP_STRING, sizeof(coap_upa_chain_t) + strlen(list[i].upa_path) + 1); + if (next == NULL) + goto cleanup; + + next->upa_value = list[i].upa_value; + next->upa_path = (char *)next + sizeof(coap_upa_chain_t); + strcpy(next->upa_path, list[i].upa_path); + next->next = chain; + chain = next; + } + return chain; + +cleanup: + coap_delete_upa_chain(chain); + return NULL; +} + +void +coap_upa_client_fallback(coap_upa_abbrev_t *list, uint32_t count) { + coap_lock_lock(return); + coap_delete_upa_chain(coap_upa_client_fallback_chain); + coap_upa_client_fallback_chain = coap_build_upa_chain(list, count); + coap_lock_unlock(); +} + +void +coap_upa_server_mapping(coap_upa_abbrev_t *list, uint32_t count) { + coap_lock_lock(return); + coap_delete_upa_chain(coap_upa_server_mapping_chain); + coap_upa_server_mapping_chain = coap_build_upa_chain(list, count); + coap_lock_unlock(); +} diff --git a/src/oscore/oscore_context.c b/src/oscore/oscore_context.c index c2a1c70fa0..9924074d94 100644 --- a/src/oscore/oscore_context.c +++ b/src/oscore/oscore_context.c @@ -710,7 +710,8 @@ oscore_new_association(coap_session_t *session, const uint8_t *data; association->sent_pdu = coap_pdu_duplicate_lkd(sent_pdu, session, - token->length, token->s, NULL); + token->length, token->s, + NULL, COAP_BOOL_FALSE); if (association->sent_pdu == NULL) goto error; if (coap_get_data(sent_pdu, &size, &data)) {