/** * Add required SimpleDB parameters to the array * * @param sdb the SimpleDB handle * @param params the parameter array */ int sdb_params_add_required(struct SDB* sdb, struct sdb_params* params) { SDB_SAFE(sdb_params_add(params, "SignatureVersion", sdb->sdb_signature_ver_str)); SDB_SAFE(sdb_params_add(params, "Version", "2007-11-07")); SDB_SAFE(sdb_params_add(params, "AWSAccessKeyId", sdb->sdb_key)); return SDB_OK; }
/** * Create the URL-encoded parameters string * * @param sdb the SimpleDB handle * @param params the parameter array * @param pbuffer the variable for the output buffer (will be allocated by the routine, but has to be freed by caller) * @return SDB_OK if no errors occurred */ int sdb_params_export(struct SDB* sdb, struct sdb_params* params, char** pbuffer) { // Sign the parameters char signature[EVP_MAX_MD_SIZE * 2]; SDB_SAFE(sdb_params_sign(sdb, params, signature, NULL)); SDB_SAFE(sdb_params_add(params, "Signature", signature)); // Determine the appropriate size size_t i, l = 0; for (i = 0; i < params->size; i++) { l += strlen(params->params[i].key) + 1; l += strlen(params->params[i].value) * 3 + 1; } // Allocate buffer *pbuffer = (char*) malloc(l + 4); char* b = *pbuffer; *b = '\0'; // Build the string for (i = 0; i < params->size; i++) { if (i > 0) strcat(b, "&"); strcat(b, params->params[i].key); strcat(b, "="); char* e = curl_easy_escape(sdb->curl_handle, params->params[i].value, strlen(params->params[i].value)); if (e == NULL) { free(b); *pbuffer = NULL; return SDB_E_URL_ENCODE_FAILED; } strcat(b, e); curl_free(e); } return SDB_OK; }
/** * Add required SimpleDB parameters to the array * * @param sdb the SimpleDB handle * @param params the parameter array */ int sdb_params_add_required(struct SDB* sdb, struct sdb_params* params) { SDB_SAFE(sdb_params_add(params, "SignatureVersion", sdb->sdb_signature_ver_str)); SDB_SAFE(sdb_params_add(params, "SignatureMethod", "HmacSHA256")); SDB_SAFE(sdb_params_add(params, "AWSAccessKeyId", sdb->sdb_key)); // Version 2007-11-07 deprecated Aug 24, 2010 size_t i = 0; for (i = 0; i < params->size; i++) { if (strcmp(params->params[i].key, "Action") == 0) { if (strcmp(params->params[i].value, "Query") == 0 || strcmp(params->params[i].value, "QueryWithAttributes") == 0) { SDB_SAFE(sdb_params_add(params, "Version", "2007-11-07")); } else { SDB_SAFE(sdb_params_add(params, "Version", "2009-04-15")); } break; } } return SDB_OK; }
/** * Sign the parameters * * @param sdb the SimpleDB handle * @param params the parameter array * @param buffer the buffer to write the signature to (must be at least EVP_MAX_MD_SIZE * 2 bytes) * @param plen the pointer to the place to store the length of the signature (can be NULL) * @return SDB_OK if no errors occurred */ int sdb_params_sign(struct SDB* sdb, struct sdb_params* params, char* buffer, size_t* plen) { assert(params->size >= 2); // Signature version 0 if (sdb->sdb_signature_ver == 0) { assert(strcmp(params->params[0].key, "Action") == 0); assert(strcmp(params->params[1].key, "Timestamp") == 0); size_t l = strlen(params->params[0].value) + strlen(params->params[1].value) + 4; char* b = (char*) alloca(l); strcpy(b, params->params[0].value); strcat(b, params->params[1].value); return sdb_sign(sdb, b, buffer, plen); } // Signature version 1 if (sdb->sdb_signature_ver == 1) { SDB_SAFE(sdb_params_sort(params)); size_t i, l = 0; for (i = 0; i < params->size; i++) { l += strlen(params->params[i].key); l += strlen(params->params[i].value); } char* b = (char*) alloca(l); *b = '\0'; for (i = 0; i < params->size; i++) { strcat(b, params->params[i].key); strcat(b, params->params[i].value); } return sdb_sign(sdb, b, buffer, plen); } return SDB_E_INVALID_SIGNATURE_VER; }
/** * Execute a command * * @param sdb the SimpleDB handle * @param cmd the command name * @param params the parameters * @param response the pointer to the result-set * @return the result */ int sdb_execute_rs(struct SDB* sdb, const char* cmd, struct sdb_params* _params, struct sdb_response** response) { // Prepare the command execution struct sdb_params* params = sdb_params_alloc(_params->size + 8); SDB_SAFE(sdb_params_add(params, "Action", cmd)); char timestamp[32]; sdb_timestamp(timestamp); SDB_SAFE(sdb_params_add(params, "Timestamp", timestamp)); // Add the command parameters SDB_SAFE(sdb_params_add_all(params, _params)); // Add the next token if (*response != NULL) { if ((*response)->has_more) { assert((*response)->internal->next_token); SDB_SAFE(sdb_params_add(params, "NextToken", (const char*) (*response)->internal->next_token)); } } // Add the required parameters SDB_SAFE(sdb_params_add_required(sdb, params)); char* post = sdb_post(sdb, params); long postsize = strlen(post); sdb_params_free(params); // Configure Curl and execute the command CURL* curl = sdb->curl_handle; sdb->rec.size = 0; curl_easy_setopt(curl, CURLOPT_URL, AWS_URL); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sdb->rec); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post); #ifdef _DEBUG_PRINT_RESPONSE curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); #endif CURLcode cr = curl_easy_perform(curl); free(post); // Statistics if (cr == CURLE_OK) { sdb->stat.num_commands++; if (strncmp(cmd, "Put", 3) == 0) sdb->stat.num_puts++; sdb_update_size_stats(sdb, curl, postsize, sdb->rec.size); } // Handle Curl errors and internal AWS errors if (cr != CURLE_OK) return SDB_CURL_ERROR(cr); if (strncmp(sdb->rec.buffer, "<html", 5) == 0) return SDB_E_AWS_INTERNAL_ERROR_2; #ifdef _DEBUG_PRINT_RESPONSE sdb->rec.buffer[sdb->rec.size] = '\0'; printf("\n%s\n\n", sdb->rec.buffer); #endif // Parse the response and check for errors if (*response == NULL) { *response = sdb_response_allocate(); } else { sdb_response_prepare_append(*response); } (*response)->internal->errout = sdb->errout; int __ret = sdb_response_parse(*response, sdb->rec.buffer, sdb->rec.size); if (SDB_FAILED(__ret)) { sdb_free(response); return __ret; } sdb->stat.box_usage += (*response)->box_usage; if ((*response)->error != 0) { __ret = (*response)->error; if (SDB_AWS_ERROR(__ret) != SDB_E_AWS_SERVICE_UNAVAILABLE) sdb_free(response); return SDB_AWS_ERROR(__ret); } // Save the parameters in the case manual NEXT handling is allowed if (!sdb->auto_next && (*response)->has_more) { if ((*response)->internal->next == NULL) { (*response)->internal->params = sdb_params_deep_copy(_params); (*response)->internal->command = (char*) malloc(strlen(cmd) + 4); strcpy((*response)->internal->command, cmd); } else if ((*response)->internal->params == NULL) { (*response)->internal->params = (*response)->internal->next->params; (*response)->internal->command = (*response)->internal->next->command; (*response)->internal->next->params = NULL; (*response)->internal->next->command = NULL; assert((*response)->internal->params); assert((*response)->internal->command); } } return SDB_OK; }
/** * Execute a command and ignore the result-set * * @param sdb the SimpleDB handle * @param cmd the command name * @param _params the parameters * @return the result */ int sdb_execute(struct SDB* sdb, const char* cmd, struct sdb_params* _params) { // Prepare the command execution struct sdb_params* params = sdb_params_alloc(_params->size + 8); SDB_SAFE(sdb_params_add(params, "Action", cmd)); char timestamp[32]; sdb_timestamp(timestamp); SDB_SAFE(sdb_params_add(params, "Timestamp", timestamp)); // Add the command parameters SDB_SAFE(sdb_params_add_all(params, _params)); // Add the required parameters SDB_SAFE(sdb_params_add_required(sdb, params)); char* post = sdb_post(sdb, params); long postsize = strlen(post); sdb_params_free(params); // Configure Curl and execute the command CURL* curl = sdb->curl_handle; sdb->rec.size = 0; curl_easy_setopt(curl, CURLOPT_URL, AWS_URL); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sdb->rec); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post); #ifdef _DEBUG_PRINT_RESPONSE curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); #endif CURLcode cr = curl_easy_perform(curl); free(post); // Statistics if (cr == CURLE_OK) { sdb->stat.num_commands++; if (strncmp(cmd, "Put", 3) == 0) sdb->stat.num_puts++; sdb_update_size_stats(sdb, curl, postsize, sdb->rec.size); } // Handle Curl errors and internal AWS errors if (cr != CURLE_OK) return SDB_CURL_ERROR(cr); if (strncmp(sdb->rec.buffer, "<html", 5) == 0) return SDB_E_AWS_INTERNAL_ERROR_2; #ifdef _DEBUG_PRINT_RESPONSE sdb->rec.buffer[sdb->rec.size] = '\0'; printf("\n%s\n\n", sdb->rec.buffer); #endif // Parse the response and check for errors struct sdb_response response; sdb_response_init(&response); response.internal->errout = sdb->errout; int __ret = sdb_response_parse(&response, sdb->rec.buffer, sdb->rec.size); if (SDB_FAILED(__ret)) { sdb_response_cleanup(&response); return __ret; } sdb->stat.box_usage += response.box_usage; if (response.error != 0) { __ret = response.error; sdb_response_cleanup(&response); return SDB_AWS_ERROR(__ret); } // Cleanup sdb_response_cleanup(&response); return SDB_OK; }
/** * Create the URL-encoded parameters string with SignatureVersion 2 sig. * * @param sdb the SimpleDB handle * @param params the parameter array * @param pbuffer the variable for the output buffer (will be allocated by the routine, but has to be freed by caller) * @return SDB_OK if no errors occurred */ int sdb_params_export(struct SDB* sdb, struct sdb_params* params, char** pbuffer) { assert(params->size >= 2); // Sign the parameters char signature[EVP_MAX_MD_SIZE * 2]; // Determine the appropriate size size_t i, l = 0; for (i = 0; i < params->size; i++) { l += strlen(params->params[i].key) + 1; l += strlen(params->params[i].value) * 3 + 1; } // Allocate buffer *pbuffer = (char*) malloc(l + 4); char* b = *pbuffer; *b = '\0'; // Build the string SDB_SAFE(sdb_params_sort(params)); for (i = 0; i < params->size; i++) { if (i > 0) strcat(b, "&"); strcat(b, params->params[i].key); strcat(b, "="); char* e = sdb_escape(sdb, params->params[i].value, strlen(params->params[i].value)); if (e == NULL) { free(b); *pbuffer = NULL; return SDB_E_URL_ENCODE_FAILED; } strcat(b, e); curl_free(e); } // string is built, now build complete string to sign l = strlen(b) + 25; char* s = (char*) alloca(l); *s = '\0'; strcat(s, "POST\n"); strcat(s, "sdb.amazonaws.com\n"); strcat(s, "/\n"); strcat(s, b); // Create the signature and add it to the URL-encoded param string SDB_SAFE(sdb_sign(sdb, s, signature, NULL)); strcat(b, "&Signature="); char* e = sdb_escape(sdb->curl_handle, signature, strlen(signature)); if (e == NULL) { free(b); *pbuffer = NULL; return SDB_E_URL_ENCODE_FAILED; } strcat(b, e); free(e); return SDB_OK; }
/** * Parse the list of items * * @param response the response data structure (must be initalized) * @param items the list of items * @return SDB_OK if no errors occurred */ int sdb_response_parse_items(struct sdb_response* response, xmlNodePtr items) { xmlNodePtr cur, content; int size = 0; // Count the attributes and determine whether we have more incoming data response->has_more = FALSE; for (cur = items->children; cur != NULL; cur = cur->next) { // The item if (strcmp((char*) cur->name, "Item") == 0 || strcmp((char*) cur->name, "ItemName") == 0) { size++; continue; } // The next token if (strcmp((char*) cur->name, "NextToken") == 0) { assert(cur->children != NULL); content = cur->children; response->internal->next_token = XML_GET_CONTENT(content); assert(response->internal->next_token != NULL); response->has_more = TRUE; continue; } // The list of nodes to ignore if (strcmp((char*) cur->name, "RequestID") == 0) continue; if (strcmp((char*) cur->name, "RequestId") == 0) continue; // Handle errors if (response->internal->errout != NULL) { fprintf(response->internal->errout, "SimpleDB ERROR: Invalid node \"%s\" in the AWS list of items\n", cur->name); } return SDB_E_INVALID_META_RESPONSE; } // Allocate the memory if this is a new result set, otherwise prepare to append int start = 0; if (response->type == SDB_R_NONE) { response->size = size; response->type = SDB_R_ITEM_LIST; response->items = (struct sdb_item*) malloc(size * sizeof(struct sdb_item)); } else if (response->type == SDB_R_ITEM_LIST) { struct sdb_item* old = response->items; int newsize = response->size + size; start = response->size; response->items = (struct sdb_item*) malloc(newsize * sizeof(struct sdb_item)); memcpy(response->items, old, response->size * sizeof(struct sdb_item)); free(old); response->size = newsize; } else assert(0); // Pull out the items int index = start; for (cur = items->children; cur != NULL; cur = cur->next) { // Item name if (strcmp((char*) cur->name, "ItemName") == 0) { assert(cur->children != NULL); content = cur->children; response->items[index].name = (char*) XML_GET_CONTENT(content); response->items[index].size = 0; response->items[index].attributes = NULL; assert(response->items[index].name != NULL); index++; continue; } // Item with attributes if (strcmp((char*) cur->name, "Item") == 0) { SDB_SAFE(sdb_response_parse_item(response, &response->items[index++], cur)); } } assert(index - start == size); return SDB_OK; }
/** * Parse the response * * @param response the response data structure * @param buffer the buffer to parse * @param length the length of the data in the buffer * @return SDB_OK if no errors occurred */ int sdb_response_parse(struct sdb_response* response, const char* buffer, size_t length) { // Parse the XML response->internal->doc = xmlReadMemory(buffer, length, "response.xml", NULL, 0); if (response->internal->doc == NULL) return SDB_E_INVALID_XML_RESPONSE; // Get the root node xmlNodePtr root = xmlDocGetRootElement(response->internal->doc); // Iterate over the root node xmlNodePtr cur; for (cur = root->children; cur != NULL; cur = cur->next) { // Error node if (strcmp((char*) cur->name, "Errors") == 0) { SDB_SAFE(sdb_response_parse_errors(response, cur)); continue; } // Response metadata if (strcmp((char*) cur->name, "ResponseMetadata") == 0) { SDB_SAFE(sdb_response_parse_metadata(response, cur)); continue; } // List domains result if (strcmp((char*) cur->name, "ListDomainsResult") == 0) { SDB_SAFE(sdb_response_parse_domains(response, cur)); continue; } // Domain metadata result if (strcmp((char*) cur->name, "DomainMetadataResult") == 0) { SDB_SAFE(sdb_response_parse_domain_metadata(response, cur)); continue; } // Get attributes result if (strcmp((char*) cur->name, "GetAttributesResult") == 0) { SDB_SAFE(sdb_response_parse_attributes(response, cur)); continue; } // Get the items with attributes result if (strcmp((char*) cur->name, "QueryResult") == 0 || strcmp((char*) cur->name, "QueryWithAttributesResult") == 0 || strcmp((char*) cur->name, "SelectResult") == 0) { SDB_SAFE(sdb_response_parse_items(response, cur)); continue; } // The list of response nodes to ignore if (strcmp((char*) cur->name, "RequestID") == 0) continue; if (strcmp((char*) cur->name, "RequestId") == 0) continue; // Deal with errors here if (response->internal->errout != NULL) { fprintf(response->internal->errout, "SimpleDB ERROR: Invalid node \"%s\" in the AWS response\n", cur->name); } return SDB_E_INVALID_ERR_RESPONSE; } return SDB_OK; }