diff options
-rw-r--r-- | NEWS | 9 | ||||
-rw-r--r-- | doc/eclat.conf.5 | 113 | ||||
-rw-r--r-- | lib/libeclat.h | 12 | ||||
-rw-r--r-- | lib/req2url.c | 52 | ||||
-rw-r--r-- | lib/reqfree.c | 1 | ||||
-rw-r--r-- | lib/reqsign.c | 4 | ||||
-rw-r--r-- | src/config.c | 33 | ||||
-rw-r--r-- | src/eclat.c | 14 | ||||
-rw-r--r-- | src/eclat.h | 1 | ||||
-rw-r--r-- | src/util.c | 12 |
10 files changed, 163 insertions, 88 deletions
@@ -1,4 +1,4 @@ -Eclat NEWS -- history of user-visible changes. 2015-01-22 +Eclat NEWS -- history of user-visible changes. 2015-01-23 Copyright (C) 2012-2015 Sergey Poznyakoff See the end of file for copying conditions. @@ -19,6 +19,13 @@ following statement in the eclat configuration file: signature-version 4; +* POST support + +POST HTTP method is supported. It is enabled by the following +configuration statement: + + http-method post; + * If availability region is not supplied, it is read from the instance store. * Authentication providers diff --git a/doc/eclat.conf.5 b/doc/eclat.conf.5 index 4fa7102..32147e8 100644 --- a/doc/eclat.conf.5 +++ b/doc/eclat.conf.5 @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with Eclat. If not, see <http://www.gnu.org/licenses/>. -.TH ECLAT.CONF 5 "January 22, 2015" "ECLAT" "Eclat User Reference" +.TH ECLAT.CONF 5 "January 23, 2015" "ECLAT" "Eclat User Reference" .SH NAME eclat.conf \- configuration file for .BR eclat (1). @@ -341,10 +341,23 @@ listed in the corresponding sections below. .SH EC2 CONFIGURATION The statements in this group configure EC2 endpoints and regions. .PP -An \fBendpoint\fR is a URI of the Amazon server. When selecting the -endpoint to send the query to -.B eclat -uses the following algorithm: +A \fIregion\fR determines physical location of servers supporting +given AWS resources. +.PP +An \fIendpoint\fR is a URI of the Amazon server. The endpoint to +use is selected based on the region name. +.TP +\fBdefault\-endpoint\fR \fIhostname\fR; +Defines the endpoint to use if no region-specific endpoint is +configured. +.TP +\fBdefault\-region\fR \fIname\fR; +Defines the name of the default region. +.TP +\fBregion\fR \fIname\fR \fIendpoint\fR; +Declares a region and the corresponding endpoint. +.PP +The region is determined by the following algorithm: .nr step 1 1 .IP \n[step]. If the \fB\-\-region\fR option is given, take its argument as the @@ -353,19 +366,17 @@ region name. If the \fBdefault\-region\fR statement is defined in the configuration file, use its argument as the region name. .IP \n+[step]. -If a region name is defined in one of the previous steps, select the -endpoint defined in the \fBregion\fR statement with the corresponding -name. -.IP \n+[step]. -Otherwise, use the endpoint specified by the \fBdefault\-endpoint\fR -statement. -.TP -\fBdefault\-endpoint\fR \fIhostname\fR; -Defines the endpoint to use in the absence of the \fB\-\-region\fR -option and \fBdefault\-region\fR statement. -.TP -\fBregion\fR \fIname\fR \fIendpoint\fR; -Declares a region and the corresponding endpoint. +Otherwise, attempt to obtain region from the \fIinstance store\fR. +Obviously, this step can succeed only if \fBeclat\fR is run on an +EC2 instance. +.PP +If none of these steps succeed, the program aborts. +.PP +Endpoint is selected by looking up a \fBregion\fR statement with +the \fIname\fR argument matching the currently selected region. +If such a statement is found, its \fIendpoint\fR argument defines +the endpoint to use. Otherwise, endpoint is taken from the +\fBdefault\-endpoint\fR statement. .PP An example of the EC2 endpoint configuration follows: .PP @@ -378,37 +389,12 @@ region us\-east\-1 ec2.us\-east\-1.amazonaws.com; # US West (Oregon) Region region us\-west\-2 ec2.us\-west\-2.amazonaws.com; .EE -.SS AUTHENTICATION +.SS HTTP METHOD .TP -\fBauthentication\-provider\fR \fITYPE\fR [\fIARG\fR]\fB;\fR -Defines authentication provider to use. \fIAuthentication provider\fR -is a service that supplies AWS access key ID and secret key. See -.BR eclat (1), -section -.BR AUTHENTICATION , -for a detailed description. - -The \fITYPE\fR argument defines the provider. Allowed values are -.BR file , -and -.BR instance\-store . - -If \fITYPE\fR is \fBfile\fR, the \fIARG\fR parameter is treated as a -shell globbing pattern: all files matching this pattern are attempted -in turn, until a keypair is found in one of them. - -If \fITYPE\fR is \fBinstance\-store\fR, credentials will be obtained -from the instance store. \fIARG\fR is optional. If supplied, it -should be the name of the IAM role this instance is launched with. -At the time of this writing, an instance can be associated with a -single role, which will be used by default. -.TP -\fBaccess\-file\fR \fIname\fR; -This is a shortcut for \fBauthentication\-provider file \fIname\fR. -.TP -\fBsignature\-version\fR \fIN\fR; -Declares the signature version. Valid values for \fIN\fR are \fB2\fR, -which is the default, and \fB4\fR, which provides a better security. +\fBhttp\-method\fR \fIARG\fR; +Configures HTTP method. Allowed values for \fIARG\fR are \fBGET\fR +(the default), and \fBPOST\fR. \fIARG\fR is case-insensitive. The +\fBPOST\fR method implies using signature version 4. .SS SSL CONFIGURATION The \fBssl\fR statement has two forms, and can be used as scalar or as a block statement. In scalar form it is used to enable SSL: @@ -448,6 +434,37 @@ By default the CA certificates shipped with .BR libcurl (3) will be used. You would rarely need to use \fBca\-file\fR or \fBca\-path\fR statements. +.SS AUTHENTICATION +.TP +\fBauthentication\-provider\fR \fITYPE\fR [\fIARG\fR]\fB;\fR +Defines authentication provider to use. \fIAuthentication provider\fR +is a service that supplies AWS access key ID and secret key. See +.BR eclat (1), +section +.BR AUTHENTICATION , +for a detailed description. + +The \fITYPE\fR argument defines the provider. Allowed values are +.BR file , +and +.BR instance\-store . + +If \fITYPE\fR is \fBfile\fR, the \fIARG\fR parameter is treated as a +shell globbing pattern: all files matching this pattern are attempted +in turn, until a keypair is found in one of them. + +If \fITYPE\fR is \fBinstance\-store\fR, credentials will be obtained +from the instance store. \fIARG\fR is optional. If supplied, it +should be the name of the IAM role this instance is launched with. +At the time of this writing, an instance can be associated with a +single role, which will be used by default. +.TP +\fBaccess\-file\fR \fIname\fR; +This is a shortcut for \fBauthentication\-provider file \fIname\fR. +.TP +\fBsignature\-version\fR \fIN\fR; +Declares the signature version. Valid values for \fIN\fR are \fB2\fR, +which is the default, and \fB4\fR, which provides a better security. .SH INSTANCE STORE CONFIGURATION The \fBinstance\-store\fR compound statement configures HTTP access to the instance store. By default, \fBeclat\fR uses standard AWS values. diff --git a/lib/libeclat.h b/lib/libeclat.h index a5c8c79..b33f0a2 100644 --- a/lib/libeclat.h +++ b/lib/libeclat.h @@ -94,6 +94,7 @@ struct ec2_request { char *uri; /* URI without parameters */ struct grecs_symtab *params; /* Query parameters */ struct grecs_list *headers; /* Query headers */ + char *postdata; char *region; char *access_key; char *token; @@ -104,18 +105,19 @@ struct ec2_request *eclat_request_create(int flags, const char *endpoint, const char *uri, char const *region, char const *access_key, char const *token); void eclat_request_free(struct ec2_request *); -void eclat_request_add_param(struct ec2_request *q, const char *name, +void eclat_request_add_param(struct ec2_request *req, const char *name, const char *value); -void eclat_request_add_param_encoded(struct ec2_request *q, const char *name, +void eclat_request_add_param_encoded(struct ec2_request *req, const char *name, const char *value); -void eclat_request_add_header(struct ec2_request *q, const char *name, +void eclat_request_add_header(struct ec2_request *req, const char *name, const char *value); void eclat_request_sign(struct ec2_request *req, char *secret, char *version); -char *eclat_request_to_url(struct ec2_request *req, char **post_params); +char *eclat_request_to_url(struct ec2_request *req); -void eclat_request_encode(struct ec2_request *q); +void eclat_request_encode(struct ec2_request *req); +void eclat_request_finalize(struct ec2_request *req); typedef struct eclat_partial_tree *eclat_partial_tree_t; diff --git a/lib/req2url.c b/lib/req2url.c index 25277a2..b9d7512 100644 --- a/lib/req2url.c +++ b/lib/req2url.c @@ -21,7 +21,7 @@ struct param_closure { struct grecs_txtacc *acc; - int count; + int delim; }; static int @@ -30,23 +30,22 @@ add_param(void *sym, void *data) struct ec2_param *p = sym; struct param_closure *pc = data; - if (pc->count) - grecs_txtacc_grow_char(pc->acc, '&'); - ++pc->count; + if (pc->delim) + grecs_txtacc_grow_char(pc->acc, pc->delim); + pc->delim = '&'; grecs_txtacc_grow_string(pc->acc, p->name); if (p->value) { grecs_txtacc_grow_char(pc->acc, '='); grecs_txtacc_grow_string(pc->acc, p->value); - } - + } return 0; } char * -eclat_request_to_url(struct ec2_request *req, char **post_params) +eclat_request_to_url(struct ec2_request *req) { struct grecs_txtacc *acc; - char *ret = NULL, *p; + char *url = NULL; struct param_closure pc; acc = grecs_txtacc_create(); @@ -58,27 +57,32 @@ eclat_request_to_url(struct ec2_request *req, char **post_params) grecs_txtacc_grow(acc, req->endpoint, strlen(req->endpoint)); grecs_txtacc_grow(acc, req->uri, strlen(req->uri)); - if (req->flags & EC2_RF_POST) { - grecs_txtacc_grow_char(acc, 0); - ret = grecs_txtacc_finish(acc, 1); - } else { - grecs_txtacc_grow_char(acc, '?'); - } - - /* Add other parameters */ + /* Add parameters */ pc.acc = acc; - pc.count = 0; + pc.delim = '?'; grecs_symtab_enumerate(req->params, add_param, &pc); grecs_txtacc_grow_char(acc, 0); - - if (!ret) - ret = grecs_txtacc_finish(acc, 1); - else - *post_params = grecs_txtacc_finish(acc, 1); - + url = grecs_txtacc_finish(acc, 1); grecs_txtacc_free(acc); - return ret; + return url; } +void +eclat_request_finalize(struct ec2_request *req) +{ + struct param_closure pc; + + if (!(req->flags & EC2_RF_POST) || req->postdata) + return; + + eclat_request_encode(req); + pc.acc = grecs_txtacc_create(); + pc.delim = 0; + grecs_symtab_enumerate(req->params, add_param, &pc); + grecs_txtacc_grow_char(pc.acc, 0); + req->postdata = grecs_txtacc_finish(pc.acc, 1); + grecs_txtacc_free(pc.acc); + grecs_symtab_clear(req->params); +} diff --git a/lib/reqfree.c b/lib/reqfree.c index f5145a4..ee0611e 100644 --- a/lib/reqfree.c +++ b/lib/reqfree.c @@ -23,6 +23,7 @@ eclat_request_free(struct ec2_request *req) { free(req->endpoint); free(req->uri); + free(req->postdata); grecs_symtab_free(req->params); free(req); } diff --git a/lib/reqsign.c b/lib/reqsign.c index f3083c6..9572961 100644 --- a/lib/reqsign.c +++ b/lib/reqsign.c @@ -252,9 +252,7 @@ requestsign4(struct ec2_request *req, char *secret) grecs_txtacc_grow_char(acc, '\n'); /* Payload hash */ if (req->flags & EC2_RF_POST) { - /* FIXME: payload = req->request */ - err("%s:%d: POST is not yet implemented", __FILE__, __LINE__); - abort(); + payload = req->postdata; } else payload = ""; diff --git a/src/config.c b/src/config.c index 548b11a..49b47f9 100644 --- a/src/config.c +++ b/src/config.c @@ -296,8 +296,8 @@ cb_authentication_provider(enum grecs_callback_command cmd, } if (strcmp(type, "file") == 0) { - if (arg) { - grecs_error(locus, 0, "requered argument missing"); + if (!arg) { + grecs_error(locus, 0, "required argument missing"); return 1; } authentication_provider = authp_file; @@ -337,6 +337,29 @@ cb_access_file(enum grecs_callback_command cmd, *(char**)varptr = grecs_strdup(value->v.string); return 0; } + +static int +cb_http_method(enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, + grecs_value_t *value, + void *cb_data) +{ + if (cmd != grecs_callback_set_value) { + grecs_error(locus, 0, "Unexpected block statement"); + return 1; + } + if (!value || value->type != GRECS_TYPE_STRING) { + grecs_error(locus, 0, "expected string value"); + return 1; + } + if (strcasecmp(value->v.string, "post") == 0) + use_post = 1; + else if (strcasecmp(value->v.string, "get")) + grecs_error(&value->locus, 0, "invalid http method"); + return 0; +} + static struct grecs_keyword instance_store_kw[] = { { "base-url", "URL", @@ -387,6 +410,9 @@ static struct grecs_keyword eclat_kw[] = { grecs_type_section, GRECS_DFLT, NULL, 0, cb_ssl, NULL, ssl_kw }, + { "http-method", "arg: post|get", + "Define HTTP method to use", + grecs_type_string, GRECS_DFLT, NULL, 0, cb_http_method }, { "format", "<command: string> <format: string>", "Set default format for <command>", grecs_type_string, GRECS_MULT, NULL, 0, cb_format }, @@ -465,6 +491,9 @@ config_finish(struct grecs_node *tree) exit(EX_CONFIG); if (grecs_error_count || run_config_finish_hooks()) exit(EX_CONFIG); + + if (use_post) + signature_version = "4"; } diff --git a/src/eclat.c b/src/eclat.c index beaa934..2e46930 100644 --- a/src/eclat.c +++ b/src/eclat.c @@ -31,6 +31,7 @@ char *secret_key; char *security_token; char *region_name; int use_ssl; +int use_post; int ssl_verify_peer = 1; char *ssl_ca_file; char *ssl_ca_path; @@ -646,10 +647,15 @@ eclat_do_command(eclat_command_env_t *env, struct eclat_command *command, int rc; if (!(command->flags & CMD_NOQRY)) { - env->request = eclat_request_create(use_ssl ? EC2_RF_HTTPS : 0, - endpoint, "/", - region_name, access_key, - security_token); + int flags = 0; + if (use_ssl) + flags |= EC2_RF_HTTPS; + if (use_post) + flags |= EC2_RF_POST; + env->request = eclat_request_create(flags, + endpoint, "/", + region_name, access_key, + security_token); } if (command->tag) diff --git a/src/eclat.h b/src/eclat.h index a8ac2a0..604ffdc 100644 --- a/src/eclat.h +++ b/src/eclat.h @@ -49,6 +49,7 @@ enum authentication_provider { extern char *endpoint; extern char *signature_version; extern int use_ssl; +extern int use_post; extern int ssl_verify_peer; extern char *ssl_ca_file; extern char *ssl_ca_path; @@ -227,8 +227,15 @@ eclat_send_request(CURL *curl, struct ec2_request *req) struct curl_slist *headers = NULL; /* Prepare the request */ + if (req->flags & EC2_RF_POST) { + eclat_request_finalize(req); + curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->postdata); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, + strlen(req->postdata)); + } eclat_request_sign(req, secret_key, signature_version); - url = eclat_request_to_url(req, NULL); + url = eclat_request_to_url(req); curl_easy_setopt(curl, CURLOPT_URL, url); debug(ECLAT_DEBCAT_MAIN, 1, ("using URL: %s", url)); free(url); @@ -263,6 +270,9 @@ eclat_send_request(CURL *curl, struct ec2_request *req) curl_easy_strerror(rc)); } + if (req->flags & EC2_RF_POST) + debug(ECLAT_DEBCAT_MAIN, 1, ("DATA: %s", req->postdata)); + if (dry_run_mode) debug(ECLAT_DEBCAT_MAIN, 1, ("not sending request")); else { |