diff --git a/src/config/config.c b/src/config/config.c index c8cb5399..f063523c 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -263,6 +263,9 @@ REQUIRE_OBJECT ( nslookup_cmd ); #ifdef PCI_CMD REQUIRE_OBJECT ( pci_cmd ); #endif +#ifdef PARAM_CMD +REQUIRE_OBJECT ( param_cmd ); +#endif /* * Drag in miscellaneous objects diff --git a/src/core/params.c b/src/core/params.c new file mode 100644 index 00000000..21361c1f --- /dev/null +++ b/src/core/params.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Form parameters + * + */ + +#include +#include +#include + +/** List of all parameter lists */ +static LIST_HEAD ( parameters ); + +/** + * Find form parameter list by name + * + * @v name Parameter list name (may be NULL) + * @ret params Parameter list, or NULL if not found + */ +struct parameters * find_parameters ( const char *name ) { + struct parameters *params; + + list_for_each_entry ( params, ¶meters, list ) { + if ( ( params->name == name ) || + ( strcmp ( params->name, name ) == 0 ) ) { + return params; + } + } + return NULL; +} + +/** + * Create form parameter list + * + * @v name Parameter list name (may be NULL) + * @ret params Parameter list, or NULL on failure + */ +struct parameters * create_parameters ( const char *name ) { + struct parameters *params; + size_t name_len; + char *name_copy; + + /* Destroy any existing parameter list of this name */ + params = find_parameters ( name ); + if ( params ) + destroy_parameters ( params ); + + /* Allocate parameter list */ + name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 ); + params = zalloc ( sizeof ( *params ) + name_len ); + if ( ! params ) + return NULL; + name_copy = ( ( void * ) ( params + 1 ) ); + + /* Populate parameter list */ + if ( name ) { + strcpy ( name_copy, name ); + params->name = name_copy; + } + INIT_LIST_HEAD ( ¶ms->entries ); + + /* Add to list of parameter lists */ + list_add_tail ( ¶ms->list, ¶meters ); + + DBGC ( params, "PARAMS \"%s\" created\n", params->name ); + return params; +} + +/** + * Add form parameter + * + * @v params Parameter list + * @v key Parameter key + * @v value Parameter value + * @ret param Parameter, or NULL on failure + */ +struct parameter * add_parameter ( struct parameters *params, + const char *key, const char *value ) { + struct parameter *param; + size_t key_len; + size_t value_len; + char *key_copy; + char *value_copy; + + /* Allocate parameter */ + key_len = ( strlen ( key ) + 1 /* NUL */ ); + value_len = ( strlen ( value ) + 1 /* NUL */ ); + param = zalloc ( sizeof ( *param ) + key_len + value_len ); + if ( ! param ) + return NULL; + key_copy = ( ( void * ) ( param + 1 ) ); + value_copy = ( key_copy + key_len ); + + /* Populate parameter */ + strcpy ( key_copy, key ); + param->key = key_copy; + strcpy ( value_copy, value ); + param->value = value_copy; + + /* 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 ); + return param; +} + +/** + * Destroy form parameter list + * + * @v params Parameter list + */ +void destroy_parameters ( struct parameters *params ) { + struct parameter *param; + struct parameter *tmp; + + DBGC ( params, "PARAMS \"%s\" destroyed\n", params->name ); + + /* Free all parameters */ + list_for_each_entry_safe ( param, tmp, ¶ms->entries, list ) { + list_del ( ¶m->list ); + free ( param ); + } + + /* Free parameter list */ + list_del ( ¶ms->list ); + free ( params ); +} + +/** + * Claim ownership of form parameter list + * + * @v params Parameter list + */ +void claim_parameters ( struct parameters *params ) { + + DBGC ( params, "PARAMS \"%s\" claimed\n", params->name ); + + /* Remove from list of parameter lists */ + list_del ( ¶ms->list ); + + /* Reinitialise list to allow for subsequent destroy_parameters() */ + INIT_LIST_HEAD ( ¶ms->list ); +} diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 65ad4ec5..38b1f29f 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @file @@ -250,6 +251,29 @@ int parse_autovivified_setting ( char *text, struct named_setting *setting ) { return parse_setting ( text, setting, autovivify_child_settings ); } +/** + * Parse form parameter list name + * + * @v text Text + * @ret params Parameter list + * @ret rc Return status code + */ +int parse_parameters ( char *text, struct parameters **params ) { + + /* Find parameter list */ + *params = find_parameters ( text ); + if ( ! *params ) { + if ( text ) { + printf ( "\"%s\": no such parameter list\n", text ); + } else { + printf ( "No default parameter list\n" ); + } + return -ENOENT; + } + + return 0; +} + /** * Print command usage message * diff --git a/src/core/uri.c b/src/core/uri.c index e9526882..bc55e4d8 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @@ -59,6 +60,21 @@ static void dump_uri ( struct uri *uri ) { DBG ( " query \"%s\"", uri->query ); if ( uri->fragment ) DBG ( " fragment \"%s\"", uri->fragment ); + if ( uri->params ) + DBG ( " params \"%s\"", uri->params->name ); +} + +/** + * Free URI + * + * @v refcnt Reference count + */ +static void free_uri ( struct refcnt *refcnt ) { + struct uri *uri = container_of ( refcnt, struct uri, refcnt ); + + if ( uri->params ) + destroy_parameters ( uri->params ); + free ( uri ); } /** @@ -85,12 +101,25 @@ struct uri * parse_uri ( const char *uri_string ) { uri = zalloc ( sizeof ( *uri ) + raw_len ); if ( ! uri ) return NULL; + ref_init ( &uri->refcnt, free_uri ); raw = ( ( ( char * ) uri ) + sizeof ( *uri ) ); /* Copy in the raw string */ memcpy ( raw, uri_string, raw_len ); - /* Start by chopping off the fragment, if it exists */ + /* Identify the parameter list, if present */ + if ( ( tmp = strstr ( raw, "##params" ) ) ) { + *tmp = '\0'; + tmp += 8 /* "##params" */; + uri->params = find_parameters ( *tmp ? ( tmp + 1 ) : NULL ); + if ( uri->params ) { + claim_parameters ( uri->params ); + } else { + /* Ignore non-existent submission blocks */ + } + } + + /* Chop off the fragment, if it exists */ if ( ( tmp = strchr ( raw, '#' ) ) ) { *(tmp++) = '\0'; uri->fragment = tmp; diff --git a/src/drivers/net/3c90x.c b/src/drivers/net/3c90x.c index c1900574..853de2b5 100644 --- a/src/drivers/net/3c90x.c +++ b/src/drivers/net/3c90x.c @@ -346,11 +346,12 @@ static int a3c90x_transmit(struct net_device *netdev, tx_cur_desc->DnNextPtr = 0; /* FrameStartHeader differs in 90x and >= 90xB - * It contains length in 90x and a round up boundary and packet ID for - * 90xB and 90xC. We can leave this to 0 for 90xB and 90xC. + * It contains the packet length in 90x and a round up boundary and + * packet ID for 90xB and 90xC. Disable packet length round-up on the + * later revisions. */ tx_cur_desc->FrameStartHeader = - fshTxIndicate | (inf_3c90x->isBrev ? 0x00 : len); + fshTxIndicate | (inf_3c90x->isBrev ? fshRndupDefeat : len); tx_cur_desc->DataAddr = virt_to_bus(iob->data); tx_cur_desc->DataLength = len | downLastFrag; @@ -813,10 +814,18 @@ static int a3c90x_open(struct net_device *netdev) goto error; } + a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl, upStall); + /* send rx_ring address to NIC */ outl(virt_to_bus(inf_3c90x->rx_ring), inf_3c90x->IOAddr + regUpListPtr_l); + a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl, upUnStall); + + /* set maximum allowed receive packet length */ + a3c90x_internal_SetWindow(inf_3c90x, winTxRxOptions3); + outl(RX_BUF_SIZE, inf_3c90x->IOAddr + regMaxPktSize_3_w); + /* enable packet transmission and reception */ a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxEnable, 0); a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxEnable, 0); diff --git a/src/drivers/net/3c90x.h b/src/drivers/net/3c90x.h index 53fc522b..8bffa37f 100644 --- a/src/drivers/net/3c90x.h +++ b/src/drivers/net/3c90x.h @@ -202,6 +202,7 @@ enum GlobalResetParams { enum FrameStartHeader { fshTxIndicate = 0x8000, fshDnComplete = 0x10000, + fshRndupDefeat = 0x10000000, }; enum UpDownDesc { diff --git a/src/hci/commands/param_cmd.c b/src/hci/commands/param_cmd.c new file mode 100644 index 00000000..1b466cda --- /dev/null +++ b/src/hci/commands/param_cmd.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Form parameter commands + * + */ + +#include +#include +#include +#include +#include +#include + +/** "params" options */ +struct params_options { + /** Name */ + char *name; + /** Delete */ + int delete; +}; + +/** "params" option list */ +static struct option_descriptor params_opts[] = { + OPTION_DESC ( "name", 'n', required_argument, + struct params_options, name, parse_string ), + OPTION_DESC ( "delete", 'd', no_argument, + struct params_options, delete, parse_flag ), +}; + +/** "params" command descriptor */ +static struct command_descriptor params_cmd = + COMMAND_DESC ( struct params_options, params_opts, 0, 0, + "[--name ] [--delete]" ); + +/** + * The "params" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int params_exec ( int argc, char **argv ) { + struct params_options opts; + struct parameters *params; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, ¶ms_cmd, &opts ) ) != 0) + return rc; + + /* Create parameter list */ + params = create_parameters ( opts.name ); + if ( ! params ) + return -ENOMEM; + + /* Destroy parameter list, if applicable */ + if ( opts.delete ) + destroy_parameters ( params ); + + return 0; +} + +/** "param" options */ +struct param_options { + /** Parameter list name */ + char *params; +}; + +/** "param" option list */ +static struct option_descriptor param_opts[] = { + OPTION_DESC ( "params", 'p', required_argument, + struct param_options, params, parse_string ), +}; + +/** "param" command descriptor */ +static struct command_descriptor param_cmd = + COMMAND_DESC ( struct param_options, param_opts, 1, MAX_ARGUMENTS, + "[--params ] []" ); + +/** + * The "param" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int param_exec ( int argc, char **argv ) { + struct param_options opts; + char *key; + char *value; + struct parameters *params; + struct parameter *param; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, ¶m_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse key */ + key = argv[optind]; + + /* Parse value */ + value = concat_args ( &argv[ optind + 1 ] ); + if ( ! value ) { + rc = -ENOMEM; + goto err_parse_value; + } + + /* Identify parameter list */ + if ( ( rc = parse_parameters ( opts.params, ¶ms ) ) != 0 ) + goto err_parse_parameters; + + /* Add parameter */ + param = add_parameter ( params, key, value ); + if ( ! param ) { + rc = -ENOMEM; + goto err_add_parameter; + } + + /* Success */ + rc = 0; + + err_add_parameter: + err_parse_parameters: + free ( value ); + err_parse_value: + err_parse_options: + return rc; +} + +/** Form parameter commands */ +struct command param_commands[] __command = { + { + .name = "params", + .exec = params_exec, + }, + { + .name = "param", + .exec = param_exec, + }, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 4053923b..801d579b 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -286,6 +286,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_pci_settings ( ERRFILE_OTHER | 0x003d0000 ) #define ERRFILE_efi_reboot ( ERRFILE_OTHER | 0x003e0000 ) #define ERRFILE_memmap_settings ( ERRFILE_OTHER | 0x003f0000 ) +#define ERRFILE_param_cmd ( ERRFILE_OTHER | 0x00400000 ) /** @} */ diff --git a/src/include/ipxe/params.h b/src/include/ipxe/params.h new file mode 100644 index 00000000..d78adf56 --- /dev/null +++ b/src/include/ipxe/params.h @@ -0,0 +1,46 @@ +#ifndef _IPXE_PARAMS_H +#define _IPXE_PARAMS_H + +/** @file + * + * Form parameters + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** A form parameter list */ +struct parameters { + /** List of all parameter lists */ + struct list_head list; + /** Name */ + const char *name; + /** Parameters */ + struct list_head entries; +}; + +/** A form parameter */ +struct parameter { + /** List of form parameters */ + struct list_head list; + /** Key */ + const char *key; + /** Value */ + const char *value; +}; + +/** Iterate over all form parameters in a list */ +#define for_each_param( param, params ) \ + list_for_each_entry ( (param), &(params)->entries, list ) + +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 ); +extern void destroy_parameters ( struct parameters *params ); +extern void claim_parameters ( struct parameters *params ); + +#endif /* _IPXE_PARAMS_H */ diff --git a/src/include/ipxe/parseopt.h b/src/include/ipxe/parseopt.h index 2e38d231..108ce04c 100644 --- a/src/include/ipxe/parseopt.h +++ b/src/include/ipxe/parseopt.h @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct net_device; struct menu; +struct parameters; /** A command-line option descriptor */ struct option_descriptor { @@ -135,6 +136,7 @@ extern int parse_setting ( char *text, struct named_setting *setting, extern int parse_existing_setting ( char *text, struct named_setting *setting ); extern int parse_autovivified_setting ( char *text, struct named_setting *setting ); +extern int parse_parameters ( char *text, struct parameters **params ); extern void print_usage ( struct command_descriptor *cmd, char **argv ); extern int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, void *opts ); diff --git a/src/include/ipxe/uri.h b/src/include/ipxe/uri.h index 9a134690..a9ec4555 100644 --- a/src/include/ipxe/uri.h +++ b/src/include/ipxe/uri.h @@ -13,6 +13,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +struct parameters; + /** A Uniform Resource Identifier * * Terminology for this data structure is as per uri(7), except that @@ -65,6 +67,8 @@ struct uri { const char *query; /** Fragment */ const char *fragment; + /** Form parameters */ + struct parameters *params; } __attribute__ (( packed )); /** A field in a URI diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index bccb35f5..bfa7d7f7 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -49,6 +49,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /* Disambiguate the various error causes */ @@ -1059,17 +1060,101 @@ static char * http_digest_auth ( struct http_request *http, return auth; } +/** + * Generate HTTP POST parameter list + * + * @v http HTTP request + * @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_post_params ( struct http_request *http, + char *buf, size_t len ) { + struct parameter *param; + ssize_t remaining = len; + size_t frag_len; + + /* Add each parameter in the form "key=value", joined with "&" */ + len = 0; + for_each_param ( param, http->uri->params ) { + + /* Add the "&", if applicable */ + if ( len ) { + if ( remaining > 0 ) + *buf = '&'; + buf++; + len++; + remaining--; + } + + /* URI-encode the key */ + frag_len = uri_encode ( param->key, buf, remaining, 0 ); + buf += frag_len; + len += frag_len; + remaining -= frag_len; + + /* Add the "=" */ + if ( remaining > 0 ) + *buf = '='; + buf++; + len++; + remaining--; + + /* URI-encode the value */ + frag_len = uri_encode ( param->value, buf, remaining, 0 ); + buf += frag_len; + len += frag_len; + remaining -= frag_len; + } + + /* Ensure string is NUL-terminated even if no parameters are present */ + if ( remaining > 0 ) + *buf = '\0'; + + return len; +} + +/** + * Generate HTTP POST body + * + * @v http HTTP request + * @ret post I/O buffer containing POST body, or NULL on error + */ +static struct io_buffer * http_post ( struct http_request *http ) { + struct io_buffer *post; + size_t len; + size_t check_len; + + /* Calculate length of parameter list */ + len = http_post_params ( http, NULL, 0 ); + + /* Allocate parameter list */ + post = alloc_iob ( len + 1 /* NUL */ ); + if ( ! post ) + return NULL; + + /* Fill parameter list */ + check_len = http_post_params ( http, iob_put ( post, len ), + ( len + 1 /* NUL */ ) ); + assert ( len == check_len ); + DBGC ( http, "HTTP %p POST %s\n", http, ( ( char * ) post->data ) ); + + return post; +} + /** * HTTP process * * @v http HTTP request */ static void http_step ( struct http_request *http ) { + struct io_buffer *post; size_t uri_len; char *method; char *uri; char *range; char *auth; + char *content; int len; int rc; @@ -1088,7 +1173,8 @@ static void http_step ( struct http_request *http ) { } /* Determine method */ - method = ( ( http->flags & HTTP_HEAD_ONLY ) ? "HEAD" : "GET" ); + method = ( ( http->flags & HTTP_HEAD_ONLY ) ? "HEAD" : + ( http->uri->params ? "POST" : "GET" ) ); /* Construct path?query request */ uri_len = ( unparse_uri ( NULL, 0, http->uri, @@ -1136,6 +1222,25 @@ static void http_step ( struct http_request *http ) { auth = NULL; } + /* Construct POST content, if applicable */ + if ( http->uri->params ) { + post = http_post ( http ); + if ( ! post ) { + rc = -ENOMEM; + goto err_post; + } + len = asprintf ( &content, "Content-Type: " + "application/x-www-form-urlencoded\r\n" + "Content-Length: %zd\r\n", iob_len ( post ) ); + if ( len < 0 ) { + rc = len; + goto err_content; + } + } else { + post = NULL; + content = NULL; + } + /* Mark request as transmitted */ http->flags &= ~HTTP_TX_PENDING; @@ -1144,7 +1249,7 @@ static void http_step ( struct http_request *http ) { "%s %s HTTP/1.1\r\n" "User-Agent: iPXE/%s\r\n" "Host: %s%s%s\r\n" - "%s%s%s" + "%s%s%s%s" "\r\n", method, uri, product_version, http->uri->host, ( http->uri->port ? @@ -1154,11 +1259,24 @@ static void http_step ( struct http_request *http ) { ( ( http->flags & HTTP_CLIENT_KEEPALIVE ) ? "Connection: keep-alive\r\n" : "" ), ( range ? range : "" ), - ( auth ? auth : "" ) ) ) != 0 ) { + ( auth ? auth : "" ), + ( content ? content : "" ) ) ) != 0 ) { goto err_xfer; } + /* Send POST content, if applicable */ + if ( post ) { + if ( ( rc = xfer_deliver_iob ( &http->socket, + iob_disown ( post ) ) ) != 0 ) + goto err_xfer_post; + } + + err_xfer_post: err_xfer: + free ( content ); + err_content: + free ( post ); + err_post: free ( auth ); err_auth: free ( range );