From 60531ff6e25d363f36f843cae29a95031aac2147 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 1 Mar 2023 11:06:46 +0000 Subject: [http] Use POST method only if the form parameter list is non-empty An attempt to use an existent but empty form parameter list will currently result in an invalid POST request since the Content-Length header will be missing. Fix by using GET instead of POST if the form parameter list is empty. This is a non-breaking change (since the current behaviour produces an invalid request), and simplifies the imminent generalisation of the parameter list concept to handle both header and form parameters. Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 76 +++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 44 deletions(-) (limited to 'src/net/tcp/httpcore.c') diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index fd94b5f08..c970d54bf 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -1904,53 +1904,58 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) { } /** - * Open HTTP transaction for simple GET URI - * - * @v xfer Data transfer interface - * @v uri Request URI - * @ret rc Return status code - */ -static int http_open_get_uri ( struct interface *xfer, struct uri *uri ) { - - return http_open ( xfer, &http_get, uri, NULL, NULL ); -} - -/** - * Open HTTP transaction for simple POST URI + * Open HTTP transaction for simple URI * * @v xfer Data transfer interface * @v uri Request URI * @ret rc Return status code */ -static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) { +int http_open_uri ( struct interface *xfer, struct uri *uri ) { struct parameters *params = uri->params; struct http_request_content content; + struct http_method *method; + const char *type; void *data; size_t len; size_t check_len; int rc; - /* Calculate length of parameter list */ - len = http_params ( params, NULL, 0 ); + /* Calculate length of parameter list, if any */ + len = ( params ? http_params ( params, NULL, 0 ) : 0 ); - /* Allocate temporary parameter list */ - data = zalloc ( len + 1 /* NUL */ ); - if ( ! data ) { - rc = -ENOMEM; - goto err_alloc; - } + /* Use POST if and only if there are parameters */ + if ( len ) { - /* Construct temporary parameter list */ - check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); - assert ( check_len == len ); + /* Use POST */ + method = &http_post; + type = "application/x-www-form-urlencoded"; + + /* Allocate temporary parameter list */ + data = zalloc ( len + 1 /* NUL */ ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct temporary parameter list */ + check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); + + } else { + + /* Use GET */ + method = &http_get; + type = NULL; + data = NULL; + } /* Construct request content */ - content.type = "application/x-www-form-urlencoded"; + content.type = type; content.data = data; content.len = len; /* Open HTTP transaction */ - if ( ( rc = http_open ( xfer, &http_post, uri, NULL, &content ) ) != 0 ) + if ( ( rc = http_open ( xfer, method, uri, NULL, &content ) ) != 0 ) goto err_open; err_open: @@ -1959,23 +1964,6 @@ static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) { return rc; } -/** - * Open HTTP transaction for simple URI - * - * @v xfer Data transfer interface - * @v uri Request URI - * @ret rc Return status code - */ -int http_open_uri ( struct interface *xfer, struct uri *uri ) { - - /* Open GET/POST URI as applicable */ - if ( uri->params ) { - return http_open_post_uri ( xfer, uri ); - } else { - return http_open_get_uri ( xfer, uri ); - } -} - /* Drag in HTTP extensions */ REQUIRING_SYMBOL ( http_open ); REQUIRE_OBJECT ( config_http ); -- cgit v1.2.3-55-g7522 From 96bb6ba441653a30729ade38dc6c23bc9e2d2339 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Feb 2023 17:46:13 +0000 Subject: [params] Allow for arbitrary HTTP request headers to be specified Extend the request parameter mechanism to allow for arbitrary HTTP headers to be specified via e.g.: params param --header Referer http://www.example.com imgfetch http://192.168.0.1/script.ipxe##params Signed-off-by: Michael Brown --- src/core/params.c | 11 ++++++++--- src/hci/commands/param_cmd.c | 10 +++++++++- src/include/ipxe/params.h | 11 ++++++++++- src/net/tcp/httpcore.c | 43 ++++++++++++++++++++++++++++++++++--------- src/tests/uri_test.c | 13 ++++++++++++- 5 files changed, 73 insertions(+), 15 deletions(-) (limited to 'src/net/tcp/httpcore.c') diff --git a/src/core/params.c b/src/core/params.c index 23206bef6..58c829f62 100644 --- a/src/core/params.c +++ b/src/core/params.c @@ -123,10 +123,12 @@ struct parameters * create_parameters ( const char *name ) { * @v params Parameter list * @v key Parameter key * @v value Parameter value + * @v flags Parameter flags * @ret param Parameter, or NULL on failure */ struct parameter * add_parameter ( struct parameters *params, - const char *key, const char *value ) { + const char *key, const char *value, + unsigned int flags ) { struct parameter *param; size_t key_len; size_t value_len; @@ -147,11 +149,14 @@ struct parameter * add_parameter ( struct parameters *params, param->key = key_copy; strcpy ( value_copy, value ); param->value = value_copy; + param->flags = flags; /* Add to list of parameters */ list_add_tail ( ¶m->list, ¶ms->entries ); - DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"\n", - params->name, param->key, param->value ); + DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"%s%s\n", + params->name, param->key, param->value, + ( ( param->flags & PARAMETER_FORM ) ? " (form)" : "" ), + ( ( param->flags & PARAMETER_HEADER ) ? " (header)" : "" ) ); return param; } diff --git a/src/hci/commands/param_cmd.c b/src/hci/commands/param_cmd.c index 9e3260de1..dad99f840 100644 --- a/src/hci/commands/param_cmd.c +++ b/src/hci/commands/param_cmd.c @@ -90,12 +90,16 @@ static int params_exec ( int argc, char **argv ) { struct param_options { /** Parameter list name */ char *params; + /** Parameter is a header */ + int header; }; /** "param" option list */ static struct option_descriptor param_opts[] = { OPTION_DESC ( "params", 'p', required_argument, struct param_options, params, parse_string ), + OPTION_DESC ( "header", 'H', no_argument, + struct param_options, header, parse_flag ), }; /** "param" command descriptor */ @@ -114,6 +118,7 @@ static int param_exec ( int argc, char **argv ) { struct param_options opts; char *key; char *value; + unsigned int flags; struct parameters *params; struct parameter *param; int rc; @@ -132,12 +137,15 @@ static int param_exec ( int argc, char **argv ) { goto err_parse_value; } + /* Construct flags */ + flags = ( opts.header ? PARAMETER_HEADER : PARAMETER_FORM ); + /* Identify parameter list */ if ( ( rc = parse_parameters ( opts.params, ¶ms ) ) != 0 ) goto err_parse_parameters; /* Add parameter */ - param = add_parameter ( params, key, value ); + param = add_parameter ( params, key, value, flags ); if ( ! param ) { rc = -ENOMEM; goto err_add_parameter; diff --git a/src/include/ipxe/params.h b/src/include/ipxe/params.h index 955f57acc..61e46e029 100644 --- a/src/include/ipxe/params.h +++ b/src/include/ipxe/params.h @@ -32,8 +32,16 @@ struct parameter { const char *key; /** Value */ const char *value; + /** Flags */ + unsigned int flags; }; +/** Request parameter is a form parameter */ +#define PARAMETER_FORM 0x0001 + +/** Request parameter is a header parameter */ +#define PARAMETER_HEADER 0x0002 + /** * Increment request parameter list reference count * @@ -78,6 +86,7 @@ claim_parameters ( struct parameters *params ) { extern struct parameters * find_parameters ( const char *name ); extern struct parameters * create_parameters ( const char *name ); extern struct parameter * add_parameter ( struct parameters *params, - const char *key, const char *value ); + const char *key, const char *value, + unsigned int flags ); #endif /* _IPXE_PARAMS_H */ diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index c970d54bf..9ad39656d 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -830,7 +830,9 @@ static int http_transfer_complete ( struct http_transaction *http ) { */ static int http_format_headers ( struct http_transaction *http, char *buf, size_t len ) { + struct parameters *params = http->uri->params; struct http_request_header *header; + struct parameter *param; size_t used; size_t remaining; char *line; @@ -844,7 +846,7 @@ static int http_format_headers ( struct http_transaction *http, char *buf, DBGC2 ( http, "HTTP %p TX %s\n", http, buf ); used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); - /* Construct all headers */ + /* Construct all fixed headers */ for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) { /* Determine header value length */ @@ -869,6 +871,23 @@ static int http_format_headers ( struct http_transaction *http, char *buf, used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); } + /* Construct parameter headers, if any */ + if ( params ) { + + /* Construct all parameter headers */ + for_each_param ( param, params ) { + + /* Skip non-header parameters */ + if ( ! ( param->flags & PARAMETER_HEADER ) ) + continue; + + /* Add parameter */ + used += ssnprintf ( ( buf + used ), ( len - used ), + "%s: %s\r\n", param->key, + param->value ); + } + } + /* Construct terminating newline */ used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); @@ -1851,14 +1870,15 @@ static struct http_state http_trailers = { */ /** - * Construct HTTP parameter list + * Construct HTTP form parameter list * * @v params Parameter list * @v buf Buffer to contain HTTP POST parameters * @v len Length of buffer * @ret len Length of parameter list (excluding terminating NUL) */ -static size_t http_params ( struct parameters *params, char *buf, size_t len ) { +static size_t http_form_params ( struct parameters *params, char *buf, + size_t len ) { struct parameter *param; ssize_t remaining = len; size_t frag_len; @@ -1867,6 +1887,10 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) { len = 0; for_each_param ( param, params ) { + /* Skip non-form parameters */ + if ( ! ( param->flags & PARAMETER_FORM ) ) + continue; + /* Add the "&", if applicable */ if ( len ) { if ( remaining > 0 ) @@ -1920,25 +1944,26 @@ int http_open_uri ( struct interface *xfer, struct uri *uri ) { size_t check_len; int rc; - /* Calculate length of parameter list, if any */ - len = ( params ? http_params ( params, NULL, 0 ) : 0 ); + /* Calculate length of form parameter list, if any */ + len = ( params ? http_form_params ( params, NULL, 0 ) : 0 ); - /* Use POST if and only if there are parameters */ + /* Use POST if and only if there are form parameters */ if ( len ) { /* Use POST */ method = &http_post; type = "application/x-www-form-urlencoded"; - /* Allocate temporary parameter list */ + /* Allocate temporary form parameter list */ data = zalloc ( len + 1 /* NUL */ ); if ( ! data ) { rc = -ENOMEM; goto err_alloc; } - /* Construct temporary parameter list */ - check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); + /* Construct temporary form parameter list */ + check_len = http_form_params ( params, data, + ( len + 1 /* NUL */ ) ); assert ( check_len == len ); } else { diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 5e7060323..9d2f6dba5 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -98,6 +98,8 @@ struct uri_params_test_list { const char *key; /** Value */ const char *value; + /** Flags */ + unsigned int flags; }; /** A request parameter URI test */ @@ -428,6 +430,7 @@ static void uri_params_list_okx ( struct uri_params_test *test, file, line ); okx ( strcmp ( param->value, list->value ) == 0, file, line ); + okx ( param->flags == list->flags, file, line ); list++; } okx ( list->key == NULL, file, line ); @@ -456,7 +459,8 @@ static void uri_params_okx ( struct uri_params_test *test, const char *file, okx ( params != NULL, file, line ); if ( params ) { for ( list = test->list ; list->key ; list++ ) { - param = add_parameter ( params, list->key, list->value); + param = add_parameter ( params, list->key, list->value, + list->flags ); okx ( param != NULL, file, line ); } } @@ -884,18 +888,22 @@ static struct uri_params_test_list uri_params_list[] = { { "vendor", "10ec", + PARAMETER_FORM, }, { "device", "8139", + PARAMETER_FORM, }, { "uuid", "f59fac00-758f-498f-9fe5-87d790045d94", + PARAMETER_HEADER, }, { NULL, NULL, + 0, } }; @@ -917,14 +925,17 @@ static struct uri_params_test_list uri_named_params_list[] = { { "mac", "00:1e:65:80:d3:b6", + PARAMETER_FORM, }, { "serial", "LXTQ20Z1139322762F2000", + PARAMETER_FORM, }, { NULL, NULL, + 0, } }; -- cgit v1.2.3-55-g7522