mirror of
https://github.com/estkme-group/lpac
synced 2025-06-29 08:12:57 +02:00
* refactor: revert println refactor This reverts commitae3a87803c
. This reverts commit37ec17eeab
. * fix(apdu/at): make fuart unbuffered related #167 Signed-off-by: Coelacanthus <uwu@coelacanthus.name> * refactor: always use \n for newline > C89 Standard section 5.2.2 Character display semantics > \n (new line) Moves the active position to the initial position of the next line. '\n' in C have including meaning of both '\r' and '\n' on Windows, and libc SHALL translate them when do input and output. If not, it SHALL be a bug of libc. FYI, all mingw64 toolchain code use '\n' directly, so it's impossible to be caused by '\n', otherwise mingw64 itself will broken. Mingw64 also set _fmode to _O_TEXT by default.[1] So as MSVC documents said[2], it should do translate between LF and CRLF. [1]: https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-crt/crt/txtmode.c [2]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen Signed-off-by: Coelacanthus <uwu@coelacanthus.name> --------- Signed-off-by: Coelacanthus <uwu@coelacanthus.name>
353 lines
7.2 KiB
C
353 lines
7.2 KiB
C
#include "stdio.h"
|
|
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cjson/cJSON_ex.h>
|
|
#include <euicc/interface.h>
|
|
#include <euicc/hexutil.h>
|
|
|
|
// getline is a GNU extension, Mingw32 macOS and FreeBSD don't have (a working) one
|
|
static int afgets(char **obuf, FILE *fp)
|
|
{
|
|
uint32_t len = 0;
|
|
char buffer[2];
|
|
char *obuf_new = NULL;
|
|
|
|
*obuf = malloc(1);
|
|
if ((*obuf) == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
(*obuf)[0] = '\0';
|
|
|
|
while (fgets(buffer, sizeof(buffer), fp) != NULL)
|
|
{
|
|
uint32_t fgets_len = strlen(buffer);
|
|
|
|
len += fgets_len + 1;
|
|
obuf_new = realloc(*obuf, len);
|
|
if (obuf_new == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
*obuf = obuf_new;
|
|
strcat(*obuf, buffer);
|
|
|
|
if (buffer[fgets_len - 1] == '\n')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
(*obuf)[strcspn(*obuf, "\n")] = 0;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
free(*obuf);
|
|
*obuf = NULL;
|
|
return -1;
|
|
}
|
|
|
|
static int json_print(cJSON *jpayload)
|
|
{
|
|
cJSON *jroot = NULL;
|
|
char *jstr = NULL;
|
|
|
|
if (jpayload == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
jroot = cJSON_CreateObject();
|
|
if (jroot == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
if (cJSON_AddStringOrNullToObject(jroot, "type", "apdu") == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
if (cJSON_AddItemReferenceToObject(jroot, "payload", jpayload) == 0)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
jstr = cJSON_PrintUnformatted(jroot);
|
|
|
|
if (jstr == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
cJSON_Delete(jroot);
|
|
|
|
fprintf(stdout, "%s\n", jstr);
|
|
fflush(stdout);
|
|
|
|
free(jstr);
|
|
jstr = NULL;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
cJSON_Delete(jroot);
|
|
free(jstr);
|
|
return -1;
|
|
}
|
|
|
|
static int json_request(const char *func, const uint8_t *param, unsigned param_len)
|
|
{
|
|
int fret = 0;
|
|
char *param_hex = NULL;
|
|
cJSON *jpayload = NULL;
|
|
|
|
if (param && param_len)
|
|
{
|
|
param_hex = malloc((2 * param_len) + 1);
|
|
if (param_hex == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
if (euicc_hexutil_bin2hex(param_hex, (2 * param_len) + 1, param, param_len) < 0)
|
|
{
|
|
goto err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
param_hex = NULL;
|
|
}
|
|
|
|
jpayload = cJSON_CreateObject();
|
|
if (jpayload == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
if (cJSON_AddStringOrNullToObject(jpayload, "func", func) == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
if (cJSON_AddStringOrNullToObject(jpayload, "param", param_hex) == NULL)
|
|
{
|
|
goto err;
|
|
}
|
|
free(param_hex);
|
|
param_hex = NULL;
|
|
|
|
fret = json_print(jpayload);
|
|
cJSON_Delete(jpayload);
|
|
jpayload = NULL;
|
|
goto exit;
|
|
|
|
err:
|
|
fret = -1;
|
|
exit:
|
|
cJSON_Delete(jpayload);
|
|
free(param_hex);
|
|
return fret;
|
|
}
|
|
|
|
static int json_response(int *ecode, uint8_t **data, uint32_t *data_len)
|
|
{
|
|
int fret = 0;
|
|
char *data_json;
|
|
cJSON *data_jroot;
|
|
cJSON *data_payload;
|
|
cJSON *jtmp;
|
|
|
|
if (data)
|
|
{
|
|
*data = NULL;
|
|
}
|
|
|
|
if (afgets(&data_json, stdin) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
data_jroot = cJSON_Parse(data_json);
|
|
free(data_json);
|
|
data_json = NULL;
|
|
if (data_jroot == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
jtmp = cJSON_GetObjectItem(data_jroot, "type");
|
|
if (!jtmp)
|
|
{
|
|
goto err;
|
|
}
|
|
if (!cJSON_IsString(jtmp))
|
|
{
|
|
goto err;
|
|
}
|
|
if (strcmp("apdu", jtmp->valuestring) != 0)
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
data_payload = cJSON_GetObjectItem(data_jroot, "payload");
|
|
if (!data_payload)
|
|
{
|
|
goto err;
|
|
}
|
|
if (!cJSON_IsObject(data_payload))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
jtmp = cJSON_GetObjectItem(data_payload, "ecode");
|
|
if (!jtmp)
|
|
{
|
|
goto err;
|
|
}
|
|
if (!cJSON_IsNumber(jtmp))
|
|
{
|
|
goto err;
|
|
}
|
|
*ecode = jtmp->valueint;
|
|
|
|
jtmp = cJSON_GetObjectItem(data_payload, "data");
|
|
if (jtmp && cJSON_IsString(jtmp) && data && data_len)
|
|
{
|
|
*data_len = strlen(jtmp->valuestring) / 2;
|
|
*data = malloc(*data_len);
|
|
if (!*data)
|
|
{
|
|
goto err;
|
|
}
|
|
if (euicc_hexutil_hex2bin_r(*data, *data_len, jtmp->valuestring, strlen(jtmp->valuestring)) < 0)
|
|
{
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
fret = 0;
|
|
goto exit;
|
|
|
|
err:
|
|
fret = -1;
|
|
free(*data);
|
|
if (data)
|
|
{
|
|
*data = NULL;
|
|
}
|
|
if (data_len)
|
|
{
|
|
*data_len = 0;
|
|
}
|
|
*ecode = -1;
|
|
exit:
|
|
free(data_json);
|
|
cJSON_Delete(data_jroot);
|
|
return fret;
|
|
}
|
|
|
|
// {"type":"apdu","payload":{"ecode":0}}
|
|
static int apdu_interface_connect(struct euicc_ctx *ctx)
|
|
{
|
|
int ecode;
|
|
|
|
if (json_request("connect", NULL, 0))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (json_response(&ecode, NULL, NULL))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return ecode;
|
|
}
|
|
|
|
// {"type":"apdu","payload":{"ecode":0}}
|
|
static void apdu_interface_disconnect(struct euicc_ctx *ctx)
|
|
{
|
|
int ecode;
|
|
|
|
json_request("disconnect", NULL, 0);
|
|
json_response(&ecode, NULL, NULL);
|
|
}
|
|
|
|
// {"type":"apdu","payload":{"ecode":1}}
|
|
static int apdu_interface_logic_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, uint8_t aid_len)
|
|
{
|
|
int ecode;
|
|
|
|
if (json_request("logic_channel_open", aid, aid_len))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (json_response(&ecode, NULL, NULL))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return ecode;
|
|
}
|
|
|
|
// {"type":"apdu","payload":{"ecode":0}}
|
|
static void apdu_interface_logic_channel_close(struct euicc_ctx *ctx, uint8_t channel)
|
|
{
|
|
int ecode;
|
|
|
|
json_request("logic_channel_close", &channel, sizeof(channel));
|
|
json_response(&ecode, NULL, NULL);
|
|
}
|
|
|
|
// {"type":"apdu","payload":{"ecode":0,"data":"BF3E125A10890490320010012345000123456789019000"}}
|
|
// {"type":"apdu","payload":{"ecode":0,"data":"BF3C17811574657374726F6F74736D64732E67736D612E636F6D9000"}}
|
|
// {"type":"apdu","payload":{"ecode":0,"data":"BF2281C6810302010082030202008303040600840F8101008204000628248304000019228504067F36C08603090200870302030088020490A916041481370F5125D0B1D408D4C3B232E6D25E795BEBFBAA16041481370F5125D0B1D408D4C3B232E6D25E795BEBFB990206C004030000010C0D47492D42412D55502D30343139AC48801F312E322E3834302E313233343536372F6D79506C6174666F726D4C6162656C812568747470733A2F2F6D79636F6D70616E792E636F6D2F6D79444C4F415265676973747261729000"}}
|
|
static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len)
|
|
{
|
|
int ecode;
|
|
|
|
if (json_request("transmit", tx, tx_len))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (json_response(&ecode, rx, rx_len))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return ecode;
|
|
}
|
|
|
|
static int libapduinterface_init(struct euicc_apdu_interface *ifstruct)
|
|
{
|
|
ifstruct->connect = apdu_interface_connect;
|
|
ifstruct->disconnect = apdu_interface_disconnect;
|
|
ifstruct->logic_channel_open = apdu_interface_logic_channel_open;
|
|
ifstruct->logic_channel_close = apdu_interface_logic_channel_close;
|
|
ifstruct->transmit = apdu_interface_transmit;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int libapduinterface_main(int argc, char **argv)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void libapduinterface_fini(struct euicc_apdu_interface *ifstruct)
|
|
{
|
|
}
|
|
|
|
const struct euicc_driver driver_apdu_stdio = {
|
|
.type = DRIVER_APDU,
|
|
.name = "stdio",
|
|
.init = (int (*)(void *))libapduinterface_init,
|
|
.main = libapduinterface_main,
|
|
.fini = (void (*)(void *))libapduinterface_fini,
|
|
};
|