Skip to content

Commit

Permalink
reverse-proxy: Support for .well-known/core being passed
Browse files Browse the repository at this point in the history
context is left locked when calling pseudo .well-known/core
handler to prevent mismatch in size and output.

Move get_wkc_len() into hnd_get_wellknown_lkd() to simply document how
hnd_get_wellknown should be done.
  • Loading branch information
mrdeep1 committed Jun 3, 2024
1 parent 7c33808 commit 0124236
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 75 deletions.
7 changes: 7 additions & 0 deletions include/coap3/coap_resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ typedef void (*coap_method_handler_t)(coap_resource_t *resource,
*/
#define COAP_RESOURCE_FLAGS_OSCORE_ONLY 0x400

/**
* Define this when invoking *coap_resource_unknown_init2*() if .well-known/core
* is to be passed to the unknown URI handler rather than processed locally.
* Used for easily passing on a request as a reverse-proxy request.
*/
#define COAP_RESOURCE_HANDLE_WELLKNOWN_CORE 0x800

/**
* Creates a new resource object and initializes the link field to the string
* @p uri_path. This function returns the new coap_resource_t object.
Expand Down
16 changes: 14 additions & 2 deletions man/coap_resource.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,22 @@ to the request handler.
*COAP_RESOURCE_FLAGS_OSCORE_ONLY*::
Define this resource as an OSCORE enabled access only.

*COAP_RESOURCE_HANDLE_WELLKNOWN_CORE::
Define this when invoking *coap_resource_unknown_init2*() if .well-known/core
is to be passed to the unknown URI handler rather than processed locally.
Used for easily passing on a request as a reverse-proxy request.

*NOTE:* The following flags are only tested against if
*coap_mcast_per_resource*() has been called. If *coap_mcast_per_resource*()
has not been called, then all resources have multicast support, libcoap adds
in random delays to the responses, and 4.xx / 5.xx responses are dropped.

*NOTE:* The pseudo resource for ".well-known/core" always has multicast
support enabled and is not configurable.
support enabled and is not configurable. It is possible for a server to
create a resource for ".well-known/core" that can then control the
multicast support and the provided GET request handler can call
*coap_print_wellknown*() to produce the same information as the pseudo
resource.

[horizontal]
*COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT*::
Expand All @@ -173,7 +182,7 @@ sending the response back to the client. It is then the responsibility of
the app to delay the responses for multicast requests. See
"https://rfc-editor.org/rfc/rfc7252#section-8.2[RFC7252 8.2. Request/Response
Layer]".
However, the pseudo resource for ".well-known/core" always has multicast
However, the pseudo resource for ".well-known/core" always has multicast
support enabled.

*COAP_RESOURCE_FLAGS_LIB_ENA_MCAST_SUPPRESS_2_05*::
Expand Down Expand Up @@ -302,6 +311,9 @@ identified by the unique string _uri_path_ associated with _context_.
The *coap_print_wellknown*() function prints the names of all known resources
of the given _context_ into _buf_ which has a maximum size of _buflen_. The
first _offset_ bytes are skipped from the output to handle block transfers.
Setting _offset_ to 0 means the entire (matching) information is output.
Setting _offset_ to UINT_MAX skips, but calculates the size of, the (matching)
output.
The _query_filter_ is usually defined by the CoAP Uri-Query options as a query.

RETURN VALUES
Expand Down
149 changes: 81 additions & 68 deletions src/coap_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -2713,40 +2713,6 @@ coap_new_error_response(const coap_pdu_t *request, coap_pdu_code_t code,
}

#if COAP_SERVER_SUPPORT
/**
* Quick hack to determine the size of the resource description for
* .well-known/core.
*/
COAP_STATIC_INLINE ssize_t
get_wkc_len(coap_context_t *context,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query_filter) {
unsigned char buf[1];
size_t len = 0;
int result = 0;

if (context->print_wellknown_userdata) {
result = context->print_wellknown_userdata(context,
session,
request,
buf,
&len,
UINT_MAX,
query_filter);
} else {
coap_lock_lock(session->context, return COAP_PRINT_STATUS_ERROR);
result = coap_print_wellknown_lkd(context, buf, &len, UINT_MAX, query_filter);
coap_lock_unlock(session->context);
}
if (result & COAP_PRINT_STATUS_ERROR) {
coap_log_warn("cannot determine length of /.well-known/core\n");
return -1L;
}

return len;
}

#define SZX_TO_BYTES(SZX) ((size_t)(1 << ((SZX) + 4)))

static void
Expand All @@ -2755,39 +2721,63 @@ free_wellknown_response(coap_session_t *session COAP_UNUSED, void *app_ptr) {
}

/*
* Caution - this handler is being treated as if in app space.
* Caution: As this handler is in libcoap space, it is called with
* context locked.
*/
static void
hnd_get_wellknown(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
hnd_get_wellknown_lkd(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response) {
size_t len = 0;
coap_string_t *data_string = NULL;
int result = 0;
ssize_t wkc_len = get_wkc_len(session->context, session, request, query);
coap_print_status_t result = 0;
size_t wkc_len = 0;
uint8_t buf[4];

if (wkc_len) {
if (wkc_len < 0)
goto error;
/*
* Quick hack to determine the size of the resource descriptions for
* .well-known/core.
*/
if (session->context->print_wellknown_userdata) {
/* Need to retain the lock so that the length cannot change. */
coap_lock_callback_ret(result,
session->context,
session->context->print_wellknown_userdata(session->context,
session,
request,
buf,
&wkc_len,
UINT_MAX,
query));
} else {
result = coap_print_wellknown_lkd(session->context, buf, &wkc_len, UINT_MAX, query);
}
if (result & COAP_PRINT_STATUS_ERROR) {
coap_log_warn("cannot determine length of /.well-known/core\n");
goto error;
}

if (wkc_len > 0) {
data_string = coap_new_string(wkc_len);
if (!data_string)
goto error;

len = wkc_len;
if (session->context->print_wellknown_userdata) {
result = session->context->print_wellknown_userdata(session->context,
session,
request,
data_string->s,
&len,
0,
query);
/* Need to retain the lock so that the length cannot change. */
coap_lock_callback_ret(result,
session->context,
session->context->print_wellknown_userdata(session->context,
session,
request,
data_string->s,
&len,
0,
query));
} else {
coap_lock_lock(session->context, goto error);
result = coap_print_wellknown_lkd(session->context, data_string->s, &len, 0, query);
coap_lock_unlock(session->context);
}
if ((result & COAP_PRINT_STATUS_ERROR) != 0) {
coap_log_debug("coap_print_wellknown failed\n");
Expand All @@ -2797,8 +2787,6 @@ hnd_get_wellknown(coap_resource_t *resource,
data_string->length = len;

if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
uint8_t buf[4];

if (!coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT,
coap_encode_var_safe(buf, sizeof(buf),
COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf)) {
Expand Down Expand Up @@ -2826,6 +2814,12 @@ hnd_get_wellknown(coap_resource_t *resource,
data_string)) {
goto error_released;
}
} else {
if (!coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT,
coap_encode_var_safe(buf, sizeof(buf),
COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf)) {
goto error;
}
}
response->code = COAP_RESPONSE_CODE(205);
return;
Expand Down Expand Up @@ -3215,20 +3209,31 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu
* if configured, for the unknown handler.
*
* if a PROXY URI/Scheme request and proxy URI handler defined, call the
* proxy URI handler
* proxy URI handler.
*
* else if well-known URI generate a default response
* else if unknown URI handler defined and COAP_RESOURCE_HANDLE_WELLKNOWN_CORE
* set, call the unknown URI handler with any unknown URI (including
* .well-known/core) if the appropriate method is defined.
*
* else if well-known URI generate a default response.
*
* else if unknown URI handler defined, call the unknown
* URI handler (to allow for potential generation of resource
* [RFC7272 5.8.3]) if the appropriate method is defined.
*
* else if DELETE return 2.02 (RFC7252: 5.8.4. DELETE)
* else if DELETE return 2.02 (RFC7252: 5.8.4. DELETE).
*
* else return 4.04 */
* else return 4.04.
*/

if (is_proxy_uri || is_proxy_scheme) {
resource = context->proxy_uri_resource;
} else if (context->unknown_resource != NULL &&
context->unknown_resource->flags & COAP_RESOURCE_HANDLE_WELLKNOWN_CORE &&
((size_t)pdu->code - 1 <
(sizeof(resource->handler) / sizeof(coap_method_handler_t))) &&
(context->unknown_resource->handler[pdu->code - 1])) {
resource = context->unknown_resource;
} else if (coap_string_equal(uri_path, &coap_default_uri_wellknown)) {
/* request for .well-known/core */
resource = &resource_uri_wellknown;
Expand Down Expand Up @@ -3446,13 +3451,21 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu
/*
* Call the request handler with everything set up
*/
coap_log_debug("call custom handler for resource '%*.*s' (3)\n",
(int)resource->uri_path->length, (int)resource->uri_path->length,
resource->uri_path->s);
coap_lock_callback_release(context,
h(resource, session, pdu, query, response),
/* context is being freed off */
goto finish);
if (resource == &resource_uri_wellknown) {
/* Leave context locked */
coap_log_debug("call handler for pseudo resource '%*.*s' (3)\n",
(int)resource->uri_path->length, (int)resource->uri_path->length,
resource->uri_path->s);
h(resource, session, pdu, query, response);
} else {
coap_log_debug("call custom handler for resource '%*.*s' (3)\n",
(int)resource->uri_path->length, (int)resource->uri_path->length,
resource->uri_path->s);
coap_lock_callback_release(context,
h(resource, session, pdu, query, response),
/* context is being freed off */
goto finish);
}

/* Check validity of response code */
if (!coap_check_code_class(session, response)) {
Expand Down Expand Up @@ -4411,7 +4424,7 @@ coap_startup(void) {
(const uint8_t *)".well-known/core"
};
memset(&resource_uri_wellknown, 0, sizeof(resource_uri_wellknown));
resource_uri_wellknown.handler[COAP_REQUEST_GET-1] = hnd_get_wellknown;
resource_uri_wellknown.handler[COAP_REQUEST_GET-1] = hnd_get_wellknown_lkd;
resource_uri_wellknown.flags = COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT;
resource_uri_wellknown.uri_path = &well_known;
#endif /* COAP_SERVER_SUPPORT */
Expand Down
23 changes: 18 additions & 5 deletions src/coap_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,18 @@ match(const coap_str_const_t *text, const coap_str_const_t *pattern,
* @param offset The offset in bytes where the output shall start and is
* shifted accordingly with the characters that have been
* processed. This parameter is used to support the block
* option.
* @param query_filter A filter query according to <a href="http://tools.ietf.org/html/draft-ietf-core-link-format-11#section-4.1">Link Format</a>
* option. Setting _offset_ to 0 means the entire (matching) information
* is output to @p buf. Setting _offset_ to UINT_MAX skips, but calculates
* the size of, the (matching) output.
* @param query_filter A filter query according to
* <a href="http://tools.ietf.org/html/draft-ietf-core-link-format-11#section-4.1">Link Format</a>
*
* @return COAP_PRINT_STATUS_ERROR on error. Otherwise, the lower 28 bits are
* set to the number of bytes that have actually been written to
* @p buf. COAP_PRINT_STATUS_TRUNC is set when the output has been
* truncated.
*/
coap_print_status_t
COAP_API coap_print_status_t
coap_print_wellknown(coap_context_t *context, unsigned char *buf,
size_t *buflen, size_t offset,
const coap_string_t *query_filter) {
Expand All @@ -134,6 +137,11 @@ coap_print_wellknown(coap_context_t *context, unsigned char *buf,
return result;
}

static coap_str_const_t coap_default_uri_wellknown = {
sizeof(COAP_DEFAULT_URI_WELLKNOWN)-1,
(const uint8_t *)COAP_DEFAULT_URI_WELLKNOWN
};

coap_print_status_t
coap_print_wellknown_lkd(coap_context_t *context, unsigned char *buf,
size_t *buflen, size_t offset,
Expand Down Expand Up @@ -161,6 +169,7 @@ coap_print_wellknown_lkd(coap_context_t *context, unsigned char *buf,
};
#endif /* WITHOUT_QUERY_FILTER */

coap_lock_check_locked(context);
#ifndef WITHOUT_QUERY_FILTER
/* split query filter, if any */
if (query_filter) {
Expand Down Expand Up @@ -207,6 +216,10 @@ coap_print_wellknown_lkd(coap_context_t *context, unsigned char *buf,

RESOURCES_ITER(context->resources, r) {

if (coap_string_equal(r->uri_path, &coap_default_uri_wellknown)) {
/* server app has defined a resource for .well-known/core - ignore */
continue;
}
#ifndef WITHOUT_QUERY_FILTER
if (resource_param.length) { /* there is a query filter */

Expand Down Expand Up @@ -314,7 +327,7 @@ coap_resource_unknown_init2(coap_method_handler_t put_handler, int flags) {
r->is_unknown = 1;
/* Something unlikely to be used, but it shows up in the logs */
r->uri_path = coap_new_str_const(coap_unknown_resource_uri, sizeof(coap_unknown_resource_uri)-1);
r->flags = flags & COAP_RESOURCE_FLAGS_MCAST_LIST;
r->flags = flags & ~COAP_RESOURCE_FLAGS_RELEASE_URI;
coap_register_handler(r, COAP_REQUEST_PUT, put_handler);
} else {
coap_log_debug("coap_resource_unknown_init: no memory left\n");
Expand Down Expand Up @@ -372,7 +385,7 @@ coap_resource_proxy_uri_init2(coap_method_handler_t handler,
r->proxy_name_count = i;
}
}
r->flags = flags & COAP_RESOURCE_FLAGS_MCAST_LIST;
r->flags = flags & ~COAP_RESOURCE_FLAGS_RELEASE_URI;
} else {
coap_log_debug("coap_resource_proxy_uri_init2: no memory left\n");
}
Expand Down

0 comments on commit 0124236

Please sign in to comment.