diff --git a/Makefile.am b/Makefile.am index 6a22eedaff..c1bde83bc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -174,9 +174,11 @@ coap_dtls_session_update_mtu \ coap_dtls_shutdown \ coap_dtls_startup \ coap_epoll_ctl_mod \ +coap_insert_option \ coap_io_do_events \ coap_mfree_endpoint \ coap_packet_extract_pbuf \ +coap_pdu_check_resize \ coap_pdu_from_pbuf \ coap_session_mfree \ coap_session_new_dtls_session \ @@ -199,6 +201,7 @@ coap_tls_new_client_session \ coap_tls_new_server_session \ coap_tls_read \ coap_tls_write \ +coap_update_option \ " # This helper is called by libcoap-$(LIBCOAP_API_VERSION).{map,sym} to see if diff --git a/README.md b/README.md index dd93af8d4b..e4e696c77c 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,9 @@ The following RFCs are supported * RFC8132: PATCH and FETCH Methods for the Constrained Application Protocol (CoAP) * RFC8323: CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets + [No WebSockets support] + +* RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option There is (D)TLS support for the following libraries diff --git a/examples/client.c b/examples/client.c index f5b99859ee..95571bd3c9 100644 --- a/examples/client.c +++ b/examples/client.c @@ -251,15 +251,16 @@ clear_obs(coap_context_t *ctx, coap_session_t *session) { for (option = optlist; option; option = option->next ) { switch (option->number) { - case COAP_OPTION_URI_PORT : - case COAP_OPTION_URI_PATH : - case COAP_OPTION_URI_QUERY : + case COAP_OPTION_URI_PORT : /* 7 */ + case COAP_OPTION_URI_PATH : /* 11 */ + case COAP_OPTION_URI_QUERY : /* 15 */ + case COAP_OPTION_HOP_LIMIT : /* 16 */ if (!coap_add_option(pdu, option->number, option->length, option->data)) { goto error; } break; - default: + default: ; } } @@ -453,13 +454,14 @@ message_handler(struct coap_context_t *ctx, /* create pdu with request for next block */ pdu = coap_new_request(ctx, session, method, NULL, NULL, 0); /* first, create bare PDU w/o any option */ if ( pdu ) { - /* add URI components from optlist */ + /* add URI components from optlist in ascending order */ for (option = optlist; option; option = option->next ) { switch (option->number) { - case COAP_OPTION_URI_HOST : - case COAP_OPTION_URI_PORT : - case COAP_OPTION_URI_PATH : - case COAP_OPTION_URI_QUERY : + case COAP_OPTION_URI_HOST : /* 3 */ + case COAP_OPTION_URI_PORT : /* 7 */ + case COAP_OPTION_URI_PATH : /* 11 */ + case COAP_OPTION_URI_QUERY : /* 15 */ + case COAP_OPTION_HOP_LIMIT : /* 16 */ coap_add_option(pdu, option->number, option->length, option->data); break; @@ -550,11 +552,12 @@ message_handler(struct coap_context_t *ctx, /* add URI components from optlist */ for (option = optlist; option; option = option->next ) { switch (option->number) { - case COAP_OPTION_URI_HOST : - case COAP_OPTION_URI_PORT : - case COAP_OPTION_URI_PATH : - case COAP_OPTION_CONTENT_FORMAT : - case COAP_OPTION_URI_QUERY : + case COAP_OPTION_URI_HOST : /* 3 */ + case COAP_OPTION_URI_PORT : /* 7 */ + case COAP_OPTION_URI_PATH : /* 11 */ + case COAP_OPTION_CONTENT_FORMAT : /* 12 */ + case COAP_OPTION_URI_QUERY : /* 15 */ + case COAP_OPTION_HOP_LIMIT : /* 16 */ coap_add_option(pdu, option->number, option->length, option->data); break; @@ -641,8 +644,8 @@ usage( const char *program, const char *version) { "%s\n\n" "Usage: %s [-a addr] [-b [num,]size] [-e text] [-f file] [-l loss]\n" "\t\t[-m method] [-o file] [-p port] [-r] [-s duration] [-t type]\n" - "\t\t[-v num] [-A type] [-B seconds] [-K interval] [-N] [-O num,text]\n" - "\t\t[-P addr[:port]] [-T token] [-U]\n" + "\t\t[-v num] [-A type] [-B seconds] [-H hoplimit] [-K interval] [-N]\n" + "\t\t[-O num,text] [-P addr[:port]] [-T token] [-U]\n" "\t\t[[-h match_hint_file] [-k key] [-u user]]\n" "\t\t[[-c certfile] [-j keyfile] [-C cafile] [-J pkcs11_pin]\n" "\t\t[-R root_cafile] [-S match_pki_sni_file]] URI\n" @@ -674,6 +677,9 @@ usage( const char *program, const char *version) { "\t-A type\t\tAccepted media type\n" "\t-B seconds\tBreak operation after waiting given seconds\n" "\t \t\t(default is %d)\n" + "\t-H hoplimit\tSet the Hop Limit count to hoplimit for proxies. Must\n" + "\t \t\thave a value between 1 and 255 inclusive.\n" + "\t \t\tDefault is '16'\n" "\t-K interval\tsend a ping after interval seconds of inactivity\n" "\t-N \t\tSend NON-confirmable message\n" "\t-O num,text\tAdd option num with contents text to request. If the\n" @@ -722,7 +728,8 @@ usage( const char *program, const char *version) { "\t \t\t'trusted' for the verification.\n" "\t \t\tAlternatively, this can point to a directory containing\n" "\t \t\ta set of CA PEM files\n" - "\n" + ); + fprintf( stderr, "Examples:\n" "\tcoap-client -m get coap://[::1]/\n" "\tcoap-client -m get coap://[::1]/.well-known/core\n" @@ -788,6 +795,24 @@ cmdline_content_type(char *arg, uint16_t key) { } } +static int +cmdline_hop_limit(char *arg) { + coap_optlist_t *node; + uint32_t value; + uint8_t buf[4]; + + value = strtol(arg, NULL, 10); + if (value < 1 || value > 255) { + return 0; + } + node = coap_new_optlist(COAP_OPTION_HOP_LIMIT, coap_encode_var_safe(buf, sizeof(buf), value), buf); + if (node) { + coap_insert_optlist(&optlist, node); + } + return 1; +} + + static uint16_t get_default_port(const coap_uri_t *u) { return coap_uri_scheme_is_secure(u) ? COAPS_DEFAULT_PORT : COAP_DEFAULT_PORT; @@ -1533,7 +1558,7 @@ main(int argc, char **argv) { struct sigaction sa; #endif - while ((opt = getopt(argc, argv, "a:b:c:e:f:h:j:k:l:m:o:p:rs:t:u:v:A:B:C:J:K:NO:P:R:T:U")) != -1) { + while ((opt = getopt(argc, argv, "a:b:c:e:f:h:j:k:l:m:o:p:rs:t:u:v:A:B:C:J:K:H:NO:P:R:T:U")) != -1) { switch (opt) { case 'a': strncpy(node_str, optarg, NI_MAXHOST - 1); @@ -1643,6 +1668,10 @@ main(int argc, char **argv) { exit(1); } break; + case 'H': + if (!cmdline_hop_limit(optarg)) + fprintf(stderr, "Hop Limit has to be > 0 and < 256\n"); + break; default: usage( argv[0], LIBCOAP_PACKAGE_VERSION ); exit( 1 ); diff --git a/include/coap2/pdu.h b/include/coap2/pdu.h index 2487bc3178..6f808e50aa 100644 --- a/include/coap2/pdu.h +++ b/include/coap2/pdu.h @@ -32,6 +32,10 @@ struct coap_session_t; #define COAP_DEFAULT_MTU 1152 #endif /* COAP_DEFAULT_MTU */ +#ifndef COAP_DEFAULT_HOP_LIMIT +#define COAP_DEFAULT_HOP_LIMIT 16 +#endif /* COAP_DEFAULT_HOP_LIMIT */ + /* TCP Message format constants, do not modify */ #define COAP_MESSAGE_SIZE_OFFSET_TCP8 13 #define COAP_MESSAGE_SIZE_OFFSET_TCP16 269 /* 13 + 256 */ @@ -87,41 +91,60 @@ typedef enum coap_request_t { } coap_request_t; /* - * CoAP option types (be sure to update coap_option_check_critical() when - * adding options + * CoAP option types (be sure to update coap_option_check_critical() and + * coap_add_option() when adding options */ -#define COAP_OPTION_IF_MATCH 1 /* C, opaque, 0-8 B, (none) */ -#define COAP_OPTION_URI_HOST 3 /* C, String, 1-255 B, destination address */ -#define COAP_OPTION_ETAG 4 /* E, opaque, 1-8 B, (none) */ -#define COAP_OPTION_IF_NONE_MATCH 5 /* empty, 0 B, (none) */ -#define COAP_OPTION_URI_PORT 7 /* C, uint, 0-2 B, destination port */ -#define COAP_OPTION_LOCATION_PATH 8 /* E, String, 0-255 B, - */ -#define COAP_OPTION_URI_PATH 11 /* C, String, 0-255 B, (none) */ -#define COAP_OPTION_CONTENT_FORMAT 12 /* E, uint, 0-2 B, (none) */ -#define COAP_OPTION_CONTENT_TYPE COAP_OPTION_CONTENT_FORMAT -#define COAP_OPTION_MAXAGE 14 /* E, uint, 0--4 B, 60 Seconds */ -#define COAP_OPTION_URI_QUERY 15 /* C, String, 1-255 B, (none) */ -#define COAP_OPTION_ACCEPT 17 /* C, uint, 0-2 B, (none) */ -#define COAP_OPTION_LOCATION_QUERY 20 /* E, String, 0-255 B, (none) */ -#define COAP_OPTION_SIZE2 28 /* E, uint, 0-4 B, (none) */ -#define COAP_OPTION_PROXY_URI 35 /* C, String, 1-1034 B, (none) */ -#define COAP_OPTION_PROXY_SCHEME 39 /* C, String, 1-255 B, (none) */ -#define COAP_OPTION_SIZE1 60 /* E, uint, 0-4 B, (none) */ - -/* option types from RFC 7641 */ - -#define COAP_OPTION_OBSERVE 6 /* E, empty/uint, 0 B/0-3 B, (none) */ -#define COAP_OPTION_SUBSCRIPTION COAP_OPTION_OBSERVE - -/* selected option types from RFC 7959 */ - -#define COAP_OPTION_BLOCK2 23 /* C, uint, 0--3 B, (none) */ -#define COAP_OPTION_BLOCK1 27 /* C, uint, 0--3 B, (none) */ - -/* selected option types from RFC 7967 */ +/* + * The C, U, and N flags indicate the properties + * Critical, Unsafe, and NoCacheKey, respectively. + * If U is set, then N has no meaning as per + * https://tools.ietf.org/html/rfc7252#section-5.10 + * and is set to a -. + * + * Separately, R is for the options that can be repeated + * + * The least significant byte of the option is set as followed + * as per https://tools.ietf.org/html/rfc7252#section-5.4.6 + * + * 0 1 2 3 4 5 6 7 + * --+---+---+---+---+---+---+---+ + * | NoCacheKey| U | C | + * --+---+---+---+---+---+---+---+ + * + * https://tools.ietf.org/html/rfc8613#section-4 goes on to define E, I and U + * properties Encrypted and Integrity Protected, Integrity Protected Only and + * Unprotected respectively. Integretity Protected Only is not currently used. + * + * An Option is tagged with CUNREIU with any of the letters replaced with _ if + * not set, or - for N if U is set (see above) for aiding understanding of the + * Option. + */ -#define COAP_OPTION_NORESPONSE 258 /* N, uint, 0--1 B, 0 */ +#define COAP_OPTION_IF_MATCH 1 /* C__RE__, opaque, 0-8 B, RFC7252 */ +#define COAP_OPTION_URI_HOST 3 /* CU-___U, String, 1-255 B, RFC7252 */ +#define COAP_OPTION_ETAG 4 /* ___RE__, opaque, 1-8 B, RFC7252 */ +#define COAP_OPTION_IF_NONE_MATCH 5 /* C___E__, empty, 0 B, RFC7252 */ +#define COAP_OPTION_OBSERVE 6 /* _U-_E_U, empty/uint,0/0-3 B, RFC7641 */ +#define COAP_OPTION_URI_PORT 7 /* CU-___U, uint, 0-2 B, RFC7252 */ +#define COAP_OPTION_LOCATION_PATH 8 /* ___RE__, String, 0-255 B, RFC7252 */ +#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_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 */ +#define COAP_OPTION_URI_QUERY 15 /* CU-RE__, String, 1-255 B, RFC7252 */ +#define COAP_OPTION_HOP_LIMIT 16 /* ______U, uint, 1 B, RFC8768 */ +#define COAP_OPTION_ACCEPT 17 /* C___E__, uint, 0-2 B, RFC7252 */ +#define COAP_OPTION_LOCATION_QUERY 20 /* ___RE__, String, 0-255 B, RFC7252 */ +#define COAP_OPTION_BLOCK2 23 /* CU-_E_U, uint, 0-3 B, RFC7959 */ +#define COAP_OPTION_BLOCK1 27 /* CU-_E_U, uint, 0-3 B, RFC7959 */ +#define COAP_OPTION_SIZE2 28 /* __N_E_U, uint, 0-4 B, RFC7959 */ +#define COAP_OPTION_PROXY_URI 35 /* CU-___U, String, 1-1034 B, RFC7252 */ +#define COAP_OPTION_PROXY_SCHEME 39 /* CU-___U, String, 1-255 B, RFC7252 */ +#define COAP_OPTION_SIZE1 60 /* __N_E_U, uint, 0-4 B, RFC7252 */ +#define COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */ #define COAP_MAX_OPT 65535 /**< the highest option number we know */ @@ -273,7 +296,7 @@ typedef struct coap_pdu_t { uint8_t type; /**< message type */ uint8_t code; /**< request method (value 1--31) or response code (value 64-255) */ uint8_t max_hdr_size; /**< space reserved for protocol-specific header */ - uint8_t hdr_size; /**< actaul size used for protocol-specific header */ + uint8_t hdr_size; /**< actual size used for protocol-specific header */ uint8_t token_length; /**< length of Token */ uint16_t tid; /**< transaction id, if any, in regular host byte order */ uint16_t max_delta; /**< highest option number */ @@ -359,6 +382,19 @@ coap_pdu_init(uint8_t type, uint8_t code, uint16_t tid, size_t size); */ int coap_pdu_resize(coap_pdu_t *pdu, size_t new_size); +/** + * Dynamically grows the size of @p pdu to @p new_size if needed. The new size + * must not exceed the PDU's configure maximum size. On success, this + * function returns 1, otherwise 0. + * + * Internal use only. + * + * @param pdu The PDU to resize. + * @param new_size The new size in bytes. + * @return 1 if the operation succeeded, 0 otherwise. + */ +int coap_pdu_check_resize(coap_pdu_t *pdu, size_t new_size); + /** * Clears any contents from @p pdu and resets @c used_size, * and @c data pointers. @c max_size is set to @p size, any @@ -489,6 +525,25 @@ uint8_t *coap_add_option_later(coap_pdu_t *pdu, uint16_t type, size_t len); + +/** + * Inserts option of given type in the @p pdu with the appropriate data. + * The option will be inserted in the appropriate place in the options in the pdu. + * + * Internal use only. + */ +size_t coap_insert_option(coap_pdu_t *pdu, uint16_t type, + size_t len, const uint8_t *data); + +/** + * Updates existing first option of given type in the @p pdu with the new data. + * + * Internal use only. + */ +int coap_update_option(coap_pdu_t *pdu, + uint16_t type, + size_t len, + const uint8_t *data); /** * Adds given data to the pdu that is passed as first parameter. Note that the * PDU's data is destroyed by coap_add_option(). coap_add_data() must be called @@ -500,7 +555,7 @@ int coap_add_data(coap_pdu_t *pdu, /** * Adds given data to the pdu that is passed as first parameter but does not - * copyt it. Note that the PDU's data is destroyed by coap_add_option(). + * copy it. Note that the PDU's data is destroyed by coap_add_option(). * coap_add_data() must be have been called once for this PDU, otherwise the * result is undefined. * The actual data must be copied at the returned location. diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index 9f1dcc07fe..ffa18a63b4 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -16,8 +16,9 @@ SYNOPSIS -------- *coap-client* [*-a* addr] [*-b* [num,]size] [*-e* text] [*-f* file] [*-l* loss] [*-m* method] [*-o* file] [*-p* port] [*-r*] [*-s duration*] - [*-t* type] [*-v* num] [*-A* type] [*-B* seconds] [*-K* interval] - [*-N*] [*-O* num,text] [*-P* addr[:port]] [*-T* token] [*-U*] + [*-t* type] [*-v* num] [*-A* type] [*-B* seconds] + [*-H* hoplimit] [*-K* interval] [*-N*] [*-O* num,text] + [*-P* addr[:port]] [*-T* token] [*-U*] [[*-h* match_hint_file] [*-k* key] [*-u* user]] [[*-c* certfile] [*-j* keyfile] [*-C* cafile] [*-J* pkcs11_pin] [*-R* root_cafile]] URI @@ -109,6 +110,10 @@ OPTIONS - General *-B* seconds:: Break operation after waiting given seconds (default is 90). +*-H* hoplimit:: + Set the Hop Limit count to hoplimit for proxies. Must have a value between + 1 and 255 inclusive. Default is '16'. + *-K* interval:: Send a ping after interval seconds of inactivity. If not specified (or 0), keep-alive is disabled (default). diff --git a/man/coap.txt.in b/man/coap.txt.in index 34f33179a9..cb31b87a60 100644 --- a/man/coap.txt.in +++ b/man/coap.txt.in @@ -61,6 +61,8 @@ See "RFC8323: CoAP (Constrained Application Protocol) over TCP, TLS, and WebSockets" +"RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option" + for further information. BUGS diff --git a/man/coap_pdu_setup.txt.in b/man/coap_pdu_setup.txt.in index d35422795a..159094845c 100644 --- a/man/coap_pdu_setup.txt.in +++ b/man/coap_pdu_setup.txt.in @@ -149,26 +149,54 @@ of the data of the option, and _data_ points to the content of the option. The following is the current list of options with their numeric value ---- -#define COAP_OPTION_IF_MATCH 1 /* opaque, 0-8 B */ -#define COAP_OPTION_URI_HOST 3 /* String, 1-255 B */ -#define COAP_OPTION_ETAG 4 /* opaque, 1-8 B */ -#define COAP_OPTION_IF_NONE_MATCH 5 /* empty, 0 B */ -#define COAP_OPTION_OBSERVE 6 /* empty/uint, 0 B/0-3 B */ -#define COAP_OPTION_URI_PORT 7 /* uint, 0-2 B */ -#define COAP_OPTION_LOCATION_PATH 8 /* String, 0-255 B */ -#define COAP_OPTION_URI_PATH 11 /* String, 0-255 B */ -#define COAP_OPTION_CONTENT_FORMAT 12 /* uint, 0-2 B */ -#define COAP_OPTION_MAXAGE 14 /* uint, 0-4 B, default 60 Seconds */ -#define COAP_OPTION_URI_QUERY 15 /* String, 1-255 B */ -#define COAP_OPTION_ACCEPT 17 /* uint, 0-2 B */ -#define COAP_OPTION_LOCATION_QUERY 20 /* String, 0-255 B */ -#define COAP_OPTION_BLOCK2 23 /* uint, 0-3 B */ -#define COAP_OPTION_BLOCK1 27 /* uint, 0-3 B */ -#define COAP_OPTION_SIZE2 28 /* uint, 0-4 B */ -#define COAP_OPTION_PROXY_URI 35 /* String, 1-1034 B */ -#define COAP_OPTION_PROXY_SCHEME 39 /* String, 1-255 B */ -#define COAP_OPTION_SIZE1 60 /* uint, 0-4 B */ -#define COAP_OPTION_NORESPONSE 258 /* uint, 0-1 B */ +/* + * The C, U, and N flags indicate the properties + * Critical, Unsafe, and NoCacheKey, respectively. + * If U is set, then N has no meaning as per + * https://tools.ietf.org/html/rfc7252#section-5.10 + * and is set to a -. + * Separately, R is for the options that can be repeated + * + * The least significant byte of the option is set as followed + * as per https://tools.ietf.org/html/rfc7252#section-5.4.6 + * + * 0 1 2 3 4 5 6 7 + * --+---+---+---+---+---+---+---+ + * | NoCacheKey| U | C | + * --+---+---+---+---+---+---+---+ + * + * https://tools.ietf.org/html/rfc8613#section-4 goes on to define E, I and U + * properties Encrypted and Integrity Protected, Integrity Protected Only and + * Unprotected respectively. Integretity Protected Only is not currently used. + * + * An Option is tagged with CUNREIU with any of the letters replaced with _ if + * not set, or - for N if U is set (see above) for aiding understanding of the + * Option. + */ + +#define COAP_OPTION_IF_MATCH 1 /* C__RE__, opaque, 0-8 B, RFC7252 */ +#define COAP_OPTION_URI_HOST 3 /* CU-___U, String, 1-255 B, RFC7252 */ +#define COAP_OPTION_ETAG 4 /* ___RE__, opaque, 1-8 B, RFC7252 */ +#define COAP_OPTION_IF_NONE_MATCH 5 /* C___E__, empty, 0 B, RFC7252 */ +#define COAP_OPTION_OBSERVE 6 /* _U-_E_U, empty/uint, 0 B/0-3 B, RFC7641 */ +#define COAP_OPTION_URI_PORT 7 /* CU-___U, uint, 0-2 B, RFC7252 */ +#define COAP_OPTION_LOCATION_PATH 8 /* ___RE__, String, 0-255 B, RFC7252 */ +#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 */ +/* COAP_OPTION_MAXAGE default 60 seconds if not set */ +#define COAP_OPTION_MAXAGE 14 /* _U-_E_U, uint, 0-4 B, RFC7252 */ +#define COAP_OPTION_URI_QUERY 15 /* CU-RE__, String, 1-255 B, RFC7252 */ +#define COAP_OPTION_HOP_LIMIT 16 /* ______U, uint, 1 B, RFC8768 */ +#define COAP_OPTION_ACCEPT 17 /* C___E__, uint, 0-2 B, RFC7252 */ +#define COAP_OPTION_LOCATION_QUERY 20 /* ___RE__, String, 0-255 B, RFC7252 */ +#define COAP_OPTION_BLOCK2 23 /* CU-_E_U, uint, 0-3 B, RFC7959 */ +#define COAP_OPTION_BLOCK1 27 /* CU-_E_U, uint, 0-3 B, RFC7959 */ +#define COAP_OPTION_SIZE2 28 /* __N_E_U, uint, 0-4 B, RFC7959 */ +#define COAP_OPTION_PROXY_URI 35 /* CU-___U, String, 1-1034 B, RFC7252 */ +#define COAP_OPTION_PROXY_SCHEME 39 /* CU-___U, String, 1-255 B, RFC7252 */ +#define COAP_OPTION_SIZE1 60 /* __N_E_U, uint, 0-4 B, RFC7252 */ +#define COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */ ---- See FURTHER INFORMATION as to how to get the latest list. diff --git a/src/coap_debug.c b/src/coap_debug.c index 547ced1a1b..95faaeb96b 100644 --- a/src/coap_debug.c +++ b/src/coap_debug.c @@ -316,14 +316,15 @@ msg_option_string(uint8_t code, uint16_t option_type) { { COAP_OPTION_CONTENT_FORMAT, "Content-Format" }, { COAP_OPTION_MAXAGE, "Max-Age" }, { COAP_OPTION_URI_QUERY, "Uri-Query" }, + { COAP_OPTION_HOP_LIMIT, "Hop-Limit" }, { COAP_OPTION_ACCEPT, "Accept" }, { COAP_OPTION_LOCATION_QUERY, "Location-Query" }, { COAP_OPTION_BLOCK2, "Block2" }, { COAP_OPTION_BLOCK1, "Block1" }, + { COAP_OPTION_SIZE2, "Size2" }, { COAP_OPTION_PROXY_URI, "Proxy-Uri" }, { COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" }, { COAP_OPTION_SIZE1, "Size1" }, - { COAP_OPTION_SIZE2, "Size2" }, { COAP_OPTION_NORESPONSE, "No-Response" } }; @@ -571,6 +572,7 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { case COAP_OPTION_OBSERVE: case COAP_OPTION_SIZE1: case COAP_OPTION_SIZE2: + case COAP_OPTION_HOP_LIMIT: /* show values as unsigned decimal value */ buf_len = snprintf((char *)buf, sizeof(buf), "%u", coap_decode_var_bytes(coap_opt_value(option), @@ -584,6 +586,7 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { opt_iter.type == COAP_OPTION_URI_HOST || opt_iter.type == COAP_OPTION_LOCATION_PATH || opt_iter.type == COAP_OPTION_LOCATION_QUERY || + opt_iter.type == COAP_OPTION_PROXY_SCHEME || opt_iter.type == COAP_OPTION_URI_QUERY) { encode = 0; } else { diff --git a/src/net.c b/src/net.c index 5a3fbf1a38..262c7397da 100644 --- a/src/net.c +++ b/src/net.c @@ -51,6 +51,10 @@ #include #endif +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 40 +#endif + #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -999,6 +1003,110 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { uint8_t r; ssize_t bytes_written; + if (pdu->code == COAP_RESPONSE_CODE(508)) { + /* + * Need to prepend our IP identifier to the data as per + * https://www.rfc-editor.org/rfc/rfc8768.html#section-4 + */ + char addr_str[INET6_ADDRSTRLEN + 8 + 1]; + coap_opt_iterator_t opt_iter; + coap_opt_t *opt; + size_t hop_limit; + + addr_str[sizeof(addr_str)-1] = '\000'; + if (coap_print_addr(&session->addr_info.local, (uint8_t*)addr_str, + sizeof(addr_str) - 1)) { + char *cp; + int len; + + if (addr_str[0] == '[') { + cp = strchr(addr_str, ']'); + if (cp) *cp = '\000'; + if (memcmp(&addr_str[1], "::ffff:", 7) == 0) { + /* IPv4 embedded into IPv6 */ + cp = &addr_str[8]; + } + else { + cp = &addr_str[1]; + } + } + else { + cp = strchr(addr_str, ':'); + if (cp) *cp = '\000'; + cp = addr_str; + } + len = strlen(cp); + + /* See if Hop Limit option is being used in return path */ + opt = coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter); + if (opt) { + uint8_t buf[4]; + + hop_limit = + coap_decode_var_bytes (coap_opt_value (opt), coap_opt_length (opt)); + if (hop_limit == 1) { + coap_log(LOG_WARNING, "Proxy loop detected '%s'\n", + (char*)pdu->data); + coap_delete_pdu(pdu); + return (coap_tid_t)COAP_DROPPED_RESPONSE; + } + else if (hop_limit < 1 || hop_limit > 255) { + /* Something is bad - need to drop this pdu (TODO or delete option) */ + coap_log(LOG_WARNING, "Proxy return has bad hop limit count '%zu'\n", + hop_limit); + coap_delete_pdu(pdu); + return (coap_tid_t)COAP_DROPPED_RESPONSE; + } + hop_limit--; + coap_update_option(pdu, COAP_OPTION_HOP_LIMIT, + coap_encode_var_safe8(buf, sizeof(buf), hop_limit), + buf); + } + + /* Need to check that we are not seeing this proxy in the return loop */ + if (pdu->data && opt == NULL) { + if (pdu->used_size + 1 <= pdu->max_size) { + char *a_match; + size_t data_len = pdu->used_size - (pdu->data - pdu->token); + pdu->data[data_len] = '\000'; + a_match = strstr((char*)pdu->data, cp); + if (a_match && (a_match == (char*)pdu->data || a_match[-1] == ' ') && + ((size_t)(a_match - (char*)pdu->data + len) == data_len || + a_match[len] == ' ')) { + coap_log(LOG_WARNING, "Proxy loop detected '%s'\n", + (char*)pdu->data); + coap_delete_pdu(pdu); + return (coap_tid_t)COAP_DROPPED_RESPONSE; + } + } + } + if (pdu->used_size + len + 1 <= pdu->max_size) { + size_t old_size = pdu->used_size; + if (coap_pdu_resize(pdu, pdu->used_size + len + 1)) { + if (pdu->data == NULL) { + /* + * Set Hop Limit to max for return path. If this libcoap is in + * a proxy loop path, it will always decrement hop limit in code + * above and hence timeout / drop the response as appropriate + */ + hop_limit = 255; + coap_insert_option(pdu, COAP_OPTION_HOP_LIMIT, 1, + (uint8_t *)&hop_limit); + coap_add_data(pdu, len, (uint8_t*)cp); + } + else { + /* prepend with space separator, leaving hop limit "as is" */ + memmove(pdu->data + len + 1, pdu->data, + old_size - (pdu->data - pdu->token)); + memcpy(pdu->data, cp, len); + pdu->data[len] = ' '; + pdu->used_size += len + 1; + } + } + } + } + } + if (!coap_pdu_encode_header(pdu, session->proto)) { goto error; } @@ -1801,11 +1909,22 @@ coap_new_error_response(coap_pdu_t *request, unsigned char code, uint16_t opt_type = 0; /* used for calculating delta-storage */ #if COAP_ERROR_PHRASE_LENGTH > 0 - const char *phrase = coap_response_phrase(code); + const char *phrase; + if (code != COAP_RESPONSE_CODE(508)) { + phrase = coap_response_phrase(code); - /* Need some more space for the error phrase and payload start marker */ - if (phrase) - size += strlen(phrase) + 1; + /* Need some more space for the error phrase and payload start marker */ + if (phrase) + size += strlen(phrase) + 1; + } + else { + /* + * Need space for IP for 5.08 response which is filled in in coap_send() + * https://www.rfc-editor.org/rfc/rfc8768.html#section-4 + */ + phrase = NULL; + size += INET6_ADDRSTRLEN; + } #endif assert(request); @@ -1819,6 +1938,7 @@ coap_new_error_response(coap_pdu_t *request, unsigned char code, * request. We always need the Token, for 4.02 the unknown critical * options must be included as well. */ coap_option_clrb(opts, COAP_OPTION_CONTENT_TYPE); /* we do not want this */ + coap_option_clrb(opts, COAP_OPTION_HOP_LIMIT); /* we do not want this */ coap_option_iterator_init(request, &opt_iter, opts); @@ -2161,15 +2281,46 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu coap_method_handler_t h = NULL; coap_pdu_t *response = NULL; coap_opt_filter_t opt_filter; - coap_resource_t *resource; + coap_resource_t *resource = NULL; /* The respond field indicates whether a response must be treated * specially due to a No-Response option that declares disinterest * or interest in a specific response class. DEFAULT indicates that * No-Response has not been specified. */ enum respond_t respond = RESPONSE_DEFAULT; + coap_opt_iterator_t opt_iter; + coap_opt_t *opt; + int skip_hop_limit_check = 0; + int resp; coap_option_filter_clear(opt_filter); + /* If !skip_hop_limit_check test placeholder for Proxy-Uri: code */ + if (!skip_hop_limit_check) { + opt = coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter); + if (opt) { + size_t hop_limit; + uint8_t buf[4]; + + hop_limit = + coap_decode_var_bytes (coap_opt_value (opt), coap_opt_length (opt)); + if (hop_limit == 1) { + /* coap_send() will fill in the IP address for us */ + resp = 508; + goto fail_response; + } + else if (hop_limit < 1 || hop_limit > 255) { + /* Need to return a 4.00 RFC8768 Section 3 */ + coap_log(LOG_INFO, "Invalid Hop Limit\n"); + resp = 400; + goto fail_response; + } + hop_limit--; + coap_update_option(pdu, COAP_OPTION_HOP_LIMIT, + coap_encode_var_safe8(buf, sizeof(buf), hop_limit), + buf); + } + } + /* try to find the resource from the request URI */ coap_string_t *uri_path = coap_get_uri_path(pdu); if (!uri_path) @@ -2285,7 +2436,6 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu if response == NULL */ if (coap_add_token(response, pdu->token_length, pdu->token)) { coap_binary_t token = { pdu->token_length, pdu->token }; - coap_opt_iterator_t opt_iter; coap_opt_t *observe = NULL; int observe_action = COAP_OBSERVE_CANCEL; coap_string_t *query = coap_get_query(pdu); @@ -2391,6 +2541,17 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu assert(response == NULL); coap_delete_string(uri_path); + return; + +fail_response: + response = + coap_new_error_response(pdu, COAP_RESPONSE_CODE(resp), + opt_filter); + if (response) { + if (coap_send(session, response) == COAP_INVALID_TID) + coap_log(LOG_WARNING, "cannot send response for transaction %u\n", + pdu->tid); + } } static void @@ -2462,9 +2623,6 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, int is_ping_rst; if (LOG_DEBUG <= coap_get_log_level()) { -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 40 -#endif /* FIXME: get debug to work again ** unsigned char addr[INET6_ADDRSTRLEN+8], localaddr[INET6_ADDRSTRLEN+8]; if (coap_print_addr(remote, addr, INET6_ADDRSTRLEN+8) && diff --git a/src/pdu.c b/src/pdu.c index 4fe88352dd..181f4818c7 100644 --- a/src/pdu.c +++ b/src/pdu.c @@ -170,7 +170,7 @@ coap_pdu_resize(coap_pdu_t *pdu, size_t new_size) { return 1; } -static int +int coap_pdu_check_resize(coap_pdu_t *pdu, size_t size) { if (size > pdu->alloc_size) { size_t new_size = max(256, pdu->alloc_size * 2); @@ -210,19 +210,182 @@ coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) { return 1; } +size_t +coap_insert_option(coap_pdu_t *pdu, uint16_t type, size_t len, + const uint8_t *data) { + coap_opt_iterator_t opt_iter; + coap_opt_t *option; + uint16_t prev_type = 0; + size_t shift; + size_t opt_delta; + coap_option_t decode; + size_t shrink = 0; + + if (type >= pdu->max_delta) + return coap_add_option(pdu, type, len, data); + + /* Need to locate where in current options to insert this one */ + coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); + while ((option = coap_option_next(&opt_iter))) { + if (opt_iter.type > type) { + /* Found where to insert */ + break; + } + prev_type = opt_iter.type; + } + assert(option != NULL); + /* size of option inc header to insert */ + shift = coap_opt_encode_size(type - prev_type, len); + + /* size of next option (header may shrink in size as delta changes */ + if (!coap_opt_parse(option, opt_iter.length + 5, &decode)) + return 0; + opt_delta = opt_iter.type - type; + + if (!coap_pdu_check_resize(pdu, + pdu->used_size + shift - shrink)) + return 0; + + /* Possible a re-size took place with a realloc() */ + /* Need to locate where in current options to insert this one */ + coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); + while ((option = coap_option_next(&opt_iter))) { + if (opt_iter.type > type) { + /* Found where to insert */ + break; + } + } + assert(option != NULL); + + if (decode.delta <= 12) { + /* can simply patch in the new delta of next option */ + option[0] = (option[0] & 0x0f) + (opt_delta << 4); + } + else if (decode.delta <= 269 && opt_delta <= 12) { + /* option header is going to shrink by one byte */ + option[1] = (option[0] & 0x0f) + (opt_delta << 4); + shrink = 1; + } + else if (decode.delta <= 269 && opt_delta <= 269) { + /* can simply patch in the new delta of next option */ + option[1] = opt_delta - 13; + } + else if (opt_delta <= 12) { + /* option header is going to shrink by two bytes */ + option[2] = (option[0] & 0x0f) + (opt_delta << 4); + shrink = 2; + } + else if (opt_delta <= 269) { + /* option header is going to shrink by one bytes */ + option[1] = (option[0] & 0x0f) + 0xd0; + option[2] = opt_delta - 13; + shrink = 1; + } + else { + /* can simply patch in the new delta of next option */ + option[1] = (opt_delta - 269) >> 8; + option[2] = (opt_delta - 269) & 0xff; + } + + memmove(&option[shift], &option[shrink], + pdu->used_size - (option - pdu->token) - shrink); + if (!coap_opt_encode(option, pdu->alloc_size - pdu->used_size, + type - prev_type, data, len)) + return 0; + + pdu->used_size += shift - shrink; + if (pdu->data) + pdu->data += shift - shrink; + return shift; +} + +int +coap_update_option(coap_pdu_t *pdu, uint16_t type, size_t len, + const uint8_t *data) { + coap_opt_iterator_t opt_iter; + coap_opt_t *option; + coap_option_t decode; + size_t new_length = 0; + size_t old_length = 0; + + option = coap_check_option(pdu, type, &opt_iter); + if (!option) + return coap_insert_option(pdu, type, len, data); + + old_length = coap_opt_parse(option, (size_t)-1, &decode); + if (old_length == 0) + return 0; + new_length = coap_opt_encode_size(decode.delta, len); + + if (new_length > old_length) { + if (!coap_pdu_check_resize(pdu, + pdu->used_size + new_length - old_length)) + return 0; + /* Possible a re-size took place with a realloc() */ + option = coap_check_option(pdu, type, &opt_iter); + } + + if (new_length != old_length) + memmove(&option[new_length], &option[old_length], + pdu->used_size - (option - pdu->token) - old_length); + + if (!coap_opt_encode(option, new_length, + decode.delta, data, len)) + return 0; + + pdu->used_size += new_length - old_length; + if (pdu->data) + pdu->data += new_length - old_length; + return 1; +} + /* FIXME: de-duplicate code with coap_add_option_later */ size_t -coap_add_option(coap_pdu_t *pdu, uint16_t type, size_t len, const uint8_t *data) { +coap_add_option(coap_pdu_t *pdu, uint16_t type, size_t len, + const uint8_t *data) { size_t optsize; coap_opt_t *opt; assert(pdu); pdu->data = NULL; + if (type == pdu->max_delta) { + /* Validate that the option is repeatable */ + switch (type) { + /* Ignore list of genuine repeatable */ + case COAP_OPTION_IF_MATCH: + case COAP_OPTION_ETAG: + case COAP_OPTION_LOCATION_PATH: + case COAP_OPTION_URI_PATH: + case COAP_OPTION_URI_QUERY: + case COAP_OPTION_LOCATION_QUERY: + break; + default: + coap_log(LOG_INFO, "Option %d is not defined as repeatable\n", type); + /* Accepting it after warning as there may be user defineable options */ + break; + } + } + + if (COAP_PDU_IS_REQUEST(pdu) && + (type == COAP_OPTION_PROXY_URI || type == COAP_OPTION_PROXY_SCHEME)) { + /* + * Need to check whether there is a hop-limit option. If not, it needs + * to be inserted by default (RFC 8768). + */ + coap_opt_iterator_t opt_iter; + + if (coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter) == NULL) { + size_t hop_limit = COAP_OPTION_HOP_LIMIT; + + coap_insert_option(pdu, COAP_OPTION_HOP_LIMIT, 1, (uint8_t *)&hop_limit); + } + } + if (type < pdu->max_delta) { - coap_log(LOG_WARNING, + coap_log(LOG_DEBUG, "coap_add_option: options are not in correct order\n"); - return 0; + return coap_insert_option(pdu, type, len, data); } if (!coap_pdu_check_resize(pdu, @@ -363,6 +526,7 @@ error_desc_t coap_error[] = { { COAP_RESPONSE_CODE(503), "Service Unavailable" }, { COAP_RESPONSE_CODE(504), "Gateway Timeout" }, { COAP_RESPONSE_CODE(505), "Proxying Not Supported" }, + { COAP_RESPONSE_CODE(508), "Hop Limit Reached" }, { 0, NULL } /* end marker */ }; diff --git a/tests/test_pdu.c b/tests/test_pdu.c index 094b38a000..c715c34276 100644 --- a/tests/test_pdu.c +++ b/tests/test_pdu.c @@ -754,7 +754,6 @@ t_encode_pdu13(void) { coap_delete_optlist(optlist); } - static void t_encode_pdu14(void) { coap_optlist_t *optlist = NULL; @@ -790,6 +789,159 @@ t_encode_pdu14(void) { coap_delete_optlist(optlist); } +/* Check inserting options with random types get put into the PDU in the + right order */ +static void +t_encode_pdu15(void) { + size_t n; + uint16_t opt_num[] = { 300, 13, 10, 7, 11, 268, 269, 12, 8, 9 }; + uint8_t opt_val[] = { 59, 56, 53, 50, 54, 57, 58, 55, 51, 52 }; + uint8_t opt_srt[] = { 50, 51, 52, 53, 54, 55, 56, 57, 58, 59 }; + coap_opt_iterator_t oi; + coap_opt_t *option; + + coap_pdu_clear(pdu, pdu->max_size); /* clear PDU */ + + CU_ASSERT(pdu->data == NULL); + + for (n = 0; n < (sizeof(opt_num)/sizeof(opt_num[0])); n++) { + coap_insert_option(pdu, opt_num[n], + sizeof(opt_val[n]), &opt_val[n]); + } + + /* Check options in pdu are in right order */ + coap_option_iterator_init(pdu, &oi, COAP_OPT_ALL); + for (n = 0; n < (sizeof(opt_num)/sizeof(opt_num[0])); n++) { + option = coap_option_next(&oi); + CU_ASSERT(oi.bad == 0); + CU_ASSERT(option != NULL); + CU_ASSERT(coap_opt_length(option) == 1); + CU_ASSERT(*coap_opt_value(option) == opt_srt[n]); + } + option = coap_option_next(&oi); + CU_ASSERT(oi.bad == 1); + CU_ASSERT(option == NULL); +} + +/* Check changing value of options works */ +static void +t_encode_pdu16(void) { + size_t n; + uint16_t opt_num[] = { 300, 10, 7 }; + uint8_t opt_val[] = { 53, 51, 50 }; + uint8_t data[] = { 'd', 'a', 't', 'a' }; + uint8_t data1[] = { 0x71, 0x32, 0x31, 0x33, 0xe1, 0x00, 0x15, 0x35, + 0xff, 0x64, 0x61, 0x74, 0x61 }; + uint8_t data2[] = { 0x71, 0x32, 0x33, 0x01, 0x23, 0x45, 0xe1, 0x00, + 0x15, 0x35, 0xff, 0x64, 0x61, 0x74, 0x61 }; + uint8_t data3[] = { 0x70, 0x31, 0x33, 0xe1, 0x00, 0x15, 0x35, 0xff, + 0x64, 0x61, 0x74, 0x61 }; + uint8_t data4[] = { 0x71, 0x32, 0x31, 0x33, 0xe4, 0x00, 0x15, 0x06, + 0x54, 0x32, 0x10, 0xff, 0x64, 0x61, 0x74, 0x61 }; + int new_val; + unsigned char buf[4]; + + coap_pdu_clear(pdu, pdu->max_size); /* clear PDU */ + + CU_ASSERT(pdu->data == NULL); + + for (n = 0; n < (sizeof(opt_num)/sizeof(opt_num[0])); n++) { + coap_add_option(pdu, opt_num[n], + sizeof(opt_val[n]), &opt_val[n]); + } + coap_add_data(pdu, sizeof(data), data); + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); + /* Now update an option in the middle */ + new_val = 0x12345; + coap_update_option(pdu, 10, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data2, pdu->used_size) == 0); + /* Shrink it back again */ + new_val = 51; + coap_update_option(pdu, 10, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); + /* Now update an option at the start */ + new_val = 0; + coap_update_option(pdu, 7, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data3, pdu->used_size) == 0); + /* put it back again */ + new_val = 50; + coap_update_option(pdu, 7, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); + /* Now update an option at the end */ + new_val = 0x6543210; + coap_update_option(pdu, 300, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data4, pdu->used_size) == 0); + /* put it back again */ + new_val = 53; + coap_update_option(pdu, 300, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); +} + +/* Same as t_encode_pdu16, but without any data, but with a token */ +static void +t_encode_pdu17(void) { + size_t n; + uint8_t token[] = { 't' }; + uint16_t opt_num[] = { 300, 10, 7 }; + uint8_t opt_val[] = { 53, 51, 50 }; + uint8_t data1[] = { 0x74, 0x71, 0x32, 0x31, 0x33, 0xe1, 0x00, 0x15, + 0x35 }; + uint8_t data2[] = { 0x74, 0x71, 0x32, 0x33, 0x01, 0x23, 0x45, 0xe1, + 0x00, 0x15, 0x35 }; + uint8_t data3[] = { 0x74, 0x70, 0x31, 0x33, 0xe1, 0x00, 0x15, 0x35 }; + uint8_t data4[] = { 0x74, 0x71, 0x32, 0x31, 0x33, 0xe4, 0x00, 0x15, + 0x06, 0x54, 0x32, 0x10 }; + int new_val; + unsigned char buf[4]; + + coap_pdu_clear(pdu, pdu->max_size); /* clear PDU */ + + CU_ASSERT(pdu->data == NULL); + + coap_add_token(pdu, sizeof(token), token); + for (n = 0; n < (sizeof(opt_num)/sizeof(opt_num[0])); n++) { + coap_add_option(pdu, opt_num[n], + sizeof(opt_val[n]), &opt_val[n]); + } + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); + /* Now update an option in the middle */ + new_val = 0x12345; + coap_update_option(pdu, 10, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data2, pdu->used_size) == 0); + /* Shrink it back again */ + new_val = 51; + coap_update_option(pdu, 10, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); + /* Now update an option at the start */ + new_val = 0; + coap_update_option(pdu, 7, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data3, pdu->used_size) == 0); + /* put it back again */ + new_val = 50; + coap_update_option(pdu, 7, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); + /* Now update an option at the end */ + new_val = 0x6543210; + coap_update_option(pdu, 300, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data4, pdu->used_size) == 0); + /* put it back again */ + new_val = 53; + coap_update_option(pdu, 300, + coap_encode_var_safe(buf, sizeof(buf), new_val), buf); + CU_ASSERT(memcmp(pdu->token, data1, pdu->used_size) == 0); +} + static int t_pdu_tests_create(void) { pdu = coap_pdu_init(0, 0, 0, COAP_DEFAULT_MTU); @@ -859,6 +1011,9 @@ t_init_pdu_tests(void) { PDU_ENCODER_TEST(suite[1], t_encode_pdu12); PDU_ENCODER_TEST(suite[1], t_encode_pdu13); PDU_ENCODER_TEST(suite[1], t_encode_pdu14); + PDU_ENCODER_TEST(suite[1], t_encode_pdu15); + PDU_ENCODER_TEST(suite[1], t_encode_pdu16); + PDU_ENCODER_TEST(suite[1], t_encode_pdu17); } else /* signal error */ fprintf(stderr, "W: cannot add pdu parser test suite (%s)\n",