#include "H5private.h"
#include "H5Eprivate.h"
#include "H5MMprivate.h"
#include "H5FDs3comms.h"
#ifdef H5_HAVE_ROS3_VFD
#define S3COMMS_DEBUG 0
#define S3COMMS_CURL_VERBOSITY 0
#define S3COMMS_MAX_RANGE_STRING_SIZE 128
struct s3r_datastruct {
unsigned long magic;
char *data;
size_t size;
};
#define S3COMMS_CALLBACK_DATASTRUCT_MAGIC 0x28c2b2ul
size_t curlwritecallback(char *ptr,
size_t size,
size_t nmemb,
void *userdata);
herr_t H5FD_s3comms_s3r_getsize(s3r_t *handle);
size_t
curlwritecallback(char *ptr,
size_t size,
size_t nmemb,
void *userdata)
{
struct s3r_datastruct *sds = (struct s3r_datastruct *)userdata;
size_t product = (size * nmemb);
size_t written = 0;
if(sds->magic != S3COMMS_CALLBACK_DATASTRUCT_MAGIC)
return written;
if (size > 0) {
HDmemcpy(&(sds->data[sds->size]), ptr, product);
sds->size += product;
written = product;
}
return written;
}
herr_t
H5FD_s3comms_hrb_node_set(
hrb_node_t **L,
const char *name,
const char *value)
{
size_t i = 0;
char *valuecpy = NULL;
char *namecpy = NULL;
size_t namelen = 0;
char *lowername = NULL;
char *nvcat = NULL;
hrb_node_t *node_ptr = NULL;
hrb_node_t *new_node = NULL;
hbool_t is_looking = TRUE;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_hrb_node_set.");
HDprintf("NAME: %s\n", name);
HDprintf("VALUE: %s\n", value);
HDprintf("LIST:\n->");
for(node_ptr = (*L); node_ptr != NULL; node_ptr = node_ptr->next)
HDfprintf(stdout, "{%s}\n->", node_ptr->cat);
HDprintf("(null)\n");
HDfflush(stdout);
node_ptr = NULL;
#endif
if(name == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to operate on null name");
namelen = HDstrlen(name);
lowername = (char *)H5MM_malloc(sizeof(char) * (namelen + 1));
if(lowername == NULL)
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "cannot make space for lowercase name copy.");
for(i = 0; i < namelen; i++)
lowername[i] = (char)HDtolower((int)name[i]);
lowername[namelen] = 0;
if(value != NULL) {
int ret = 0;
size_t valuelen = HDstrlen(value);
size_t catlen = namelen + valuelen + 2;
size_t catwrite = catlen + 3;
namecpy = (char *)H5MM_malloc(sizeof(char) * (namelen + 1));
if(namecpy == NULL)
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "cannot make space for name copy.");
HDmemcpy(namecpy, name, (namelen + 1));
valuecpy = (char *)H5MM_malloc(sizeof(char) * (valuelen + 1));
if(valuecpy == NULL)
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "cannot make space for value copy.");
HDmemcpy(valuecpy, value, (valuelen + 1));
nvcat = (char *)H5MM_malloc(sizeof(char) * catwrite);
if(nvcat == NULL)
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "cannot make space for concatenated string.");
ret = HDsnprintf(nvcat, catwrite, "%s: %s", name, value);
if(ret < 0 || (size_t)ret > catlen)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot concatenate `%s: %s", name, value);
HDassert( catlen == HDstrlen(nvcat) );
new_node = (hrb_node_t *)H5MM_malloc(sizeof(hrb_node_t));
if(new_node == NULL)
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "cannot make space for new set.");
new_node->magic = S3COMMS_HRB_NODE_MAGIC;
new_node->name = NULL;
new_node->value = NULL;
new_node->cat = NULL;
new_node->lowername = NULL;
new_node->next = NULL;
}
if(*L == NULL) {
if(value == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "trying to remove node from empty list")
else {
#if S3COMMS_DEBUG
HDprintf("CREATE NEW\n"); HDfflush(stdout);
#endif
new_node->cat = nvcat;
new_node->name = namecpy;
new_node->lowername = lowername;
new_node->value = valuecpy;
*L = new_node;
goto done;
}
}
HDassert( (*L) != NULL );
HDassert( (*L)->magic == S3COMMS_HRB_NODE_MAGIC );
node_ptr = (*L);
if(HDstrcmp(lowername, node_ptr->lowername) == 0) {
is_looking = FALSE;
if (value == NULL) {
#if S3COMMS_DEBUG
HDprintf("REMOVE HEAD\n"); HDfflush(stdout);
#endif
*L = node_ptr->next;
#if S3COMMS_DEBUG
HDprintf("FREEING CAT (node)\n"); HDfflush(stdout);
#endif
H5MM_xfree(node_ptr->cat);
#if S3COMMS_DEBUG
HDprintf("FREEING LOWERNAME (node)\n"); HDfflush(stdout);
#endif
H5MM_xfree(node_ptr->lowername);
#if S3COMMS_DEBUG
HDprintf("FREEING NAME (node)\n"); HDfflush(stdout);
#endif
H5MM_xfree(node_ptr->name);
#if S3COMMS_DEBUG
HDprintf("FREEING VALUE (node)\n"); HDfflush(stdout);
#endif
H5MM_xfree(node_ptr->value);
#if S3COMMS_DEBUG
HDprintf("MAGIC OK? %s\n",
(node_ptr->magic == S3COMMS_HRB_NODE_MAGIC) ? "YES" : "NO");
HDfflush(stdout);
#endif
HDassert( node_ptr->magic == S3COMMS_HRB_NODE_MAGIC );
node_ptr->magic += 1ul;
#if S3COMMS_DEBUG
HDprintf("FREEING POINTER\n"); HDfflush(stdout);
#endif
H5MM_xfree(node_ptr);
#if S3COMMS_DEBUG
HDprintf("FREEING WORKING LOWERNAME\n"); HDfflush(stdout);
#endif
H5MM_xfree(lowername); lowername = NULL;
}
else {
#if S3COMMS_DEBUG
HDprintf("MODIFY HEAD\n"); HDfflush(stdout);
#endif
H5MM_xfree(node_ptr->cat);
H5MM_xfree(node_ptr->name);
H5MM_xfree(node_ptr->value);
node_ptr->name = namecpy;
node_ptr->value = valuecpy;
node_ptr->cat = nvcat;
H5MM_xfree(lowername);
lowername = NULL;
new_node->magic += 1ul;
H5MM_xfree(new_node);
new_node = NULL;
}
}
else
if (HDstrcmp(lowername, node_ptr->lowername) < 0) {
is_looking = FALSE;
if(value == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "trying to remove a node 'before' head")
else {
#if S3COMMS_DEBUG
HDprintf("PREPEND NEW HEAD\n"); HDfflush(stdout);
#endif
new_node->name = namecpy;
new_node->value = valuecpy;
new_node->lowername = lowername;
new_node->cat = nvcat;
new_node->next = node_ptr;
*L = new_node;
}
}
while(is_looking) {
if(node_ptr->next == NULL) {
is_looking = FALSE;
if(value == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "trying to remove absent node")
else {
#if S3COMMS_DEBUG
HDprintf("APPEND A NODE\n"); HDfflush(stdout);
#endif
HDassert( HDstrcmp(lowername, node_ptr->lowername) > 0 );
new_node->name = namecpy;
new_node->value = valuecpy;
new_node->lowername = lowername;
new_node->cat = nvcat;
node_ptr->next = new_node;
}
}
else
if(HDstrcmp(lowername, node_ptr->next->lowername) < 0) {
is_looking = FALSE;
if(value == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "trying to remove absent node")
else {
#if S3COMMS_DEBUG
HDprintf("INSERT A NODE\n"); HDfflush(stdout);
#endif
HDassert( HDstrcmp(lowername, node_ptr->lowername) > 0 );
new_node->name = namecpy;
new_node->value = valuecpy;
new_node->lowername = lowername;
new_node->cat = nvcat;
new_node->next = node_ptr->next;
node_ptr->next = new_node;
}
}
else
if(HDstrcmp(lowername, node_ptr->next->lowername) == 0) {
is_looking = FALSE;
if(value == NULL) {
hrb_node_t *tmp = node_ptr->next;
node_ptr->next = tmp->next;
#if S3COMMS_DEBUG
HDprintf("REMOVE A NODE\n"); HDfflush(stdout);
#endif
H5MM_xfree(tmp->cat);
H5MM_xfree(tmp->lowername);
H5MM_xfree(tmp->name);
H5MM_xfree(tmp->value);
HDassert( tmp->magic == S3COMMS_HRB_NODE_MAGIC );
tmp->magic += 1ul;
H5MM_xfree(tmp);
H5MM_xfree(lowername);
lowername = NULL;
}
else {
#if S3COMMS_DEBUG
HDprintf("MODIFY A NODE\n"); HDfflush(stdout);
#endif
node_ptr = node_ptr->next;
H5MM_xfree(node_ptr->name);
H5MM_xfree(node_ptr->value);
H5MM_xfree(node_ptr->cat);
HDassert( new_node->magic == S3COMMS_HRB_NODE_MAGIC );
new_node->magic += 1ul;
H5MM_xfree(new_node);
H5MM_xfree(lowername);
new_node = NULL;
lowername = NULL;
node_ptr->name = namecpy;
node_ptr->value = valuecpy;
node_ptr->cat = nvcat;
}
}
else {
node_ptr = node_ptr->next;
}
}
done:
if(ret_value == FAIL) {
if(nvcat != NULL)
H5MM_xfree(nvcat);
if(namecpy != NULL)
H5MM_xfree(namecpy);
if(lowername != NULL)
H5MM_xfree(lowername);
if(valuecpy != NULL)
H5MM_xfree(valuecpy);
if(new_node != NULL) {
HDassert( new_node->magic == S3COMMS_HRB_NODE_MAGIC );
new_node->magic += 1ul;
H5MM_xfree(new_node);
}
}
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_hrb_destroy(hrb_t **_buf)
{
hrb_t *buf = NULL;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_hrb_destroy.\n");
#endif
if(_buf != NULL && *_buf != NULL) {
buf = *_buf;
if(buf->magic != S3COMMS_HRB_MAGIC)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "pointer's magic does not match.");
H5MM_xfree(buf->verb);
H5MM_xfree(buf->version);
H5MM_xfree(buf->resource);
buf->magic += 1ul;
H5MM_xfree(buf);
*_buf = NULL;
}
done:
FUNC_LEAVE_NOAPI(ret_value)
}
hrb_t *
H5FD_s3comms_hrb_init_request(const char *_verb,
const char *_resource,
const char *_http_version)
{
hrb_t *request = NULL;
char *res = NULL;
size_t reslen = 0;
hrb_t *ret_value = NULL;
char *verb = NULL;
size_t verblen = 0;
char *vrsn = NULL;
size_t vrsnlen = 0;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_hrb_init_request.\n");
#endif
if(_resource == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "resource string cannot be null.");
if(_verb == NULL)
_verb = "GET";
if(_http_version == NULL)
_http_version = "HTTP/1.1";
request = (hrb_t *)H5MM_malloc(sizeof(hrb_t));
if(request == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, NULL, "no space for request structure");
request->magic = S3COMMS_HRB_MAGIC;
request->body = NULL;
request->body_len = 0;
request->first_header = NULL;
reslen = HDstrlen(_resource);
if (_resource[0] == '/') {
res = (char *)H5MM_malloc(sizeof(char) * (reslen+1));
if(res == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, NULL, "no space for resource string");
HDmemcpy(res, _resource, (reslen+1));
}
else {
res = (char *)H5MM_malloc(sizeof(char) * (reslen+2));
if(res == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, NULL, "no space for resource string");
*res = '/';
HDmemcpy((&res[1]), _resource, (reslen+1));
HDassert( (reslen+1) == HDstrlen(res) );
}
verblen = HDstrlen(_verb) + 1;
verb = (char *)H5MM_malloc(sizeof(char) * verblen);
if(verb == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "no space for verb string");
HDstrncpy(verb, _verb, verblen);
vrsnlen = HDstrlen(_http_version) + 1;
vrsn = (char *)H5MM_malloc(sizeof(char) * vrsnlen);
if(vrsn == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "no space for http-version string");
HDstrncpy(vrsn, _http_version, vrsnlen);
request->resource = res;
request->verb = verb;
request->version = vrsn;
ret_value = request;
done:
if (ret_value == NULL) {
if(request != NULL)
H5MM_xfree(request);
if(vrsn != NULL)
H5MM_xfree(vrsn);
if(verb != NULL)
H5MM_xfree(verb);
if(res != NULL)
H5MM_xfree(res);
}
FUNC_LEAVE_NOAPI(ret_value)
}
herr_t
H5FD_s3comms_s3r_close(s3r_t *handle)
{
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_s3r_close.\n");
#endif
if(handle == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle cannot be null.");
if(handle->magic != S3COMMS_S3R_MAGIC)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle has invalid magic.");
curl_easy_cleanup(handle->curlhandle);
H5MM_xfree(handle->secret_id);
H5MM_xfree(handle->region);
H5MM_xfree(handle->signing_key);
HDassert( handle->httpverb != NULL );
H5MM_xfree(handle->httpverb);
if(FAIL == H5FD_s3comms_free_purl(handle->purl))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to release parsed url structure")
H5MM_xfree(handle);
done:
FUNC_LEAVE_NOAPI(ret_value)
}
size_t
H5FD_s3comms_s3r_get_filesize(s3r_t *handle)
{
size_t ret_value = 0;
FUNC_ENTER_NOAPI_NOINIT_NOERR
if(handle != NULL)
ret_value = handle->filesize;
FUNC_LEAVE_NOAPI(ret_value)
}
herr_t
H5FD_s3comms_s3r_getsize(s3r_t *handle)
{
uintmax_t content_length = 0;
CURL *curlh = NULL;
char *end = NULL;
char *headerresponse = NULL;
struct s3r_datastruct sds = {
S3COMMS_CALLBACK_DATASTRUCT_MAGIC,
NULL,
0 };
char *start = NULL;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_s3r_getsize.\n");
#endif
if(handle == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle cannot be null.");
if(handle->magic != S3COMMS_S3R_MAGIC)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle has invalid magic.");
if(handle->curlhandle == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle has bad (null) curlhandle.")
curlh = handle->curlhandle;
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_NOBODY, 1L))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "error while setting CURL option (CURLOPT_NOBODY).");
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_HEADERDATA, &sds))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "error while setting CURL option (CURLOPT_HEADERDATA).");
HDassert( handle->httpverb == NULL );
handle->httpverb = (char *)H5MM_malloc(sizeof(char) * 16);
if(handle->httpverb == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "unable to allocate space for S3 request HTTP verb");
HDmemcpy(handle->httpverb, "HEAD", 5);
headerresponse = (char *)H5MM_malloc(sizeof(char) * CURL_MAX_HTTP_HEADER);
if(headerresponse == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "unable to allocate space for curl header response");
sds.data = headerresponse;
if(FAIL == H5FD_s3comms_s3r_read(handle, 0, 0, NULL))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem in reading during getsize.");
if(sds.size > CURL_MAX_HTTP_HEADER)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "HTTP metadata buffer overrun")
else if (sds.size == 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "No HTTP metadata")
#if S3COMMS_DEBUG
else
HDfprintf(stderr, "GETSIZE: OK\n");
#endif
start = HDstrstr(headerresponse, "\r\nContent-Length: ");
if(start == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not find \"Content-Length\" in response.");
start = start + HDstrlen("\r\nContent-Length: ");
end = HDstrstr(start, "\r\n");
if(end == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not find end of content length line");
*end = '\0';
content_length = HDstrtoumax((const char *)start, NULL, 0);
if(UINTMAX_MAX > SIZE_MAX && content_length > SIZE_MAX)
HGOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL, "content_length overflows size_t");
if(content_length == 0 || errno == ERANGE)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not convert found \"Content-Length\" response (\"%s\")", start);
handle->filesize = (size_t)content_length;
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_NOBODY, NULL))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "error while setting CURL option (CURLOPT_NOBODY).");
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_HEADERDATA, NULL))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "error while setting CURL option (CURLOPT_HEADERDATA).");
done:
H5MM_xfree(headerresponse);
sds.magic += 1;
FUNC_LEAVE_NOAPI(ret_value);
}
s3r_t *
H5FD_s3comms_s3r_open(const char *url,
const char *region,
const char *id,
const unsigned char *signing_key)
{
size_t tmplen = 0;
CURL *curlh = NULL;
s3r_t *handle = NULL;
parsed_url_t *purl = NULL;
s3r_t *ret_value = NULL;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_s3r_open.\n");
#endif
if(url == NULL || url[0] == '\0')
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "url cannot be null.");
if(FAIL == H5FD_s3comms_parse_url(url, &purl))
HGOTO_ERROR(H5E_ARGS, H5E_CANTCREATE, NULL, "unable to create parsed url structure");
HDassert( purl != NULL );
HDassert( purl->magic == S3COMMS_PARSED_URL_MAGIC );
handle = (s3r_t *)H5MM_malloc(sizeof(s3r_t));
if(handle == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, NULL, "could not malloc space for handle.");
handle->magic = S3COMMS_S3R_MAGIC;
handle->purl = purl;
handle->filesize = 0;
handle->region = NULL;
handle->secret_id = NULL;
handle->signing_key = NULL;
handle->httpverb = NULL;
if((region != NULL && *region != '\0') ||
(id != NULL && *id != '\0') ||
(signing_key != NULL)) {
if(region == NULL || region[0] == '\0')
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "region cannot be null.");
if(id == NULL || id[0] == '\0')
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "secret id cannot be null.");
if(signing_key == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "signing key cannot be null.");
tmplen = HDstrlen(region) + 1;
handle->region = (char *)H5MM_malloc(sizeof(char) * tmplen);
if(handle->region == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "could not malloc space for handle region copy.");
HDmemcpy(handle->region, region, tmplen);
tmplen = HDstrlen(id) + 1;
handle->secret_id = (char *)H5MM_malloc(sizeof(char) * tmplen);
if(handle->secret_id == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "could not malloc space for handle ID copy.");
HDmemcpy(handle->secret_id, id, tmplen);
tmplen = SHA256_DIGEST_LENGTH;
handle->signing_key = (unsigned char *)H5MM_malloc(sizeof(unsigned char) * tmplen);
if(handle->signing_key == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "could not malloc space for handle key copy.");
HDmemcpy(handle->signing_key, signing_key, tmplen);
}
curlh = curl_easy_init();
if(curlh == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "problem creating curl easy handle!");
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_HTTPGET, 1L))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "error while setting CURL option (CURLOPT_HTTPGET).");
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "error while setting CURL option (CURLOPT_HTTP_VERSION).");
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_FAILONERROR, 1L))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "error while setting CURL option (CURLOPT_FAILONERROR).");
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_WRITEFUNCTION, curlwritecallback))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "error while setting CURL option (CURLOPT_WRITEFUNCTION).");
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_URL, url))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "error while setting CURL option (CURLOPT_URL).");
#if S3COMMS_CURL_VERBOSITY > 1
curl_easy_setopt(curlh, CURLOPT_VERBOSE, 1L);
#endif
handle->curlhandle = curlh;
if(FAIL == H5FD_s3comms_s3r_getsize(handle))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "problem in H5FD_s3comms_s3r_getsize.");
HDassert( handle->httpverb != NULL );
HDmemcpy(handle->httpverb, "GET", 4);
ret_value = handle;
done:
if(ret_value == NULL) {
if(curlh != NULL)
curl_easy_cleanup(curlh);
if(FAIL == H5FD_s3comms_free_purl(purl))
HDONE_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "unable to free parsed url structure")
if(handle != NULL) {
H5MM_xfree(handle->region);
H5MM_xfree(handle->secret_id);
H5MM_xfree(handle->signing_key);
if(handle->httpverb != NULL)
H5MM_xfree(handle->httpverb);
H5MM_xfree(handle);
}
}
FUNC_LEAVE_NOAPI(ret_value)
}
herr_t
H5FD_s3comms_s3r_read(s3r_t *handle,
haddr_t offset,
size_t len,
void *dest)
{
CURL *curlh = NULL;
CURLcode p_status = CURLE_OK;
struct curl_slist *curlheaders = NULL;
hrb_node_t *headers = NULL;
hrb_node_t *node = NULL;
struct tm *now = NULL;
char *rangebytesstr = NULL;
hrb_t *request = NULL;
int ret = 0;
struct s3r_datastruct *sds = NULL;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_s3r_read.\n");
#endif
if(handle == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle cannot be null.");
if(handle->magic != S3COMMS_S3R_MAGIC)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle has invalid magic.");
if(handle->curlhandle == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle has bad (null) curlhandle.")
if(handle->purl == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle has bad (null) url.")
HDassert( handle->purl->magic == S3COMMS_PARSED_URL_MAGIC );
if(offset > handle->filesize || (len + offset) > handle->filesize)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to read past EoF")
curlh = handle->curlhandle;
if(dest != NULL) {
sds = (struct s3r_datastruct *)H5MM_malloc(sizeof(struct s3r_datastruct));
if(sds == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "could not malloc destination datastructure.");
sds->magic = S3COMMS_CALLBACK_DATASTRUCT_MAGIC;
sds->data = (char *)dest;
sds->size = 0;
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_WRITEDATA, sds))
HGOTO_ERROR(H5E_ARGS, H5E_UNINITIALIZED, FAIL, "error while setting CURL option (CURLOPT_WRITEDATA).");
}
if (len > 0) {
rangebytesstr = (char *)H5MM_malloc(sizeof(char) * (S3COMMS_MAX_RANGE_STRING_SIZE+1) );
if(rangebytesstr == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "could not malloc range format string.");
ret = HDsnprintf(rangebytesstr, (S3COMMS_MAX_RANGE_STRING_SIZE),
"bytes="H5_PRINTF_HADDR_FMT"-"H5_PRINTF_HADDR_FMT,
offset, offset + len - 1);
if(ret <= 0 || ret >= S3COMMS_MAX_RANGE_STRING_SIZE)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to format HTTP Range value");
} else if(offset > 0) {
rangebytesstr = (char *)H5MM_malloc(sizeof(char) * (S3COMMS_MAX_RANGE_STRING_SIZE+1));
if(rangebytesstr == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "could not malloc range format string.");
ret = HDsnprintf(rangebytesstr, (S3COMMS_MAX_RANGE_STRING_SIZE),
"bytes="H5_PRINTF_HADDR_FMT"-", offset);
if(ret <= 0 || ret >= S3COMMS_MAX_RANGE_STRING_SIZE)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to format HTTP Range value");
}
if (handle->signing_key == NULL) {
if(rangebytesstr != NULL) {
char *bytesrange_ptr = NULL;
bytesrange_ptr = HDstrchr(rangebytesstr, '=');
HDassert( bytesrange_ptr != NULL );
bytesrange_ptr++;
HDassert( *bytesrange_ptr != '\0' );
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_RANGE, bytesrange_ptr))
HGOTO_ERROR(H5E_VFL, H5E_UNINITIALIZED, FAIL, "error while setting CURL option (CURLOPT_RANGE). ");
}
} else {
char authorization[512+1];
char buffer1[512+1];
char buffer2[256+1];
char iso8601now[ISO8601_SIZE];
char signed_headers[48+1];
authorization[0] = 0;
buffer1[0] = 0;
buffer2[0] = 0;
iso8601now[0] = 0;
signed_headers[0] = 0;
if(handle->region == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle must have non-null region.");
if(handle->secret_id == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle must have non-null secret_id.");
if(handle->signing_key == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle must have non-null signing_key.");
if(handle->httpverb == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle must have non-null httpverb.");
if(handle->purl->host == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle must have non-null host.");
if(handle->purl->path == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "handle must have non-null resource.");
request = H5FD_s3comms_hrb_init_request(
(const char *)handle->httpverb,
(const char *)handle->purl->path,
"HTTP/1.1");
if(request == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not allocate hrb_t request.");
HDassert( request->magic == S3COMMS_HRB_MAGIC );
now = gmnow();
if(ISO8601NOW(iso8601now, now) != (ISO8601_SIZE - 1))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not format ISO8601 time.");
if(FAIL == H5FD_s3comms_hrb_node_set( &headers, "x-amz-date", (const char *)iso8601now))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to set x-amz-date header")
if(headers == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem building headers list.");
HDassert( headers->magic == S3COMMS_HRB_NODE_MAGIC );
if(FAIL == H5FD_s3comms_hrb_node_set(&headers, "x-amz-content-sha256", (const char *)EMPTY_SHA256))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to set x-amz-content-sha256 header")
if(headers == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem building headers list.");
HDassert( headers->magic == S3COMMS_HRB_NODE_MAGIC );
if(rangebytesstr != NULL) {
if(FAIL == H5FD_s3comms_hrb_node_set( &headers, "Range", rangebytesstr))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to set range header")
if(headers == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem building headers list.");
HDassert( headers->magic == S3COMMS_HRB_NODE_MAGIC );
}
if(FAIL == H5FD_s3comms_hrb_node_set(&headers, "Host", handle->purl->host))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to set host header")
if(headers == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem building headers list.");
HDassert( headers->magic == S3COMMS_HRB_NODE_MAGIC );
request->first_header = headers;
if(FAIL == H5FD_s3comms_aws_canonical_request(buffer1, 512, signed_headers, 48, request))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "bad canonical request");
if(FAIL == H5FD_s3comms_tostringtosign(buffer2, buffer1, iso8601now, handle->region))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "bad string-to-sign");
if(FAIL == H5FD_s3comms_HMAC_SHA256(handle->signing_key, SHA256_DIGEST_LENGTH, buffer2, HDstrlen(buffer2), buffer1))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "bad signature");
iso8601now[8] = 0;
ret = S3COMMS_FORMAT_CREDENTIAL(buffer2, handle->secret_id, iso8601now, handle->region, "s3");
if(ret == 0 || ret >= S3COMMS_MAX_CREDENTIAL_SIZE)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to format aws4 credential string");
ret = HDsnprintf( authorization, 512,
"AWS4-HMAC-SHA256 Credential=%s,SignedHeaders=%s,Signature=%s",
buffer2, signed_headers, buffer1);
if(ret <= 0 || ret >= 512)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to format aws4 authorization string");
if(H5FD_s3comms_hrb_node_set(&headers, "Authorization", (const char *)authorization) == FAIL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to set Authorization header")
if(headers == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem building headers list.");
request->first_header = headers;
node = request->first_header;
while(node != NULL) {
HDassert( node->magic == S3COMMS_HRB_NODE_MAGIC );
curlheaders = curl_slist_append(curlheaders, (const char *)node->cat);
if(curlheaders == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not append header to curl slist.");
node = node->next;
}
if(curlheaders == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "curlheaders was never populated.");
if(curl_easy_setopt(curlh, CURLOPT_HTTPHEADER, curlheaders) != CURLE_OK)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "error while setting CURL option (CURLOPT_HTTPHEADER).");
}
#if S3COMMS_CURL_VERBOSITY > 0
{
long int httpcode = 0;
char curlerrbuf[CURL_ERROR_SIZE];
curlerrbuf[0] = '\0';
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_ERRORBUFFER, curlerrbuf))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem setting error buffer")
p_status = curl_easy_perform(curlh);
if(p_status != CURLE_OK) {
if(CURLE_OK != curl_easy_getinfo(curlh, CURLINFO_RESPONSE_CODE, &httpcode))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem getting response code")
HDfprintf(stderr, "CURL ERROR CODE: %d\nHTTP CODE: %d\n",
p_status, httpcode);
HDfprintf(stderr, "%s\n", curl_easy_strerror(p_status));
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "problem while performing request.");
}
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_ERRORBUFFER, NULL))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem unsetting error buffer")
}
#else
p_status = curl_easy_perform(curlh);
if(p_status != CURLE_OK)
HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "curl cannot perform request")
#endif
#if S3COMMS_DEBUG
if(dest != NULL) {
HDfprintf(stderr, "len: %d\n", (int)len);
HDfprintf(stderr, "CHECKING FOR BUFFER OVERFLOW\n");
if(sds == NULL)
HDfprintf(stderr, "sds is NULL!\n");
else {
HDfprintf(stderr, "sds: 0x%lx\n", (long long)sds);
HDfprintf(stderr, "sds->size: %d\n", (int)sds->size);
if(len > sds->size)
HDfprintf(stderr, "buffer overwrite\n");
}
}
else
HDfprintf(stderr, "performed on entire file\n");
#endif
done:
if(curlheaders != NULL) {
curl_slist_free_all(curlheaders);
curlheaders = NULL;
}
if(rangebytesstr != NULL) {
H5MM_xfree(rangebytesstr);
rangebytesstr = NULL;
}
if(sds != NULL) {
H5MM_xfree(sds);
sds = NULL;
}
if(request != NULL) {
while(headers != NULL)
if(FAIL == H5FD_s3comms_hrb_node_set(&headers, headers->name, NULL))
HDONE_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot release header node")
HDassert( NULL == headers );
if(FAIL == H5FD_s3comms_hrb_destroy(&request))
HDONE_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot release header request structure")
HDassert( NULL == request );
}
if(curlh != NULL) {
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_RANGE, NULL))
HDONE_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot unset CURLOPT_RANGE")
if(CURLE_OK != curl_easy_setopt(curlh, CURLOPT_HTTPHEADER, NULL))
HDONE_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot unset CURLOPT_HTTPHEADER")
}
FUNC_LEAVE_NOAPI(ret_value);
}
struct tm *
gmnow(void)
{
time_t now;
time_t *now_ptr = &now;
struct tm *ret_value = NULL;
if((time_t)(-1) != HDtime(now_ptr))
ret_value = HDgmtime(now_ptr);
HDassert( ret_value != NULL );
return ret_value;
}
herr_t
H5FD_s3comms_aws_canonical_request(
char *canonical_request_dest,
int _cr_size,
char *signed_headers_dest,
int _sh_size,
hrb_t *http_request)
{
hrb_node_t *node = NULL;
const char *query_params = "";
herr_t ret_value = SUCCEED;
int ret = 0;
size_t cr_size = (size_t)_cr_size;
size_t sh_size = (size_t)_sh_size;
size_t cr_len = 0;
size_t sh_len = 0;
char tmpstr[256+1];
tmpstr[256] = 0;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_aws_canonical_request.\n");
#endif
if(http_request == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "hrb object cannot be null.");
HDassert( http_request->magic == S3COMMS_HRB_MAGIC );
if(canonical_request_dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "canonical request destination cannot be null.");
if(signed_headers_dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "signed headers destination cannot be null.");
cr_len = (HDstrlen(http_request->verb) +
HDstrlen(http_request->resource) +
HDstrlen(query_params) +
(size_t)3);
if(cr_len >= cr_size)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not enough space in canonical request");
ret = HDsnprintf(canonical_request_dest, (cr_size - 1),
"%s\n%s\n%s\n",
http_request->verb, http_request->resource, query_params);
if(ret < 0 || (size_t)ret >= cr_size)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to compose canonical request first line");
node = http_request->first_header;
while(node != NULL) {
HDassert(node->magic == S3COMMS_HRB_NODE_MAGIC);
ret = HDsnprintf( tmpstr, 256, "%s:%s\n", node->lowername, node->value);
if(ret < 0 || ret >= 256)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to concatenate HTTP header %s:%s", node->lowername, node->value);
cr_len += HDstrlen(tmpstr);
if(cr_len + 1 > cr_size)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not enough space in canonical request");
HDstrcat(canonical_request_dest, tmpstr);
ret = HDsnprintf( tmpstr, 256, "%s;", node->lowername);
if(ret < 0 || ret >= 256)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to append semicolon to lowername %s", node->lowername);
sh_len += HDstrlen(tmpstr);
if(sh_len + 1 > sh_size)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not enough space in signed headers");
HDstrcat(signed_headers_dest, tmpstr);
node = node->next;
}
signed_headers_dest[HDstrlen(signed_headers_dest) - 1] = '\0';
HDstrcat(canonical_request_dest, "\n");
HDstrcat(canonical_request_dest, signed_headers_dest);
HDstrcat(canonical_request_dest, "\n");
HDstrcat(canonical_request_dest, EMPTY_SHA256);
done:
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_bytes_to_hex(
char *dest,
const unsigned char *msg,
size_t msg_len,
hbool_t lowercase)
{
size_t i = 0;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_bytes_to_hex.\n");
#endif
if(dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "hex destination cannot be null.")
if(msg == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "bytes sequence cannot be null.")
for (i = 0; i < msg_len; i++) {
int chars_written = HDsnprintf(&(dest[i * 2]), 3,
(lowercase == TRUE) ? "%02x" : "%02X", msg[i]);
if(chars_written != 2)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem while writing hex chars for %c", msg[i]);
}
done:
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_free_purl(parsed_url_t *purl)
{
FUNC_ENTER_NOAPI_NOINIT_NOERR
#if S3COMMS_DEBUG
HDprintf("called H5FD_s3comms_free_purl.\n");
#endif
if(purl != NULL) {
HDassert( purl->magic == S3COMMS_PARSED_URL_MAGIC );
if(purl->scheme != NULL)
H5MM_xfree(purl->scheme);
if(purl->host != NULL)
H5MM_xfree(purl->host);
if(purl->port != NULL)
H5MM_xfree(purl->port);
if(purl->path != NULL)
H5MM_xfree(purl->path);
if(purl->query != NULL)
H5MM_xfree(purl->query);
purl->magic += 1ul;
H5MM_xfree(purl);
}
FUNC_LEAVE_NOAPI(SUCCEED)
}
herr_t
H5FD_s3comms_HMAC_SHA256(
const unsigned char *key,
size_t key_len,
const char *msg,
size_t msg_len,
char *dest)
{
unsigned char md[SHA256_DIGEST_LENGTH];
unsigned int md_len = SHA256_DIGEST_LENGTH;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_HMAC_SHA256.\n");
#endif
if(dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "destination cannot be null.");
HMAC(EVP_sha256(), key, (int)key_len, (const unsigned char *)msg,
msg_len, md, &md_len);
if(H5FD_s3comms_bytes_to_hex(dest, (const unsigned char *)md, (size_t)md_len, true) == FAIL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not convert to hex string.");
done:
FUNC_LEAVE_NOAPI(ret_value);
}
static herr_t
H5FD__s3comms_load_aws_creds_from_file(
FILE *file,
const char *profile_name,
char *key_id,
char *access_key,
char *aws_region)
{
char profile_line[32];
char buffer[128];
const char *setting_names[] = {
"region",
"aws_access_key_id",
"aws_secret_access_key",
};
char * const setting_pointers[] = {
aws_region,
key_id,
access_key,
};
unsigned setting_count = 3;
herr_t ret_value = SUCCEED;
unsigned buffer_i = 0;
unsigned setting_i = 0;
int found_setting = 0;
char *line_buffer = &(buffer[0]);
FUNC_ENTER_STATIC
#if S3COMMS_DEBUG
HDfprintf(stdout, "called load_aws_creds_from_file.\n");
#endif
if(32 < HDsnprintf(profile_line, 32, "[%s]", profile_name))
HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, FAIL, "unable to format profile label")
do {
for(buffer_i=0; buffer_i < 128; buffer_i++)
buffer[buffer_i] = 0;
line_buffer = fgets(line_buffer, 128, file);
if(line_buffer == NULL)
goto done;
} while (HDstrncmp(line_buffer, profile_line, HDstrlen(profile_line)));
do {
for(buffer_i=0; buffer_i < 128; buffer_i++)
buffer[buffer_i] = 0;
line_buffer = fgets(line_buffer, 128, file);
if(line_buffer == NULL)
goto done;
for(setting_i = 0; setting_i < setting_count; setting_i++) {
size_t setting_name_len = 0;
const char *setting_name = NULL;
char line_prefix[128];
setting_name = setting_names[setting_i];
setting_name_len = HDstrlen(setting_name);
if(HDsnprintf(line_prefix, 128, "%s=", setting_name) < 0)
HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, FAIL, "unable to format line prefix")
if(!HDstrncmp(line_buffer, line_prefix, setting_name_len + 1)) {
found_setting = 1;
if(setting_pointers[setting_i] == NULL)
break;
do {
line_buffer++;
} while (*line_buffer != 0 && *line_buffer != '=');
if(*line_buffer == 0 || *(line_buffer+1) == 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "incomplete assignment in file")
line_buffer++;
HDstrncpy(setting_pointers[setting_i], (const char *)line_buffer, HDstrlen(line_buffer));
buffer_i = 0;
while(!HDisspace(setting_pointers[setting_i][buffer_i]))
buffer_i++;
setting_pointers[setting_i][buffer_i] = '\0';
break;
}
}
} while (found_setting);
done:
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_load_aws_profile(const char *profile_name,
char *key_id_out,
char *secret_access_key_out,
char *aws_region_out)
{
herr_t ret_value = SUCCEED;
FILE *credfile = NULL;
char awspath[117];
char filepath[128];
int ret = 0;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_load_aws_profile.\n");
#endif
#ifdef H5_HAVE_WIN32_API
ret = HDsnprintf(awspath, 117, "%s/.aws/", getenv("USERPROFILE")) ;
#else
ret = HDsnprintf(awspath, 117, "%s/.aws/", getenv("HOME")) ;
#endif
if(ret < 0 || (size_t)ret >= 117)
HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, FAIL, "unable to format home-aws path")
ret = HDsnprintf(filepath, 128, "%s%s", awspath, "credentials");
if(ret < 0 || (size_t)ret >= 128)
HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, FAIL, "unable to format credentials path")
credfile = HDfopen(filepath, "r");
if(credfile != NULL) {
if(H5FD__s3comms_load_aws_creds_from_file(credfile, profile_name, key_id_out,
secret_access_key_out, aws_region_out) == FAIL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to load from aws credentials")
if(HDfclose(credfile) == EOF)
HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to close credentials file")
credfile = NULL;
}
ret = HDsnprintf(filepath, 128, "%s%s", awspath, "config");
if(ret < 0 || (size_t)ret >= 128)
HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, FAIL, "unable to format config path")
credfile = HDfopen(filepath, "r");
if(credfile != NULL) {
if(H5FD__s3comms_load_aws_creds_from_file( credfile, profile_name,
(*key_id_out == 0) ? key_id_out : NULL,
(*secret_access_key_out == 0) ? secret_access_key_out : NULL,
(*aws_region_out == 0) ? aws_region_out : NULL) == FAIL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to load from aws config")
if(HDfclose(credfile) == EOF)
HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to close config file")
credfile = NULL;
}
if(*key_id_out == 0 || *secret_access_key_out == 0 || *aws_region_out == 0)
ret_value = FAIL;
done:
if(credfile != NULL)
if(HDfclose(credfile) == EOF)
HDONE_ERROR(H5E_ARGS, H5E_ARGS, FAIL, "problem error-closing aws configuration file")
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_nlowercase(
char *dest,
const char *s,
size_t len)
{
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_nlowercase.\n");
#endif
if(dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "destination cannot be null.");
if(len > 0) {
HDmemcpy(dest, s, len);
do {
len--;
dest[len] = (char)HDtolower((int)dest[len]);
} while (len > 0);
}
done:
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_parse_url(
const char *str,
parsed_url_t **_purl)
{
parsed_url_t *purl = NULL;
const char *tmpstr = NULL;
const char *curstr = str;
long int len = 0;
long int urllen = 0;
unsigned int i = 0;
herr_t ret_value = FAIL;
FUNC_ENTER_NOAPI_NOINIT;
#if S3COMMS_DEBUG
HDprintf("called H5FD_s3comms_parse_url.\n");
#endif
if(str == NULL || *str == '\0')
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid url string");
urllen = (long int)HDstrlen(str);
purl = (parsed_url_t *)H5MM_malloc(sizeof(parsed_url_t));
if(purl == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "can't allocate space for parsed_url_t");
purl->magic = S3COMMS_PARSED_URL_MAGIC;
purl->scheme = NULL;
purl->host = NULL;
purl->port = NULL;
purl->path = NULL;
purl->query = NULL;
tmpstr = HDstrchr(curstr, ':');
if(tmpstr == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid SCHEME construction: probably not URL");
len = tmpstr - curstr;
HDassert( (0 <= len) && (len < urllen) );
for(i = 0; i < len; i++) {
if(!HDisalpha(curstr[i]) && '+' != curstr[i] && '-' != curstr[i] &&
'.' != curstr[i])
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid SCHEME construction");
}
purl->scheme = (char *)H5MM_malloc(sizeof(char) * (size_t)(len + 1));
if(purl->scheme == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "can't allocate space for SCHEME");
HDstrncpy(purl->scheme, curstr, (size_t)len);
purl->scheme[len] = '\0';
for(i = 0; i < len; i++ )
purl->scheme[i] = (char)HDtolower(purl->scheme[i]);
tmpstr += 3;
curstr = tmpstr;
if(*curstr == '[') {
while(']' != *tmpstr) {
if(tmpstr == 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "reached end of URL: incomplete IPv6 HOST");
tmpstr++;
}
tmpstr++;
}
else {
while(0 != *tmpstr) {
if(':' == *tmpstr || '/' == *tmpstr || '?' == *tmpstr)
break;
tmpstr++;
}
}
len = tmpstr - curstr;
if(len == 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "HOST substring cannot be empty")
else if(len > urllen)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem with length of HOST substring");
purl->host = (char *)H5MM_malloc(sizeof(char) * (size_t)(len + 1));
if(purl->host == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "can't allocate space for HOST");
HDstrncpy(purl->host, curstr, (size_t)len);
purl->host[len] = 0;
if(':' == *tmpstr) {
tmpstr += 1;
curstr = tmpstr;
while((0 != *tmpstr) && ('/' != *tmpstr) && ('?' != *tmpstr))
tmpstr++;
len = tmpstr - curstr;
if(len == 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "PORT element cannot be empty")
else if(len > urllen)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem with length of PORT substring");
for(i = 0; i < len; i ++)
if(!HDisdigit(curstr[i]))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "PORT is not a decimal string");
purl->port = (char *)H5MM_malloc(sizeof(char) * (size_t)(len + 1));
if(purl->port == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "can't allocate space for PORT");
HDstrncpy(purl->port, curstr, (size_t)len);
purl->port[len] = 0;
}
if('/' == *tmpstr) {
tmpstr += 1;
curstr = tmpstr;
while((0 != *tmpstr) && ('?' != *tmpstr))
tmpstr++;
len = tmpstr - curstr;
if(len > urllen)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem with length of PATH substring");
if(len > 0) {
purl->path = (char *)H5MM_malloc(sizeof(char) * (size_t)(len + 1));
if(purl->path == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "can't allocate space for PATH");
HDstrncpy(purl->path, curstr, (size_t)len);
purl->path[len] = 0;
}
}
if('?' == *tmpstr) {
tmpstr += 1;
curstr = tmpstr;
while(0 != *tmpstr)
tmpstr++;
len = tmpstr - curstr;
if(len == 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "QUERY cannot be empty")
else if(len > urllen)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem with length of QUERY substring");
purl->query = (char *)H5MM_malloc(sizeof(char) * (size_t)(len + 1));
if(purl->query == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_CANTALLOC, FAIL, "can't allocate space for QUERY");
HDstrncpy(purl->query, curstr, (size_t)len);
purl->query[len] = 0;
}
*_purl = purl;
ret_value = SUCCEED;
done:
if(ret_value == FAIL)
H5FD_s3comms_free_purl(purl);
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_percent_encode_char(
char *repr,
const unsigned char c,
size_t *repr_len)
{
unsigned int i = 0;
int chars_written = 0;
herr_t ret_value = SUCCEED;
#if S3COMMS_DEBUG
unsigned char s[2] = {c, 0};
unsigned char hex[3] = {0, 0, 0};
#endif
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_percent_encode_char.\n");
#endif
if(repr == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no destination `repr`.")
#if S3COMMS_DEBUG
H5FD_s3comms_bytes_to_hex((char *)hex, s, 1, FALSE);
HDfprintf(stdout, " CHAR: \'%s\'\n", s);
HDfprintf(stdout, " CHAR-HEX: \"%s\"\n", hex);
#endif
if(c <= (unsigned char)0x7f) {
#if S3COMMS_DEBUG
HDfprintf(stdout, " SINGLE-BYTE\n");
#endif
*repr_len = 3;
chars_written = HDsnprintf(repr, 4, "%%%02X", c);
if(chars_written < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot write char %c", c);
}
else {
unsigned int acc = 0;
unsigned int k = 0;
unsigned int stack_size = 0;
unsigned char stack[4] = {0, 0, 0, 0};
#if S3COMMS_DEBUG
HDfprintf(stdout, " MULTI-BYTE\n");
#endif
stack_size = 0;
k = (unsigned int)c;
*repr_len = 0;
do {
acc = k;
acc >>= 6;
acc <<= 6;
stack[stack_size++] = (unsigned char)(k - acc);
k = acc >> 6;
} while (k > 0);
#if S3COMMS_DEBUG
HDfprintf(stdout, " STACK:\n {\n");
for (i = 0; i < stack_size; i++) {
H5FD_s3comms_bytes_to_hex(
(char *)hex,
(&stack[i]),
1,
FALSE);
hex[2] = 0;
HDfprintf(stdout, " %s,\n", hex);
}
HDfprintf(stdout, " }\n");
#endif
acc = 0xC0;
acc += (stack_size > 2) ? 0x20 : 0;
acc += (stack_size > 3) ? 0x10 : 0;
stack_size--;
chars_written = HDsnprintf( repr, 4, "%%%02X", (unsigned char)(acc + stack[stack_size]));
if(chars_written < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot write char %c", c);
*repr_len += 3;
for(i = 0; i < stack_size; i++) {
chars_written = HDsnprintf( &repr[i*3 + 3], 4,
"%%%02X", (unsigned char)(0x80 + stack[stack_size - 1 - i]));
if(chars_written < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot write char %c", c);
*repr_len += 3;
}
}
*(repr + *repr_len) = '\0';
done:
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_signing_key(
unsigned char *md,
const char *secret,
const char *region,
const char *iso8601now)
{
char *AWS4_secret = NULL;
size_t AWS4_secret_len = 0;
unsigned char datekey[SHA256_DIGEST_LENGTH];
unsigned char dateregionkey[SHA256_DIGEST_LENGTH];
unsigned char dateregionservicekey[SHA256_DIGEST_LENGTH];
int ret = 0;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_signing_key.\n");
#endif
if(md == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Destination `md` cannot be NULL.")
if(secret == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "`secret` cannot be NULL.")
if(region == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "`region` cannot be NULL.")
if(iso8601now == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "`iso8601now` cannot be NULL.")
AWS4_secret_len = 4 + HDstrlen(secret) + 1;
AWS4_secret = (char*)H5MM_malloc(sizeof(char *) * AWS4_secret_len);
if(AWS4_secret == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Could not allocate space.")
ret = HDsnprintf(AWS4_secret, AWS4_secret_len,"%s%s", "AWS4", secret);
if((size_t)ret != (AWS4_secret_len - 1))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem writing AWS4+secret `%s`", secret);
HMAC(EVP_sha256(),
(const unsigned char *)AWS4_secret,
(int)HDstrlen(AWS4_secret),
(const unsigned char*)iso8601now,
8,
datekey,
NULL);
HMAC(EVP_sha256(),
(const unsigned char *)datekey,
SHA256_DIGEST_LENGTH,
(const unsigned char *)region,
HDstrlen(region),
dateregionkey,
NULL);
HMAC(EVP_sha256(),
(const unsigned char *)dateregionkey,
SHA256_DIGEST_LENGTH,
(const unsigned char *)"s3",
2,
dateregionservicekey,
NULL);
HMAC(EVP_sha256(),
(const unsigned char *)dateregionservicekey,
SHA256_DIGEST_LENGTH,
(const unsigned char *)"aws4_request",
12,
md,
NULL);
done:
H5MM_xfree(AWS4_secret);
FUNC_LEAVE_NOAPI(ret_value);
}
herr_t
H5FD_s3comms_tostringtosign(
char *dest,
const char *req,
const char *now,
const char *region)
{
unsigned char checksum[SHA256_DIGEST_LENGTH * 2 + 1];
size_t d = 0;
char day[9];
char hexsum[SHA256_DIGEST_LENGTH * 2 + 1];
size_t i = 0;
int ret = 0;
herr_t ret_value = SUCCEED;
char tmp[128];
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_tostringtosign.\n");
#endif
if(dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "destination buffer cannot be null.")
if(req == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "canonical request cannot be null.")
if(now == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Timestring cannot be NULL.")
if(region == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Region cannot be NULL.")
for(i = 0; i < 128; i++)
tmp[i] = '\0';
for(i = 0; i < SHA256_DIGEST_LENGTH * 2 + 1; i++) {
checksum[i] = '\0';
hexsum[i] = '\0';
}
HDstrncpy(day, now, 8);
day[8] = '\0';
ret = HDsnprintf(tmp, 127, "%s/%s/s3/aws4_request", day, region);
if(ret <= 0 || ret >= 127)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "problem adding day and region to string")
HDmemcpy((dest + d), "AWS4-HMAC-SHA256\n", 17);
d = 17;
HDmemcpy((dest+d), now, HDstrlen(now));
d += HDstrlen(now);
dest[d++] = '\n';
HDmemcpy((dest + d), tmp, HDstrlen(tmp));
d += HDstrlen(tmp);
dest[d++] = '\n';
SHA256((const unsigned char *)req, HDstrlen(req), checksum);
if(H5FD_s3comms_bytes_to_hex(hexsum, (const unsigned char *)checksum,
SHA256_DIGEST_LENGTH, true) == FAIL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "could not create hex string");
for(i = 0; i < SHA256_DIGEST_LENGTH * 2; i++)
dest[d++] = hexsum[i];
dest[d] = '\0';
done:
FUNC_LEAVE_NOAPI(ret_value)
}
herr_t
H5FD_s3comms_trim(char *dest,
char *s,
size_t s_len,
size_t *n_written)
{
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "called H5FD_s3comms_trim.\n");
#endif
if(dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "destination cannot be null.")
if(s == NULL)
s_len = 0;
if(s_len > 0) {
while((s_len > 0) && HDisspace((unsigned char)s[0]) && s_len > 0) {
s++;
s_len--;
}
if(s_len > 0) {
do {
s_len--;
} while(HDisspace((unsigned char)s[s_len]));
s_len++;
HDmemcpy(dest, s, s_len);
}
}
*n_written = s_len;
done:
FUNC_LEAVE_NOAPI(ret_value)
}
herr_t
H5FD_s3comms_uriencode(
char *dest,
const char *s,
size_t s_len,
hbool_t encode_slash,
size_t *n_written)
{
char c = 0;
size_t dest_off = 0;
char hex_buffer[13];
size_t hex_off = 0;
size_t hex_len = 0;
herr_t ret_value = SUCCEED;
size_t s_off = 0;
FUNC_ENTER_NOAPI_NOINIT
#if S3COMMS_DEBUG
HDfprintf(stdout, "H5FD_s3comms_uriencode called.\n");
#endif
if(s == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "source string cannot be NULL");
if(dest == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "destination cannot be NULL");
for(s_off = 0; s_off < s_len; s_off++) {
c = s[s_off];
if(HDisalnum(c) || c == '.' || c == '-' || c == '_' ||
c == '~' || (c == '/' && encode_slash == FALSE))
dest[dest_off++] = c;
else {
hex_off = 0;
if(H5FD_s3comms_percent_encode_char(hex_buffer, (const unsigned char)c,
&hex_len) == FAIL) {
hex_buffer[0] = c;
hex_buffer[1] = 0;
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to percent-encode character \'%s\' " "at %d in \"%s\"", hex_buffer, (int)s_off, s);
}
for(hex_off = 0; hex_off < hex_len; hex_off++)
dest[dest_off++] = hex_buffer[hex_off];
}
}
if(dest_off < s_len)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "buffer overflow");
*n_written = dest_off;
done:
FUNC_LEAVE_NOAPI(ret_value)
}
#endif