lpac/driver/http/curl.c
Robert Marko 3bde4a1d3d
driver(APDU): add QMI backend (#131)
* driver: pass EUICC APDU or HTTP struct to driver fini OP

Currently, the .fini driver OP can only be used with global variables, but
that will be an issue when we cant use global variables.

This will be used with direct QMI in order to free the memory used by the
driver private structure instead of global variables.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: rename QRTR QMI helpers to QMI helpers

Almost all of the QRTR QMI helpers will be reused for direct QMI so,
lets rename the sources to indicate that.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: extract common QMI code

Support for using QMI directly and not over QRTR would use a lot of the
same code as QMI over QRTR, so in order to prevent code duplication lets
extract that common code so it can be reused.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: qmi-helpers: allow compiling without libqrtr

Direct QMI backend wont use any of the libqmi QRTR functionality so it
wont depend on libqrtr and thus we need to make sure QMI helpers still
compile without libqrtr in the system.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: dont use global variables for QMI

Since we split out the common QMI code then we cannot be using global
variables since existing QRTR and the coming QMI drivers will clash
by trying to use the same global variable names with the common code.

So, lets move to passing a structure which is driver specific instead.

Since we are not using global variables anymore, we cannot be using atexit
anymore, so lets move that cleanup step to driver finish OP instead.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: add QMI backend

Previously QMI over QRTR support was added, however that is only present in
modern Qualcomm modems and only when running in PCIe mode and its quite
common to still only use even the latest modems via USB.

In that case, they are all still controllable via QMI but it is directly
exposed as a character device in Linux so we can reuse most of the code
from QMI over QRTR support but drop the support for libqrtr to talk to
the modems.

We require the QMI device path to be passed via QMI_DEVICE env variable
for the backend to operate.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* docs: ENVVARS: document QMI backend

Document the new direct QMI backend ENV variables as well as make it
clear that UIM_SLOT is not a QMI QRTR only variable.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

* driver: apdu: qmi-helpers: support opening QMI device in proxy mode

Currently, we are using QMI_DEVICE_OPEN_FLAGS_NONE to open the QMI device
and thus we are requesting exclusive access to the device while we need
to talk to it.

This will work fine as long as we are the only thing trying to use that
QMI device at the same time or the device was not already opened in proxy
mode.

This is an issue since ModemManager will open the device in proxy mode so
that qmicli or other applications can still talk to the same QMI device
but it will break lpac from trying to use QMI.

So, lets try and open the device in proxy mode, libqmi will the start the
qmi-proxy service automatically as its built and installed as part of it.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>

---------

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
2024-07-24 15:59:37 +08:00

205 lines
5.9 KiB
C

#include "curl.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <euicc/interface.h>
#ifndef _WIN32
#include <curl/curl.h>
#else
#include <dlfcn-win32/dlfcn.h>
#define CURL_GLOBAL_DEFAULT ((1 << 0) | (1 << 1))
#define CURLE_OK 0
#define CURLOPT_URL 10002
#define CURLOPT_WRITEFUNCTION 20011
#define CURLOPT_WRITEDATA 10001
#define CURLOPT_SSL_VERIFYPEER 64
#define CURLOPT_SSL_VERIFYHOST 81
#define CURLOPT_HTTPHEADER 10023
#define CURLOPT_POSTFIELDS 10015
#define CURLOPT_POSTFIELDSIZE 60
#define CURLINFO_RESPONSE_CODE 2097154
typedef void CURL;
typedef int CURLcode;
typedef int CURLoption;
typedef int CURLINFO;
static void *libcurl_interface_dlhandle = NULL;
#endif
struct http_trans_response_data
{
uint8_t *data;
size_t size;
};
static struct libcurl_interface
{
CURLcode (*_curl_global_init)(long flags);
CURL *(*_curl_easy_init)(void);
CURLcode (*_curl_easy_setopt)(CURL *curl, CURLoption option, ...);
CURLcode (*_curl_easy_perform)(CURL *curl);
CURLcode (*_curl_easy_getinfo)(CURL *curl, CURLINFO info, ...);
const char *(*_curl_easy_strerror)(CURLcode);
void (*_curl_easy_cleanup)(CURL *curl);
struct curl_slist *(*_curl_slist_append)(struct curl_slist *list, const char *data);
void (*_curl_slist_free_all)(struct curl_slist *list);
} libcurl;
static size_t http_trans_write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct http_trans_response_data *mem = (struct http_trans_response_data *)userp;
mem->data = realloc(mem->data, mem->size + realsize + 1);
if (mem->data == NULL)
{
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
memcpy(&(mem->data[mem->size]), contents, realsize);
mem->size += realsize;
mem->data[mem->size] = 0;
return realsize;
}
static int http_interface_transmit(struct euicc_ctx *ctx, const char *url, uint32_t *rcode, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len, const char **h)
{
int fret = 0;
CURL *curl;
CURLcode res;
struct http_trans_response_data responseData = {0};
struct curl_slist *headers = NULL, *nheaders = NULL;
long response_code;
(*rx) = NULL;
(*rcode) = 0;
curl = libcurl._curl_easy_init();
if (!curl)
{
goto err;
}
libcurl._curl_easy_setopt(curl, CURLOPT_URL, url);
libcurl._curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_trans_write_callback);
libcurl._curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&responseData);
libcurl._curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
libcurl._curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
for (int i = 0; h[i] != NULL; i++)
{
nheaders = libcurl._curl_slist_append(headers, h[i]);
if (nheaders == NULL)
{
goto err;
}
headers = nheaders;
}
libcurl._curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
if (tx != NULL)
{
libcurl._curl_easy_setopt(curl, CURLOPT_POSTFIELDS, tx);
libcurl._curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, tx_len);
}
res = libcurl._curl_easy_perform(curl);
if (res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n", libcurl._curl_easy_strerror(res));
goto err;
}
libcurl._curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
*rcode = response_code;
*rx = responseData.data;
*rx_len = responseData.size;
fret = 0;
goto exit;
err:
fret = -1;
free(responseData.data);
exit:
libcurl._curl_easy_cleanup(curl);
libcurl._curl_slist_free_all(headers);
return fret;
}
static int _init_libcurl(void)
{
#ifdef _WIN32
if (!(libcurl_interface_dlhandle = dlopen("libcurl.dll", RTLD_LAZY)))
{
fprintf(stderr, "libcurl init err: %s\n", dlerror());
return -1;
}
libcurl._curl_global_init = dlsym(libcurl_interface_dlhandle, "curl_global_init");
libcurl._curl_easy_init = dlsym(libcurl_interface_dlhandle, "curl_easy_init");
libcurl._curl_easy_setopt = dlsym(libcurl_interface_dlhandle, "curl_easy_setopt");
libcurl._curl_easy_perform = dlsym(libcurl_interface_dlhandle, "curl_easy_perform");
libcurl._curl_easy_getinfo = dlsym(libcurl_interface_dlhandle, "curl_easy_getinfo");
libcurl._curl_easy_strerror = dlsym(libcurl_interface_dlhandle, "curl_easy_strerror");
libcurl._curl_easy_cleanup = dlsym(libcurl_interface_dlhandle, "curl_easy_cleanup");
libcurl._curl_slist_append = dlsym(libcurl_interface_dlhandle, "curl_slist_append");
libcurl._curl_slist_free_all = dlsym(libcurl_interface_dlhandle, "curl_slist_free_all");
#else
libcurl._curl_global_init = curl_global_init;
libcurl._curl_easy_init = curl_easy_init;
libcurl._curl_easy_setopt = curl_easy_setopt;
libcurl._curl_easy_perform = curl_easy_perform;
libcurl._curl_easy_getinfo = curl_easy_getinfo;
libcurl._curl_easy_strerror = curl_easy_strerror;
libcurl._curl_easy_cleanup = curl_easy_cleanup;
libcurl._curl_slist_append = curl_slist_append;
libcurl._curl_slist_free_all = curl_slist_free_all;
#endif
return 0;
}
static int libhttpinterface_init(struct euicc_http_interface *ifstruct)
{
memset(ifstruct, 0, sizeof(struct euicc_http_interface));
if (_init_libcurl() != 0)
{
return -1;
}
if (libcurl._curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
{
return -1;
}
ifstruct->transmit = http_interface_transmit;
return 0;
}
static int libhttpinterface_main(int argc, char **argv)
{
return 0;
}
static void libhttpinterface_fini(struct euicc_http_interface *ifstruct)
{
}
const struct euicc_driver driver_http_curl = {
.type = DRIVER_HTTP,
.name = "curl",
.init = (int (*)(void *))libhttpinterface_init,
.main = libhttpinterface_main,
.fini = (void (*)(void *))libhttpinterface_fini,
};