Skip to content

Instantly share code, notes, and snippets.

@iandelahorne
Last active December 20, 2015 22:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iandelahorne/6206839 to your computer and use it in GitHub Desktop.
Save iandelahorne/6206839 to your computer and use it in GitHub Desktop.
#ifndef _S3_H
#define _S3_H
struct s3_string {
char *ptr;
size_t len;
};
struct s3_string * s3_string_init();
size_t s3_string_curl_writefunc(void *ptr, size_t len, size_t nmemb, struct s3_string *s);
void s3_string_free(struct s3_string *str);
#endif /* _S3_H */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "s3.h"
size_t
s3_string_curl_writefunc(void *ptr, size_t len, size_t nmemb, struct s3_string *s) {
size_t new_len = s->len + len *nmemb;
s->ptr = realloc(s->ptr, new_len + 1);
if (s->ptr == NULL) {
fprintf(stderr, "realloc() failed\n");
exit(EXIT_FAILURE);
}
memcpy(s->ptr+s->len, ptr, len*nmemb);
s->ptr[new_len] = '\0';
s->len = new_len;
return len*nmemb;
}
struct s3_string *
s3_string_init() {
struct s3_string *s;
s = malloc(sizeof (struct s3_string));
s->len = 0;
s->ptr = malloc(s->len+1);
if (s->ptr == NULL) {
fprintf(stderr, "malloc() failed\n");
exit(EXIT_FAILURE);
}
s->ptr[0] = '\0';
return s;
}
void
s3_string_free(struct s3_string *str) {
free(str->ptr);
free(str);
}
#include <stdio.h>
#include <string.h>
#include <openssl/engine.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <curl/curl.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#define NS_DELIM '.'
#include "s3.h"
/*
* Example code that signs a request for listing the bucket's contents
*/
struct s3_content {
char *key;
char *lastmod; /* time_t */
size_t size;
char *etag;
};
struct s3_xml_data {
int level;
};
#define S3_SECRET_LENGTH 128
#define S3_ID_LENGTH 128
struct S3 {
char *secret;
char *id;
char *base_url;
};
char *
hmac_sign(const char *key, const char *str, size_t len) {
unsigned char *digest;
char *buf;
unsigned int digest_len = EVP_MAX_MD_SIZE; /* HMAC_Final needs at most EVP_MAX_MD_SIZE bytes */
HMAC_CTX ctx;
BIO *bmem, *b64;
BUF_MEM *bufptr;
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
/* Setup HMAC context, init with sha1 and our key*/
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, key, strlen((char *)key), EVP_sha1(), NULL);
/* Create Base64 BIO filter that outputs to memory */
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
/* Give us a buffer to write result into */
digest = malloc(digest_len);
/* Push data into HMAC */
HMAC_Update(&ctx, (unsigned char *)str, (unsigned int)len);
/* Flush HMAC data into buffer */
HMAC_Final(&ctx, digest, &digest_len);
/* Write data into BIO, flush and fetch the data */
BIO_write(b64, digest, digest_len);
(void) BIO_flush(b64);
BIO_get_mem_ptr(b64, &bufptr);
buf = malloc(bufptr->length);
memcpy(buf, bufptr->data, bufptr->length - 1);
buf[bufptr->length - 1] = '\0';
BIO_free_all(b64);
HMAC_CTX_cleanup(&ctx);
free(digest);
return buf;
}
static struct S3 *
s3_init(const char *id, const char *secret, const char *base_url) {
struct S3 *s3 = malloc(sizeof (struct S3));
s3->id = malloc(S3_ID_LENGTH);
s3->secret = malloc(S3_SECRET_LENGTH);
/* XXX better length */
s3->base_url = malloc(256);
strlcpy(s3->id, id, S3_ID_LENGTH);
strlcpy(s3->secret, secret, S3_SECRET_LENGTH);
strlcpy(s3->base_url, base_url, 255);
return s3;
}
static void
s3_free(struct S3 *s3) {
free(s3->id);
free(s3->secret);
free(s3->base_url);
free(s3);
}
void
register_namespaces(xmlXPathContextPtr ctx, const xmlChar *nsList) {
xmlChar* nsListDup;
xmlChar* prefix;
xmlChar* href;
xmlChar* next;
nsListDup = xmlStrdup(nsList);
if(nsListDup == NULL) {
fprintf(stderr, "Error: unable to strdup namespaces list\n");
return;
}
next = nsListDup;
while(next != NULL) {
/* skip spaces */
while((*next) == ' ') next++;
if((*next) == '\0') break;
/* find prefix */
prefix = next;
next = (xmlChar*)xmlStrchr(next, '=');
if(next == NULL) {
fprintf(stderr,"Error: invalid namespaces list format\n");
xmlFree(nsListDup);
return;
}
*(next++) = '\0';
/* find href */
href = next;
next = (xmlChar*)xmlStrchr(next, ' ');
if(next != NULL) {
*(next++) = '\0';
}
/* do register namespace */
if(xmlXPathRegisterNs(ctx, prefix, href) != 0) {
fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", prefix, href);
xmlFree(nsListDup);
return;
}
}
xmlFree(nsListDup);
}
struct s3_content *
parse_content(xmlNode *root) {
struct s3_content *content = malloc(sizeof(struct s3_content));
xmlNode *node = NULL;
for (node = root; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
xmlChar *value = xmlNodeGetContent(node->children);
size_t len = strlen((const char *) value);
if (strcasecmp("key", (const char *)node->name) == 0) {
content->key = malloc(len + 2);
strlcpy(content->key, (const char *)value, len + 1);
} else if (strcasecmp("lastmodified", (const char *)node->name) == 0) {
content->lastmod = malloc(len + 2);
strlcpy(content->lastmod, (const char *)value, len + 1);
} else if (strcasecmp("etag", (const char *)node->name) == 0) {
content->etag = malloc(len + 2);
strlcpy(content->etag, (const char *)value, len + 1);
} else {
printf("node type: Element, name: %s\n", node->name);
printf("node xmlsNodeGetContent: %s\n",value);
}
xmlFree(value);
}
}
return content;
}
void
libxml_walk_nodes(xmlNode *root) {
xmlNode *node = NULL;
for (node = root; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
printf("node type: Element, name: %s\n", node->name);
}
libxml_walk_nodes(node->children);
}
}
void walk_xpath_nodes(xmlNodeSetPtr nodes) {
xmlNodePtr cur;
int size;
int i;
size = (nodes) ? nodes->nodeNr : 0;
printf("size is %d nodes\n", size);
for (i = 0; i < size ; i++) {
if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE) {
cur = nodes->nodeTab[i];
/*printf("name: %s\n", cur->name); */
struct s3_content *content = parse_content(cur->children);
printf("Got %s\n", content->key);
}
}
}
void
execute_xpath_expr(const xmlDocPtr doc, const xmlChar *xpath_expr, const xmlChar *ns_list) {
xmlXPathContextPtr xpath_ctx;
xmlXPathObjectPtr xpath_obj;
xpath_ctx = xmlXPathNewContext(doc);
if (xpath_ctx == NULL) {
fprintf(stderr,"Error: unable to create new XPath context\n");
}
register_namespaces(xpath_ctx, ns_list);
xpath_obj = xmlXPathEvalExpression(xpath_expr, xpath_ctx);
if(xpath_obj == NULL) {
fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpath_expr);
}
walk_xpath_nodes(xpath_obj->nodesetval);
xmlXPathFreeObject(xpath_obj);
xmlXPathFreeContext(xpath_ctx);
}
void
libxml_do_stuff(char *str) {
xmlDocPtr doc;
doc = xmlReadMemory(str, strlen(str), "noname.xml", NULL, 0);
/* xmlNode *root_element = xmlDocGetRootElement(doc); */
/* Since Amazon uses an XML Namespace, we need to declare it and use it as a prefix in Xpath queries, even though it's */
execute_xpath_expr(doc, (const xmlChar *)"//amzn:Contents", (const xmlChar *)"amzn=http://s3.amazonaws.com/doc/2006-03-01/");
xmlFreeDoc(doc);
}
static void
s3_list_bucket(struct S3 *s3, const char *bucket) {
char *date;
char *data, *digest;
char *hdr;
time_t now;
struct tm tm;
char *url;
CURL *curl;
struct curl_slist *headers = NULL;
struct s3_string *str;
const char *method = "GET";
str = s3_string_init();
date = malloc(128);
now = time(0);
tm = *gmtime(&now);
strftime(date, 128, "%a, %d %b %Y %H:%M:%S %Z", &tm);
data = malloc(1024);
snprintf(data, 1023, "%s\n\n\n%s\n/%s/", method, date, bucket);
fprintf(stderr, "data is %s\n", data);
digest = hmac_sign(s3->secret, data, strlen(data));
fprintf(stderr, "Authentication: AWS %s:%s\n", s3->id, digest);
asprintf(&url, "http://%s.%s/?delimiter=/", bucket, s3->base_url);
fprintf(stderr, "url is %s\n", url);
curl = curl_easy_init();
hdr = malloc(1024);
snprintf(hdr, 1023, "Date: %s", date);
headers = curl_slist_append(headers, hdr);
snprintf(hdr, 1023, "Authorization: AWS %s:%s", s3->id, digest);
headers = curl_slist_append(headers, hdr);
free(hdr);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
/* curl_easy_setopt(curl, CURLOPT_HEADER, 1); */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
/* http://stackoverflow.com/questions/2329571/c-libcurl-get-output-into-a-string */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, s3_string_curl_writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, str);
curl_easy_setopt(curl, CURLOPT_URL, url);
#if 0
curl_easy_setopt(curl, CURLOPT_PROXY, "http://localhost:8080");
#endif
curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
libxml_do_stuff(str->ptr);
printf("%s\n", str->ptr);
s3_string_free(str);
free(data);
free(date);
free(digest);
}
int main (int argc, char **argv) {
struct S3 *s3;
const char *s3_id = "TRATT";
const char *s3_key = "HEMLIGT";
s3 = s3_init(s3_id, s3_key "s3.amazonaws.com");
const char *bucket = "drutmule";
s3_list_bucket(s3, bucket);
return 0;
s3_free(s3);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment