blob: 9f0bdf8d1de784bc83d75aad1be7cbd5f31723e2 [file] [log] [blame] [edit]
//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See License.txt for details
//!
//! Example code that uses libcurl to post memfault the memfault test data from:
//! https://mflt.io/chunks-api-integration
//!
//! Compiling:
//! In Memfault UI, navigate to Settings->General Settings and Copy/Paste "Project API Key"
//! into PROJECT_KEY below:
//!
//! $ gcc libcurl_example.c -lcurl -lssl -lcrypto -Wall -DMEMFAULT_PROJECT_KEY=\"PROJECT_KEY\"
//!
//! NOTE: Requires libcurl and libopenssl. On Debian, 'sudo apt install -y libcurl4-openssl-dev'
//!
//! Usage:
//! $ ./a.out
//! $ Chunk successfully sent!
#include <curl/curl.h>
#include <stdbool.h>
#include <stdint.h>
#include "../../components/include/memfault/http/root_certs.h"
#ifndef MEMFAULT_PROJECT_KEY
#error "MEMFAULT_PROJECT_KEY definition required"
#endif
#include <curl/curl.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <stdio.h>
//! Provide the CA cert from openssl directly as an in-memory cert, instead of
//! retrieving from the default system certificate store.
//!
//! This strategy is adapted from the example here:
//! https://github.com/curl/curl/blob/master/docs/examples/cacertinmem.c
static CURLcode prv_install_root_certs(CURL *curl, void *sslctx, void *param) {
CURLcode rv = CURLE_ABORTED_BY_CALLBACK;
static const char mypem[] = MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2;
BIO *cbio = BIO_new_mem_buf(mypem, sizeof(mypem));
X509_STORE *cts = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
int i;
STACK_OF(X509_INFO) * inf;
(void)curl;
(void)param;
if (!cts || !cbio) {
return rv;
}
inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL);
if (!inf) {
BIO_free(cbio);
return rv;
}
for (i = 0; i < sk_X509_INFO_num(inf); i++) {
X509_INFO *itmp = sk_X509_INFO_value(inf, i);
if (itmp->x509) {
X509_STORE_add_cert(cts, itmp->x509);
}
if (itmp->crl) {
X509_STORE_add_crl(cts, itmp->crl);
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
BIO_free(cbio);
rv = CURLE_OK;
return rv;
}
static int prv_post_chunk(const char *device_serial, const void *chunk, size_t chunk_len,
bool verbose) {
CURLcode ret;
CURL *hnd;
struct curl_slist *slist1;
slist1 = NULL;
slist1 = curl_slist_append(slist1, "Memfault-Project-Key:" MEMFAULT_PROJECT_KEY);
slist1 = curl_slist_append(slist1, "Content-Type: application/octet-stream");
hnd = curl_easy_init();
char dest_url[512];
snprintf(dest_url, sizeof(dest_url), "https://chunks.memfault.com/api/v0/chunks/%s",
device_serial);
curl_easy_setopt(hnd, CURLOPT_URL, dest_url);
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, chunk);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)chunk_len);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/8.5.0");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
/* Turn off the default CA locations, otherwise libcurl loads CA
* certificates from the locations that were detected/specified at
* build-time
*/
curl_easy_setopt(hnd, CURLOPT_CAINFO, NULL);
curl_easy_setopt(hnd, CURLOPT_CAPATH, NULL);
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 1L);
curl_easy_setopt(hnd, CURLOPT_SSLCERTTYPE, "PEM");
curl_easy_setopt(hnd, CURLOPT_SSL_CTX_FUNCTION, prv_install_root_certs);
if (verbose) {
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
}
ret = curl_easy_perform(hnd);
if (ret == 0) {
long http_code = 0;
curl_easy_getinfo(hnd, CURLINFO_RESPONSE_CODE, &http_code);
ret = !(http_code == 202);
}
curl_easy_cleanup(hnd);
hnd = NULL;
curl_slist_free_all(slist1);
slist1 = NULL;
return (int)ret;
}
int main(int argc, char *argv[]) {
// This is the example chunk from:
// https://docs.memfault.com/docs/embedded/test-patterns-for-chunks-endpoint#event-message-encoded-in-a-single-chunk
const uint8_t chunk[] = { 0x08, 0x02, 0xa7, 0x02, 0x01, 0x03, 0x01, 0x07, 0x6a, 0x54, 0x45,
0x53, 0x54, 0x53, 0x45, 0x52, 0x49, 0x41, 0x4c, 0x0a, 0x6d, 0x74,
0x65, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
0x65, 0x09, 0x6a, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2d, 0x74, 0x65,
0x73, 0x74, 0x06, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x2d, 0x68, 0x61,
0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x04, 0xa1, 0x01, 0xa1, 0x72,
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x5f,
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x01, 0x31, 0xe4 };
int rv = prv_post_chunk("TESTSERIAL", chunk, sizeof(chunk), true /* verbose */);
if (rv != 0) {
printf("\n\nERROR: Chunk post failed, rv=%d\n", rv);
} else {
printf("\n\nChunk successfully sent!\n\n");
}
return rv;
}