http: udp proxy uri template parsing
Parse a URI template that has variables "target_host" and "target_port", where varaibles are at the end of the path: "/{target_host}/{target_port}/". Type: improvement Change-Id: I440b7f4951bffa1fd9971740b9890b221193943b Signed-off-by: Matus Fabian <matfabia@cisco.com>
This commit is contained in:

committed by
Florin Coras

parent
1c170f571a
commit
0af11f537f
@ -406,7 +406,7 @@ hcs_ts_rx_callback (session_t *ts)
|
|||||||
}
|
}
|
||||||
if (is_encoded)
|
if (is_encoded)
|
||||||
{
|
{
|
||||||
u8 *decoded = http_percent_decode (args.buf);
|
u8 *decoded = http_percent_decode (args.buf, vec_len (args.buf));
|
||||||
vec_free (args.buf);
|
vec_free (args.buf);
|
||||||
args.buf = decoded;
|
args.buf = decoded;
|
||||||
}
|
}
|
||||||
|
@ -522,18 +522,19 @@ http_validate_query_syntax (u8 *query, int *is_encoded)
|
|||||||
* Decode percent-encoded data.
|
* Decode percent-encoded data.
|
||||||
*
|
*
|
||||||
* @param src Data to decode.
|
* @param src Data to decode.
|
||||||
|
* @param len Length of data to decode.
|
||||||
*
|
*
|
||||||
* @return New vector with decoded data.
|
* @return New vector with decoded data.
|
||||||
*
|
*
|
||||||
* The caller is always responsible to free the returned vector.
|
* The caller is always responsible to free the returned vector.
|
||||||
*/
|
*/
|
||||||
always_inline u8 *
|
always_inline u8 *
|
||||||
http_percent_decode (u8 *src)
|
http_percent_decode (u8 *src, u32 len)
|
||||||
{
|
{
|
||||||
int i;
|
u32 i;
|
||||||
u8 *decoded_uri = 0;
|
u8 *decoded_uri = 0;
|
||||||
|
|
||||||
for (i = 0; i < vec_len (src); i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (src[i] == '%')
|
if (src[i] == '%')
|
||||||
{
|
{
|
||||||
@ -995,6 +996,31 @@ typedef struct
|
|||||||
u8 host_is_ip6;
|
u8 host_is_ip6;
|
||||||
} http_url_t;
|
} http_url_t;
|
||||||
|
|
||||||
|
always_inline int
|
||||||
|
_parse_port (u8 **pos, u8 *end, u16 *port)
|
||||||
|
{
|
||||||
|
u32 value = 0;
|
||||||
|
u8 *p = *pos;
|
||||||
|
|
||||||
|
if (!isdigit (*p))
|
||||||
|
return -1;
|
||||||
|
value = *p - '0';
|
||||||
|
p++;
|
||||||
|
|
||||||
|
while (p != end)
|
||||||
|
{
|
||||||
|
if (!isdigit (*p))
|
||||||
|
break;
|
||||||
|
value = value * 10 + *p - '0';
|
||||||
|
if (value > CLIB_U16_MAX)
|
||||||
|
return -1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
*pos = p;
|
||||||
|
*port = clib_host_to_net_u16 ((u16) value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An "absolute-form" URL parsing.
|
* An "absolute-form" URL parsing.
|
||||||
*
|
*
|
||||||
@ -1102,27 +1128,12 @@ http_parse_absolute_form (u8 *url, http_url_t *parsed)
|
|||||||
/* parse port, if any */
|
/* parse port, if any */
|
||||||
if (token_start != end && *token_start == ':')
|
if (token_start != end && *token_start == ':')
|
||||||
{
|
{
|
||||||
u32 port = 0;
|
|
||||||
token_end = ++token_start;
|
token_end = ++token_start;
|
||||||
while (token_end != end && *token_end != '/')
|
if (_parse_port (&token_end, end, &parsed->port))
|
||||||
{
|
{
|
||||||
if (isdigit (*token_end))
|
clib_warning ("invalid port");
|
||||||
{
|
return -1;
|
||||||
port = port * 10 + *token_end - '0';
|
|
||||||
if (port > 65535)
|
|
||||||
{
|
|
||||||
clib_warning ("invalid port number");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
clib_warning ("expected digit '%u'", *token_end);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
token_end++;
|
|
||||||
}
|
}
|
||||||
parsed->port = clib_host_to_net_u16 ((u16) port);
|
|
||||||
token_start = token_end;
|
token_start = token_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1140,6 +1151,61 @@ http_parse_absolute_form (u8 *url, http_url_t *parsed)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse target host and port of UDP tunnel over HTTP.
|
||||||
|
*
|
||||||
|
* @param path Path in format "{target_host}/{target_port}/".
|
||||||
|
* @param path_len Length of given path.
|
||||||
|
* @param parsed Parsed target in case of success..
|
||||||
|
*
|
||||||
|
* @return @c 0 on success.
|
||||||
|
*
|
||||||
|
* @note Only IPv4 literals and IPv6 literals supported.
|
||||||
|
*/
|
||||||
|
always_inline int
|
||||||
|
http_parse_masque_host_port (u8 *path, u32 path_len, http_uri_t *parsed)
|
||||||
|
{
|
||||||
|
u8 *p, *end, *decoded_host;
|
||||||
|
u32 host_len;
|
||||||
|
unformat_input_t input;
|
||||||
|
|
||||||
|
p = path;
|
||||||
|
end = path + path_len;
|
||||||
|
clib_memset (parsed, 0, sizeof (*parsed));
|
||||||
|
|
||||||
|
while (p != end && *p != '/')
|
||||||
|
p++;
|
||||||
|
|
||||||
|
host_len = p - path;
|
||||||
|
if (!host_len || (host_len == path_len) || (host_len + 1 == path_len))
|
||||||
|
return -1;
|
||||||
|
decoded_host = http_percent_decode (path, host_len);
|
||||||
|
unformat_init_vector (&input, decoded_host);
|
||||||
|
if (unformat (&input, "%U", unformat_ip4_address, &parsed->ip.ip4))
|
||||||
|
parsed->is_ip4 = 1;
|
||||||
|
else if (unformat (&input, "%U", unformat_ip6_address, &parsed->ip.ip6))
|
||||||
|
parsed->is_ip4 = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unformat_free (&input);
|
||||||
|
clib_warning ("unsupported target_host format");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
unformat_free (&input);
|
||||||
|
|
||||||
|
p++;
|
||||||
|
if (_parse_port (&p, end, &parsed->port))
|
||||||
|
{
|
||||||
|
clib_warning ("invalid port");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == end || *p != '/')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* SRC_PLUGINS_HTTP_HTTP_H_ */
|
#endif /* SRC_PLUGINS_HTTP_HTTP_H_ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -16,7 +16,8 @@ Usage
|
|||||||
|
|
||||||
The plugin exposes following inline functions: ``http_validate_abs_path_syntax``, ``http_validate_query_syntax``,
|
The plugin exposes following inline functions: ``http_validate_abs_path_syntax``, ``http_validate_query_syntax``,
|
||||||
``http_percent_decode``, ``http_path_remove_dot_segments``, ``http_parse_headers``, ``http_get_header``,
|
``http_percent_decode``, ``http_path_remove_dot_segments``, ``http_parse_headers``, ``http_get_header``,
|
||||||
``http_free_header_table``, ``http_add_header``, ``http_serialize_headers``.
|
``http_free_header_table``, ``http_add_header``, ``http_serialize_headers``, ``http_parse_authority_form_target``,
|
||||||
|
``http_serialize_authority_form_target``, ``http_parse_absolute_form``, ``http_parse_masque_host_port``.
|
||||||
|
|
||||||
It relies on the hoststack constructs and uses ``http_msg_data_t`` data structure for passing metadata to/from applications.
|
It relies on the hoststack constructs and uses ``http_msg_data_t`` data structure for passing metadata to/from applications.
|
||||||
|
|
||||||
@ -82,7 +83,7 @@ Example bellow validates "absolute-path" rule, as described in RFC9110 section 4
|
|||||||
}
|
}
|
||||||
if (is_encoded)
|
if (is_encoded)
|
||||||
{
|
{
|
||||||
u8 *decoded = http_percent_decode (target_path);
|
u8 *decoded = http_percent_decode (target_path, vec_len (target_path));
|
||||||
vec_free (target_path);
|
vec_free (target_path);
|
||||||
target_path = decoded;
|
target_path = decoded;
|
||||||
}
|
}
|
||||||
|
@ -84,10 +84,6 @@ http_test_absolute_form (vlib_main_t *vm)
|
|||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
url = format (0, "https://example.org/.well-known/masque/udp/1.2.3.4/123/");
|
url = format (0, "https://example.org/.well-known/masque/udp/1.2.3.4/123/");
|
||||||
clib_warning (
|
|
||||||
"strlen %u vec_len %u",
|
|
||||||
strlen ("https://example.org/.well-known/masque/udp/1.2.3.4/123/"),
|
|
||||||
vec_len (url));
|
|
||||||
rv = http_parse_absolute_form (url, &parsed_url);
|
rv = http_parse_absolute_form (url, &parsed_url);
|
||||||
HTTP_TEST ((rv == 0), "'%v' should be valid", url);
|
HTTP_TEST ((rv == 0), "'%v' should be valid", url);
|
||||||
HTTP_TEST ((parsed_url.scheme == HTTP_URL_SCHEME_HTTPS),
|
HTTP_TEST ((parsed_url.scheme == HTTP_URL_SCHEME_HTTPS),
|
||||||
@ -251,6 +247,74 @@ http_test_absolute_form (vlib_main_t *vm)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
http_test_parse_masque_host_port (vlib_main_t *vm)
|
||||||
|
{
|
||||||
|
u8 *path = 0;
|
||||||
|
http_uri_t target;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
path = format (0, "10.10.2.45/443/");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv == 0), "'%v' should be valid", path);
|
||||||
|
HTTP_TEST ((target.is_ip4 == 1), "is_ip4=%d should be 1", target.is_ip4);
|
||||||
|
HTTP_TEST ((clib_net_to_host_u16 (target.port) == 443),
|
||||||
|
"port=%u should be 443", clib_net_to_host_u16 (target.port));
|
||||||
|
HTTP_TEST ((target.ip.ip4.data[0] == 10 && target.ip.ip4.data[1] == 10 &&
|
||||||
|
target.ip.ip4.data[2] == 2 && target.ip.ip4.data[3] == 45),
|
||||||
|
"target.ip=%U should be 10.10.2.45", format_ip4_address,
|
||||||
|
&target.ip.ip4);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
path = format (0, "dead%%3Abeef%%3A%%3A1234/80/");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv == 0), "'%v' should be valid", path);
|
||||||
|
HTTP_TEST ((target.is_ip4 == 0), "is_ip4=%d should be 0", target.is_ip4);
|
||||||
|
HTTP_TEST ((clib_net_to_host_u16 (target.port) == 80),
|
||||||
|
"port=%u should be 80", clib_net_to_host_u16 (target.port));
|
||||||
|
HTTP_TEST ((clib_net_to_host_u16 (target.ip.ip6.as_u16[0]) == 0xdead &&
|
||||||
|
clib_net_to_host_u16 (target.ip.ip6.as_u16[1]) == 0xbeef &&
|
||||||
|
target.ip.ip6.as_u16[2] == 0 && target.ip.ip6.as_u16[3] == 0 &&
|
||||||
|
target.ip.ip6.as_u16[4] == 0 && target.ip.ip6.as_u16[5] == 0 &&
|
||||||
|
target.ip.ip6.as_u16[6] == 0 &&
|
||||||
|
clib_net_to_host_u16 (target.ip.ip6.as_u16[7]) == 0x1234),
|
||||||
|
"target.ip=%U should be dead:beef::1234", format_ip6_address,
|
||||||
|
&target.ip.ip6);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
path = format (0, "example.com/443/");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv != 0), "'%v' reg-name not supported", path);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
path = format (0, "10.10.2.45/443443/");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv != 0), "'%v' should be invalid", path);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
path = format (0, "/443/");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv != 0), "'%v' should be invalid", path);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
path = format (0, "10.10.2.45/");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv != 0), "'%v' should be invalid", path);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
path = format (0, "10.10.2.45");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv != 0), "'%v' should be invalid", path);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
path = format (0, "10.10.2.45/443");
|
||||||
|
rv = http_parse_masque_host_port (path, vec_len (path), &target);
|
||||||
|
HTTP_TEST ((rv != 0), "'%v' should be invalid", path);
|
||||||
|
vec_free (path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static clib_error_t *
|
static clib_error_t *
|
||||||
test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
|
test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
|
||||||
vlib_cli_command_t *cmd)
|
vlib_cli_command_t *cmd)
|
||||||
@ -262,12 +326,16 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
|
|||||||
res = http_test_authority_form (vm);
|
res = http_test_authority_form (vm);
|
||||||
else if (unformat (input, "absolute-form"))
|
else if (unformat (input, "absolute-form"))
|
||||||
res = http_test_absolute_form (vm);
|
res = http_test_absolute_form (vm);
|
||||||
|
else if (unformat (input, "parse-masque-host-port"))
|
||||||
|
res = http_test_parse_masque_host_port (vm);
|
||||||
else if (unformat (input, "all"))
|
else if (unformat (input, "all"))
|
||||||
{
|
{
|
||||||
if ((res = http_test_authority_form (vm)))
|
if ((res = http_test_authority_form (vm)))
|
||||||
goto done;
|
goto done;
|
||||||
if ((res = http_test_absolute_form (vm)))
|
if ((res = http_test_absolute_form (vm)))
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((res = http_test_parse_masque_host_port (vm)))
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
Reference in New Issue
Block a user