Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/SoftEtherVPN/SoftEtherVPN_Stable.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mayaqua')
-rw-r--r--src/Mayaqua/Encrypt.c1063
-rw-r--r--src/Mayaqua/Encrypt.h26
-rw-r--r--src/Mayaqua/FileIO.c6
-rw-r--r--src/Mayaqua/Kernel.c91
-rw-r--r--src/Mayaqua/Kernel.h2
-rw-r--r--src/Mayaqua/MayaType.h7
-rw-r--r--src/Mayaqua/Memory.c81
-rw-r--r--src/Mayaqua/Memory.h6
-rw-r--r--src/Mayaqua/Microsoft.c54
-rw-r--r--src/Mayaqua/Microsoft.h3
-rw-r--r--src/Mayaqua/Network.c583
-rw-r--r--src/Mayaqua/Network.h25
-rw-r--r--src/Mayaqua/Pack.c994
-rw-r--r--src/Mayaqua/Pack.h84
-rw-r--r--src/Mayaqua/Str.c2110
-rw-r--r--src/Mayaqua/Str.h255
-rw-r--r--src/Mayaqua/Table.c2
-rw-r--r--src/Mayaqua/Unix.c23
-rw-r--r--src/Mayaqua/Unix.h1
19 files changed, 5201 insertions, 215 deletions
diff --git a/src/Mayaqua/Encrypt.c b/src/Mayaqua/Encrypt.c
index f56fc86f..d93ef7d5 100644
--- a/src/Mayaqua/Encrypt.c
+++ b/src/Mayaqua/Encrypt.c
@@ -142,6 +142,8 @@
LOCK *openssl_lock = NULL;
+int ssl_clientcert_index = 0;
+
LOCK **ssl_lock_obj = NULL;
UINT ssl_lock_num;
static bool openssl_inited = false;
@@ -2456,7 +2458,6 @@ bool RsaVerifyEx(void *data, UINT data_size, void *sign, K *k, UINT bits)
UCHAR hash_data[SIGN_HASH_SIZE];
UCHAR *decrypt_data;
RSA *rsa;
- UINT rsa_size;
// Validate arguments
if (data == NULL || sign == NULL || k == NULL || k->private_key != false)
{
@@ -2473,16 +2474,15 @@ bool RsaVerifyEx(void *data, UINT data_size, void *sign, K *k, UINT bits)
return false;
}
+ decrypt_data = ZeroMalloc(RSA_size(rsa));
+
// Hash the data
if (HashForSign(hash_data, sizeof(hash_data), data, data_size) == false)
{
+ Free(decrypt_data);
return false;
}
- rsa_size = RSA_size(rsa);
- rsa_size = MAX(rsa_size, 1024); // For just in case
- decrypt_data = ZeroMalloc(rsa_size);
-
// Decode the signature
if (RSA_public_decrypt(bits / 8, sign, decrypt_data, rsa, RSA_PKCS1_PADDING) <= 0)
{
@@ -4137,6 +4137,8 @@ void InitCryptLibrary()
ERR_load_crypto_strings();
SSL_load_error_strings();
+ ssl_clientcert_index = SSL_get_ex_new_index(0, "struct SslClientCertInfo *", NULL, NULL, NULL);
+
#ifdef OS_UNIX
{
char *name1 = "/dev/random";
@@ -5171,5 +5173,1056 @@ static unsigned char *Internal_SHA0(const unsigned char *d, size_t n, unsigned c
}
+int GetSslClientCertIndex()
+{
+ return ssl_clientcert_index;
+}
+
+
+
+//// RFC 8439: ChaCha20 and Poly1305 for IETF Protocols
+//// Implementation from libsodium: https://github.com/jedisct1/libsodium
+////
+//// SoftEther VPN must support OpenSSL versions between 1.0.2 to the latest version.
+//// Since we are unable to use ChaCha20 and Poly1305 on OpenSSL 1.0.x,
+//// we copied the C implementation from libsodium.
+//// Please note that the C implementation for ChaCha20 and Poly1305 is slow than
+//// the OpenSSL 1.0.0 or later's implementation for ChaCha20 and Poly1305.
+////
+//// If OpenSSL 1.1.0 or later is linked, we use OpenSSL's ChaCha20 and Poly1305 implementation.
+
+/*
+ * ISC License
+ *
+ * Copyright (c) 2013-2018
+ * Frank Denis <j at pureftpd dot org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef OS_WIN32
+#define inline __inline
+#endif
+
+#define poly1305_block_size 16
+
+#define U32C(v) (v##U)
+#define U32V(v) ((UINT)(v) &U32C(0xFFFFFFFF))
+#define ROTATE(v, c) (ROTL32(v, c))
+#define XOR(v, w) ((v) ^ (w))
+#define PLUS(v, w) (U32V((v) + (w)))
+#define PLUSONE(v) (PLUS((v), 1))
+
+#define QUARTERROUND(a, b, c, d) \
+ a = PLUS(a, b); \
+ d = ROTATE(XOR(d, a), 16); \
+ c = PLUS(c, d); \
+ b = ROTATE(XOR(b, c), 12); \
+ a = PLUS(a, b); \
+ d = ROTATE(XOR(d, a), 8); \
+ c = PLUS(c, d); \
+ b = ROTATE(XOR(b, c), 7);
+
+#define ROTL32(X, B) rotl32((X), (B))
+static inline UINT
+rotl32(const UINT x, const int b)
+{
+ return (x << b) | (x >> (32 - b));
+}
+
+
+#define LOAD32_LE(SRC) load32_le(SRC)
+
+static inline UINT
+load32_le(const UCHAR src[4])
+{
+ if (IsBigEndian() == false)
+ {
+ UINT w;
+ memcpy(&w, src, sizeof w);
+ return w;
+ }
+ else
+ {
+ UINT w = (UINT) src[0];
+ w |= (UINT) src[1] << 8;
+ w |= (UINT) src[2] << 16;
+ w |= (UINT) src[3] << 24;
+ return w;
+ }
+}
+
+#define STORE32_LE(DST, W) store32_le((DST), (W))
+static inline void
+store32_le(UCHAR dst[4], UINT w)
+{
+ if (IsBigEndian() == false)
+ {
+ memcpy(dst, &w, sizeof w);
+ }
+ else
+ {
+ dst[0] = (UCHAR) w; w >>= 8;
+ dst[1] = (UCHAR) w; w >>= 8;
+ dst[2] = (UCHAR) w; w >>= 8;
+ dst[3] = (UCHAR) w;
+ }
+}
+
+
+#define LOAD64_LE(SRC) load64_le(SRC)
+static inline UINT64
+load64_le(const UCHAR src[8])
+{
+ if (IsBigEndian() == false)
+ {
+ UINT64 w;
+ memcpy(&w, src, sizeof w);
+ return w;
+ }
+ else
+ {
+ UINT64 w = (UINT64) src[0];
+ w |= (UINT64) src[1] << 8;
+ w |= (UINT64) src[2] << 16;
+ w |= (UINT64) src[3] << 24;
+ w |= (UINT64) src[4] << 32;
+ w |= (UINT64) src[5] << 40;
+ w |= (UINT64) src[6] << 48;
+ w |= (UINT64) src[7] << 56;
+ return w;
+ }
+}
+
+#define STORE64_LE(DST, W) store64_le((DST), (W))
+static inline void
+store64_le(UCHAR dst[8], UINT64 w)
+{
+ if (IsBigEndian() == false)
+ {
+ memcpy(dst, &w, sizeof w);
+ }
+ else
+ {
+ dst[0] = (UCHAR) w; w >>= 8;
+ dst[1] = (UCHAR) w; w >>= 8;
+ dst[2] = (UCHAR) w; w >>= 8;
+ dst[3] = (UCHAR) w; w >>= 8;
+ dst[4] = (UCHAR) w; w >>= 8;
+ dst[5] = (UCHAR) w; w >>= 8;
+ dst[6] = (UCHAR) w; w >>= 8;
+ dst[7] = (UCHAR) w;
+ }
+}
+
+
+
+typedef struct chacha_ctx {
+ UINT input[16];
+} chacha_ctx;
+
+
+
+
+#define crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX \
+ (64ULL * (1ULL << 32))
+
+typedef struct crypto_onetimeauth_poly1305_state {
+ unsigned char opaque[256];
+} crypto_onetimeauth_poly1305_state;
+
+/* 17 + sizeof(unsigned long long) + 14*sizeof(unsigned long) */
+typedef struct poly1305_state_internal_t {
+ unsigned long r[5];
+ unsigned long h[5];
+ unsigned long pad[4];
+ unsigned long long leftover;
+ unsigned char buffer[poly1305_block_size];
+ unsigned char final;
+} poly1305_state_internal_t;
+static void
+chacha_keysetup(chacha_ctx *ctx, const UCHAR *k)
+{
+ ctx->input[0] = U32C(0x61707865);
+ ctx->input[1] = U32C(0x3320646e);
+ ctx->input[2] = U32C(0x79622d32);
+ ctx->input[3] = U32C(0x6b206574);
+ ctx->input[4] = LOAD32_LE(k + 0);
+ ctx->input[5] = LOAD32_LE(k + 4);
+ ctx->input[6] = LOAD32_LE(k + 8);
+ ctx->input[7] = LOAD32_LE(k + 12);
+ ctx->input[8] = LOAD32_LE(k + 16);
+ ctx->input[9] = LOAD32_LE(k + 20);
+ ctx->input[10] = LOAD32_LE(k + 24);
+ ctx->input[11] = LOAD32_LE(k + 28);
+}
+
+static void
+chacha_ietf_ivsetup(chacha_ctx *ctx, const UCHAR *iv, const UCHAR *counter)
+{
+ ctx->input[12] = counter == NULL ? 0 : LOAD32_LE(counter);
+ ctx->input[13] = LOAD32_LE(iv + 0);
+ ctx->input[14] = LOAD32_LE(iv + 4);
+ ctx->input[15] = LOAD32_LE(iv + 8);
+}
+
+static void
+chacha20_encrypt_bytes(chacha_ctx *ctx, const UCHAR *m, UCHAR *c,
+ unsigned long long bytes)
+{
+ UINT x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14,
+ x15;
+ UINT j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14,
+ j15;
+ UCHAR *ctarget = NULL;
+ UCHAR tmp[64];
+ unsigned int i;
+
+ if (!bytes) {
+ return; /* LCOV_EXCL_LINE */
+ }
+ j0 = ctx->input[0];
+ j1 = ctx->input[1];
+ j2 = ctx->input[2];
+ j3 = ctx->input[3];
+ j4 = ctx->input[4];
+ j5 = ctx->input[5];
+ j6 = ctx->input[6];
+ j7 = ctx->input[7];
+ j8 = ctx->input[8];
+ j9 = ctx->input[9];
+ j10 = ctx->input[10];
+ j11 = ctx->input[11];
+ j12 = ctx->input[12];
+ j13 = ctx->input[13];
+ j14 = ctx->input[14];
+ j15 = ctx->input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ memset(tmp, 0, 64);
+ for (i = 0; i < bytes; ++i) {
+ tmp[i] = m[i];
+ }
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20; i > 0; i -= 2) {
+ QUARTERROUND(x0, x4, x8, x12)
+ QUARTERROUND(x1, x5, x9, x13)
+ QUARTERROUND(x2, x6, x10, x14)
+ QUARTERROUND(x3, x7, x11, x15)
+ QUARTERROUND(x0, x5, x10, x15)
+ QUARTERROUND(x1, x6, x11, x12)
+ QUARTERROUND(x2, x7, x8, x13)
+ QUARTERROUND(x3, x4, x9, x14)
+ }
+ x0 = PLUS(x0, j0);
+ x1 = PLUS(x1, j1);
+ x2 = PLUS(x2, j2);
+ x3 = PLUS(x3, j3);
+ x4 = PLUS(x4, j4);
+ x5 = PLUS(x5, j5);
+ x6 = PLUS(x6, j6);
+ x7 = PLUS(x7, j7);
+ x8 = PLUS(x8, j8);
+ x9 = PLUS(x9, j9);
+ x10 = PLUS(x10, j10);
+ x11 = PLUS(x11, j11);
+ x12 = PLUS(x12, j12);
+ x13 = PLUS(x13, j13);
+ x14 = PLUS(x14, j14);
+ x15 = PLUS(x15, j15);
+
+ x0 = XOR(x0, LOAD32_LE(m + 0));
+ x1 = XOR(x1, LOAD32_LE(m + 4));
+ x2 = XOR(x2, LOAD32_LE(m + 8));
+ x3 = XOR(x3, LOAD32_LE(m + 12));
+ x4 = XOR(x4, LOAD32_LE(m + 16));
+ x5 = XOR(x5, LOAD32_LE(m + 20));
+ x6 = XOR(x6, LOAD32_LE(m + 24));
+ x7 = XOR(x7, LOAD32_LE(m + 28));
+ x8 = XOR(x8, LOAD32_LE(m + 32));
+ x9 = XOR(x9, LOAD32_LE(m + 36));
+ x10 = XOR(x10, LOAD32_LE(m + 40));
+ x11 = XOR(x11, LOAD32_LE(m + 44));
+ x12 = XOR(x12, LOAD32_LE(m + 48));
+ x13 = XOR(x13, LOAD32_LE(m + 52));
+ x14 = XOR(x14, LOAD32_LE(m + 56));
+ x15 = XOR(x15, LOAD32_LE(m + 60));
+
+ j12 = PLUSONE(j12);
+ /* LCOV_EXCL_START */
+ if (!j12) {
+ j13 = PLUSONE(j13);
+ }
+ /* LCOV_EXCL_STOP */
+
+ STORE32_LE(c + 0, x0);
+ STORE32_LE(c + 4, x1);
+ STORE32_LE(c + 8, x2);
+ STORE32_LE(c + 12, x3);
+ STORE32_LE(c + 16, x4);
+ STORE32_LE(c + 20, x5);
+ STORE32_LE(c + 24, x6);
+ STORE32_LE(c + 28, x7);
+ STORE32_LE(c + 32, x8);
+ STORE32_LE(c + 36, x9);
+ STORE32_LE(c + 40, x10);
+ STORE32_LE(c + 44, x11);
+ STORE32_LE(c + 48, x12);
+ STORE32_LE(c + 52, x13);
+ STORE32_LE(c + 56, x14);
+ STORE32_LE(c + 60, x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0; i < (unsigned int) bytes; ++i) {
+ ctarget[i] = c[i]; /* ctarget cannot be NULL */
+ }
+ }
+ ctx->input[12] = j12;
+ ctx->input[13] = j13;
+
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ m += 64;
+ }
+}
+
+static int
+stream_ietf_ext_ref(unsigned char *c, unsigned long long clen,
+ const unsigned char *n, const unsigned char *k)
+{
+ struct chacha_ctx ctx;
+
+ if (!clen) {
+ return 0;
+ }
+ chacha_keysetup(&ctx, k);
+ chacha_ietf_ivsetup(&ctx, n, NULL);
+ memset(c, 0, (UINT)clen);
+ chacha20_encrypt_bytes(&ctx, c, c, clen);
+ Zero(&ctx, sizeof ctx);
+
+ return 0;
+}
+
+int
+crypto_stream_chacha20_ietf(unsigned char *c, unsigned long long clen,
+ const unsigned char *n, const unsigned char *k)
+{
+ return stream_ietf_ext_ref(c, clen, n, k);
+}
+
+static void
+poly1305_init(poly1305_state_internal_t *st, const unsigned char key[32])
+{
+ /* r &= 0xffffffc0ffffffc0ffffffc0fffffff - wiped after finalization */
+ st->r[0] = (LOAD32_LE(&key[0])) & 0x3ffffff;
+ st->r[1] = (LOAD32_LE(&key[3]) >> 2) & 0x3ffff03;
+ st->r[2] = (LOAD32_LE(&key[6]) >> 4) & 0x3ffc0ff;
+ st->r[3] = (LOAD32_LE(&key[9]) >> 6) & 0x3f03fff;
+ st->r[4] = (LOAD32_LE(&key[12]) >> 8) & 0x00fffff;
+
+ /* h = 0 */
+ st->h[0] = 0;
+ st->h[1] = 0;
+ st->h[2] = 0;
+ st->h[3] = 0;
+ st->h[4] = 0;
+
+ /* save pad for later */
+ st->pad[0] = LOAD32_LE(&key[16]);
+ st->pad[1] = LOAD32_LE(&key[20]);
+ st->pad[2] = LOAD32_LE(&key[24]);
+ st->pad[3] = LOAD32_LE(&key[28]);
+
+ st->leftover = 0;
+ st->final = 0;
+}
+
+static void
+poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m,
+ unsigned long long bytes)
+{
+ const unsigned long hibit = (st->final) ? 0UL : (1UL << 24); /* 1 << 128 */
+ unsigned long r0, r1, r2, r3, r4;
+ unsigned long s1, s2, s3, s4;
+ unsigned long h0, h1, h2, h3, h4;
+ unsigned long long d0, d1, d2, d3, d4;
+ unsigned long c;
+
+ r0 = st->r[0];
+ r1 = st->r[1];
+ r2 = st->r[2];
+ r3 = st->r[3];
+ r4 = st->r[4];
+
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ h0 = st->h[0];
+ h1 = st->h[1];
+ h2 = st->h[2];
+ h3 = st->h[3];
+ h4 = st->h[4];
+
+ while (bytes >= poly1305_block_size) {
+ /* h += m[i] */
+ h0 += (LOAD32_LE(m + 0)) & 0x3ffffff;
+ h1 += (LOAD32_LE(m + 3) >> 2) & 0x3ffffff;
+ h2 += (LOAD32_LE(m + 6) >> 4) & 0x3ffffff;
+ h3 += (LOAD32_LE(m + 9) >> 6) & 0x3ffffff;
+ h4 += (LOAD32_LE(m + 12) >> 8) | hibit;
+
+ /* h *= r */
+ d0 = ((unsigned long long) h0 * r0) + ((unsigned long long) h1 * s4) +
+ ((unsigned long long) h2 * s3) + ((unsigned long long) h3 * s2) +
+ ((unsigned long long) h4 * s1);
+ d1 = ((unsigned long long) h0 * r1) + ((unsigned long long) h1 * r0) +
+ ((unsigned long long) h2 * s4) + ((unsigned long long) h3 * s3) +
+ ((unsigned long long) h4 * s2);
+ d2 = ((unsigned long long) h0 * r2) + ((unsigned long long) h1 * r1) +
+ ((unsigned long long) h2 * r0) + ((unsigned long long) h3 * s4) +
+ ((unsigned long long) h4 * s3);
+ d3 = ((unsigned long long) h0 * r3) + ((unsigned long long) h1 * r2) +
+ ((unsigned long long) h2 * r1) + ((unsigned long long) h3 * r0) +
+ ((unsigned long long) h4 * s4);
+ d4 = ((unsigned long long) h0 * r4) + ((unsigned long long) h1 * r3) +
+ ((unsigned long long) h2 * r2) + ((unsigned long long) h3 * r1) +
+ ((unsigned long long) h4 * r0);
+
+ /* (partial) h %= p */
+ c = (unsigned long) (d0 >> 26);
+ h0 = (unsigned long) d0 & 0x3ffffff;
+ d1 += c;
+ c = (unsigned long) (d1 >> 26);
+ h1 = (unsigned long) d1 & 0x3ffffff;
+ d2 += c;
+ c = (unsigned long) (d2 >> 26);
+ h2 = (unsigned long) d2 & 0x3ffffff;
+ d3 += c;
+ c = (unsigned long) (d3 >> 26);
+ h3 = (unsigned long) d3 & 0x3ffffff;
+ d4 += c;
+ c = (unsigned long) (d4 >> 26);
+ h4 = (unsigned long) d4 & 0x3ffffff;
+ h0 += c * 5;
+ c = (h0 >> 26);
+ h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ m += poly1305_block_size;
+ bytes -= poly1305_block_size;
+ }
+
+ st->h[0] = h0;
+ st->h[1] = h1;
+ st->h[2] = h2;
+ st->h[3] = h3;
+ st->h[4] = h4;
+}
+
+static void
+poly1305_update(poly1305_state_internal_t *st, const unsigned char *m,
+ unsigned long long bytes)
+{
+ unsigned long long i;
+
+ /* handle leftover */
+ if (st->leftover) {
+ unsigned long long want = (poly1305_block_size - st->leftover);
+
+ if (want > bytes) {
+ want = bytes;
+ }
+ for (i = 0; i < want; i++) {
+ st->buffer[st->leftover + i] = m[i];
+ }
+ bytes -= want;
+ m += want;
+ st->leftover += want;
+ if (st->leftover < poly1305_block_size) {
+ return;
+ }
+ poly1305_blocks(st, st->buffer, poly1305_block_size);
+ st->leftover = 0;
+ }
+
+ /* process full blocks */
+ if (bytes >= poly1305_block_size) {
+ unsigned long long want = (bytes & ~(poly1305_block_size - 1));
+
+ poly1305_blocks(st, m, want);
+ m += want;
+ bytes -= want;
+ }
+
+ /* store leftover */
+ if (bytes) {
+ for (i = 0; i < bytes; i++) {
+ st->buffer[st->leftover + i] = m[i];
+ }
+ st->leftover += bytes;
+ }
+}
+
+static int
+crypto_onetimeauth_poly1305_init(crypto_onetimeauth_poly1305_state *state,
+ const unsigned char *key)
+{
+ poly1305_init((poly1305_state_internal_t *) (void *) state, key);
+
+ return 0;
+}
+
+static int
+crypto_onetimeauth_poly1305_update(
+ crypto_onetimeauth_poly1305_state *state, const unsigned char *in,
+ unsigned long long inlen)
+{
+ poly1305_update((poly1305_state_internal_t *) (void *) state, in, inlen);
+
+ return 0;
+}
+
+static int
+stream_ietf_ext_ref_xor_ic(unsigned char *c, const unsigned char *m,
+ unsigned long long mlen, const unsigned char *n,
+ UINT ic, const unsigned char *k)
+{
+ struct chacha_ctx ctx;
+ UCHAR ic_bytes[4];
+
+ if (!mlen) {
+ return 0;
+ }
+ STORE32_LE(ic_bytes, ic);
+ chacha_keysetup(&ctx, k);
+ chacha_ietf_ivsetup(&ctx, n, ic_bytes);
+ chacha20_encrypt_bytes(&ctx, m, c, mlen);
+ Zero(&ctx, sizeof ctx);
+
+ return 0;
+}
+
+int
+crypto_stream_chacha20_ietf_xor_ic(unsigned char *c, const unsigned char *m,
+ unsigned long long mlen,
+ const unsigned char *n, UINT ic,
+ const unsigned char *k)
+{
+ return stream_ietf_ext_ref_xor_ic(c, m, mlen, n, ic, k);
+}
+
+
+static void
+poly1305_finish(poly1305_state_internal_t *st, unsigned char mac[16])
+{
+ unsigned long h0, h1, h2, h3, h4, c;
+ unsigned long g0, g1, g2, g3, g4;
+ unsigned long long f;
+ unsigned long mask;
+
+ /* process the remaining block */
+ if (st->leftover) {
+ unsigned long long i = st->leftover;
+
+ st->buffer[i++] = 1;
+ for (; i < poly1305_block_size; i++) {
+ st->buffer[i] = 0;
+ }
+ st->final = 1;
+ poly1305_blocks(st, st->buffer, poly1305_block_size);
+ }
+
+ /* fully carry h */
+ h0 = st->h[0];
+ h1 = st->h[1];
+ h2 = st->h[2];
+ h3 = st->h[3];
+ h4 = st->h[4];
+
+ c = h1 >> 26;
+ h1 = h1 & 0x3ffffff;
+ h2 += c;
+ c = h2 >> 26;
+ h2 = h2 & 0x3ffffff;
+ h3 += c;
+ c = h3 >> 26;
+ h3 = h3 & 0x3ffffff;
+ h4 += c;
+ c = h4 >> 26;
+ h4 = h4 & 0x3ffffff;
+ h0 += c * 5;
+ c = h0 >> 26;
+ h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ /* compute h + -p */
+ g0 = h0 + 5;
+ c = g0 >> 26;
+ g0 &= 0x3ffffff;
+ g1 = h1 + c;
+ c = g1 >> 26;
+ g1 &= 0x3ffffff;
+ g2 = h2 + c;
+ c = g2 >> 26;
+ g2 &= 0x3ffffff;
+ g3 = h3 + c;
+ c = g3 >> 26;
+ g3 &= 0x3ffffff;
+ g4 = h4 + c - (1UL << 26);
+
+ /* select h if h < p, or h + -p if h >= p */
+ mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
+ g0 &= mask;
+ g1 &= mask;
+ g2 &= mask;
+ g3 &= mask;
+ g4 &= mask;
+ mask = ~mask;
+
+ h0 = (h0 & mask) | g0;
+ h1 = (h1 & mask) | g1;
+ h2 = (h2 & mask) | g2;
+ h3 = (h3 & mask) | g3;
+ h4 = (h4 & mask) | g4;
+
+ /* h = h % (2^128) */
+ h0 = ((h0) | (h1 << 26)) & 0xffffffff;
+ h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
+ h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
+ h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
+
+ /* mac = (h + pad) % (2^128) */
+ f = (unsigned long long) h0 + st->pad[0];
+ h0 = (unsigned long) f;
+ f = (unsigned long long) h1 + st->pad[1] + (f >> 32);
+ h1 = (unsigned long) f;
+ f = (unsigned long long) h2 + st->pad[2] + (f >> 32);
+ h2 = (unsigned long) f;
+ f = (unsigned long long) h3 + st->pad[3] + (f >> 32);
+ h3 = (unsigned long) f;
+
+ STORE32_LE(mac + 0, (UINT) h0);
+ STORE32_LE(mac + 4, (UINT) h1);
+ STORE32_LE(mac + 8, (UINT) h2);
+ STORE32_LE(mac + 12, (UINT) h3);
+
+ /* zero out the state */
+ Zero((void *) st, sizeof *st);
+}
+
+static int
+crypto_onetimeauth_poly1305_final(
+ crypto_onetimeauth_poly1305_state *state, unsigned char *out)
+{
+ poly1305_finish((poly1305_state_internal_t *) (void *) state, out);
+
+ return 0;
+}
+
+static const unsigned char _pad0[16] = { 0 };
+
+int
+crypto_aead_chacha20poly1305_ietf_encrypt_detached(unsigned char *c,
+ unsigned char *mac,
+ unsigned long long *maclen_p,
+ const unsigned char *m,
+ unsigned long long mlen,
+ const unsigned char *ad,
+ unsigned long long adlen,
+ const unsigned char *nsec,
+ const unsigned char *npub,
+ const unsigned char *k)
+{
+ crypto_onetimeauth_poly1305_state state;
+ unsigned char block0[64U];
+ unsigned char slen[8U];
+
+ (void) nsec;
+ Zero(block0, sizeof block0);
+ crypto_stream_chacha20_ietf(block0, sizeof block0, npub, k);
+ crypto_onetimeauth_poly1305_init(&state, block0);
+
+ crypto_onetimeauth_poly1305_update(&state, ad, adlen);
+ crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - adlen) & 0xf);
+
+ crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, npub, 1U, k);
+
+ crypto_onetimeauth_poly1305_update(&state, c, mlen);
+ crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - mlen) & 0xf);
+
+ STORE64_LE(slen, (UINT64) adlen);
+ crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
+
+ STORE64_LE(slen, (UINT64) mlen);
+ crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
+
+ crypto_onetimeauth_poly1305_final(&state, mac);
+ Zero(&state, sizeof state);
+
+ if (maclen_p != NULL) {
+ *maclen_p = 16;
+ }
+ return 0;
+}
+
+
+
+int
+crypto_aead_chacha20poly1305_ietf_decrypt_detached(unsigned char *m,
+ unsigned char *nsec,
+ const unsigned char *c,
+ unsigned long long clen,
+ const unsigned char *mac,
+ const unsigned char *ad,
+ unsigned long long adlen,
+ const unsigned char *npub,
+ const unsigned char *k)
+{
+ crypto_onetimeauth_poly1305_state state;
+ unsigned char block0[64U];
+ unsigned char slen[8U];
+ unsigned char computed_mac[16];
+ unsigned long long mlen;
+ int ret;
+
+ (void) nsec;
+ Zero(block0, sizeof block0);
+ crypto_stream_chacha20_ietf(block0, sizeof block0, npub, k);
+ crypto_onetimeauth_poly1305_init(&state, block0);
+
+ crypto_onetimeauth_poly1305_update(&state, ad, adlen);
+ crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - adlen) & 0xf);
+
+ mlen = clen;
+ crypto_onetimeauth_poly1305_update(&state, c, mlen);
+ crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - mlen) & 0xf);
+
+ STORE64_LE(slen, (UINT64) adlen);
+ crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
+
+ STORE64_LE(slen, (UINT64) mlen);
+ crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
+
+ crypto_onetimeauth_poly1305_final(&state, computed_mac);
+ Zero(&state, sizeof state);
+
+ ret = Cmp((void *)computed_mac, (void *)mac, 16);
+ Zero(computed_mac, sizeof computed_mac);
+ if (m == NULL) {
+ return ret;
+ }
+ if (ret != 0) {
+ memset(m, 0, (UINT)mlen);
+ return -1;
+ }
+ crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, npub, 1U, k);
+
+ return 0;
+}
+
+int
+crypto_aead_chacha20poly1305_ietf_decrypt(unsigned char *m,
+ unsigned long long *mlen_p,
+ unsigned char *nsec,
+ const unsigned char *c,
+ unsigned long long clen,
+ const unsigned char *ad,
+ unsigned long long adlen,
+ const unsigned char *npub,
+ const unsigned char *k)
+{
+ unsigned long long mlen = 0ULL;
+ int ret = -1;
+
+ if (clen >= 16) {
+ ret = crypto_aead_chacha20poly1305_ietf_decrypt_detached
+ (m, nsec,
+ c, clen - 16,
+ c + clen - AEAD_CHACHA20_POLY1305_MAC_SIZE,
+ ad, adlen, npub, k);
+ }
+ if (mlen_p != NULL) {
+ if (ret == 0) {
+ mlen = clen - AEAD_CHACHA20_POLY1305_MAC_SIZE;
+ }
+ *mlen_p = mlen;
+ }
+ return ret;
+}
+
+
+int
+crypto_aead_chacha20poly1305_ietf_encrypt(unsigned char *c,
+ unsigned long long *clen_p,
+ const unsigned char *m,
+ unsigned long long mlen,
+ const unsigned char *ad,
+ unsigned long long adlen,
+ const unsigned char *nsec,
+ const unsigned char *npub,
+ const unsigned char *k)
+{
+ unsigned long long clen = 0ULL;
+ int ret;
+
+ ret = crypto_aead_chacha20poly1305_ietf_encrypt_detached(c,
+ c + mlen, NULL,
+ m, mlen,
+ ad, adlen,
+ nsec, npub, k);
+ if (clen_p != NULL) {
+ if (ret == 0) {
+ clen = mlen + AEAD_CHACHA20_POLY1305_MAC_SIZE;
+ }
+ *clen_p = clen;
+ }
+ return ret;
+}
+// RFC 8439: ChaCha20-Poly1305-IETF Encryption with AEAD
+void Aead_ChaCha20Poly1305_Ietf_Encrypt(void *dst, void *src, UINT src_size,
+ void *key, void *nonce, void *aad, UINT aad_size)
+{
+#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
+ Aead_ChaCha20Poly1305_Ietf_Encrypt_OpenSSL(dst, src, src_size, key, nonce, aad, aad_size);
+#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
+ Aead_ChaCha20Poly1305_Ietf_Encrypt_Embedded(dst, src, src_size, key, nonce, aad, aad_size);
+#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
+}
+void Aead_ChaCha20Poly1305_Ietf_Encrypt_OpenSSL(void *dst, void *src, UINT src_size,
+ void *key, void *nonce, void *aad, UINT aad_size)
+{
+#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
+ EVP_CIPHER_CTX *ctx;
+ int outlen = 0;
+
+ if ((src_size != 0 && (dst == NULL || src == NULL)) ||
+ key == NULL || nonce == NULL ||
+ (aad_size != 0 && aad == NULL))
+ {
+ Zero(dst, src_size);
+ return;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, AEAD_CHACHA20_POLY1305_NONCE_SIZE, 0);
+ EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce);
+ EVP_EncryptUpdate(ctx, NULL, &outlen, aad, aad_size);
+ EVP_EncryptUpdate(ctx, dst, &outlen, src, src_size);
+ EVP_EncryptFinal_ex(ctx, dst, &outlen);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, AEAD_CHACHA20_POLY1305_MAC_SIZE,
+ ((UCHAR *)dst) + src_size);
+
+ EVP_CIPHER_CTX_free(ctx);
+#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
+}
+void Aead_ChaCha20Poly1305_Ietf_Encrypt_Embedded(void *dst, void *src, UINT src_size,
+ void *key, void *nonce, void *aad, UINT aad_size)
+{
+ if ((src_size != 0 && (dst == NULL || src == NULL)) ||
+ key == NULL || nonce == NULL ||
+ (aad_size != 0 && aad == NULL))
+ {
+ Zero(dst, src_size);
+ return;
+ }
+ crypto_aead_chacha20poly1305_ietf_encrypt(dst, NULL, src, src_size, aad, aad_size,
+ NULL, nonce, key);
+}
+
+// RFC 8439: ChaCha20-Poly1305-IETF Decryption with AEAD
+bool Aead_ChaCha20Poly1305_Ietf_Decrypt(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size)
+{
+#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
+ return Aead_ChaCha20Poly1305_Ietf_Decrypt_OpenSSL(dst, src, src_size, key,
+ nonce, aad, aad_size);
+#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
+ return Aead_ChaCha20Poly1305_Ietf_Decrypt_Embedded(dst, src, src_size, key,
+ nonce, aad, aad_size);
+#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
+}
+bool Aead_ChaCha20Poly1305_Ietf_Decrypt_OpenSSL(void *dst, void *src, UINT src_size, void *key,
+ void *nonce, void *aad, UINT aad_size)
+{
+#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
+ EVP_CIPHER_CTX *ctx;
+ int outlen = 0;
+ bool ret = false;
+
+ if ((src_size != 0 && (dst == NULL || src == NULL)) ||
+ key == NULL || nonce == NULL ||
+ (aad_size != 0 && aad == NULL) ||
+ (src_size < AEAD_CHACHA20_POLY1305_MAC_SIZE))
+ {
+ Zero(dst, src_size);
+ return false;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, AEAD_CHACHA20_POLY1305_NONCE_SIZE, 0);
+
+ if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce) == 1)
+ {
+ if (EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_size) == 1)
+ {
+ if (EVP_DecryptUpdate(ctx, dst, &outlen, src, src_size - AEAD_CHACHA20_POLY1305_MAC_SIZE) == 1)
+ {
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, AEAD_CHACHA20_POLY1305_MAC_SIZE,
+ ((UCHAR *)src) + (src_size - AEAD_CHACHA20_POLY1305_MAC_SIZE));
+
+ if (EVP_DecryptFinal_ex(ctx, dst, &outlen))
+ {
+ ret = true;
+ }
+ }
+ }
+ }
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ return ret;
+#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
+ return false;
+#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
+}
+bool Aead_ChaCha20Poly1305_Ietf_Decrypt_Embedded(void *dst, void *src, UINT src_size, void *key,
+ void *nonce, void *aad, UINT aad_size)
+{
+ int ret;
+ if ((src_size != 0 && (dst == NULL || src == NULL)) ||
+ key == NULL || nonce == NULL ||
+ (aad_size != 0 && aad == NULL) ||
+ (src_size < AEAD_CHACHA20_POLY1305_MAC_SIZE))
+ {
+ Zero(dst, src_size);
+ return false;
+ }
+
+ ret = crypto_aead_chacha20poly1305_ietf_decrypt(
+ dst, NULL, NULL, src, src_size, aad, aad_size, nonce, key);
+
+ if (ret == -1)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool Aead_ChaCha20Poly1305_Ietf_IsOpenSSL()
+{
+#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
+ return true;
+#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
+ return false;
+#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
+}
+
+// RFC 8439: ChaCha20-Poly1305-IETF AEAD Test
+void Aead_ChaCha20Poly1305_Ietf_Test()
+{
+ char *nonce_hex = "07 00 00 00 40 41 42 43 44 45 46 47";
+ char *plaintext_hex =
+ "4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c "
+ "65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73 "
+ "73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63 "
+ "6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f "
+ "6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20 "
+ "74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73 "
+ "63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69 "
+ "74 2e";
+ char *aad_hex = "50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7";
+ char *key_hex =
+ "80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f "
+ "90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f";
+ BUF *nonce = StrToBin(nonce_hex);
+ BUF *plaintext = StrToBin(plaintext_hex);
+ BUF *aad = StrToBin(aad_hex);
+ BUF *key = StrToBin(key_hex);
+ UINT plaintext_size = plaintext->Size;
+ UCHAR *encrypted = Malloc(plaintext_size + AEAD_CHACHA20_POLY1305_MAC_SIZE);
+ UCHAR *decrypted = Malloc(plaintext_size);
+ char encrypted_hex[MAX_SIZE];
+ char mac_hex[MAX_SIZE];
+
+ Print("Aead_ChaCha20Poly1305_Ietf_Test()\n\n");
+
+ Aead_ChaCha20Poly1305_Ietf_Encrypt(encrypted, plaintext->Buf, plaintext_size,
+ key->Buf, nonce->Buf, aad->Buf, aad->Size);
+
+ BinToStrEx(encrypted_hex, sizeof(encrypted_hex), encrypted, plaintext_size);
+
+ BinToStrEx(mac_hex, sizeof(mac_hex), encrypted + plaintext_size, AEAD_CHACHA20_POLY1305_MAC_SIZE);
+
+ Print("Encrypted:\n%s\n\n", encrypted_hex);
+
+ Print("MAC:\n%s\n\n", mac_hex);
+
+ Print("Please check the results with https://tools.ietf.org/html/rfc8439#section-2.8.2 by your great eyes.\n\n");
+
+ if (Aead_ChaCha20Poly1305_Ietf_Decrypt(decrypted, encrypted, plaintext_size + AEAD_CHACHA20_POLY1305_MAC_SIZE,
+ key->Buf, nonce->Buf, aad->Buf, aad->Size) == false)
+ {
+ Print("Decrypt failed.\n");
+ }
+ else
+ {
+ Print("Decrypt OK.\n");
+ if (Cmp(plaintext->Buf, decrypted, plaintext_size) == 0)
+ {
+ Print("Same OK.\n");
+ }
+ else
+ {
+ Print("Different !!!\n");
+ }
+ }
+
+ FreeBuf(nonce);
+ FreeBuf(plaintext);
+ FreeBuf(aad);
+ FreeBuf(key);
+ Free(encrypted);
+ Free(decrypted);
+}
diff --git a/src/Mayaqua/Encrypt.h b/src/Mayaqua/Encrypt.h
index 1be31649..d5b334b7 100644
--- a/src/Mayaqua/Encrypt.h
+++ b/src/Mayaqua/Encrypt.h
@@ -105,6 +105,10 @@
#ifndef ENCRYPT_H
#define ENCRYPT_H
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+//#define USE_OPENSSL_AEAD_CHACHA20POLY1305
+#endif
+
// Function of OpenSSL
void RAND_Init_For_SoftEther();
void RAND_Free_For_SoftEther();
@@ -132,6 +136,12 @@ void RAND_Free_For_SoftEther();
#define AES_IV_SIZE 16 // AES IV size
#define AES_MAX_KEY_SIZE 32 // Maximum AES key size
+// RFC 8439: ChaCha20 and Poly1305 for IETF Protocols
+#define AEAD_CHACHA20_POLY1305_MAC_SIZE 16 // MAC size
+#define AEAD_CHACHA20_POLY1305_NONCE_SIZE 12 // Nonce size
+#define AEAD_CHACHA20_POLY1305_KEY_SIZE 32 // Key size
+
+
// IANA definitions taken from IKEv1 Phase 1
#define SHA1_160 2
#define SHA2_256 4
@@ -574,6 +584,22 @@ BUF *EasyDecrypt(BUF *src_buf);
void DisableIntelAesAccel();
+int GetSslClientCertIndex();
+
+void Aead_ChaCha20Poly1305_Ietf_Encrypt_Embedded(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size);
+bool Aead_ChaCha20Poly1305_Ietf_Decrypt_Embedded(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size);
+
+void Aead_ChaCha20Poly1305_Ietf_Encrypt_OpenSSL(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size);
+bool Aead_ChaCha20Poly1305_Ietf_Decrypt_OpenSSL(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size);
+
+void Aead_ChaCha20Poly1305_Ietf_Encrypt(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size);
+bool Aead_ChaCha20Poly1305_Ietf_Decrypt(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size);
+
+bool Aead_ChaCha20Poly1305_Ietf_IsOpenSSL();
+
+void Aead_ChaCha20Poly1305_Ietf_Test();
+
+
#ifdef ENCRYPT_C
// Inner function
diff --git a/src/Mayaqua/FileIO.c b/src/Mayaqua/FileIO.c
index 7d731404..f40d3b3a 100644
--- a/src/Mayaqua/FileIO.c
+++ b/src/Mayaqua/FileIO.c
@@ -1292,6 +1292,12 @@ void BuildHamcore(char *dst_filename, char *src_dir, bool unix_only)
}
}
+ if (InStr(rpath, "\\node_modules\\"))
+ {
+ // Exclude node_modules in the hamcore\webroot
+ ok = false;
+ }
+
if (ok)
{
b = ReadDump(s);
diff --git a/src/Mayaqua/Kernel.c b/src/Mayaqua/Kernel.c
index ddc9b748..28dd4b6e 100644
--- a/src/Mayaqua/Kernel.c
+++ b/src/Mayaqua/Kernel.c
@@ -1622,11 +1622,102 @@ void GetDateTimeStrMilli(char *str, UINT size, SYSTEMTIME *st)
st->wMilliseconds);
}
+// Convert string RFC3339 format (example: 2017-09-27T18:25:55.434-9:00) to UINT64
+UINT64 DateTimeStrRFC3339ToSystemTime64(char *str)
+{
+ SYSTEMTIME st;
+ if (DateTimeStrRFC3339ToSystemTime(&st, str))
+ {
+ return SystemToUINT64(&st);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// Convert string RFC3339 format (example: 2017-09-27T18:25:55.434-9:00) to SYSTEMTIME
+bool DateTimeStrRFC3339ToSystemTime(SYSTEMTIME *st, char *str)
+{
+ bool ok = false;
+ UINT index_plus;
+ char tmp[MAX_PATH];
+ Zero(st, sizeof(SYSTEMTIME));
+ if (st == NULL || str == NULL)
+ {
+ return false;
+ }
+
+ StrCpy(tmp, sizeof(tmp), str);
+
+ index_plus = SearchStrEx(tmp, "+", 0, false);
+ if (index_plus != INFINITE)
+ {
+ tmp[index_plus] = 0;
+ }
+
+ if (StrLen(tmp) >= 19)
+ {
+ if (tmp[4] == '-' && tmp[7] == '-' && tmp[10] == 'T' && tmp[13] == ':' &&
+ tmp[16] == ':')
+ {
+ char str_year[16], str_month[16], str_day[16], str_hour[16], str_minute[16],
+ str_second[16], str_msec[16];
+
+ StrCpy(str_year, sizeof(str_year), tmp + 0);
+ str_year[4] = 0;
+
+ StrCpy(str_month, sizeof(str_month), tmp + 5);
+ str_month[2] = 0;
+
+ StrCpy(str_day, sizeof(str_day), tmp + 8);
+ str_day[2] = 0;
+
+ StrCpy(str_hour, sizeof(str_hour), tmp + 11);
+ str_hour[2] = 0;
+
+ StrCpy(str_minute, sizeof(str_minute), tmp + 14);
+ str_minute[2] = 0;
+
+ StrCpy(str_second, sizeof(str_second), tmp + 17);
+ str_second[2] = 0;
+
+ str_msec[0] = 0;
+
+ if (StrLen(tmp) >= 21 && tmp[19] == '.')
+ {
+ StrCpy(str_msec, sizeof(str_msec), tmp + 20);
+ str_msec[StrLen(tmp) - 21] = 0;
+ while (StrLen(str_msec) < 3)
+ {
+ StrCat(str_msec, sizeof(str_msec), "0");
+ }
+ str_msec[3] = 0;
+ }
+
+ st->wYear = ToInt(str_year);
+ st->wMonth = ToInt(str_month);
+ st->wDay = ToInt(str_day);
+ st->wHour = ToInt(str_hour);
+ st->wMinute = ToInt(str_minute);
+ st->wSecond = ToInt(str_second);
+ st->wMilliseconds = ToInt(str_msec);
+
+ NormalizeSystem(st);
+
+ ok = true;
+ }
+ }
+
+ return ok;
+}
+
// Get the date and time string in RFC3339 format (example: 2017-09-27T18:25:55.434-9:00)
void GetDateTimeStrRFC3339(char *str, UINT size, SYSTEMTIME *st, int timezone_min){
// Validate arguments
if (str == NULL || st == NULL)
{
+ ClearStr(str, size);
return;
}
diff --git a/src/Mayaqua/Kernel.h b/src/Mayaqua/Kernel.h
index 5ba4a72c..35dd9679 100644
--- a/src/Mayaqua/Kernel.h
+++ b/src/Mayaqua/Kernel.h
@@ -240,6 +240,8 @@ void GetDateStrEx64(wchar_t *str, UINT size, UINT64 sec64, LOCALE *locale);
void GetTimeStrMilli64(char *str, UINT size, UINT64 sec64);
void GetTimeStr64(char *str, UINT size, UINT64 sec64);
void GetDateTimeStrRFC3339(char *str, UINT size, SYSTEMTIME *st, int timezone_min);
+bool DateTimeStrRFC3339ToSystemTime(SYSTEMTIME *st, char *str);
+UINT64 DateTimeStrRFC3339ToSystemTime64(char *str);
UINT64 SafeTime64(UINT64 sec64);
bool Run(char *filename, char *arg, bool hide, bool wait);
bool RunW(wchar_t *filename, wchar_t *arg, bool hide, bool wait);
diff --git a/src/Mayaqua/MayaType.h b/src/Mayaqua/MayaType.h
index 01810e7c..ae173617 100644
--- a/src/Mayaqua/MayaType.h
+++ b/src/Mayaqua/MayaType.h
@@ -136,7 +136,7 @@ typedef struct x509_crl_st X509_CRL;
#define BUF_SIZE 512
// Support Windows OS list
-#define SUPPORTED_WINDOWS_LIST "Windows 98 / 98 SE / ME / NT 4.0 SP6a / 2000 SP4 / XP SP2, SP3 / Vista SP1, SP2 / 7 SP1 / 8 / 8.1 / 10 / Server 2003 SP2 / Server 2008 SP1, SP2 / Hyper-V Server 2008 / Server 2008 R2 SP1 / Hyper-V Server 2008 R2 / Server 2012 / Hyper-V Server 2012 / Server 2012 R2 / Hyper-V Server 2012 R2 / Server 2016"
+#define SUPPORTED_WINDOWS_LIST "Windows 98 / 98 SE / ME / NT 4.0 SP6a / 2000 SP4 / XP SP2, SP3 / Vista SP1, SP2 / 7 SP1 / 8 / 8.1 / 10 / Server 2003 SP2 / Server 2008 SP1, SP2 / Hyper-V Server 2008 / Server 2008 R2 SP1 / Hyper-V Server 2008 R2 / Server 2012 / Hyper-V Server 2012 / Server 2012 R2 / Hyper-V Server 2012 R2 / Server 2016 / Server 2019"
// Infinite
#ifndef WINDOWS_H
@@ -421,6 +421,9 @@ typedef struct PRAND PRAND;
// Str.h
typedef struct TOKEN_LIST TOKEN_LIST;
typedef struct INI_ENTRY INI_ENTRY;
+typedef struct JSON_OBJECT JSON_OBJECT;
+typedef struct JSON_ARRAY JSON_ARRAY;
+typedef struct JSON_VALUE JSON_VALUE;
// Internat.h
typedef struct UNI_TOKEN_LIST UNI_TOKEN_LIST;
@@ -457,6 +460,8 @@ typedef struct INSTANCE INSTANCE;
typedef struct VALUE VALUE;
typedef struct ELEMENT ELEMENT;
typedef struct PACK PACK;
+typedef struct JSONPACKHINT JSONPACKHINT;
+typedef struct JSONPACKHINT_ITEM JSONPACKHINT_ITEM;
// Cfg.h
typedef struct FOLDER FOLDER;
diff --git a/src/Mayaqua/Memory.c b/src/Mayaqua/Memory.c
index f13d0e47..14bbcafb 100644
--- a/src/Mayaqua/Memory.c
+++ b/src/Mayaqua/Memory.c
@@ -1646,6 +1646,48 @@ bool ReplaceListPointer(LIST *o, void *oldptr, void *newptr)
return false;
}
+// New string list
+LIST *NewStrList()
+{
+ return NewListFast(CompareStr);
+}
+
+// Release string list
+void ReleaseStrList(LIST *o)
+{
+ UINT i;
+ if (o == NULL)
+ {
+ return;
+ }
+
+ for (i = 0;i < LIST_NUM(o);i++)
+ {
+ char *s = LIST_DATA(o, i);
+ Free(s);
+ }
+
+ ReleaseList(o);
+}
+
+// Add a string distinct to the string list
+bool AddStrToStrListDistinct(LIST *o, char *str)
+{
+ if (o == NULL || str == NULL)
+ {
+ return false;
+ }
+
+ if (IsInListStr(o, str) == false)
+ {
+ Add(o, CopyStr(str));
+
+ return true;
+ }
+
+ return false;
+}
+
// Examine whether a string items are present in the list
bool IsInListStr(LIST *o, char *str)
{
@@ -3361,6 +3403,43 @@ void WriteBufBuf(BUF *b, BUF *bb)
WriteBuf(b, bb->Buf, bb->Size);
}
+// Write the buffer (from the offset) to a buffer
+void WriteBufBufWithOffset(BUF *b, BUF *bb)
+{
+ // Validate arguments
+ if (b == NULL || bb == NULL)
+ {
+ return;
+ }
+
+ WriteBuf(b, ((UCHAR *)bb->Buf) + bb->Current, bb->Size - bb->Current);
+}
+
+// Skip UTF-8 BOM
+bool BufSkipUtf8Bom(BUF *b)
+{
+ if (b == NULL)
+ {
+ return false;
+ }
+
+ SeekBufToBegin(b);
+
+ if (b->Size >= 3)
+ {
+ UCHAR *data = b->Buf;
+
+ if (data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF)
+ {
+ SeekBuf(b, 3, 1);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
// Read into a buffer from the buffer
BUF *ReadBufFromBuf(BUF *b, UINT size)
{
@@ -4226,7 +4305,7 @@ void *InternalReAlloc(void *addr, UINT size)
}
#ifndef DONT_USE_KERNEL_STATUS
- TrackChangeObjSize((DWORD)addr, size, (DWORD)new_addr);
+ TrackChangeObjSize(POINTER_TO_UINT64(addr), size, POINTER_TO_UINT64(new_addr));
#endif // DONT_USE_KERNEL_STATUS
return new_addr;
diff --git a/src/Mayaqua/Memory.h b/src/Mayaqua/Memory.h
index 8b981aa9..04392e7e 100644
--- a/src/Mayaqua/Memory.h
+++ b/src/Mayaqua/Memory.h
@@ -305,7 +305,9 @@ BUF *NewBufFromMemory(void *buf, UINT size);
void ClearBuf(BUF *b);
void WriteBuf(BUF *b, void *buf, UINT size);
void WriteBufBuf(BUF *b, BUF *bb);
+void WriteBufBufWithOffset(BUF *b, BUF *bb);
UINT ReadBuf(BUF *b, void *buf, UINT size);
+bool BufSkipUtf8Bom(BUF *b);
BUF *ReadBufFromBuf(BUF *b, UINT size);
void AdjustBufSize(BUF *b, UINT new_size);
void SeekBuf(BUF *b, UINT offset, int mode);
@@ -469,5 +471,9 @@ void CleanupSharedBuffer(SHARED_BUFFER *b);
void AppendBufUtf8(BUF *b, wchar_t *str);
void AppendBufStr(BUF *b, char *str);
+LIST *NewStrList();
+void ReleaseStrList(LIST *o);
+bool AddStrToStrListDistinct(LIST *o, char *str);
+
#endif // MEMORY_H
diff --git a/src/Mayaqua/Microsoft.c b/src/Mayaqua/Microsoft.c
index 8d71f29c..b4e95a2a 100644
--- a/src/Mayaqua/Microsoft.c
+++ b/src/Mayaqua/Microsoft.c
@@ -3413,6 +3413,60 @@ HANDLE MsCreateUserToken()
}
+// Check whether SHA-2 kernel mode signature is supported
+bool MsIsSha2KernelModeSignatureSupported()
+{
+ HINSTANCE hDll;
+ bool ret = false;
+
+ if (MsIsWindows8())
+ {
+ return true;
+ }
+
+ hDll = LoadLibrary("Wintrust.dll");
+ if (hDll == NULL)
+ {
+ return false;
+ }
+
+ if (GetProcAddress(hDll, "CryptCATAdminAcquireContext2") != NULL)
+ {
+ ret = true;
+ }
+
+ FreeLibrary(hDll);
+
+ return ret;
+}
+
+// Check whether KB3033929 is required
+bool MsIsKB3033929RequiredAndMissing()
+{
+ OS_INFO *info = GetOsInfo();
+
+ if (info == NULL)
+ {
+ return false;
+ }
+
+ if (OS_IS_WINDOWS_NT(info->OsType))
+ {
+ if (GET_KETA(info->OsType, 100) == 6)
+ {
+ if (MsIsX64())
+ {
+ if (MsIsSha2KernelModeSignatureSupported() == false)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
// Check the digital signature of the file
bool MsCheckFileDigitalSignature(HWND hWnd, char *name, bool *danger)
{
diff --git a/src/Mayaqua/Microsoft.h b/src/Mayaqua/Microsoft.h
index c5d85d50..8046e95d 100644
--- a/src/Mayaqua/Microsoft.h
+++ b/src/Mayaqua/Microsoft.h
@@ -1017,6 +1017,9 @@ bool MsIsInfCatalogRequired();
bool MsCheckFileDigitalSignature(HWND hWnd, char *name, bool *danger);
bool MsCheckFileDigitalSignatureW(HWND hWnd, wchar_t *name, bool *danger);
+bool MsIsKB3033929RequiredAndMissing();
+bool MsIsSha2KernelModeSignatureSupported();
+
bool MsGetProcessExeName(char *path, UINT size, UINT id);
bool MsGetProcessExeNameW(wchar_t *path, UINT size, UINT id);
diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c
index f7768a57..ded99b77 100644
--- a/src/Mayaqua/Network.c
+++ b/src/Mayaqua/Network.c
@@ -2651,70 +2651,128 @@ void RUDPBulkSend(RUDP_STACK *r, RUDP_SESSION *se, void *data, UINT data_size)
UCHAR crypt_key_src[SHA1_SIZE * 2];
UCHAR crypt_key[SHA1_SIZE];
UINT icmp_type;
- UCHAR sign[SHA1_SIZE];
- UCHAR iv[SHA1_SIZE + 1];
// Validate arguments
if (r == NULL || se == NULL || (data == NULL && data_size != 0))
{
return;
}
- padding_size = Rand32() % 31 + 1;
+ if (se->BulkSendKey->Size == RUDP_BULK_KEY_SIZE_V2)
+ {
+ // Ver 2
+ UCHAR iv[RUDP_BULK_IV_SIZE_V2];
- buf_size = SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + padding_size;
- buf = Malloc(buf_size);
+ padding_size = Rand32() % 31 + 1;
- // SEQ NO
- WRITE_UINT64(buf + SHA1_SIZE + SHA1_SIZE, se->BulkNextSeqNo);
- se->BulkNextSeqNo++;
+ // Packet: IV + Encrypted(SEQ_NO + Data + padding) + MAC
+ buf_size = RUDP_BULK_IV_SIZE_V2 + sizeof(UINT64) + data_size + padding_size + RUDP_BULK_MAC_SIZE_V2;
+ buf = Malloc(buf_size);
- // Data
- Copy(buf + SHA1_SIZE + SHA1_SIZE + sizeof(UINT64), data, data_size);
+ // IV
+ Copy(iv, se->BulkNextIv_V2, RUDP_BULK_IV_SIZE_V2);
+ Copy(buf, iv, RUDP_BULK_IV_SIZE_V2);
- // Padding
- for (i = 0;i < padding_size;i++)
- {
- buf[SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + i] = (UCHAR)padding_size;
- }
+ // SEQ NO
+ WRITE_UINT64(buf + RUDP_BULK_IV_SIZE_V2, se->BulkNextSeqNo);
+ se->BulkNextSeqNo++;
- // Encryption
- Copy(iv, se->BulkNextIv, SHA1_SIZE);
- Copy(crypt_key_src + 0, se->BulkSendKey->Data, SHA1_SIZE);
- Copy(crypt_key_src + SHA1_SIZE, iv, SHA1_SIZE);
- HashSha1(crypt_key, crypt_key_src, SHA1_SIZE * 2);
- c = NewCrypt(crypt_key, sizeof(crypt_key));
- Encrypt(c, buf + SHA1_SIZE + SHA1_SIZE, buf + SHA1_SIZE + SHA1_SIZE, sizeof(UINT64) + data_size + padding_size);
- FreeCrypt(c);
+ // Data
+ Copy(buf + RUDP_BULK_IV_SIZE_V2 + sizeof(UINT64), data, data_size);
- // IV
- Copy(buf + SHA1_SIZE, iv, SHA1_SIZE);
+ // Padding
+ for (i = 0;i < padding_size;i++)
+ {
+ buf[RUDP_BULK_IV_SIZE_V2 + sizeof(UINT64) + data_size + i] = (UCHAR)padding_size;
+ }
- // Sign
- if (se->UseHMac == false)
- {
- Copy(buf + 0, se->BulkSendKey->Data, SHA1_SIZE);
- HashSha1(sign, buf, SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + padding_size);
- Copy(buf + 0, sign, SHA1_SIZE);
+ // Encryption
+ Aead_ChaCha20Poly1305_Ietf_Encrypt(buf + RUDP_BULK_IV_SIZE_V2,
+ buf + RUDP_BULK_IV_SIZE_V2,
+ sizeof(UINT64) + data_size + padding_size,
+ se->BulkSendKey->Data,
+ iv,
+ NULL,
+ 0);
+
+ // Next IV
+ Copy(se->BulkNextIv_V2, buf + RUDP_BULK_IV_SIZE_V2 + sizeof(UINT64) + data_size + padding_size - RUDP_BULK_IV_SIZE_V2,
+ RUDP_BULK_IV_SIZE_V2);
+
+ if (r->Protocol == RUDP_PROTOCOL_ICMP)
+ {
+ icmp_type = se->Icmp_Type;
+ }
+ else if (r->Protocol == RUDP_PROTOCOL_DNS)
+ {
+ icmp_type = se->Dns_TranId;
+ }
+ RUDPSendPacket(r, &se->YourIp, se->YourPort, buf, buf_size, icmp_type);
+
+ Free(buf);
}
else
{
- HMacSha1(buf + 0, se->BulkSendKey->Data, SHA1_SIZE, buf + SHA1_SIZE, SHA1_SIZE + sizeof(UINT64) + data_size + padding_size);
- }
+ UCHAR sign[SHA1_SIZE];
+ UCHAR iv[SHA1_SIZE];
- // Next IV
- Copy(se->BulkNextIv, buf + buf_size - SHA1_SIZE, SHA1_SIZE);
+ // Ver 1
+ padding_size = Rand32() % 31 + 1;
- if (r->Protocol == RUDP_PROTOCOL_ICMP)
- {
- icmp_type = se->Icmp_Type;
- }
- else if (r->Protocol == RUDP_PROTOCOL_DNS)
- {
- icmp_type = se->Dns_TranId;
- }
- RUDPSendPacket(r, &se->YourIp, se->YourPort, buf, buf_size, icmp_type);
+ buf_size = SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + padding_size;
+ buf = Malloc(buf_size);
- Free(buf);
+ // SEQ NO
+ WRITE_UINT64(buf + SHA1_SIZE + SHA1_SIZE, se->BulkNextSeqNo);
+ se->BulkNextSeqNo++;
+
+ // Data
+ Copy(buf + SHA1_SIZE + SHA1_SIZE + sizeof(UINT64), data, data_size);
+
+ // Padding
+ for (i = 0;i < padding_size;i++)
+ {
+ buf[SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + i] = (UCHAR)padding_size;
+ }
+
+ // Encryption
+ Copy(iv, se->BulkNextIv, SHA1_SIZE);
+ Copy(crypt_key_src + 0, se->BulkSendKey->Data, SHA1_SIZE);
+ Copy(crypt_key_src + SHA1_SIZE, iv, SHA1_SIZE);
+ HashSha1(crypt_key, crypt_key_src, SHA1_SIZE * 2);
+ c = NewCrypt(crypt_key, sizeof(crypt_key));
+ Encrypt(c, buf + SHA1_SIZE + SHA1_SIZE, buf + SHA1_SIZE + SHA1_SIZE, sizeof(UINT64) + data_size + padding_size);
+ FreeCrypt(c);
+
+ // IV
+ Copy(buf + SHA1_SIZE, iv, SHA1_SIZE);
+
+ // Sign
+ if (se->UseHMac == false)
+ {
+ Copy(buf + 0, se->BulkSendKey->Data, SHA1_SIZE);
+ HashSha1(sign, buf, SHA1_SIZE + SHA1_SIZE + sizeof(UINT64) + data_size + padding_size);
+ Copy(buf + 0, sign, SHA1_SIZE);
+ }
+ else
+ {
+ HMacSha1(buf + 0, se->BulkSendKey->Data, SHA1_SIZE, buf + SHA1_SIZE, SHA1_SIZE + sizeof(UINT64) + data_size + padding_size);
+ }
+
+ // Next IV
+ Copy(se->BulkNextIv, buf + buf_size - SHA1_SIZE, SHA1_SIZE);
+
+ if (r->Protocol == RUDP_PROTOCOL_ICMP)
+ {
+ icmp_type = se->Icmp_Type;
+ }
+ else if (r->Protocol == RUDP_PROTOCOL_DNS)
+ {
+ icmp_type = se->Dns_TranId;
+ }
+ RUDPSendPacket(r, &se->YourIp, se->YourPort, buf, buf_size, icmp_type);
+
+ Free(buf);
+ }
}
// Start a socket for R-UDP Listening
@@ -2775,14 +2833,20 @@ SOCK *AcceptRUDP(SOCK *s)
{
case RUDP_PROTOCOL_UDP:
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NAT_T);
+ AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails),
+ "RUDP/UDP");
break;
case RUDP_PROTOCOL_DNS:
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_DNS);
+ AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails),
+ "RUDP/DNS");
break;
case RUDP_PROTOCOL_ICMP:
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_ICMP);
+ AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails),
+ "RUDP/ICMP");
break;
}
@@ -2835,24 +2899,53 @@ bool RUDPCheckSignOfRecvPacket(RUDP_STACK *r, RUDP_SESSION *se, void *recv_data,
}
// Verification signature (bulk packet)
- if (se->UseHMac == false)
+ if (se->BulkRecvKey->Size == RUDP_BULK_KEY_SIZE_V2)
{
- Copy(sign, p, SHA1_SIZE);
- Copy(p, se->BulkRecvKey->Data, SHA1_SIZE);
- HashSha1(sign2, p, recv_size);
- Copy(p, sign, SHA1_SIZE);
+ // Ver 2
+ UCHAR *iv = p;
+ // Packet: IV + Encrypted(SEQ_NO + Data + padding) + MAC
+ // IV
+ if (size < RUDP_BULK_IV_SIZE_V2)
+ {
+ return false;
+ }
+ iv = p;
+ p += RUDP_BULK_IV_SIZE_V2;
+ size -= RUDP_BULK_IV_SIZE_V2;
- if (Cmp(sign, sign2, SHA1_SIZE) == 0)
+ // Decrypt
+ if (size < (RUDP_BULK_MAC_SIZE_V2 + 1))
{
- return true;
+ return false;
}
+ if (Aead_ChaCha20Poly1305_Ietf_Decrypt(r->TmpBuf, p, size, se->BulkRecvKey->Data, iv, NULL, 0) == false)
+ {
+ return false;
+ }
+ return true;
}
-
- HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, size - SHA1_SIZE);
- if (Cmp(p, sign2, SHA1_SIZE) == 0)
+ else
{
- se->UseHMac = true;
- return true;
+ // Ver 1
+ if (se->UseHMac == false)
+ {
+ Copy(sign, p, SHA1_SIZE);
+ Copy(p, se->BulkRecvKey->Data, SHA1_SIZE);
+ HashSha1(sign2, p, recv_size);
+ Copy(p, sign, SHA1_SIZE);
+
+ if (Cmp(sign, sign2, SHA1_SIZE) == 0)
+ {
+ return true;
+ }
+ }
+
+ HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, size - SHA1_SIZE);
+ if (Cmp(p, sign2, SHA1_SIZE) == 0)
+ {
+ se->UseHMac = true;
+ return true;
+ }
}
return false;
@@ -2886,77 +2979,122 @@ bool RUDPProcessBulkRecvPacket(RUDP_STACK *r, RUDP_SESSION *se, void *recv_data,
return false;
}
- // Validate the signature
- if (se->UseHMac == false)
+ if (se->BulkRecvKey->Size == RUDP_BULK_KEY_SIZE_V2)
{
- Copy(sign, p, SHA1_SIZE);
- Copy(p, se->BulkRecvKey->Data, SHA1_SIZE);
- HashSha1(sign2, p, recv_size);
- Copy(p, sign, SHA1_SIZE);
+ // Ver 2
+ // Packet: IV + Encrypted(SEQ_NO + Data + padding) + MAC
+ // IV
+ if (size < RUDP_BULK_IV_SIZE_V2)
+ {
+ WHERE;
+ return false;
+ }
+ iv = p;
+ p += RUDP_BULK_IV_SIZE_V2;
+ size -= RUDP_BULK_IV_SIZE_V2;
- if (Cmp(sign, sign2, SHA1_SIZE) != 0)
+ // Decrypt
+ if (size < (RUDP_BULK_MAC_SIZE_V2 + 1))
{
- HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, recv_size - SHA1_SIZE);
+ WHERE;
+ return false;
+ }
+ if (Aead_ChaCha20Poly1305_Ietf_Decrypt(p, p, size, se->BulkRecvKey->Data, iv, NULL, 0) == false)
+ {
+ WHERE;
+ return false;
+ }
- if (Cmp(p, sign2, SHA1_SIZE) != 0)
+ size -= RUDP_BULK_MAC_SIZE_V2;
+
+ // padlen
+ padlen = p[size - 1];
+ if (padlen == 0)
+ {
+ WHERE;
+ return false;
+ }
+ if (size < padlen)
+ {
+ WHERE;
+ return false;
+ }
+ size -= padlen;
+ }
+ else
+ {
+ // Validate the signature
+ if (se->UseHMac == false)
+ {
+ Copy(sign, p, SHA1_SIZE);
+ Copy(p, se->BulkRecvKey->Data, SHA1_SIZE);
+ HashSha1(sign2, p, recv_size);
+ Copy(p, sign, SHA1_SIZE);
+
+ if (Cmp(sign, sign2, SHA1_SIZE) != 0)
{
- return false;
+ HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, recv_size - SHA1_SIZE);
+
+ if (Cmp(p, sign2, SHA1_SIZE) != 0)
+ {
+ return false;
+ }
+ else
+ {
+ se->UseHMac = true;
+ }
}
else
{
- se->UseHMac = true;
}
}
else
{
+ HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, recv_size - SHA1_SIZE);
+
+ if (Cmp(p, sign2, SHA1_SIZE) != 0)
+ {
+ return false;
+ }
}
- }
- else
- {
- HMacSha1(sign2, se->BulkRecvKey->Data, SHA1_SIZE, p + SHA1_SIZE, recv_size - SHA1_SIZE);
- if (Cmp(p, sign2, SHA1_SIZE) != 0)
+ p += SHA1_SIZE;
+ size -= SHA1_SIZE;
+
+ // IV
+ if (size < SHA1_SIZE)
{
return false;
}
- }
-
- p += SHA1_SIZE;
- size -= SHA1_SIZE;
-
- // IV
- if (size < SHA1_SIZE)
- {
- return false;
- }
- iv = p;
- p += SHA1_SIZE;
- size -= SHA1_SIZE;
+ iv = p;
+ p += SHA1_SIZE;
+ size -= SHA1_SIZE;
- // Decrypt
- if (size < 1)
- {
- return false;
- }
- Copy(keygen + 0, se->BulkRecvKey->Data, SHA1_SIZE);
- Copy(keygen + SHA1_SIZE, iv, SHA1_SIZE);
- HashSha1(key, keygen, sizeof(keygen));
+ // Decrypt
+ if (size < 1)
+ {
+ return false;
+ }
+ Copy(keygen + 0, se->BulkRecvKey->Data, SHA1_SIZE);
+ Copy(keygen + SHA1_SIZE, iv, SHA1_SIZE);
+ HashSha1(key, keygen, sizeof(keygen));
- c = NewCrypt(key, sizeof(key));
- Encrypt(c, p, p, size);
- FreeCrypt(c);
+ c = NewCrypt(key, sizeof(key));
+ Encrypt(c, p, p, size);
+ FreeCrypt(c);
- // padlen
- padlen = p[size - 1];
- if (padlen == 0)
- {
- return false;
- }
- if (size < padlen)
- {
- return false;
+ // padlen
+ padlen = p[size - 1];
+ if (padlen == 0)
+ {
+ return false;
+ }
+ if (size < padlen)
+ {
+ return false;
+ }
+ size -= padlen;
}
- size -= padlen;
// SEQ NO
seq_no = READ_UINT64(p);
@@ -3769,8 +3907,8 @@ RUDP_SESSION *RUDPNewSession(bool server_mode, IP *my_ip, UINT my_port, IP *your
RUDP_SESSION *se;
UCHAR key1[SHA1_SIZE];
UCHAR key2[SHA1_SIZE];
- UCHAR bulk_send_key[SHA1_SIZE];
- UCHAR bulk_recv_key[SHA1_SIZE];
+ UCHAR bulk_send_key[RUDP_BULK_KEY_SIZE_MAX];
+ UCHAR bulk_recv_key[RUDP_BULK_KEY_SIZE_MAX];
BUF *b;
se = ZeroMalloc(sizeof(RUDP_SESSION));
@@ -3856,6 +3994,8 @@ RUDP_SESSION *RUDPNewSession(bool server_mode, IP *my_ip, UINT my_port, IP *your
se->BulkRecvKey = NewSharedBuffer(bulk_recv_key, sizeof(bulk_recv_key));
Rand(se->BulkNextIv, sizeof(se->BulkNextIv));
+ Rand(se->BulkNextIv_V2, sizeof(se->BulkNextIv_V2));
+
se->BulkNextSeqNo = 1;
return se;
@@ -5806,9 +5946,54 @@ int cb_test(int a, X509_STORE_CTX *ctx)
return 1;
}
+// Verify client SSL certificate during TLS handshake.
+//
+// (actually, only save the certificate for later authentication in Protocol.c)
+int SslCertVerifyCallback(int preverify_ok, X509_STORE_CTX *ctx)
+{
+ SSL *ssl;
+ struct SslClientCertInfo *clientcert;
+
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ clientcert = SSL_get_ex_data(ssl, GetSslClientCertIndex());
+
+ if (clientcert != NULL)
+ {
+ clientcert->PreverifyErr = 0;
+ clientcert->PreverifyErrMessage[0] = '\0';
+ if (!preverify_ok)
+ {
+ char *msg;
+ clientcert->PreverifyErr = X509_STORE_CTX_get_error(ctx);
+ msg = (char *)X509_verify_cert_error_string(clientcert->PreverifyErr);
+ StrCpy(clientcert->PreverifyErrMessage, PREVERIFY_ERR_MESSAGE_SIZE, msg);
+ Debug("SslCertVerifyCallback preverify error: '%s'\n", msg);
+ }
+ else
+ {
+ if (ctx->cert != NULL)
+ {
+ X *tmpX = X509ToX(ctx->cert); // this only wraps ctx->cert, but we need to make a copy
+ X *copyX = CloneX(tmpX);
+ tmpX->do_not_free = true; // do not release inner X509 object
+ FreeX(tmpX);
+ clientcert->X = copyX;
+ }
+ }
+ }
+
+ return 1; /* allow the verification process to continue */
+}
+
// Create a new SSL pipe
SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh)
{
+ return NewSslPipeEx(server_mode, x, k, dh, false, NULL);
+}
+
+// Create a new SSL pipe with extended options
+SSL_PIPE *NewSslPipeEx(bool server_mode, X *x, K *k, DH_CTX *dh, bool verify_peer, struct SslClientCertInfo *clientcert)
+{
SSL_PIPE *s;
SSL *ssl;
SSL_CTX *ssl_ctx = NewSSLCtx(server_mode);
@@ -5832,7 +6017,10 @@ SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh)
SSL_CTX_set_ssl_version(ssl_ctx, SSLv23_client_method());
}
- //SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, cb_test);
+ if (verify_peer)
+ {
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, SslCertVerifyCallback);
+ }
if (dh != NULL)
{
@@ -5845,6 +6033,8 @@ SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh)
}
ssl = SSL_new(ssl_ctx);
+
+ SSL_set_ex_data(ssl, GetSslClientCertIndex(), clientcert);
}
Unlock(openssl_lock);
@@ -12654,6 +12844,50 @@ void InitSockSet(SOCKSET *set)
Zero(set, sizeof(SOCKSET));
}
+// Receive data and discard all of them
+bool RecvAllWithDiscard(SOCK *sock, UINT size, bool secure)
+{
+ static UCHAR buffer[4096];
+ UINT recv_size, sz, ret;
+ if (sock == NULL)
+ {
+ return false;
+ }
+ if (size == 0)
+ {
+ return true;
+ }
+ if (sock->AsyncMode)
+ {
+ return false;
+ }
+
+ recv_size = 0;
+
+ while (true)
+ {
+ sz = MIN(size - recv_size, sizeof(buffer));
+ ret = Recv(sock, buffer, sz, secure);
+ if (ret == 0)
+ {
+ return false;
+ }
+ if (ret == SOCK_LATER)
+ {
+ // I suppose that this is safe because the RecvAll() function is used only
+ // if the sock->AsyncMode == true. And the Recv() function may return
+ // SOCK_LATER only if the sock->AsyncMode == false. Therefore the call of
+ // Recv() function in the RecvAll() function never returns SOCK_LATER.
+ return false;
+ }
+ recv_size += ret;
+ if (recv_size >= size)
+ {
+ return true;
+ }
+ }
+}
+
// Receive all by TCP
bool RecvAll(SOCK *sock, void *data, UINT size, bool secure)
{
@@ -14024,6 +14258,8 @@ SOCK *Accept(SOCK *sock)
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NATIVE_V4);
+ AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "IPv4");
+
return ret;
}
@@ -14134,6 +14370,8 @@ SOCK *Accept6(SOCK *sock)
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_NATIVE_V6);
+ AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "IPv6");
+
return ret;
}
@@ -15372,6 +15610,8 @@ SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
if (nat_t_sock != NULL)
{
StrCpy(nat_t_sock->UnderlayProtocol, sizeof(nat_t_sock->UnderlayProtocol), SOCK_UNDERLAY_NAT_T);
+ AddProtocolDetailsStr(nat_t_sock->ProtocolDetails, sizeof(nat_t_sock->ProtocolDetails),
+ "RUDP");
}
Copy(ret_ip, &ip4, sizeof(IP));
@@ -15599,6 +15839,8 @@ SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
StrCpy(p2.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p2.Result_Nat_T_Sock->UnderlayProtocol),
SOCK_UNDERLAY_NAT_T);
+ AddProtocolDetailsStr(p2.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p2.Result_Nat_T_Sock->UnderlayProtocol),
+ "RUDP/UDP");
Copy(ret_ip, &ip4, sizeof(IP));
@@ -15613,6 +15855,8 @@ SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
StrCpy(p4.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p4.Result_Nat_T_Sock->UnderlayProtocol),
SOCK_UNDERLAY_DNS);
+ AddProtocolDetailsStr(p4.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p4.Result_Nat_T_Sock->UnderlayProtocol),
+ "RUDP/DNS");
Copy(ret_ip, &ip4, sizeof(IP));
@@ -15623,6 +15867,8 @@ SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
// Use this if over ICMP success
StrCpy(p3.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p3.Result_Nat_T_Sock->UnderlayProtocol),
SOCK_UNDERLAY_ICMP);
+ AddProtocolDetailsStr(p3.Result_Nat_T_Sock->UnderlayProtocol, sizeof(p3.Result_Nat_T_Sock->UnderlayProtocol),
+ "RUDP/ICMP");
Copy(ret_ip, &ip4, sizeof(IP));
@@ -15689,6 +15935,9 @@ SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
StrCpy(sock->UnderlayProtocol, sizeof(sock->UnderlayProtocol),
(is_ipv6 ? SOCK_UNDERLAY_NATIVE_V6 : SOCK_UNDERLAY_NATIVE_V4));
+ AddProtocolDetailsStr(sock->ProtocolDetails, sizeof(sock->ProtocolDetails),
+ is_ipv6 ? "IPv6" : "IPv4");
+
// Host name resolution
if (no_get_hostname || (GetHostName(tmp, sizeof(tmp), &current_ip) == false))
{
@@ -15737,6 +15986,75 @@ SOCK *ConnectEx4(char *hostname, UINT port, UINT timeout, bool *cancel_flag, cha
return sock;
}
+// Get the current accepting IPv4 address
+void TryGetCurrentAcceptingIPv4Address(IP *ip)
+{
+ SOCK *s = ConnectEx(UDP_NAT_T_GET_PRIVATE_IP_TCP_SERVER, 80, 2000);
+
+ if (s != NULL)
+ {
+ Disconnect(s);
+ ReleaseSock(s);
+ }
+
+ if (GetCurrentGlobalIP(ip, false))
+ {
+ return;
+ }
+
+ GetCurrentGlobalIPGuess(ip, false);
+}
+
+// Add a protocol details strings
+void AddProtocolDetailsStr(char *dst, UINT dst_size, char *str)
+{
+ TOKEN_LIST *t1, *t2;
+ UINT i, j;
+ if (dst == NULL || str == NULL)
+ {
+ return;
+ }
+
+ t1 = ParseTokenWithoutNullStr(dst, " ");
+ t2 = ParseTokenWithoutNullStr(str, " ");
+
+ for (i = 0;i < t2->NumTokens;i++)
+ {
+ bool exists = false;
+ for (j = 0;j < t1->NumTokens;j++)
+ {
+ if (StrCmpi(t1->Token[j], t2->Token[i]) == 0)
+ {
+ exists = true;
+ break;
+ }
+ }
+
+ if (exists == false)
+ {
+ StrCat(dst, dst_size, t2->Token[i]);
+ StrCat(dst, dst_size, " ");
+ }
+ }
+
+ FreeToken(t1);
+ FreeToken(t2);
+}
+void AddProtocolDetailsKeyValueStr(char *dst, UINT dst_size, char *key, char *value)
+{
+ char tmp[128];
+ StrCpy(tmp, sizeof(tmp), key);
+ StrCat(tmp, sizeof(tmp), "=");
+ StrCat(tmp, sizeof(tmp), value);
+ AddProtocolDetailsStr(dst, dst_size, tmp);
+}
+void AddProtocolDetailsKeyValueInt(char *dst, UINT dst_size, char *key, UINT value)
+{
+ char tmp[128];
+ ToStr(tmp, value);
+ AddProtocolDetailsKeyValueStr(dst, dst_size, key, tmp);
+}
+
// Maximize the I/O buffer size of the socket
void SetSocketSendRecvBufferSize(SOCKET s, UINT size)
{
@@ -21029,6 +21347,8 @@ SOCK *AcceptReverse(SOCK *s)
{
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_AZURE);
+ AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "VPNAzure");
+
return ret;
}
@@ -21077,6 +21397,8 @@ SOCK *AcceptInProc(SOCK *s)
{
StrCpy(ret->UnderlayProtocol, sizeof(ret->UnderlayProtocol), SOCK_UNDERLAY_INPROC);
+ AddProtocolDetailsStr(ret->ProtocolDetails, sizeof(ret->ProtocolDetails), "InProc");
+
return ret;
}
@@ -21526,6 +21848,10 @@ void FlushTubeFlushList(TUBE_FLUSH_LIST *f)
// The server receives a PACK from the client
PACK *HttpServerRecv(SOCK *s)
{
+ return HttpServerRecvEx(s, 0);
+}
+PACK *HttpServerRecvEx(SOCK *s, UINT max_data_size)
+{
BUF *b;
PACK *p;
HTTP_HEADER *h;
@@ -21533,6 +21859,7 @@ PACK *HttpServerRecv(SOCK *s)
UCHAR *tmp;
HTTP_VALUE *v;
UINT num_noop = 0;
+ if (max_data_size == 0) max_data_size = HTTP_PACK_MAX_SIZE;
// Validate arguments
if (s == NULL)
{
@@ -21563,7 +21890,7 @@ START:
}
size = GetContentLength(h);
- if (size == 0 || size > HTTP_PACK_MAX_SIZE)
+ if (size == 0 || (size > max_data_size))
{
FreeHttpHeader(h);
goto BAD_REQUEST;
@@ -21924,6 +22251,35 @@ bool HttpSendNotImplemented(SOCK *s, char *method, char *target, char *version)
return ret;
}
+// Sending a HTTP body contents
+bool HttpSendBody(SOCK *s, void *data, UINT size, char *contents_type)
+{
+ HTTP_HEADER *h;
+ char date_str[MAX_SIZE];
+ bool ret;
+ if (s == NULL || (size != 0 && data == NULL))
+ {
+ return false;
+ }
+ if (contents_type == NULL)
+ {
+ contents_type = "application/octet-stream";
+ }
+ // Creating a header
+ h = NewHttpHeader("HTTP/1.1", "200", "OK");
+
+ GetHttpDateStr(date_str, sizeof(date_str), SystemTime64());
+ AddHttpValue(h, NewHttpValue("Date", date_str));
+ AddHttpValue(h, NewHttpValue("Content-Type", contents_type));
+ AddHttpValue(h, NewHttpValue("Cache-Control", "no-cache"));
+
+ ret = PostHttp(s, h, data, size);
+
+ FreeHttpHeader(h);
+
+ return ret;
+}
+
// Sending a 404 Not Found error
bool HttpSendNotFound(SOCK *s, char *target)
{
@@ -22321,11 +22677,6 @@ HTTP_HEADER *RecvHttpHeader(SOCK *s)
// The colon does not exist
goto LABEL_ERROR;
}
- if ((pos + 1) >= StrLen(str))
- {
- // There is no data
- goto LABEL_ERROR;
- }
// Divide into the name and the data
value_name = Malloc(pos + 1);
diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h
index 14ac23d8..ad18ffea 100644
--- a/src/Mayaqua/Network.h
+++ b/src/Mayaqua/Network.h
@@ -306,6 +306,7 @@ struct SOCK
UINT CurrentTtl; // Current TTL value
RUDP_STACK *R_UDP_Stack; // R-UDP stack
char UnderlayProtocol[64]; // Underlying protocol
+ char ProtocolDetails[256]; // Protocol Details
QUEUE *ReverseAcceptQueue; // Accept queue for the reverse socket
EVENT *ReverseAcceptEvent; // Accept event for the reverse socket
bool IsReverseAcceptedSocket; // Whether it is a reverse socket
@@ -660,6 +661,12 @@ struct IPBLOCK
#define RUDP_TIMEOUT 12000 // Time-out of R-UDP communication
#define RUDP_DIRECT_CONNECT_TIMEOUT 5000 // R-UDP direct connection time-out
#define RUDP_MAX_SEGMENT_SIZE 512 // Maximum segment size
+#define RUDP_BULK_KEY_SIZE_MAX 128 // Bulk key size Max
+
+#define RUDP_BULK_KEY_SIZE_V2 32 // V2: Bulk key size
+#define RUDP_BULK_IV_SIZE_V2 12 // V2: Bulk IV size
+#define RUDP_BULK_MAC_SIZE_V2 16 // V2: Bulk MAC size
+
// Maximum R-UDP packet size
#define RUDP_MAX_PACKET_SIZE (RUDP_MAX_SEGMENT_SIZE + sizeof(UINT64) * RUDP_MAX_NUM_ACK + SHA1_SIZE * 2 + sizeof(UINT64) * 4 + sizeof(UINT) + 255)
#define RUDP_MAX_NUM_ACK 64 // Maximum number of ACKs
@@ -748,6 +755,7 @@ struct RUDP_SESSION
UINT64 BulkNextSeqNo; // Next SEQ NO to the bulk send
bool FlushBulkSendTube; // Flag to be Flush the bulk send Tube
UINT64 BulkRecvSeqNoMax; // Highest sequence number received
+ UCHAR BulkNextIv_V2[RUDP_BULK_IV_SIZE_V2]; // Next IV to the bulk send (Ver 2)
};
// NAT Traversal Server Information
@@ -1045,11 +1053,13 @@ UINT GetContentLength(HTTP_HEADER *header);
void GetHttpDateStr(char *str, UINT size, UINT64 t);
bool HttpSendForbidden(SOCK *s, char *target, char *server_id);
bool HttpSendNotFound(SOCK *s, char *target);
+bool HttpSendBody(SOCK *s, void *data, UINT size, char *contents_type);
bool HttpSendNotImplemented(SOCK *s, char *method, char *target, char *version);
bool HttpSendInvalidHostname(SOCK *s, char *method);
bool HttpServerSend(SOCK *s, PACK *p);
bool HttpClientSend(SOCK *s, PACK *p);
PACK *HttpServerRecv(SOCK *s);
+PACK *HttpServerRecvEx(SOCK *s, UINT max_data_size);
PACK *HttpClientRecv(SOCK *s);
bool HttpSendServerError(SOCK *s, char *target);
@@ -1307,7 +1317,7 @@ bool SendAll(SOCK *sock, void *data, UINT size, bool secure);
void SendAdd(SOCK *sock, void *data, UINT size);
bool SendNow(SOCK *sock, int secure);
bool RecvAll(SOCK *sock, void *data, UINT size, bool secure);
-bool RecvAllEx(SOCK *sock, void **data_new_ptr, UINT size, bool secure);
+bool RecvAllWithDiscard(SOCK *sock, UINT size, bool secure);
void InitSockSet(SOCKSET *set);
void AddSockSet(SOCKSET *set, SOCK *sock);
CANCEL *NewCancel();
@@ -1433,6 +1443,10 @@ void DebugPrintRoute(ROUTE_ENTRY *e);
void DebugPrintRouteTable(ROUTE_TABLE *r);
bool IsIPv6LocalNetworkAddress(IP *ip);
UINT GetNumWaitThread();
+void AddProtocolDetailsStr(char *dst, UINT dst_size, char *str);
+void AddProtocolDetailsKeyValueStr(char *dst, UINT dst_size, char *key, char *value);
+void AddProtocolDetailsKeyValueInt(char *dst, UINT dst_size, char *key, UINT value);
+void TryGetCurrentAcceptingIPv4Address(IP *ip);
#ifdef ENABLE_SSL_LOGGING
void SockEnableSslLogging(SOCK *s);
@@ -1605,7 +1619,16 @@ void Win32WaitForTubes(TUBE **tubes, UINT num, UINT timeout);
void UnixWaitForTubes(TUBE **tubes, UINT num, UINT timeout);
#endif // OS_WIN32
+#define PREVERIFY_ERR_MESSAGE_SIZE 100
+// Info on client certificate collected during TLS handshake
+struct SslClientCertInfo {
+ int PreverifyErr;
+ char PreverifyErrMessage[PREVERIFY_ERR_MESSAGE_SIZE];
+ X *X;
+};
+
SSL_PIPE *NewSslPipe(bool server_mode, X *x, K *k, DH_CTX *dh);
+SSL_PIPE *NewSslPipeEx(bool server_mode, X *x, K *k, DH_CTX *dh, bool verify_peer, struct SslClientCertInfo *clientcert);
void FreeSslPipe(SSL_PIPE *s);
bool SyncSslPipe(SSL_PIPE *s);
diff --git a/src/Mayaqua/Pack.c b/src/Mayaqua/Pack.c
index 881ada2e..42072bc0 100644
--- a/src/Mayaqua/Pack.c
+++ b/src/Mayaqua/Pack.c
@@ -741,13 +741,13 @@ ELEMENT *NewElement(char *name, UINT type, UINT num_value, VALUE **values)
}
// Memory allocation
- e = Malloc(sizeof(ELEMENT));
+ e = ZeroMalloc(sizeof(ELEMENT));
StrCpy(e->name, sizeof(e->name), name);
e->num_value = num_value;
e->type = type;
// Copy of the pointer list to the element
- e->values = (VALUE **)Malloc(sizeof(VALUE *) * num_value);
+ e->values = (VALUE **)ZeroMalloc(sizeof(VALUE *) * num_value);
for (i = 0;i < e->num_value;i++)
{
e->values[i] = values[i];
@@ -864,6 +864,10 @@ bool AddElement(PACK *p, ELEMENT *e)
// Adding
Add(p->elements, e);
+
+ // Set JsonHint_GroupName
+ StrCpy(e->JsonHint_GroupName, sizeof(e->JsonHint_GroupName), p->CurrentJsonHint_GroupName);
+
return true;
}
@@ -885,6 +889,11 @@ void FreePack(PACK *p)
}
Free(elements);
+ if (p->json_subitem_names != NULL)
+ {
+ FreeStrList(p->json_subitem_names);
+ }
+
ReleaseList(p->elements);
Free(p);
}
@@ -895,7 +904,7 @@ PACK *NewPack()
PACK *p;
// Memory allocation
- p = MallocEx(sizeof(PACK), true);
+ p = ZeroMallocEx(sizeof(PACK), true);
// Creating a List
p->elements = NewListFast(ComparePackName);
@@ -921,6 +930,12 @@ K *PackGetK(PACK *p, char *name)
}
k = BufToK(b, true, false, NULL);
+
+ if (k == NULL)
+ {
+ k = BufToK(b, true, true, NULL);
+ }
+
FreeBuf(b);
return k;
@@ -944,49 +959,61 @@ X *PackGetX(PACK *p, char *name)
}
x = BufToX(b, false);
+
+ if (x == NULL)
+ {
+ x = BufToX(b, true);
+ }
+
FreeBuf(b);
return x;
}
// Add the K to the PACK
-void PackAddK(PACK *p, char *name, K *k)
+ELEMENT *PackAddK(PACK *p, char *name, K *k)
{
BUF *b;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || k == NULL)
{
- return;
+ return NULL;
}
b = KToBuf(k, false, NULL);
if (b == NULL)
{
- return;
+ return NULL;
}
- PackAddBuf(p, name, b);
+ e = PackAddBuf(p, name, b);
FreeBuf(b);
+
+ return e;
}
// Add an X into the PACK
-void PackAddX(PACK *p, char *name, X *x)
+ELEMENT *PackAddX(PACK *p, char *name, X *x)
{
BUF *b;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || x == NULL)
{
- return;
+ return NULL;
}
b = XToBuf(x, false);
if (b == NULL)
{
- return;
+ return NULL;
}
- PackAddBuf(p, name, b);
+ e = PackAddBuf(p, name, b);
FreeBuf(b);
+
+ return e;
}
// Get a buffer from the PACK
@@ -1149,30 +1176,65 @@ bool PackGetBoolEx(PACK *p, char *name, UINT index)
return PackGetIntEx(p, name, index) == 0 ? false : true;
}
+// Set CurrentJsonHint_GroupName to PACK
+void PackSetCurrentJsonGroupName(PACK *p, char *json_group_name)
+{
+ if (p == NULL)
+ {
+ return;
+ }
+
+ if (json_group_name == NULL)
+ {
+ ClearStr(p->CurrentJsonHint_GroupName, sizeof(p->CurrentJsonHint_GroupName));
+ }
+ else
+ {
+ StrCpy(p->CurrentJsonHint_GroupName, sizeof(p->CurrentJsonHint_GroupName), json_group_name);
+
+ if (p->json_subitem_names == NULL)
+ {
+ p->json_subitem_names = NewStrList();
+ }
+
+ AddStrToStrListDistinct(p->json_subitem_names, json_group_name);
+ }
+}
+
// Add a bool type into the PACK
-void PackAddBool(PACK *p, char *name, bool b)
+ELEMENT *PackAddBool(PACK *p, char *name, bool b)
{
- PackAddInt(p, name, b ? 1 : 0);
+ ELEMENT *e = PackAddInt(p, name, b ? 1 : 0);
+ if (e != NULL)
+ {
+ e->JsonHint_IsBool = true;
+ }
+ return e;
}
-void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)
+ELEMENT *PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total)
{
- PackAddIntEx(p, name, b ? 1 : 0, index, total);
+ ELEMENT *e = PackAddIntEx(p, name, b ? 1 : 0, index, total);
+ if (e != NULL)
+ {
+ e->JsonHint_IsBool = true;
+ }
+ return e;
}
// Add the IPV6_ADDR to the PACK
-void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)
+ELEMENT *PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total)
{
// Validate arguments
if (p == NULL || name == NULL || addr == NULL)
{
- return;
+ return NULL;
}
- PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);
+ return PackAddDataEx(p, name, addr, sizeof(IPV6_ADDR), index, total);
}
-void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
+ELEMENT *PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
{
- PackAddIp6AddrEx(p, name, addr, 0, 1);
+ return PackAddIp6AddrEx(p, name, addr, 0, 1);
}
// Get an IPV6_ADDR from the PACK
@@ -1195,6 +1257,10 @@ bool PackGetIp6Addr(PACK *p, char *name, IPV6_ADDR *addr)
// Add the IP to the PACK
void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total)
{
+ PackAddIp32Ex2(p, name, ip32, index, total, false);
+}
+void PackAddIp32Ex2(PACK *p, char *name, UINT ip32, UINT index, UINT total, bool is_single)
+{
IP ip;
// Validate arguments
if (p == NULL || name == NULL)
@@ -1204,32 +1270,45 @@ void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total)
UINTToIP(&ip, ip32);
- PackAddIpEx(p, name, &ip, index, total);
+ PackAddIpEx2(p, name, &ip, index, total, is_single);
}
void PackAddIp32(PACK *p, char *name, UINT ip32)
{
- PackAddIp32Ex(p, name, ip32, 0, 1);
+ PackAddIp32Ex2(p, name, ip32, 0, 1, true);
}
void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)
{
+ PackAddIpEx2(p, name, ip, index, total, false);
+}
+void PackAddIpEx2(PACK *p, char *name, IP *ip, UINT index, UINT total, bool is_single)
+{
UINT i;
bool b = false;
char tmp[MAX_PATH];
+ ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || ip == NULL)
{
return;
}
+ if (total >= 2)
+ {
+ is_single = false;
+ }
b = IsIP6(ip);
Format(tmp, sizeof(tmp), "%s@ipv6_bool", name);
- PackAddBoolEx(p, tmp, b, index, total);
+ e = PackAddBoolEx(p, tmp, b, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
Format(tmp, sizeof(tmp), "%s@ipv6_array", name);
if (b)
{
- PackAddDataEx(p, tmp, ip->ipv6_addr, sizeof(ip->ipv6_addr), index, total);
+ e = PackAddDataEx(p, tmp, ip->ipv6_addr, sizeof(ip->ipv6_addr), index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
else
{
@@ -1237,17 +1316,23 @@ void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)
Zero(dummy, sizeof(dummy));
- PackAddDataEx(p, tmp, dummy, sizeof(dummy), index, total);
+ e = PackAddDataEx(p, tmp, dummy, sizeof(dummy), index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
Format(tmp, sizeof(tmp), "%s@ipv6_scope_id", name);
if (b)
{
- PackAddIntEx(p, tmp, ip->ipv6_scope_id, index, total);
+ e = PackAddIntEx(p, tmp, ip->ipv6_scope_id, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
else
{
- PackAddIntEx(p, tmp, 0, index, total);
+ e = PackAddIntEx(p, tmp, 0, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
i = IPToUINT(ip);
@@ -1257,11 +1342,13 @@ void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total)
i = Swap32(i);
}
- PackAddIntEx(p, name, i, index, total);
+ e = PackAddIntEx(p, name, i, index, total);
+ if (e != NULL && is_single) e->JsonHint_IsArray = false;
+ if (e != NULL) e->JsonHint_IsIP = true;
}
void PackAddIp(PACK *p, char *name, IP *ip)
{
- PackAddIpEx(p, name, ip, 0, 1);
+ PackAddIpEx2(p, name, ip, 0, 1, true);
}
// Get an IP from the PACK
@@ -1441,34 +1528,35 @@ bool PackGetStrEx(PACK *p, char *name, char *str, UINT size, UINT index)
}
// Add the buffer to the PACK (array)
-void PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total)
+ELEMENT *PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total)
{
// Validate arguments
if (p == NULL || name == NULL || b == NULL || total == 0)
{
- return;
+ return NULL;
}
- PackAddDataEx(p, name, b->Buf, b->Size, index, total);
+ return PackAddDataEx(p, name, b->Buf, b->Size, index, total);
}
// Add the data to the PACK (array)
-void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total)
+ELEMENT *PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || data == NULL || name == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewDataValue(data, size);
e = GetElement(p, name, VALUE_DATA);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_DATA);
e->values[index] = v;
}
else
@@ -1484,53 +1572,68 @@ void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT
e->type = VALUE_DATA;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add the buffer to the PACK
-void PackAddBuf(PACK *p, char *name, BUF *b)
+ELEMENT *PackAddBuf(PACK *p, char *name, BUF *b)
{
// Validate arguments
if (p == NULL || name == NULL || b == NULL)
{
- return;
+ return NULL;
}
- PackAddData(p, name, b->Buf, b->Size);
+ return PackAddData(p, name, b->Buf, b->Size);
}
// Add the data to the PACK
-void PackAddData(PACK *p, char *name, void *data, UINT size)
+ELEMENT *PackAddData(PACK *p, char *name, void *data, UINT size)
{
VALUE *v;
+ ELEMENT *e;
// Validate arguments
if (p == NULL || data == NULL || name == NULL)
{
- return;
+ return NULL;
}
v = NewDataValue(data, size);
- AddElement(p, NewElement(name, VALUE_DATA, 1, &v));
+ e = NewElement(name, VALUE_DATA, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+
+ return e;
}
// Add a 64 bit integer (array) to the PACK
-void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
+ELEMENT *PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewInt64Value(i);
e = GetElement(p, name, VALUE_INT64);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_INT64);
e->values[index] = v;
}
else
@@ -1546,27 +1649,36 @@ void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
e->type = VALUE_INT64;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add an integer to the PACK (array)
-void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)
+ELEMENT *PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewIntValue(i);
e = GetElement(p, name, VALUE_INT);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_INT);
e->values[index] = v;
}
else
@@ -1582,61 +1694,103 @@ void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total)
e->type = VALUE_INT;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
+// Add 64 bit integer time value to the PACK
+ELEMENT *PackAddTime64(PACK *p, char *name, UINT64 i)
+{
+ ELEMENT *e = PackAddInt64(p, name, i);
+ if (e != NULL)
+ {
+ e->JsonHint_IsDateTime = true;
+ }
+ return e;
+}
+ELEMENT *PackAddTime64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total)
+{
+ ELEMENT *e = PackAddInt64Ex(p, name, i, index, total);
+ if (e != NULL)
+ {
+ e->JsonHint_IsDateTime = true;
+ }
+ return e;
+}
+
+
// Add a 64 bit integer to the PACK
-void PackAddInt64(PACK *p, char *name, UINT64 i)
+ELEMENT *PackAddInt64(PACK *p, char *name, UINT64 i)
{
VALUE *v;
+ ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL)
{
- return;
+ return NULL;
}
v = NewInt64Value(i);
- AddElement(p, NewElement(name, VALUE_INT64, 1, &v));
+ e = NewElement(name, VALUE_INT64, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
}
// Add the number of items to the PACK
-void PackAddNum(PACK *p, char *name, UINT num)
+ELEMENT *PackAddNum(PACK *p, char *name, UINT num)
{
- PackAddInt(p, name, num);
+ return PackAddInt(p, name, num);
}
// Add an integer to the PACK
-void PackAddInt(PACK *p, char *name, UINT i)
+ELEMENT *PackAddInt(PACK *p, char *name, UINT i)
{
VALUE *v;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL)
{
- return;
+ return NULL;
}
v = NewIntValue(i);
- AddElement(p, NewElement(name, VALUE_INT, 1, &v));
+ e = NewElement(name, VALUE_INT, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
}
// Add a Unicode string (array) to the PACK
-void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total)
+ELEMENT *PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || unistr == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewUniStrValue(unistr);
e = GetElement(p, name, VALUE_UNISTR);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_UNISTR);
e->values[index] = v;
}
else
@@ -1652,41 +1806,55 @@ void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT tota
e->type = VALUE_UNISTR;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add a Unicode string to the PACK
-void PackAddUniStr(PACK *p, char *name, wchar_t *unistr)
+ELEMENT *PackAddUniStr(PACK *p, char *name, wchar_t *unistr)
{
VALUE *v;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || unistr == NULL)
{
- return;
+ return NULL;
}
v = NewUniStrValue(unistr);
- AddElement(p, NewElement(name, VALUE_UNISTR, 1, &v));
+ e = NewElement(name, VALUE_UNISTR, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
}
// Add a string to the PACK (array)
-void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)
+ELEMENT *PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)
{
VALUE *v;
ELEMENT *e;
// Validate arguments
if (p == NULL || name == NULL || str == NULL || total == 0)
{
- return;
+ return NULL;
}
v = NewStrValue(str);
e = GetElement(p, name, VALUE_STR);
if (e != NULL)
{
- if (e->num_value <= total)
+ if (e->num_value >= total)
{
+ FreeValue(e->values[index], VALUE_STR);
e->values[index] = v;
}
else
@@ -1702,22 +1870,704 @@ void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total)
e->type = VALUE_STR;
e->values = ZeroMallocEx(sizeof(VALUE *) * total, true);
e->values[index] = v;
- AddElement(p, e);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
}
+
+ e->JsonHint_IsArray = true;
+
+ return e;
}
// Add a string to the PACK
-void PackAddStr(PACK *p, char *name, char *str)
+ELEMENT *PackAddStr(PACK *p, char *name, char *str)
{
VALUE *v;
+ ELEMENT *e = NULL;
// Validate arguments
if (p == NULL || name == NULL || str == NULL)
{
- return;
+ return NULL;
}
v = NewStrValue(str);
- AddElement(p, NewElement(name, VALUE_STR, 1, &v));
+ e = NewElement(name, VALUE_STR, 1, &v);
+ if (AddElement(p, e) == false)
+ {
+ return NULL;
+ }
+ return e;
+}
+
+// Add an element of PACK array to JSON Array
+void PackArrayElementToJsonArray(JSON_ARRAY *ja, PACK *p, ELEMENT *e, UINT index)
+{
+ if (ja == NULL || p == NULL || e == NULL || index >= e->num_value)
+ {
+ return;
+ }
+
+ switch (e->type)
+ {
+ case VALUE_INT:
+ if (e->JsonHint_IsIP)
+ {
+ if (InStr(e->name, "@") == false)
+ {
+ IP ip;
+ if (PackGetIpEx(p, e->name, &ip, index))
+ {
+ char ip_str[64];
+ IPToStr(ip_str, sizeof(ip_str), &ip);
+ JsonArrayAddStr(ja, ip_str);
+ }
+ }
+ }
+ else if (e->JsonHint_IsBool)
+ {
+ JsonArrayAddBool(ja, PackGetBoolEx(p, e->name, index));
+ }
+ else
+ {
+ JsonArrayAddNumber(ja, PackGetIntEx(p, e->name, index));
+ }
+ break;
+ case VALUE_INT64:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->JsonHint_IsDateTime == false)
+ {
+ JsonArrayAddNumber(ja, PackGetInt64Ex(p, e->name, index));
+ }
+ else
+ {
+ char dtstr[64];
+
+ SystemTime64ToJsonStr(dtstr, sizeof(dtstr), PackGetInt64Ex(p, e->name, index));
+ JsonArrayAddStr(ja, dtstr);
+ }
+ }
+ break;
+ case VALUE_DATA:
+ if (e->JsonHint_IsIP == false)
+ {
+ BUF *buf = PackGetBufEx(p, e->name, index);
+ if (buf != NULL)
+ {
+ JsonArrayAddData(ja, buf->Buf, buf->Size);
+ FreeBuf(buf);
+ }
+ else
+ {
+ UCHAR zero = 0;
+ JsonArrayAddData(ja, &zero, 0);
+ }
+ }
+ break;
+ case VALUE_STR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonArrayAddStr(ja, e->values[index]->Str);
+ }
+ else
+ {
+ JsonArrayAddStr(ja, "");
+ }
+ }
+ break;
+ case VALUE_UNISTR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonArrayAddUniStr(ja, e->values[index]->UniStr);
+ }
+ else
+ {
+ JsonArrayAddUniStr(ja, L"");
+ }
+ }
+ break;
+ }
+}
+
+// Add an element of PACK to JSON Object
+void PackElementToJsonObject(JSON_OBJECT *o, PACK *p, ELEMENT *e, UINT index)
+{
+ char *suffix;
+ char name[MAX_PATH];
+ if (o == NULL || p == NULL || e == NULL)
+ {
+ return;
+ }
+
+ suffix = DetermineJsonSuffixForPackElement(e);
+
+ if (suffix == NULL)
+ {
+ return;
+ }
+
+ StrCpy(name, sizeof(name), e->name);
+ StrCat(name, sizeof(name), suffix);
+
+ switch (e->type)
+ {
+ case VALUE_INT:
+ if (e->JsonHint_IsIP)
+ {
+ if (InStr(e->name, "@") == false)
+ {
+ IP ip;
+ if (PackGetIpEx(p, e->name, &ip, index))
+ {
+ char ip_str[64];
+ IPToStr(ip_str, sizeof(ip_str), &ip);
+ JsonSetStr(o, name, ip_str);
+ }
+ }
+ }
+ else if (e->JsonHint_IsBool)
+ {
+ JsonSetBool(o, name, PackGetBoolEx(p, e->name, index));
+ }
+ else
+ {
+ JsonSetNumber(o, name, PackGetIntEx(p, e->name, index));
+ }
+ break;
+ case VALUE_INT64:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->JsonHint_IsDateTime == false)
+ {
+ JsonSetNumber(o, name, PackGetInt64Ex(p, e->name, index));
+ }
+ else
+ {
+ char dtstr[64];
+
+ SystemTime64ToJsonStr(dtstr, sizeof(dtstr), PackGetInt64Ex(p, e->name, index));
+ JsonSetStr(o, name, dtstr);
+ }
+ }
+ break;
+ case VALUE_DATA:
+ if (e->JsonHint_IsIP == false)
+ {
+ BUF *buf = PackGetBufEx(p, e->name, index);
+ if (buf != NULL)
+ {
+ JsonSetData(o, name, buf->Buf, buf->Size);
+ FreeBuf(buf);
+ }
+ else
+ {
+ UCHAR zero = 0;
+ JsonSetData(o, name, &zero, 0);
+ }
+ }
+ break;
+ case VALUE_STR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonSetStr(o, name, e->values[index]->Str);
+ }
+ else
+ {
+ JsonSetStr(o, name, "");
+ }
+ }
+ break;
+ case VALUE_UNISTR:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->values[index] != NULL)
+ {
+ JsonSetUniStr(o, name, e->values[index]->UniStr);
+ }
+ else
+ {
+ JsonSetUniStr(o, name, L"");
+ }
+ }
+ break;
+ }
+}
+
+// Determine JSON element suffix for PACK element
+char *DetermineJsonSuffixForPackElement(ELEMENT *e)
+{
+ switch (e->type)
+ {
+ case VALUE_INT:
+ if (e->JsonHint_IsIP)
+ {
+ if (InStr(e->name, "@") == false)
+ {
+ return "_ip";
+ }
+ }
+ else if (e->JsonHint_IsBool)
+ {
+ return "_bool";
+ }
+ else
+ {
+ return "_u32";
+ }
+ break;
+ case VALUE_INT64:
+ if (e->JsonHint_IsIP == false)
+ {
+ if (e->JsonHint_IsDateTime == false)
+ {
+ return "_u64";
+ }
+ else
+ {
+ return "_dt";
+ }
+ }
+ break;
+ case VALUE_DATA:
+ if (e->JsonHint_IsIP == false)
+ {
+ return "_bin";
+ }
+ break;
+ case VALUE_STR:
+ if (e->JsonHint_IsIP == false)
+ {
+ return "_str";
+ }
+ break;
+ case VALUE_UNISTR:
+ if (e->JsonHint_IsIP == false)
+ {
+ return "_utf";
+ }
+ break;
+ }
+
+ return NULL;
}
+// Convert JSON to PACK
+PACK *JsonToPack(JSON_VALUE *v)
+{
+ PACK *p = NULL;
+ JSON_OBJECT *jo;
+ if (v == NULL)
+ {
+ return NULL;
+ }
+
+ p = NewPack();
+
+ jo = JsonValueGetObject(v);
+
+ if (jo != NULL)
+ {
+ UINT i;
+ for (i = 0;i < jo->count;i++)
+ {
+ char *name = jo->names[i];
+ JSON_VALUE *value = jo->values[i];
+
+ if (value->type == JSON_TYPE_ARRAY)
+ {
+ UINT j;
+ JSON_ARRAY *ja = value->value.array;
+
+ for (j = 0;j < ja->count;j++)
+ {
+ if (ja->items[j]->type != JSON_TYPE_OBJECT)
+ {
+ JsonTryParseValueAddToPack(p, ja->items[j], name, j, ja->count, false);
+ }
+ else
+ {
+ JSON_VALUE *v = ja->items[j];
+ JSON_OBJECT *o = v->value.object;
+ UINT k;
+
+ for (k = 0;k < o->count;k++)
+ {
+ char *name2 = o->names[k];
+ JSON_VALUE *value2 = o->values[k];
+
+ PackSetCurrentJsonGroupName(p, name);
+ JsonTryParseValueAddToPack(p, value2, name2, j, ja->count, false);
+ PackSetCurrentJsonGroupName(p, NULL);
+ }
+ }
+ }
+ }
+ else
+ {
+ JsonTryParseValueAddToPack(p, value, name, 0, 1, true);
+ }
+ }
+ }
+
+ return p;
+}
+
+ELEMENT *ElementNullSafe(ELEMENT *p)
+{
+ static ELEMENT dummy;
+ if (p == NULL)
+ {
+ Zero(&dummy, sizeof(dummy));
+ return &dummy;
+ }
+ return p;
+}
+
+bool JsonTryParseValueAddToPack(PACK *p, JSON_VALUE *v, char *v_name, UINT index, UINT total, bool is_single)
+{
+ char name[MAX_PATH];
+ bool ok = true;
+ if (p == NULL || v == NULL)
+ {
+ return false;
+ }
+
+ if (TrimEndWith(name, sizeof(name), v_name, "_bool"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddBoolEx(p, name, MAKEBOOL(v->value.boolean), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddBoolEx(p, name, MAKEBOOL(v->value.number), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddBoolEx(p, name, ToBool(v->value.string), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_u32"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddIntEx(p, name, MAKEBOOL(v->value.boolean), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddIntEx(p, name, (UINT)v->value.number, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddIntEx(p, name, ToInt(v->value.string), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_u64"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, MAKEBOOL(v->value.boolean), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, v->value.number, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, ToInt64(v->value.string), index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_str"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddStrEx(p, name, MAKEBOOL(v->value.boolean) ? "true" : "false", index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ char tmp[64];
+ ToStr64(tmp, v->value.number);
+ ElementNullSafe(PackAddStrEx(p, name, tmp, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ ElementNullSafe(PackAddStrEx(p, name, v->value.string, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_utf"))
+ {
+ if (v->type == JSON_TYPE_BOOL)
+ {
+ ElementNullSafe(PackAddUniStrEx(p, name, MAKEBOOL(v->value.boolean) ? L"true" : L"false", index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_NUMBER)
+ {
+ char tmp[64];
+ wchar_t tmp2[64];
+ ToStr64(tmp, v->value.number);
+ StrToUni(tmp2, sizeof(tmp2), tmp);
+ ElementNullSafe(PackAddUniStrEx(p, name, tmp2, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ wchar_t *uni = CopyUtfToUni(v->value.string);
+ ElementNullSafe(PackAddUniStrEx(p, name, uni, index, total))->JsonHint_IsArray = !is_single;
+ Free(uni);
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_bin"))
+ {
+ if (v->type == JSON_TYPE_STRING)
+ {
+ UINT len = StrLen(v->value.string);
+ UCHAR *data = ZeroMalloc(len * 4 + 64);
+ UINT size = B64_Decode(data, v->value.string, len);
+ ElementNullSafe(PackAddDataEx(p, name, data, size, index, total))->JsonHint_IsArray = !is_single;
+ Free(data);
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_dt"))
+ {
+ if (v->type == JSON_TYPE_NUMBER)
+ {
+ ElementNullSafe(PackAddInt64Ex(p, name, v->value.number, index, total))->JsonHint_IsArray = !is_single;
+ ok = true;
+ }
+ else if (v->type == JSON_TYPE_STRING)
+ {
+ UINT64 time = DateTimeStrRFC3339ToSystemTime64(v->value.string);
+ ELEMENT *e = PackAddInt64Ex(p, name, time, index, total);
+ if (e != NULL)
+ {
+ e->JsonHint_IsArray = !is_single;
+ e->JsonHint_IsDateTime = true;
+ }
+ ok = true;
+ }
+ }
+ else if (TrimEndWith(name, sizeof(name), v_name, "_ip"))
+ {
+ if (v->type == JSON_TYPE_STRING)
+ {
+ IP ip;
+ if (StrToIP(&ip, v->value.string))
+ {
+ PackAddIpEx2(p, name, &ip, index, total, is_single);
+ ok = true;
+ }
+ }
+ }
+
+ return ok;
+}
+
+// Convert JSON string to PACK
+PACK *JsonStrToPack(char *str)
+{
+ JSON_VALUE *v = StrToJson(str);
+ PACK *ret;
+
+ if (v == NULL)
+ {
+ return NULL;
+ }
+
+ ret = JsonToPack(v);
+
+ JsonFree(v);
+
+ return ret;
+}
+
+// Convert PACK to JSON string
+char *PackToJsonStr(PACK *p)
+{
+ char *ret;
+ JSON_VALUE *json = PackToJson(p);
+
+ ret = JsonToStr(json);
+
+ JsonFree(json);
+
+ return ret;
+}
+
+// Convert PACK to JSON
+JSON_VALUE *PackToJson(PACK *p)
+{
+ JSON_VALUE *v;
+ JSON_OBJECT *o;
+ UINT i, j, k;
+ LIST *json_group_id_list;
+ if (p == NULL)
+ {
+ return JsonNewObject();
+ }
+
+ // suppress quick sort in the enumeration process
+ GetElement(p, "_dummy_", VALUE_INT);
+
+ json_group_id_list = NewStrList();
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (IsEmptyStr(e->JsonHint_GroupName) == false)
+ {
+ AddStrToStrListDistinct(json_group_id_list, e->JsonHint_GroupName);
+ }
+ }
+ }
+
+ for (i = 0;i < LIST_NUM(p->json_subitem_names);i++)
+ {
+ char *group_name = LIST_DATA(p->json_subitem_names, i);
+
+ if (IsEmptyStr(group_name) == false)
+ {
+ AddStrToStrListDistinct(json_group_id_list, group_name);
+ }
+ }
+
+ v = JsonNewObject();
+ o = JsonValueGetObject(v);
+
+ for (k = 0;k < LIST_NUM(json_group_id_list);k++)
+ {
+ char *group_name = LIST_DATA(json_group_id_list, k);
+ UINT array_count = INFINITE;
+ bool ok = true;
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (StrCmpi(e->JsonHint_GroupName, group_name) == 0)
+ {
+ if (array_count == INFINITE)
+ {
+ array_count = e->num_value;
+ }
+ else
+ {
+ if (array_count != e->num_value)
+ {
+ ok = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (array_count == INFINITE)
+ {
+ array_count = 0;
+ }
+
+ if (ok)
+ {
+ JSON_VALUE **json_objects = ZeroMalloc(sizeof(void *) * array_count);
+ JSON_VALUE *jav = JsonNewArray();
+ JSON_ARRAY *ja = JsonArray(jav);
+
+ JsonSet(o, group_name, jav);
+
+ for (j = 0;j < array_count;j++)
+ {
+ json_objects[j] = JsonNewObject();
+
+ JsonArrayAdd(ja, json_objects[j]);
+ }
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (StrCmpi(e->JsonHint_GroupName, group_name) == 0)
+ {
+ for (j = 0;j < e->num_value;j++)
+ {
+ PackElementToJsonObject(JsonValueGetObject(json_objects[j]),
+ p, e, j);
+ }
+ }
+ }
+ }
+
+ Free(json_objects);
+ }
+ }
+
+ for (i = 0;i < LIST_NUM(p->elements);i++)
+ {
+ ELEMENT *e = LIST_DATA(p->elements, i);
+
+ if (e->num_value >= 2 || e->JsonHint_IsArray)
+ {
+ if (IsEmptyStr(e->JsonHint_GroupName))
+ {
+ char *suffix = DetermineJsonSuffixForPackElement(e);
+
+ if (suffix != NULL)
+ {
+ JSON_VALUE *jav = JsonNewArray();
+ JSON_ARRAY *ja = JsonArray(jav);
+ char name[MAX_PATH];
+
+ for (j = 0;j < e->num_value;j++)
+ {
+ PackArrayElementToJsonArray(ja, p, e, j);
+ }
+
+ StrCpy(name, sizeof(name), e->name);
+ StrCat(name, sizeof(name), suffix);
+
+ JsonSet(o, name, jav);
+ }
+ }
+ }
+ else if (e->num_value == 1)
+ {
+ PackElementToJsonObject(o, p, e, 0);
+ }
+ }
+
+ ReleaseStrList(json_group_id_list);
+
+ return v;
+}
+
+
+
diff --git a/src/Mayaqua/Pack.h b/src/Mayaqua/Pack.h
index 3880d5b7..53a13840 100644
--- a/src/Mayaqua/Pack.h
+++ b/src/Mayaqua/Pack.h
@@ -152,12 +152,40 @@ struct ELEMENT
UINT num_value; // Number of values (>=1)
UINT type; // Type
VALUE **values; // List of pointers to the value
+ bool JsonHint_IsArray;
+ bool JsonHint_IsBool;
+ bool JsonHint_IsDateTime;
+ bool JsonHint_IsIP;
+ char JsonHint_GroupName[MAX_ELEMENT_NAME_LEN + 1];
};
+
+
// PACK object
struct PACK
{
LIST *elements; // Element list
+ LIST *json_subitem_names; // JSON sub-item names
+ char CurrentJsonHint_GroupName[MAX_ELEMENT_NAME_LEN + 1];
+};
+
+
+#define MAX_JSONPACK_HINT_ITEMS 64
+#define JSONPACK_HINT_TYPE_ARRAY 1
+
+// JSON/PACK converter hint element
+struct JSONPACKHINT_ITEM
+{
+ UINT Type;
+ char ArrayNumNameInPack[MAX_ELEMENT_NAME_LEN + 1];
+ char ArrayMembersInPack[MAX_SIZE + 1];
+};
+
+// JSON/PACK converter hint
+struct JSONPACKHINT
+{
+ UINT NumHints;
+ JSONPACKHINT_ITEM Hints[MAX_JSONPACK_HINT_ITEMS];
};
@@ -198,21 +226,23 @@ TOKEN_LIST *GetPackElementNames(PACK *p);
X *PackGetX(PACK *p, char *name);
K *PackGetK(PACK *p, char *name);
-void PackAddX(PACK *p, char *name, X *x);
-void PackAddK(PACK *p, char *name, K *k);
-void PackAddStr(PACK *p, char *name, char *str);
-void PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total);
-void PackAddUniStr(PACK *p, char *name, wchar_t *unistr);
-void PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total);
-void PackAddInt(PACK *p, char *name, UINT i);
-void PackAddNum(PACK *p, char *name, UINT num);
-void PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total);
-void PackAddInt64(PACK *p, char *name, UINT64 i);
-void PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total);
-void PackAddData(PACK *p, char *name, void *data, UINT size);
-void PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total);
-void PackAddBuf(PACK *p, char *name, BUF *b);
-void PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total);
+ELEMENT *PackAddX(PACK *p, char *name, X *x);
+ELEMENT *PackAddK(PACK *p, char *name, K *k);
+ELEMENT *PackAddStr(PACK *p, char *name, char *str);
+ELEMENT *PackAddStrEx(PACK *p, char *name, char *str, UINT index, UINT total);
+ELEMENT *PackAddUniStr(PACK *p, char *name, wchar_t *unistr);
+ELEMENT *PackAddUniStrEx(PACK *p, char *name, wchar_t *unistr, UINT index, UINT total);
+ELEMENT *PackAddInt(PACK *p, char *name, UINT i);
+ELEMENT *PackAddNum(PACK *p, char *name, UINT num);
+ELEMENT *PackAddIntEx(PACK *p, char *name, UINT i, UINT index, UINT total);
+ELEMENT *PackAddInt64(PACK *p, char *name, UINT64 i);
+ELEMENT *PackAddInt64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total);
+ELEMENT *PackAddTime64(PACK *p, char *name, UINT64 i);
+ELEMENT *PackAddTime64Ex(PACK *p, char *name, UINT64 i, UINT index, UINT total);
+ELEMENT *PackAddData(PACK *p, char *name, void *data, UINT size);
+ELEMENT *PackAddDataEx(PACK *p, char *name, void *data, UINT size, UINT index, UINT total);
+ELEMENT *PackAddBuf(PACK *p, char *name, BUF *b);
+ELEMENT *PackAddBufEx(PACK *p, char *name, BUF *b, UINT index, UINT total);
bool PackGetStr(PACK *p, char *name, char *str, UINT size);
bool PackGetStrEx(PACK *p, char *name, char *str, UINT size, UINT index);
bool PackGetUniStr(PACK *p, char *name, wchar_t *unistr, UINT size);
@@ -231,23 +261,39 @@ bool PackGetDataEx(PACK *p, char *name, void *data, UINT index);
BUF *PackGetBuf(PACK *p, char *name);
BUF *PackGetBufEx(PACK *p, char *name, UINT index);
bool PackGetBool(PACK *p, char *name);
-void PackAddBool(PACK *p, char *name, bool b);
-void PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total);
+ELEMENT *PackAddBool(PACK *p, char *name, bool b);
+ELEMENT *PackAddBoolEx(PACK *p, char *name, bool b, UINT index, UINT total);
bool PackGetBoolEx(PACK *p, char *name, UINT index);
void PackAddIp(PACK *p, char *name, IP *ip);
void PackAddIpEx(PACK *p, char *name, IP *ip, UINT index, UINT total);
+void PackAddIpEx2(PACK *p, char *name, IP *ip, UINT index, UINT total, bool is_single);
bool PackGetIp(PACK *p, char *name, IP *ip);
bool PackGetIpEx(PACK *p, char *name, IP *ip, UINT index);
UINT PackGetIp32(PACK *p, char *name);
UINT PackGetIp32Ex(PACK *p, char *name, UINT index);
void PackAddIp32(PACK *p, char *name, UINT ip32);
void PackAddIp32Ex(PACK *p, char *name, UINT ip32, UINT index, UINT total);
-void PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total);
+void PackAddIp32Ex2(PACK *p, char *name, UINT ip32, UINT index, UINT total, bool is_single);
+ELEMENT *PackAddIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index, UINT total);
bool PackGetIp6AddrEx(PACK *p, char *name, IPV6_ADDR *addr, UINT index);
-void PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
+ELEMENT *PackAddIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
bool PackGetIp6Addr(PACK *p, char *name, IPV6_ADDR *addr);
bool PackGetData2(PACK *p, char *name, void *data, UINT size);
bool PackGetDataEx2(PACK *p, char *name, void *data, UINT size, UINT index);
bool PackIsValueExists(PACK *p, char *name);
+void PackSetCurrentJsonGroupName(PACK *p, char *json_group_name);
+ELEMENT *ElementNullSafe(ELEMENT *p);
+
+JSON_VALUE *PackToJson(PACK *p);
+char *PackToJsonStr(PACK *p);
+
+PACK *JsonToPack(JSON_VALUE *v);
+PACK *JsonStrToPack(char *str);
+
+void PackArrayElementToJsonArray(JSON_ARRAY *ja, PACK *p, ELEMENT *e, UINT index);
+void PackElementToJsonObject(JSON_OBJECT *o, PACK *p, ELEMENT *e, UINT index);
+char *DetermineJsonSuffixForPackElement(ELEMENT *e);
+bool JsonTryParseValueAddToPack(PACK *p, JSON_VALUE *v, char *v_name, UINT index, UINT total, bool is_single);
+
#endif // PACK_H
diff --git a/src/Mayaqua/Str.c b/src/Mayaqua/Str.c
index 6e0a6ecf..ec4023da 100644
--- a/src/Mayaqua/Str.c
+++ b/src/Mayaqua/Str.c
@@ -108,6 +108,7 @@
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
+#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#include <errno.h>
@@ -133,6 +134,60 @@ static BYTESTR bytestr[] =
{0, "Bytes"},
};
+// Decode URL string
+char *UrlDecode(char *url_str)
+{
+ UINT i, len;
+ BUF *b;
+ char *ret;
+ if (url_str == NULL)
+ {
+ return NULL;
+ }
+
+ len = StrLen(url_str);
+
+ b = NewBuf();
+
+ for (i = 0;i < len;i++)
+ {
+ char c = url_str[i];
+
+ if (c == '%' && ((i + 2) < len))
+ {
+ char hex_str[8];
+ UINT value;
+
+ hex_str[0] = url_str[i + 1];
+ hex_str[1] = url_str[i + 2];
+ hex_str[2] = 0;
+
+ value = HexToInt(hex_str);
+
+ WriteBufChar(b, (UCHAR)value);
+
+ i += 2;
+ continue;
+ }
+ else
+ {
+ if (c == '+')
+ {
+ c = ' ';
+ }
+ WriteBufChar(b, c);
+ }
+ }
+
+ WriteBufChar(b, 0);
+
+ ret = CopyStr(b->Buf);
+
+ FreeBuf(b);
+
+ return ret;
+}
+
// Change the case of the string by the bit array
void SetStrCaseAccordingToBits(char *str, UINT bits)
{
@@ -1764,6 +1819,73 @@ UINT64 ToInt64(char *str)
return ret;
}
+UINT64 Json_ToInt64Ex(char *str, char **endptr, bool *error)
+{
+ UINT i;
+ UINT64 ret = 0;
+ if (error != NULL) *error = true;
+ // Validate arguments
+ if (str == NULL)
+ {
+ if (endptr != NULL)
+ {
+ *endptr = NULL;
+ }
+ return 0;
+ }
+
+ for (i = 0;;i++)
+ {
+ char c = str[i];
+ if (endptr != NULL)
+ {
+ *endptr = &str[i];
+ }
+ if (c == 0)
+ {
+ break;
+ }
+ if ('0' <= c && c <= '9')
+ {
+ ret = ret * (UINT64)10 + (UINT64)(c - '0');
+ if (error != NULL) *error = false;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+// Trim EndWith
+bool TrimEndWith(char *dst, UINT dst_size, char *str, char *key)
+{
+ if (dst == NULL || str == NULL)
+ {
+ ClearStr(dst, dst_size);
+ return false;
+ }
+
+ StrCpy(dst, dst_size, str);
+
+ if (EndWith(str, key))
+ {
+ UINT src_len = StrLen(str);
+ UINT key_len = StrLen(key);
+
+ if (src_len >= key_len)
+ {
+ dst[src_len - key_len] = 0;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
// Check whether the str ends with the key
bool EndWith(char *str, char *key)
{
@@ -3454,3 +3576,1991 @@ UINT StrLen(char *str)
}
+// *** JSON strings support
+// Original source code from Parson ( http://kgabis.github.com/parson/ )
+// Modified by dnobori
+/*
+Parson ( http://kgabis.github.com/parson/ )
+Copyright (c) 2012 - 2017 Krzysztof Gabis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+
+/* Apparently sscanf is not implemented in some "standard" libraries, so don't use it, if you
+* don't have to. */
+#define sscanf THINK_TWICE_ABOUT_USING_SSCANF
+
+#define STARTING_CAPACITY 16
+#define MAX_NESTING 2048
+#define FLOAT_FORMAT "%1.17g"
+
+#define SIZEOF_TOKEN(a) (sizeof(a) - 1)
+#define SKIP_CHAR(str) ((*str)++)
+#define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); }
+
+static JSON_Malloc_Function parson_malloc = Malloc;
+static JSON_Free_Function parson_free = Free;
+
+#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */
+
+/* Various */
+static void remove_comments(char *string, char *start_token, char *end_token);
+static char * parson_strndup(char *string, UINT n);
+static char * parson_strdup(char *string);
+static int hex_char_to_int(char c);
+static int parse_utf16_hex(char *string, unsigned int *result);
+static int num_bytes_in_utf8_sequence(unsigned char c);
+static int verify_utf8_sequence(unsigned char *string, int *len);
+static int is_valid_utf8(char *string, UINT string_len);
+static int is_decimal(char *string, UINT length);
+
+/* JSON Object */
+static JSON_OBJECT * json_object_init(JSON_VALUE *wrapping_value);
+static UINT json_object_add(JSON_OBJECT *object, char *name, JSON_VALUE *value);
+static UINT json_object_resize(JSON_OBJECT *object, UINT new_capacity);
+static JSON_VALUE * json_object_nget_value(JSON_OBJECT *object, char *name, UINT n);
+static void json_object_free(JSON_OBJECT *object);
+
+/* JSON Array */
+static JSON_ARRAY * json_array_init(JSON_VALUE *wrapping_value);
+static UINT json_array_add(JSON_ARRAY *array, JSON_VALUE *value);
+static UINT json_array_resize(JSON_ARRAY *array, UINT new_capacity);
+static void json_array_free(JSON_ARRAY *array);
+
+/* JSON Value */
+static JSON_VALUE * json_value_init_string_no_copy(char *string);
+
+/* Parser */
+static UINT skip_quotes(char **string);
+static int parse_utf16(char **unprocessed, char **processed);
+static char * process_string(char *input, UINT len);
+static char * get_quoted_string(char **string);
+static JSON_VALUE * parse_object_value(char **string, UINT nesting);
+static JSON_VALUE * parse_array_value(char **string, UINT nesting);
+static JSON_VALUE * parse_string_value(char **string);
+static JSON_VALUE * parse_boolean_value(char **string);
+static JSON_VALUE * parse_number_value(char **string);
+static JSON_VALUE * parse_null_value(char **string);
+static JSON_VALUE * parse_value(char **string, UINT nesting);
+
+/* Serialization */
+static int json_serialize_to_buffer_r(JSON_VALUE *value, char *buf, int level, int is_pretty, char *num_buf);
+static int json_serialize_string(char *string, char *buf);
+static int append_indent(char *buf, int level);
+static int append_string(char *buf, char *string);
+
+/* Various */
+static char * parson_strndup(char *string, UINT n) {
+ char *output_string = (char*)parson_malloc(n + 1);
+ if (!output_string) {
+ return NULL;
+ }
+ output_string[n] = '\0';
+ strncpy(output_string, string, n);
+ return output_string;
+}
+
+static char * parson_strdup(char *string) {
+ return parson_strndup(string, StrLen(string));
+}
+
+static int hex_char_to_int(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+ return -1;
+}
+
+static int parse_utf16_hex(char *s, unsigned int *result) {
+ int x1, x2, x3, x4;
+ if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') {
+ return 0;
+ }
+ x1 = hex_char_to_int(s[0]);
+ x2 = hex_char_to_int(s[1]);
+ x3 = hex_char_to_int(s[2]);
+ x4 = hex_char_to_int(s[3]);
+ if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) {
+ return 0;
+ }
+ *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4);
+ return 1;
+}
+
+static int num_bytes_in_utf8_sequence(unsigned char c) {
+ if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) {
+ return 0;
+ }
+ else if ((c & 0x80) == 0) { /* 0xxxxxxx */
+ return 1;
+ }
+ else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */
+ return 2;
+ }
+ else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */
+ return 3;
+ }
+ else if ((c & 0xF8) == 0xF0) { /* 11110xxx */
+ return 4;
+ }
+ return 0; /* won't happen */
+}
+
+static int verify_utf8_sequence(unsigned char *string, int *len) {
+ unsigned int cp = 0;
+ *len = num_bytes_in_utf8_sequence(string[0]);
+
+ if (*len == 1) {
+ cp = string[0];
+ }
+ else if (*len == 2 && IS_CONT(string[1])) {
+ cp = string[0] & 0x1F;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ }
+ else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
+ cp = ((unsigned char)string[0]) & 0xF;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ }
+ else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {
+ cp = string[0] & 0x7;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ cp = (cp << 6) | (string[3] & 0x3F);
+ }
+ else {
+ return 0;
+ }
+
+ /* overlong encodings */
+ if ((cp < 0x80 && *len > 1) ||
+ (cp < 0x800 && *len > 2) ||
+ (cp < 0x10000 && *len > 3)) {
+ return 0;
+ }
+
+ /* invalid unicode */
+ if (cp > 0x10FFFF) {
+ return 0;
+ }
+
+ /* surrogate halves */
+ if (cp >= 0xD800 && cp <= 0xDFFF) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int is_valid_utf8(char *string, UINT string_len) {
+ int len = 0;
+ char *string_end = string + string_len;
+ while (string < string_end) {
+ if (!verify_utf8_sequence((unsigned char*)string, &len)) {
+ return 0;
+ }
+ string += len;
+ }
+ return 1;
+}
+
+static int is_decimal(char *string, UINT length) {
+ if (length > 1 && string[0] == '0' && string[1] != '.') {
+ return 0;
+ }
+ if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') {
+ return 0;
+ }
+ while (length--) {
+ if (strchr("xX", string[length])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void remove_comments(char *string, char *start_token, char *end_token) {
+ int in_string = 0, escaped = 0;
+ UINT i;
+ char *ptr = NULL, current_char;
+ UINT start_token_len = StrLen(start_token);
+ UINT end_token_len = StrLen(end_token);
+ if (start_token_len == 0 || end_token_len == 0) {
+ return;
+ }
+ while ((current_char = *string) != '\0') {
+ if (current_char == '\\' && !escaped) {
+ escaped = 1;
+ string++;
+ continue;
+ }
+ else if (current_char == '\"' && !escaped) {
+ in_string = !in_string;
+ }
+ else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {
+ for (i = 0; i < start_token_len; i++) {
+ string[i] = ' ';
+ }
+ string = string + start_token_len;
+ ptr = strstr(string, end_token);
+ if (!ptr) {
+ return;
+ }
+ for (i = 0; i < (ptr - string) + end_token_len; i++) {
+ string[i] = ' ';
+ }
+ string = ptr + end_token_len - 1;
+ }
+ escaped = 0;
+ string++;
+ }
+}
+
+/* JSON Object */
+static JSON_OBJECT * json_object_init(JSON_VALUE *wrapping_value) {
+ JSON_OBJECT *new_obj = (JSON_OBJECT*)parson_malloc(sizeof(JSON_OBJECT));
+ if (new_obj == NULL) {
+ return NULL;
+ }
+ new_obj->wrapping_value = wrapping_value;
+ new_obj->names = (char**)NULL;
+ new_obj->values = (JSON_VALUE**)NULL;
+ new_obj->capacity = 0;
+ new_obj->count = 0;
+ return new_obj;
+}
+
+static UINT json_object_add(JSON_OBJECT *object, char *name, JSON_VALUE *value) {
+ UINT index = 0;
+ if (object == NULL || name == NULL || value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonGet(object, name) != NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (object->count >= object->capacity) {
+ UINT new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY);
+ if (json_object_resize(object, new_capacity) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ index = object->count;
+ object->names[index] = parson_strdup(name);
+ if (object->names[index] == NULL) {
+ return JSON_RET_ERROR;
+ }
+ value->parent = JsonGetWrappingValue(object);
+ object->values[index] = value;
+ object->count++;
+ return JSON_RET_OK;
+}
+
+static UINT json_object_resize(JSON_OBJECT *object, UINT new_capacity) {
+ char **temp_names = NULL;
+ JSON_VALUE **temp_values = NULL;
+
+ if ((object->names == NULL && object->values != NULL) ||
+ (object->names != NULL && object->values == NULL) ||
+ new_capacity == 0) {
+ return JSON_RET_ERROR; /* Shouldn't happen */
+ }
+ temp_names = (char**)parson_malloc(new_capacity * sizeof(char*));
+ if (temp_names == NULL) {
+ return JSON_RET_ERROR;
+ }
+ temp_values = (JSON_VALUE**)parson_malloc(new_capacity * sizeof(JSON_VALUE*));
+ if (temp_values == NULL) {
+ parson_free(temp_names);
+ return JSON_RET_ERROR;
+ }
+ if (object->names != NULL && object->values != NULL && object->count > 0) {
+ memcpy(temp_names, object->names, object->count * sizeof(char*));
+ memcpy(temp_values, object->values, object->count * sizeof(JSON_VALUE*));
+ }
+ parson_free(object->names);
+ parson_free(object->values);
+ object->names = temp_names;
+ object->values = temp_values;
+ object->capacity = new_capacity;
+ return JSON_RET_OK;
+}
+
+static JSON_VALUE * json_object_nget_value(JSON_OBJECT *object, char *name, UINT n) {
+ UINT i, name_length;
+ for (i = 0; i < JsonGetCount(object); i++) {
+ name_length = StrLen(object->names[i]);
+ if (name_length != n) {
+ continue;
+ }
+ if (strncmp(object->names[i], name, n) == 0) {
+ return object->values[i];
+ }
+ }
+ return NULL;
+}
+
+static void json_object_free(JSON_OBJECT *object) {
+ UINT i;
+ for (i = 0; i < object->count; i++) {
+ parson_free(object->names[i]);
+ JsonFree(object->values[i]);
+ }
+ parson_free(object->names);
+ parson_free(object->values);
+ parson_free(object);
+}
+
+/* JSON Array */
+static JSON_ARRAY * json_array_init(JSON_VALUE *wrapping_value) {
+ JSON_ARRAY *new_array = (JSON_ARRAY*)parson_malloc(sizeof(JSON_ARRAY));
+ if (new_array == NULL) {
+ return NULL;
+ }
+ new_array->wrapping_value = wrapping_value;
+ new_array->items = (JSON_VALUE**)NULL;
+ new_array->capacity = 0;
+ new_array->count = 0;
+ return new_array;
+}
+
+static UINT json_array_add(JSON_ARRAY *array, JSON_VALUE *value) {
+ if (array->count >= array->capacity) {
+ UINT new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);
+ if (json_array_resize(array, new_capacity) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ value->parent = JsonArrayGetWrappingValue(array);
+ array->items[array->count] = value;
+ array->count++;
+ return JSON_RET_OK;
+}
+
+static UINT json_array_resize(JSON_ARRAY *array, UINT new_capacity) {
+ JSON_VALUE **new_items = NULL;
+ if (new_capacity == 0) {
+ return JSON_RET_ERROR;
+ }
+ new_items = (JSON_VALUE**)parson_malloc(new_capacity * sizeof(JSON_VALUE*));
+ if (new_items == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (array->items != NULL && array->count > 0) {
+ memcpy(new_items, array->items, array->count * sizeof(JSON_VALUE*));
+ }
+ parson_free(array->items);
+ array->items = new_items;
+ array->capacity = new_capacity;
+ return JSON_RET_OK;
+}
+
+static void json_array_free(JSON_ARRAY *array) {
+ UINT i;
+ for (i = 0; i < array->count; i++) {
+ JsonFree(array->items[i]);
+ }
+ parson_free(array->items);
+ parson_free(array);
+}
+
+/* JSON Value */
+static JSON_VALUE * json_value_init_string_no_copy(char *string) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_STRING;
+ new_value->value.string = string;
+ return new_value;
+}
+
+/* Parser */
+static UINT skip_quotes(char **string) {
+ if (**string != '\"') {
+ return JSON_RET_ERROR;
+ }
+ SKIP_CHAR(string);
+ while (**string != '\"') {
+ if (**string == '\0') {
+ return JSON_RET_ERROR;
+ }
+ else if (**string == '\\') {
+ SKIP_CHAR(string);
+ if (**string == '\0') {
+ return JSON_RET_ERROR;
+ }
+ }
+ SKIP_CHAR(string);
+ }
+ SKIP_CHAR(string);
+ return JSON_RET_OK;
+}
+
+static int parse_utf16(char **unprocessed, char **processed) {
+ unsigned int cp, lead, trail;
+ int parse_succeeded = 0;
+ char *processed_ptr = *processed;
+ char *unprocessed_ptr = *unprocessed;
+ unprocessed_ptr++; /* skips u */
+ parse_succeeded = parse_utf16_hex(unprocessed_ptr, &cp);
+ if (!parse_succeeded) {
+ return JSON_RET_ERROR;
+ }
+ if (cp < 0x80) {
+ processed_ptr[0] = (char)cp; /* 0xxxxxxx */
+ }
+ else if (cp < 0x800) {
+ processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */
+ processed_ptr[1] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr += 1;
+ }
+ else if (cp < 0xD800 || cp > 0xDFFF) {
+ processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */
+ processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr[2] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr += 2;
+ }
+ else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */
+ lead = cp;
+ unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */
+ if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') {
+ return JSON_RET_ERROR;
+ }
+ parse_succeeded = parse_utf16_hex(unprocessed_ptr, &trail);
+ if (!parse_succeeded || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */
+ return JSON_RET_ERROR;
+ }
+ cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000;
+ processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */
+ processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr[3] = (((cp) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr += 3;
+ }
+ else { /* trail surrogate before lead surrogate */
+ return JSON_RET_ERROR;
+ }
+ unprocessed_ptr += 3;
+ *processed = processed_ptr;
+ *unprocessed = unprocessed_ptr;
+ return JSON_RET_OK;
+}
+
+
+/* Copies and processes passed string up to supplied length.
+Example: "\u006Corem ipsum" -> lorem ipsum */
+static char* process_string(char *input, UINT len) {
+ char *input_ptr = input;
+ UINT initial_size = (len + 1) * sizeof(char);
+ UINT final_size = 0;
+ char *output = NULL, *output_ptr = NULL, *resized_output = NULL;
+ output = (char*)parson_malloc(initial_size);
+ if (output == NULL) {
+ goto error;
+ }
+ output_ptr = output;
+ while ((*input_ptr != '\0') && (UINT)(input_ptr - input) < len) {
+ if (*input_ptr == '\\') {
+ input_ptr++;
+ switch (*input_ptr) {
+ case '\"': *output_ptr = '\"'; break;
+ case '\\': *output_ptr = '\\'; break;
+ case '/': *output_ptr = '/'; break;
+ case 'b': *output_ptr = '\b'; break;
+ case 'f': *output_ptr = '\f'; break;
+ case 'n': *output_ptr = '\n'; break;
+ case 'r': *output_ptr = '\r'; break;
+ case 't': *output_ptr = '\t'; break;
+ case 'u':
+ if (parse_utf16(&input_ptr, &output_ptr) == JSON_RET_ERROR) {
+ goto error;
+ }
+ break;
+ default:
+ goto error;
+ }
+ }
+ else if ((unsigned char)*input_ptr < 0x20) {
+ goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */
+ }
+ else {
+ *output_ptr = *input_ptr;
+ }
+ output_ptr++;
+ input_ptr++;
+ }
+ *output_ptr = '\0';
+ /* resize to new length */
+ final_size = (UINT)(output_ptr - output) + 1;
+ /* todo: don't resize if final_size == initial_size */
+ resized_output = (char*)parson_malloc(final_size);
+ if (resized_output == NULL) {
+ goto error;
+ }
+ memcpy(resized_output, output, final_size);
+ parson_free(output);
+ return resized_output;
+error:
+ parson_free(output);
+ return NULL;
+}
+
+/* Return processed contents of a string between quotes and
+skips passed argument to a matching quote. */
+static char * get_quoted_string(char **string) {
+ char *string_start = *string;
+ UINT string_len = 0;
+ UINT status = skip_quotes(string);
+ if (status != JSON_RET_OK) {
+ return NULL;
+ }
+ string_len = (UINT)(*string - string_start - 2); /* length without quotes */
+ return process_string(string_start + 1, string_len);
+}
+
+static JSON_VALUE * parse_value(char **string, UINT nesting) {
+ if (nesting > MAX_NESTING) {
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ switch (**string) {
+ case '{':
+ return parse_object_value(string, nesting + 1);
+ case '[':
+ return parse_array_value(string, nesting + 1);
+ case '\"':
+ return parse_string_value(string);
+ case 'f': case 't':
+ return parse_boolean_value(string);
+ case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return parse_number_value(string);
+ case 'n':
+ return parse_null_value(string);
+ default:
+ return NULL;
+ }
+}
+
+static JSON_VALUE * parse_object_value(char **string, UINT nesting) {
+ JSON_VALUE *output_value = JsonNewObject(), *new_value = NULL;
+ JSON_OBJECT *output_object = JsonValueGetObject(output_value);
+ char *new_key = NULL;
+ if (output_value == NULL || **string != '{') {
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == '}') { /* empty object */
+ SKIP_CHAR(string);
+ return output_value;
+ }
+ while (**string != '\0') {
+ new_key = get_quoted_string(string);
+ if (new_key == NULL) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ':') {
+ parson_free(new_key);
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ new_value = parse_value(string, nesting);
+ if (new_value == NULL) {
+ parson_free(new_key);
+ JsonFree(output_value);
+ return NULL;
+ }
+ if (json_object_add(output_object, new_key, new_value) == JSON_RET_ERROR) {
+ parson_free(new_key);
+ JsonFree(new_value);
+ JsonFree(output_value);
+ return NULL;
+ }
+ parson_free(new_key);
+ SKIP_WHITESPACES(string);
+ if (**string != ',') {
+ break;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != '}' || /* Trim object after parsing is over */
+ json_object_resize(output_object, JsonGetCount(output_object)) == JSON_RET_ERROR) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ return output_value;
+}
+
+static JSON_VALUE * parse_array_value(char **string, UINT nesting) {
+ JSON_VALUE *output_value = JsonNewArray(), *new_array_value = NULL;
+ JSON_ARRAY *output_array = JsonValueGetArray(output_value);
+ if (!output_value || **string != '[') {
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == ']') { /* empty array */
+ SKIP_CHAR(string);
+ return output_value;
+ }
+ while (**string != '\0') {
+ new_array_value = parse_value(string, nesting);
+ if (new_array_value == NULL) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ if (json_array_add(output_array, new_array_value) == JSON_RET_ERROR) {
+ JsonFree(new_array_value);
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ',') {
+ break;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ']' || /* Trim array after parsing is over */
+ json_array_resize(output_array, JsonArrayGetCount(output_array)) == JSON_RET_ERROR) {
+ JsonFree(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ return output_value;
+}
+
+static JSON_VALUE * parse_string_value(char **string) {
+ JSON_VALUE *value = NULL;
+ char *new_string = get_quoted_string(string);
+ if (new_string == NULL) {
+ return NULL;
+ }
+ value = json_value_init_string_no_copy(new_string);
+ if (value == NULL) {
+ parson_free(new_string);
+ return NULL;
+ }
+ return value;
+}
+
+static JSON_VALUE * parse_boolean_value(char **string) {
+ UINT true_token_size = SIZEOF_TOKEN("true");
+ UINT false_token_size = SIZEOF_TOKEN("false");
+ if (strncmp("true", *string, true_token_size) == 0) {
+ *string += true_token_size;
+ return JsonNewBool(1);
+ }
+ else if (strncmp("false", *string, false_token_size) == 0) {
+ *string += false_token_size;
+ return JsonNewBool(0);
+ }
+ return NULL;
+}
+
+static JSON_VALUE * parse_number_value(char **string) {
+ char *end;
+ bool error = false;
+ UINT64 number = 0;
+ number = Json_ToInt64Ex(*string, &end, &error);
+
+ if (error)
+ {
+ return NULL;
+ }
+ *string = end;
+ return JsonNewNumber(number);
+}
+
+static JSON_VALUE * parse_null_value(char **string) {
+ UINT token_size = SIZEOF_TOKEN("null");
+ if (strncmp("null", *string, token_size) == 0) {
+ *string += token_size;
+ return JsonNewNull();
+ }
+ return NULL;
+}
+
+/* Serialization */
+#define APPEND_STRING(str) do { written = append_string(buf, (str));\
+ if (written < 0) { return -1; }\
+ if (buf != NULL) { buf += written; }\
+ written_total += written; } while(0)
+
+#define APPEND_INDENT(level) do { written = append_indent(buf, (level));\
+ if (written < 0) { return -1; }\
+ if (buf != NULL) { buf += written; }\
+ written_total += written; } while(0)
+
+static int json_serialize_to_buffer_r(JSON_VALUE *value, char *buf, int level, int is_pretty, char *num_buf)
+{
+ char *key = NULL, *string = NULL;
+ JSON_VALUE *temp_value = NULL;
+ JSON_ARRAY *array = NULL;
+ JSON_OBJECT *object = NULL;
+ UINT i = 0, count = 0;
+ UINT64 num = 0;
+ int written = -1, written_total = 0;
+ char tmp[32];
+
+ switch (JsonValueGetType(value)) {
+ case JSON_TYPE_ARRAY:
+ array = JsonValueGetArray(value);
+ count = JsonArrayGetCount(array);
+ APPEND_STRING("[");
+ if (count > 0 && is_pretty) {
+ APPEND_STRING("\n");
+ }
+ for (i = 0; i < count; i++) {
+ if (is_pretty) {
+ APPEND_INDENT(level + 1);
+ }
+ temp_value = JsonArrayGet(array, i);
+ written = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ if (i < (count - 1)) {
+ APPEND_STRING(",");
+ }
+ if (is_pretty) {
+ APPEND_STRING("\n");
+ }
+ }
+ if (count > 0 && is_pretty) {
+ APPEND_INDENT(level);
+ }
+ APPEND_STRING("]");
+ return written_total;
+ case JSON_TYPE_OBJECT:
+ object = JsonValueGetObject(value);
+ count = JsonGetCount(object);
+ APPEND_STRING("{");
+ if (count > 0 && is_pretty) {
+ APPEND_STRING("\n");
+ }
+ for (i = 0; i < count; i++) {
+ key = JsonGetName(object, i);
+ if (key == NULL) {
+ return -1;
+ }
+ if (is_pretty) {
+ APPEND_INDENT(level + 1);
+ }
+ written = json_serialize_string(key, buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ APPEND_STRING(":");
+ if (is_pretty) {
+ APPEND_STRING(" ");
+ }
+ temp_value = JsonGet(object, key);
+ written = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ if (i < (count - 1)) {
+ APPEND_STRING(",");
+ }
+ if (is_pretty) {
+ APPEND_STRING("\n");
+ }
+ }
+ if (count > 0 && is_pretty) {
+ APPEND_INDENT(level);
+ }
+ APPEND_STRING("}");
+ return written_total;
+ case JSON_TYPE_STRING:
+ string = JsonValueGetStr(value);
+ if (string == NULL) {
+ return -1;
+ }
+ written = json_serialize_string(string, buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ return written_total;
+ case JSON_TYPE_BOOL:
+ if (JsonValueGetBool(value)) {
+ APPEND_STRING("true");
+ }
+ else {
+ APPEND_STRING("false");
+ }
+ return written_total;
+ case JSON_TYPE_NUMBER:
+ num = JsonValueGetNumber(value);
+ if (buf != NULL) {
+ num_buf = buf;
+ }
+ ToStr64(tmp, num);
+ Copy(num_buf, tmp, StrLen(tmp));
+ written = StrLen(tmp);
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ return written_total;
+ case JSON_TYPE_NULL:
+ APPEND_STRING("null");
+ return written_total;
+ case JSON_TYPE_ERROR:
+ return -1;
+ default:
+ return -1;
+ }
+}
+
+static int json_serialize_string(char *string, char *buf) {
+ UINT i = 0, len = StrLen(string);
+ char c = '\0';
+ int written = -1, written_total = 0;
+ APPEND_STRING("\"");
+ for (i = 0; i < len; i++) {
+ c = string[i];
+ switch (c) {
+ case '\"': APPEND_STRING("\\\""); break;
+ case '\\': APPEND_STRING("\\\\"); break;
+ case '/': APPEND_STRING("\\/"); break; /* to make json embeddable in xml\/html */
+ case '\b': APPEND_STRING("\\b"); break;
+ case '\f': APPEND_STRING("\\f"); break;
+ case '\n': APPEND_STRING("\\n"); break;
+ case '\r': APPEND_STRING("\\r"); break;
+ case '\t': APPEND_STRING("\\t"); break;
+ case '\x00': APPEND_STRING("\\u0000"); break;
+ case '\x01': APPEND_STRING("\\u0001"); break;
+ case '\x02': APPEND_STRING("\\u0002"); break;
+ case '\x03': APPEND_STRING("\\u0003"); break;
+ case '\x04': APPEND_STRING("\\u0004"); break;
+ case '\x05': APPEND_STRING("\\u0005"); break;
+ case '\x06': APPEND_STRING("\\u0006"); break;
+ case '\x07': APPEND_STRING("\\u0007"); break;
+ /* '\x08' duplicate: '\b' */
+ /* '\x09' duplicate: '\t' */
+ /* '\x0a' duplicate: '\n' */
+ case '\x0b': APPEND_STRING("\\u000b"); break;
+ /* '\x0c' duplicate: '\f' */
+ /* '\x0d' duplicate: '\r' */
+ case '\x0e': APPEND_STRING("\\u000e"); break;
+ case '\x0f': APPEND_STRING("\\u000f"); break;
+ case '\x10': APPEND_STRING("\\u0010"); break;
+ case '\x11': APPEND_STRING("\\u0011"); break;
+ case '\x12': APPEND_STRING("\\u0012"); break;
+ case '\x13': APPEND_STRING("\\u0013"); break;
+ case '\x14': APPEND_STRING("\\u0014"); break;
+ case '\x15': APPEND_STRING("\\u0015"); break;
+ case '\x16': APPEND_STRING("\\u0016"); break;
+ case '\x17': APPEND_STRING("\\u0017"); break;
+ case '\x18': APPEND_STRING("\\u0018"); break;
+ case '\x19': APPEND_STRING("\\u0019"); break;
+ case '\x1a': APPEND_STRING("\\u001a"); break;
+ case '\x1b': APPEND_STRING("\\u001b"); break;
+ case '\x1c': APPEND_STRING("\\u001c"); break;
+ case '\x1d': APPEND_STRING("\\u001d"); break;
+ case '\x1e': APPEND_STRING("\\u001e"); break;
+ case '\x1f': APPEND_STRING("\\u001f"); break;
+ default:
+ if (buf != NULL) {
+ buf[0] = c;
+ buf += 1;
+ }
+ written_total += 1;
+ break;
+ }
+ }
+ APPEND_STRING("\"");
+ return written_total;
+}
+
+static int append_indent(char *buf, int level) {
+ int i;
+ int written = -1, written_total = 0;
+ for (i = 0; i < level; i++) {
+ APPEND_STRING(" ");
+ }
+ return written_total;
+}
+
+static int append_string(char *buf, char *string) {
+ if (buf == NULL) {
+ return (int)strlen(string);
+ }
+ return sprintf(buf, "%s", string);
+}
+
+#undef APPEND_STRING
+#undef APPEND_INDENT
+
+JSON_VALUE * JsonParseString(char *string) {
+ if (string == NULL) {
+ return NULL;
+ }
+ if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') {
+ string = string + 3; /* Support for UTF-8 BOM */
+ }
+ return parse_value((char**)&string, 0);
+}
+
+JSON_VALUE * JsonParseStringWithComments(char *string) {
+ JSON_VALUE *result = NULL;
+ char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;
+ string_mutable_copy = parson_strdup(string);
+ if (string_mutable_copy == NULL) {
+ return NULL;
+ }
+ remove_comments(string_mutable_copy, "/*", "*/");
+ remove_comments(string_mutable_copy, "//", "\n");
+ string_mutable_copy_ptr = string_mutable_copy;
+ result = parse_value((char**)&string_mutable_copy_ptr, 0);
+ parson_free(string_mutable_copy);
+ return result;
+}
+
+/* JSON Object API */
+
+JSON_VALUE * JsonGet(JSON_OBJECT *object, char *name) {
+ if (object == NULL || name == NULL) {
+ return NULL;
+ }
+ return json_object_nget_value(object, name, StrLen(name));
+}
+
+char * JsonGetStr(JSON_OBJECT *object, char *name) {
+ return JsonValueGetStr(JsonGet(object, name));
+}
+
+UINT64 JsonGetNumber(JSON_OBJECT *object, char *name) {
+ return JsonValueGetNumber(JsonGet(object, name));
+}
+
+JSON_OBJECT * JsonGetObj(JSON_OBJECT *object, char *name) {
+ return JsonValueGetObject(JsonGet(object, name));
+}
+
+JSON_ARRAY * JsonGetArray(JSON_OBJECT *object, char *name) {
+ return JsonValueGetArray(JsonGet(object, name));
+}
+
+bool JsonGetBool(JSON_OBJECT *object, char *name) {
+ return JsonValueGetBool(JsonGet(object, name));
+}
+
+JSON_VALUE * JsonDotGet(JSON_OBJECT *object, char *name) {
+ char *dot_position = strchr(name, '.');
+ if (!dot_position) {
+ return JsonGet(object, name);
+ }
+ object = JsonValueGetObject(json_object_nget_value(object, name, (UINT)(dot_position - name)));
+ return JsonDotGet(object, dot_position + 1);
+}
+
+char * JsonDotGetStr(JSON_OBJECT *object, char *name) {
+ return JsonValueGetStr(JsonDotGet(object, name));
+}
+
+UINT64 JsonDotGetNumber(JSON_OBJECT *object, char *name) {
+ return JsonValueGetNumber(JsonDotGet(object, name));
+}
+
+JSON_OBJECT * JsonDotGetObj(JSON_OBJECT *object, char *name) {
+ return JsonValueGetObject(JsonDotGet(object, name));
+}
+
+JSON_ARRAY * JsonDotGetArray(JSON_OBJECT *object, char *name) {
+ return JsonValueGetArray(JsonDotGet(object, name));
+}
+
+bool JsonDotGetBool(JSON_OBJECT *object, char *name) {
+ return JsonValueGetBool(JsonDotGet(object, name));
+}
+
+UINT JsonGetCount(JSON_OBJECT *object) {
+ return object ? object->count : 0;
+}
+
+char * JsonGetName(JSON_OBJECT *object, UINT index) {
+ if (object == NULL || index >= JsonGetCount(object)) {
+ return NULL;
+ }
+ return object->names[index];
+}
+
+JSON_VALUE * JsonGetValueAt(JSON_OBJECT *object, UINT index) {
+ if (object == NULL || index >= JsonGetCount(object)) {
+ return NULL;
+ }
+ return object->values[index];
+}
+
+JSON_VALUE *JsonGetWrappingValue(JSON_OBJECT *object) {
+ return object->wrapping_value;
+}
+
+int JsonIsExists(JSON_OBJECT *object, char *name) {
+ return JsonGet(object, name) != NULL;
+}
+
+int JsonIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type) {
+ JSON_VALUE *val = JsonGet(object, name);
+ return val != NULL && JsonValueGetType(val) == type;
+}
+
+int JsonDotIsExists(JSON_OBJECT *object, char *name) {
+ return JsonDotGet(object, name) != NULL;
+}
+
+int JsonDotIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type) {
+ JSON_VALUE *val = JsonDotGet(object, name);
+ return val != NULL && JsonValueGetType(val) == type;
+}
+
+/* JSON Array API */
+JSON_VALUE * JsonArrayGet(JSON_ARRAY *array, UINT index) {
+ if (array == NULL || index >= JsonArrayGetCount(array)) {
+ return NULL;
+ }
+ return array->items[index];
+}
+
+char * JsonArrayGetStr(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetStr(JsonArrayGet(array, index));
+}
+
+UINT64 JsonArrayGetNumber(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetNumber(JsonArrayGet(array, index));
+}
+
+JSON_OBJECT * JsonArrayGetObj(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetObject(JsonArrayGet(array, index));
+}
+
+JSON_ARRAY * JsonArrayGetArray(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetArray(JsonArrayGet(array, index));
+}
+
+bool JsonArrayGetBool(JSON_ARRAY *array, UINT index) {
+ return JsonValueGetBool(JsonArrayGet(array, index));
+}
+
+UINT JsonArrayGetCount(JSON_ARRAY *array) {
+ return array ? array->count : 0;
+}
+
+JSON_VALUE * JsonArrayGetWrappingValue(JSON_ARRAY *array) {
+ return array->wrapping_value;
+}
+
+/* JSON Value API */
+UINT JsonValueGetType(JSON_VALUE *value) {
+ return value ? value->type : JSON_TYPE_ERROR;
+}
+
+JSON_OBJECT * JsonValueGetObject(JSON_VALUE *value) {
+ if (value == NULL)
+ {
+ return NULL;
+ }
+ return JsonValueGetType(value) == JSON_TYPE_OBJECT ? value->value.object : NULL;
+}
+
+JSON_ARRAY * JsonValueGetArray(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_ARRAY ? value->value.array : NULL;
+}
+
+char * JsonValueGetStr(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_STRING ? value->value.string : NULL;
+}
+
+UINT64 JsonValueGetNumber(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_NUMBER ? value->value.number : 0;
+}
+
+bool JsonValueGetBool(JSON_VALUE *value) {
+ return JsonValueGetType(value) == JSON_TYPE_BOOL ? value->value.boolean : 0;
+}
+
+JSON_VALUE * JsonValueGetParent(JSON_VALUE *value) {
+ return value ? value->parent : NULL;
+}
+
+void JsonFree(JSON_VALUE *value) {
+ if (value == NULL)
+ {
+ return;
+ }
+ switch (JsonValueGetType(value)) {
+ case JSON_TYPE_OBJECT:
+ json_object_free(value->value.object);
+ break;
+ case JSON_TYPE_STRING:
+ parson_free(value->value.string);
+ break;
+ case JSON_TYPE_ARRAY:
+ json_array_free(value->value.array);
+ break;
+ default:
+ break;
+ }
+ parson_free(value);
+}
+
+JSON_VALUE * JsonNewObject(void) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_OBJECT;
+ new_value->value.object = json_object_init(new_value);
+ if (!new_value->value.object) {
+ parson_free(new_value);
+ return NULL;
+ }
+ return new_value;
+}
+
+JSON_VALUE * JsonNewArray(void) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_ARRAY;
+ new_value->value.array = json_array_init(new_value);
+ if (!new_value->value.array) {
+ parson_free(new_value);
+ return NULL;
+ }
+ return new_value;
+}
+
+JSON_VALUE * JsonNewStr(char *string) {
+ char *copy = NULL;
+ JSON_VALUE *value;
+ UINT string_len = 0;
+ if (string == NULL) {
+ return NULL;
+ }
+ string_len = StrLen(string);
+ if (!is_valid_utf8(string, string_len)) {
+ return NULL;
+ }
+ copy = parson_strndup(string, string_len);
+ if (copy == NULL) {
+ return NULL;
+ }
+ value = json_value_init_string_no_copy(copy);
+ if (value == NULL) {
+ parson_free(copy);
+ }
+ return value;
+}
+
+JSON_VALUE * JsonNewNumber(UINT64 number) {
+ JSON_VALUE *new_value = NULL;
+ new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (new_value == NULL) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_NUMBER;
+ new_value->value.number = number;
+ return new_value;
+}
+
+JSON_VALUE * JsonNewBool(int boolean) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_BOOL;
+ new_value->value.boolean = boolean ? 1 : 0;
+ return new_value;
+}
+
+JSON_VALUE * JsonNewNull(void) {
+ JSON_VALUE *new_value = (JSON_VALUE*)parson_malloc(sizeof(JSON_VALUE));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSON_TYPE_NULL;
+ return new_value;
+}
+
+JSON_VALUE * JsonDeepCopy(JSON_VALUE *value) {
+ UINT i = 0;
+ JSON_VALUE *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL;
+ char *temp_string = NULL, *temp_key = NULL;
+ char *temp_string_copy = NULL;
+ JSON_ARRAY *temp_array = NULL, *temp_array_copy = NULL;
+ JSON_OBJECT *temp_object = NULL, *temp_object_copy = NULL;
+
+ switch (JsonValueGetType(value)) {
+ case JSON_TYPE_ARRAY:
+ temp_array = JsonValueGetArray(value);
+ return_value = JsonNewArray();
+ if (return_value == NULL) {
+ return NULL;
+ }
+ temp_array_copy = JsonValueGetArray(return_value);
+ for (i = 0; i < JsonArrayGetCount(temp_array); i++) {
+ temp_value = JsonArrayGet(temp_array, i);
+ temp_value_copy = JsonDeepCopy(temp_value);
+ if (temp_value_copy == NULL) {
+ JsonFree(return_value);
+ return NULL;
+ }
+ if (json_array_add(temp_array_copy, temp_value_copy) == JSON_RET_ERROR) {
+ JsonFree(return_value);
+ JsonFree(temp_value_copy);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSON_TYPE_OBJECT:
+ temp_object = JsonValueGetObject(value);
+ return_value = JsonNewObject();
+ if (return_value == NULL) {
+ return NULL;
+ }
+ temp_object_copy = JsonValueGetObject(return_value);
+ for (i = 0; i < JsonGetCount(temp_object); i++) {
+ temp_key = JsonGetName(temp_object, i);
+ temp_value = JsonGet(temp_object, temp_key);
+ temp_value_copy = JsonDeepCopy(temp_value);
+ if (temp_value_copy == NULL) {
+ JsonFree(return_value);
+ return NULL;
+ }
+ if (json_object_add(temp_object_copy, temp_key, temp_value_copy) == JSON_RET_ERROR) {
+ JsonFree(return_value);
+ JsonFree(temp_value_copy);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSON_TYPE_BOOL:
+ return JsonNewBool(JsonValueGetBool(value));
+ case JSON_TYPE_NUMBER:
+ return JsonNewNumber(JsonValueGetNumber(value));
+ case JSON_TYPE_STRING:
+ temp_string = JsonValueGetStr(value);
+ if (temp_string == NULL) {
+ return NULL;
+ }
+ temp_string_copy = parson_strdup(temp_string);
+ if (temp_string_copy == NULL) {
+ return NULL;
+ }
+ return_value = json_value_init_string_no_copy(temp_string_copy);
+ if (return_value == NULL) {
+ parson_free(temp_string_copy);
+ }
+ return return_value;
+ case JSON_TYPE_NULL:
+ return JsonNewNull();
+ case JSON_TYPE_ERROR:
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
+UINT JsonGetSerializationSize(JSON_VALUE *value) {
+ char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf);
+ return res < 0 ? 0 : (UINT)(res + 1);
+}
+
+UINT JsonSerializeToBuffer(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes) {
+ int written = -1;
+ UINT needed_size_in_bytes = JsonGetSerializationSize(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
+ return JSON_RET_ERROR;
+ }
+ written = json_serialize_to_buffer_r(value, buf, 0, 0, NULL);
+ if (written < 0) {
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+char * JsonSerializeToString(JSON_VALUE *value) {
+ UINT serialization_result = JSON_RET_ERROR;
+ UINT buf_size_bytes = JsonGetSerializationSize(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char*)parson_malloc(buf_size_bytes);
+ if (buf == NULL) {
+ return NULL;
+ }
+ serialization_result = JsonSerializeToBuffer(value, buf, buf_size_bytes);
+ if (serialization_result == JSON_RET_ERROR) {
+ JsonFreeString(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+UINT JsonGetSerializationSizePretty(JSON_VALUE *value) {
+ char num_buf[1100]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf);
+ return res < 0 ? 0 : (UINT)(res + 1);
+}
+
+UINT JsonSerializeToBufferPretty(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes) {
+ int written = -1;
+ UINT needed_size_in_bytes = JsonGetSerializationSizePretty(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
+ return JSON_RET_ERROR;
+ }
+ written = json_serialize_to_buffer_r(value, buf, 0, 1, NULL);
+ if (written < 0) {
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+JSON_VALUE *StrToJson(char *str)
+{
+ if (str == NULL)
+ {
+ return NULL;
+ }
+
+ return JsonParseString(str);
+}
+
+char *JsonToStr(JSON_VALUE *v)
+{
+ return JsonSerializeToStringPretty(v);
+}
+char * JsonSerializeToStringPretty(JSON_VALUE *value) {
+ UINT serialization_result = JSON_RET_ERROR;
+ UINT buf_size_bytes = JsonGetSerializationSizePretty(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char*)parson_malloc(buf_size_bytes);
+ if (buf == NULL) {
+ return NULL;
+ }
+ serialization_result = JsonSerializeToBufferPretty(value, buf, buf_size_bytes);
+ if (serialization_result == JSON_RET_ERROR) {
+ JsonFreeString(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+void JsonFreeString(char *string) {
+ parson_free(string);
+}
+
+UINT JsonArrayDelete(JSON_ARRAY *array, UINT ix) {
+ UINT to_move_bytes = 0;
+ if (array == NULL || ix >= JsonArrayGetCount(array)) {
+ return JSON_RET_ERROR;
+ }
+ JsonFree(JsonArrayGet(array, ix));
+ to_move_bytes = (JsonArrayGetCount(array) - 1 - ix) * sizeof(JSON_VALUE*);
+ memmove(array->items + ix, array->items + ix + 1, to_move_bytes);
+ array->count -= 1;
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplace(JSON_ARRAY *array, UINT ix, JSON_VALUE *value) {
+ if (array == NULL || value == NULL || value->parent != NULL || ix >= JsonArrayGetCount(array)) {
+ return JSON_RET_ERROR;
+ }
+ JsonFree(JsonArrayGet(array, ix));
+ value->parent = JsonArrayGetWrappingValue(array);
+ array->items[ix] = value;
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceStr(JSON_ARRAY *array, UINT i, char* string) {
+ JSON_VALUE *value = JsonNewStr(string);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceNumber(JSON_ARRAY *array, UINT i, UINT64 number) {
+ JSON_VALUE *value = JsonNewNumber(number);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceBool(JSON_ARRAY *array, UINT i, int boolean) {
+ JSON_VALUE *value = JsonNewBool(boolean);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayReplaceNull(JSON_ARRAY *array, UINT i) {
+ JSON_VALUE *value = JsonNewNull();
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayReplace(array, i, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayDeleteAll(JSON_ARRAY *array) {
+ UINT i = 0;
+ if (array == NULL) {
+ return JSON_RET_ERROR;
+ }
+ for (i = 0; i < JsonArrayGetCount(array); i++) {
+ JsonFree(JsonArrayGet(array, i));
+ }
+ array->count = 0;
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAdd(JSON_ARRAY *array, JSON_VALUE *value) {
+ if (array == NULL || value == NULL || value->parent != NULL) {
+ return JSON_RET_ERROR;
+ }
+ return json_array_add(array, value);
+}
+
+UINT JsonArrayAddStr(JSON_ARRAY *array, char *string) {
+ JSON_VALUE *value = JsonNewStr(string);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAddUniStr(JSON_ARRAY *array, wchar_t *string)
+{
+ UINT ret;
+ char *utf8 = CopyUniToUtf(string);
+
+ ret = JsonArrayAddStr(array, utf8);
+
+ Free(utf8);
+ return ret;
+}
+
+UINT JsonArrayAddNumber(JSON_ARRAY *array, UINT64 number) {
+ JSON_VALUE *value = JsonNewNumber(number);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAddData(JSON_ARRAY *array, void *data, UINT size)
+{
+ UINT ret;
+ char *b64 = ZeroMalloc(size * 4 + 32);
+ B64_Encode(b64, data, size);
+
+ ret = JsonArrayAddStr(array, b64);
+
+ Free(b64);
+ return ret;
+}
+
+UINT JsonArrayAddBool(JSON_ARRAY *array, int boolean) {
+ JSON_VALUE *value = JsonNewBool(boolean);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonArrayAddNull(JSON_ARRAY *array) {
+ JSON_VALUE *value = JsonNewNull();
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonArrayAdd(array, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonSet(JSON_OBJECT *object, char *name, JSON_VALUE *value) {
+ UINT i = 0;
+ JSON_VALUE *old_value;
+ if (object == NULL || name == NULL || value == NULL || value->parent != NULL) {
+ return JSON_RET_ERROR;
+ }
+ old_value = JsonGet(object, name);
+ if (old_value != NULL) { /* free and overwrite old value */
+ JsonFree(old_value);
+ for (i = 0; i < JsonGetCount(object); i++) {
+ if (strcmp(object->names[i], name) == 0) {
+ value->parent = JsonGetWrappingValue(object);
+ object->values[i] = value;
+ return JSON_RET_OK;
+ }
+ }
+ }
+ /* add new key value pair */
+ return json_object_add(object, name, value);
+}
+
+UINT JsonSetData(JSON_OBJECT *object, char *name, void *data, UINT size)
+{
+ UINT ret;
+ char *b64 = ZeroMalloc(size * 4 + 32);
+ B64_Encode(b64, data, size);
+
+ ret = JsonSetStr(object, name, b64);
+
+ Free(b64);
+ return ret;
+}
+
+UINT JsonSetStr(JSON_OBJECT *object, char *name, char *string) {
+ return JsonSet(object, name, JsonNewStr(string));
+}
+
+UINT JsonSetUniStr(JSON_OBJECT *object, char *name, wchar_t *string)
+{
+ UINT ret;
+ char *utf8 = CopyUniToUtf(string);
+
+ ret = JsonSetStr(object, name, utf8);
+
+ Free(utf8);
+ return ret;
+}
+
+UINT JsonSetNumber(JSON_OBJECT *object, char *name, UINT64 number) {
+ return JsonSet(object, name, JsonNewNumber(number));
+}
+
+UINT JsonSetBool(JSON_OBJECT *object, char *name, int boolean) {
+ return JsonSet(object, name, JsonNewBool(boolean));
+}
+
+UINT JsonSetNull(JSON_OBJECT *object, char *name) {
+ return JsonSet(object, name, JsonNewNull());
+}
+
+UINT JsonDotSet(JSON_OBJECT *object, char *name, JSON_VALUE *value) {
+ char *dot_pos = NULL;
+ char *current_name = NULL;
+ JSON_OBJECT *temp_obj = NULL;
+ JSON_VALUE *new_value = NULL;
+ if (object == NULL || name == NULL || value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ dot_pos = strchr(name, '.');
+ if (dot_pos == NULL) {
+ return JsonSet(object, name, value);
+ }
+ else {
+ current_name = parson_strndup(name, (UINT)(dot_pos - name));
+ temp_obj = JsonGetObj(object, current_name);
+ if (temp_obj == NULL) {
+ new_value = JsonNewObject();
+ if (new_value == NULL) {
+ parson_free(current_name);
+ return JSON_RET_ERROR;
+ }
+ if (json_object_add(object, current_name, new_value) == JSON_RET_ERROR) {
+ JsonFree(new_value);
+ parson_free(current_name);
+ return JSON_RET_ERROR;
+ }
+ temp_obj = JsonGetObj(object, current_name);
+ }
+ parson_free(current_name);
+ return JsonDotSet(temp_obj, dot_pos + 1, value);
+ }
+}
+
+UINT JsonDotSetStr(JSON_OBJECT *object, char *name, char *string) {
+ JSON_VALUE *value = JsonNewStr(string);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDotSetNumber(JSON_OBJECT *object, char *name, UINT64 number) {
+ JSON_VALUE *value = JsonNewNumber(number);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDotSetBool(JSON_OBJECT *object, char *name, int boolean) {
+ JSON_VALUE *value = JsonNewBool(boolean);
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDotSetNull(JSON_OBJECT *object, char *name) {
+ JSON_VALUE *value = JsonNewNull();
+ if (value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonDotSet(object, name, value) == JSON_RET_ERROR) {
+ JsonFree(value);
+ return JSON_RET_ERROR;
+ }
+ return JSON_RET_OK;
+}
+
+UINT JsonDelete(JSON_OBJECT *object, char *name) {
+ UINT i = 0, last_item_index = 0;
+ if (object == NULL || JsonGet(object, name) == NULL) {
+ return JSON_RET_ERROR;
+ }
+ last_item_index = JsonGetCount(object) - 1;
+ for (i = 0; i < JsonGetCount(object); i++) {
+ if (strcmp(object->names[i], name) == 0) {
+ parson_free(object->names[i]);
+ JsonFree(object->values[i]);
+ if (i != last_item_index) { /* Replace key value pair with one from the end */
+ object->names[i] = object->names[last_item_index];
+ object->values[i] = object->values[last_item_index];
+ }
+ object->count -= 1;
+ return JSON_RET_OK;
+ }
+ }
+ return JSON_RET_ERROR; /* No execution path should end here */
+}
+
+UINT JsonDotDelete(JSON_OBJECT *object, char *name) {
+ char *dot_pos = strchr(name, '.');
+ char *current_name = NULL;
+ JSON_OBJECT *temp_obj = NULL;
+ if (dot_pos == NULL) {
+ return JsonDelete(object, name);
+ }
+ else {
+ current_name = parson_strndup(name, (UINT)(dot_pos - name));
+ temp_obj = JsonGetObj(object, current_name);
+ parson_free(current_name);
+ if (temp_obj == NULL) {
+ return JSON_RET_ERROR;
+ }
+ return JsonDotDelete(temp_obj, dot_pos + 1);
+ }
+}
+
+UINT JsonDeleteAll(JSON_OBJECT *object) {
+ UINT i = 0;
+ if (object == NULL) {
+ return JSON_RET_ERROR;
+ }
+ for (i = 0; i < JsonGetCount(object); i++) {
+ parson_free(object->names[i]);
+ JsonFree(object->values[i]);
+ }
+ object->count = 0;
+ return JSON_RET_OK;
+}
+
+UINT JsonValidate(JSON_VALUE *schema, JSON_VALUE *value) {
+ JSON_VALUE *temp_schema_value = NULL, *temp_value = NULL;
+ JSON_ARRAY *schema_array = NULL, *value_array = NULL;
+ JSON_OBJECT *schema_object = NULL, *value_object = NULL;
+ UINT schema_type = JSON_TYPE_ERROR, value_type = JSON_TYPE_ERROR;
+ char *key = NULL;
+ UINT i = 0, count = 0;
+ if (schema == NULL || value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ schema_type = JsonValueGetType(schema);
+ value_type = JsonValueGetType(value);
+ if (schema_type != value_type && schema_type != JSON_TYPE_NULL) { /* null represents all values */
+ return JSON_RET_ERROR;
+ }
+ switch (schema_type) {
+ case JSON_TYPE_ARRAY:
+ schema_array = JsonValueGetArray(schema);
+ value_array = JsonValueGetArray(value);
+ count = JsonArrayGetCount(schema_array);
+ if (count == 0) {
+ return JSON_RET_OK; /* Empty array allows all types */
+ }
+ /* Get first value from array, rest is ignored */
+ temp_schema_value = JsonArrayGet(schema_array, 0);
+ for (i = 0; i < JsonArrayGetCount(value_array); i++) {
+ temp_value = JsonArrayGet(value_array, i);
+ if (JsonValidate(temp_schema_value, temp_value) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ return JSON_RET_OK;
+ case JSON_TYPE_OBJECT:
+ schema_object = JsonValueGetObject(schema);
+ value_object = JsonValueGetObject(value);
+ count = JsonGetCount(schema_object);
+ if (count == 0) {
+ return JSON_RET_OK; /* Empty object allows all objects */
+ }
+ else if (JsonGetCount(value_object) < count) {
+ return JSON_RET_ERROR; /* Tested object mustn't have less name-value pairs than schema */
+ }
+ for (i = 0; i < count; i++) {
+ key = JsonGetName(schema_object, i);
+ temp_schema_value = JsonGet(schema_object, key);
+ temp_value = JsonGet(value_object, key);
+ if (temp_value == NULL) {
+ return JSON_RET_ERROR;
+ }
+ if (JsonValidate(temp_schema_value, temp_value) == JSON_RET_ERROR) {
+ return JSON_RET_ERROR;
+ }
+ }
+ return JSON_RET_OK;
+ case JSON_TYPE_STRING: case JSON_TYPE_NUMBER: case JSON_TYPE_BOOL: case JSON_TYPE_NULL:
+ return JSON_RET_OK; /* equality already tested before switch */
+ case JSON_TYPE_ERROR: default:
+ return JSON_RET_ERROR;
+ }
+}
+
+int JsonCmp(JSON_VALUE *a, JSON_VALUE *b) {
+ JSON_OBJECT *a_object = NULL, *b_object = NULL;
+ JSON_ARRAY *a_array = NULL, *b_array = NULL;
+ char *a_string = NULL, *b_string = NULL;
+ char *key = NULL;
+ UINT a_count = 0, b_count = 0, i = 0;
+ UINT a_type, b_type;
+ UINT64 a_num, b_num;
+ a_type = JsonValueGetType(a);
+ b_type = JsonValueGetType(b);
+ if (a_type != b_type) {
+ return 0;
+ }
+ switch (a_type) {
+ case JSON_TYPE_ARRAY:
+ a_array = JsonValueGetArray(a);
+ b_array = JsonValueGetArray(b);
+ a_count = JsonArrayGetCount(a_array);
+ b_count = JsonArrayGetCount(b_array);
+ if (a_count != b_count) {
+ return 0;
+ }
+ for (i = 0; i < a_count; i++) {
+ if (!JsonCmp(JsonArrayGet(a_array, i),
+ JsonArrayGet(b_array, i))) {
+ return 0;
+ }
+ }
+ return 1;
+ case JSON_TYPE_OBJECT:
+ a_object = JsonValueGetObject(a);
+ b_object = JsonValueGetObject(b);
+ a_count = JsonGetCount(a_object);
+ b_count = JsonGetCount(b_object);
+ if (a_count != b_count) {
+ return 0;
+ }
+ for (i = 0; i < a_count; i++) {
+ key = JsonGetName(a_object, i);
+ if (!JsonCmp(JsonGet(a_object, key),
+ JsonGet(b_object, key))) {
+ return 0;
+ }
+ }
+ return 1;
+ case JSON_TYPE_STRING:
+ a_string = JsonValueGetStr(a);
+ b_string = JsonValueGetStr(b);
+ if (a_string == NULL || b_string == NULL) {
+ return 0; /* shouldn't happen */
+ }
+ return strcmp(a_string, b_string) == 0;
+ case JSON_TYPE_BOOL:
+ return JsonValueGetBool(a) == JsonValueGetBool(b);
+ case JSON_TYPE_NUMBER:
+ a_num = JsonValueGetNumber(a);
+ b_num = JsonValueGetNumber(b);
+ return a_num == b_num;
+ case JSON_TYPE_ERROR:
+ return 1;
+ case JSON_TYPE_NULL:
+ return 1;
+ default:
+ return 1;
+ }
+}
+
+UINT JsonType(JSON_VALUE *value) {
+ return JsonValueGetType(value);
+}
+
+JSON_OBJECT * JsonObject(JSON_VALUE *value) {
+ return JsonValueGetObject(value);
+}
+
+JSON_ARRAY * JsonArray(JSON_VALUE *value) {
+ return JsonValueGetArray(value);
+}
+
+char * JsonString(JSON_VALUE *value) {
+ return JsonValueGetStr(value);
+}
+
+UINT64 JsonNumber(JSON_VALUE *value) {
+ return JsonValueGetNumber(value);
+}
+
+int JsonBool(JSON_VALUE *value) {
+ return JsonValueGetBool(value);
+}
+
+void JsonSetAllocationFunctions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) {
+ parson_malloc = malloc_fun;
+ parson_free = free_fun;
+}
+
+// SYSTEMTIME to JSON string
+void SystemTimeToJsonStr(char *dst, UINT size, SYSTEMTIME *t)
+{
+ if (dst == NULL)
+ {
+ return;
+ }
+
+ if (t == NULL)
+ {
+ ClearStr(dst, size);
+ }
+ else
+ {
+ GetDateTimeStrRFC3339(dst, size, t, 0);
+ }
+}
+
+// UINT64 System Time to JSON string
+void SystemTime64ToJsonStr(char *dst, UINT size, UINT64 t)
+{
+ SYSTEMTIME st;
+ if (dst == NULL)
+ {
+ return;
+ }
+
+ if (t == 0)
+ {
+ ClearStr(dst, size);
+ }
+
+ UINT64ToSystem(&st, t);
+
+ SystemTimeToJsonStr(dst, size, &st);
+}
+
+
+
+
diff --git a/src/Mayaqua/Str.h b/src/Mayaqua/Str.h
index 6a54ece6..c5ce0532 100644
--- a/src/Mayaqua/Str.h
+++ b/src/Mayaqua/Str.h
@@ -182,7 +182,9 @@ void BinToStrW(wchar_t *str, UINT str_size, void *data, UINT data_size);
void PrintBin(void *data, UINT size);
bool StartWith(char *str, char *key);
bool EndWith(char *str, char *key);
+bool TrimEndWith(char *dst, UINT dst_size, char *str, char *key);
UINT64 ToInt64(char *str);
+UINT64 Json_ToInt64Ex(char *str, char **endptr, bool *error);
void ToStr64(char *str, UINT64 value);
char *ReplaceFormatStringFor64(char *fmt);
TOKEN_LIST *ParseCmdLine(char *str);
@@ -241,6 +243,259 @@ LIST *StrToIntList(char *str, bool sorted);
void NormalizeIntListStr(char *dst, UINT dst_size, char *src, bool sorted, char *separate_str);
void ClearStr(char *str, UINT str_size);
void SetStrCaseAccordingToBits(char *str, UINT bits);
+char *UrlDecode(char *url_str);
+
+
+// *** JSON strings support
+// Original source code from Parson ( http://kgabis.github.com/parson/ )
+// Modified by dnobori
+/*
+Parson ( http://kgabis.github.com/parson/ )
+Copyright (c) 2012 - 2017 Krzysztof Gabis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+/* Type definitions */
+typedef union JSON_VALUE_UNION {
+ char *string;
+ UINT64 number;
+ JSON_OBJECT *object;
+ JSON_ARRAY *array;
+ int boolean;
+ int null;
+} JSON_VALUE_UNION;
+
+struct JSON_VALUE {
+ JSON_VALUE *parent;
+ UINT type;
+ JSON_VALUE_UNION value;
+};
+
+struct JSON_OBJECT {
+ JSON_VALUE *wrapping_value;
+ char **names;
+ JSON_VALUE **values;
+ UINT count;
+ UINT capacity;
+};
+
+struct JSON_ARRAY {
+ JSON_VALUE *wrapping_value;
+ JSON_VALUE **items;
+ UINT count;
+ UINT capacity;
+};
+
+
+enum JSON_TYPES {
+ JSON_TYPE_ERROR = -1,
+ JSON_TYPE_NULL = 1,
+ JSON_TYPE_STRING = 2,
+ JSON_TYPE_NUMBER = 3,
+ JSON_TYPE_OBJECT = 4,
+ JSON_TYPE_ARRAY = 5,
+ JSON_TYPE_BOOL = 6
+};
+//typedef unsigned int UINT;
+
+enum JSON_RETS {
+ JSON_RET_OK = 0,
+ JSON_RET_ERROR = -1
+};
+
+typedef void * (*JSON_Malloc_Function)(UINT);
+typedef void(*JSON_Free_Function)(void *);
+
+/* Call only once, before calling any other function from parson API. If not called, malloc and free
+from stdlib will be used for all allocations */
+void JsonSetAllocationFunctions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
+
+/* Parses first JSON value in a string, returns NULL in case of error */
+JSON_VALUE * JsonParseString(char *string);
+
+/* Parses first JSON value in a string and ignores comments (/ * * / and //),
+returns NULL in case of error */
+JSON_VALUE * JsonParseStringWithComments(char *string);
+
+/* Serialization */
+UINT JsonGetSerializationSize(JSON_VALUE *value); /* returns 0 on fail */
+UINT JsonSerializeToBuffer(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes);
+char * JsonSerializeToString(JSON_VALUE *value);
+
+/* Pretty serialization */
+UINT JsonGetSerializationSizePretty(JSON_VALUE *value); /* returns 0 on fail */
+UINT JsonSerializeToBufferPretty(JSON_VALUE *value, char *buf, UINT buf_size_in_bytes);
+char * JsonSerializeToStringPretty(JSON_VALUE *value);
+char *JsonToStr(JSON_VALUE *v);
+
+void JsonFreeString(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
+
+ /* Comparing */
+int JsonCmp(JSON_VALUE *a, JSON_VALUE *b);
+
+/* Validation
+This is *NOT* JSON Schema. It validates json by checking if object have identically
+named fields with matching types.
+For example schema {"name":"", "age":0} will validate
+{"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
+but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
+In case of arrays, only first value in schema is checked against all values in tested array.
+Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
+null validates values of every type.
+*/
+UINT JsonValidate(JSON_VALUE *schema, JSON_VALUE *value);
+
+/*
+* JSON Object
+*/
+JSON_VALUE * JsonGet(JSON_OBJECT *object, char *name);
+char * JsonGetStr(JSON_OBJECT *object, char *name);
+JSON_OBJECT * JsonGetObj(JSON_OBJECT *object, char *name);
+JSON_ARRAY * JsonGetArray(JSON_OBJECT *object, char *name);
+UINT64 JsonGetNumber(JSON_OBJECT *object, char *name); /* returns 0 on fail */
+bool JsonGetBool(JSON_OBJECT *object, char *name); /* returns 0 on fail */
+
+ /* dotget functions enable addressing values with dot notation in nested objects,
+ just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
+ Because valid names in JSON can contain dots, some values may be inaccessible
+ this way. */
+JSON_VALUE * JsonDotGet(JSON_OBJECT *object, char *name);
+char * JsonDotGetStr(JSON_OBJECT *object, char *name);
+JSON_OBJECT * JsonDotGetObj(JSON_OBJECT *object, char *name);
+JSON_ARRAY * JsonDotGetArray(JSON_OBJECT *object, char *name);
+UINT64 JsonDotGetNumber(JSON_OBJECT *object, char *name); /* returns 0 on fail */
+bool JsonDotGetBool(JSON_OBJECT *object, char *name); /* returns -1 on fail */
+
+ /* Functions to get available names */
+UINT JsonGetCount(JSON_OBJECT *object);
+char * JsonGetName(JSON_OBJECT *object, UINT index);
+JSON_VALUE * JsonGetValueAt(JSON_OBJECT *object, UINT index);
+JSON_VALUE * JsonGetWrappingValue(JSON_OBJECT *object);
+
+/* Functions to check if object has a value with a specific name. Returned value is 1 if object has
+* a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */
+int JsonIsExists(JSON_OBJECT *object, char *name);
+int JsonIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type);
+
+int JsonDotIsExists(JSON_OBJECT *object, char *name);
+int JsonDotIsExistsWithValueType(JSON_OBJECT *object, char *name, UINT type);
+
+/* Creates new name-value pair or frees and replaces old value with a new one.
+* json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonSet(JSON_OBJECT *object, char *name, JSON_VALUE *value);
+UINT JsonSetStr(JSON_OBJECT *object, char *name, char *string);
+UINT JsonSetUniStr(JSON_OBJECT *object, char *name, wchar_t *string);
+UINT JsonSetNumber(JSON_OBJECT *object, char *name, UINT64 number);
+UINT JsonSetBool(JSON_OBJECT *object, char *name, int boolean);
+UINT JsonSetNull(JSON_OBJECT *object, char *name);
+UINT JsonSetData(JSON_OBJECT *object, char *name, void *data, UINT size);
+
+/* Works like dotget functions, but creates whole hierarchy if necessary.
+* json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonDotSet(JSON_OBJECT *object, char *name, JSON_VALUE *value);
+UINT JsonDotSetStr(JSON_OBJECT *object, char *name, char *string);
+UINT JsonDotSetNumber(JSON_OBJECT *object, char *name, UINT64 number);
+UINT JsonDotSetBool(JSON_OBJECT *object, char *name, int boolean);
+UINT JsonDotSetNull(JSON_OBJECT *object, char *name);
+
+/* Frees and removes name-value pair */
+UINT JsonDelete(JSON_OBJECT *object, char *name);
+
+/* Works like dotget function, but removes name-value pair only on exact match. */
+UINT JsonDotDelete(JSON_OBJECT *object, char *key);
+
+/* Removes all name-value pairs in object */
+UINT JsonDeleteAll(JSON_OBJECT *object);
+
+/*
+*JSON Array
+*/
+JSON_VALUE * JsonArrayGet(JSON_ARRAY *array, UINT index);
+char * JsonArrayGetStr(JSON_ARRAY *array, UINT index);
+JSON_OBJECT * JsonArrayGetObj(JSON_ARRAY *array, UINT index);
+JSON_ARRAY * JsonArrayGetArray(JSON_ARRAY *array, UINT index);
+UINT64 JsonArrayGetNumber(JSON_ARRAY *array, UINT index); /* returns 0 on fail */
+bool JsonArrayGetBool(JSON_ARRAY *array, UINT index); /* returns 0 on fail */
+UINT JsonArrayGetCount(JSON_ARRAY *array);
+JSON_VALUE * JsonArrayGetWrappingValue(JSON_ARRAY *array);
+
+/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
+* Order of values in array may change during execution. */
+UINT JsonArrayDelete(JSON_ARRAY *array, UINT i);
+
+/* Frees and removes from array value at given index and replaces it with given one.
+* Does nothing and returns JSONFailure if index doesn't exist.
+* json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonArrayReplace(JSON_ARRAY *array, UINT i, JSON_VALUE *value);
+UINT JsonArrayReplaceStr(JSON_ARRAY *array, UINT i, char* string);
+UINT JsonArrayReplaceNumber(JSON_ARRAY *array, UINT i, UINT64 number);
+UINT JsonArrayReplaceBool(JSON_ARRAY *array, UINT i, int boolean);
+UINT JsonArrayReplaceNull(JSON_ARRAY *array, UINT i);
+
+/* Frees and removes all values from array */
+UINT JsonArrayDeleteAll(JSON_ARRAY *array);
+
+/* Appends new value at the end of array.
+* json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
+UINT JsonArrayAdd(JSON_ARRAY *array, JSON_VALUE *value);
+UINT JsonArrayAddStr(JSON_ARRAY *array, char *string);
+UINT JsonArrayAddUniStr(JSON_ARRAY *array, wchar_t *string);
+UINT JsonArrayAddNumber(JSON_ARRAY *array, UINT64 number);
+UINT JsonArrayAddData(JSON_ARRAY *array, void *data, UINT size);
+UINT JsonArrayAddBool(JSON_ARRAY *array, int boolean);
+UINT JsonArrayAddNull(JSON_ARRAY *array);
+
+
+/*
+*JSON Value
+*/
+JSON_VALUE * JsonNewObject(void);
+JSON_VALUE * JsonNewArray(void);
+JSON_VALUE * JsonNewStr(char *string); /* copies passed string */
+JSON_VALUE * JsonNewNumber(UINT64 number);
+JSON_VALUE * JsonNewBool(int boolean);
+JSON_VALUE * JsonNewNull(void);
+JSON_VALUE * JsonDeepCopy(JSON_VALUE *value);
+void JsonFree(JSON_VALUE *value);
+
+UINT JsonValueGetType(JSON_VALUE *value);
+JSON_OBJECT * JsonValueGetObject(JSON_VALUE *value);
+JSON_ARRAY * JsonValueGetArray(JSON_VALUE *value);
+char * JsonValueGetStr(JSON_VALUE *value);
+UINT64 JsonValueGetNumber(JSON_VALUE *value);
+bool JsonValueGetBool(JSON_VALUE *value);
+JSON_VALUE * JsonValueGetParent(JSON_VALUE *value);
+
+/* Same as above, but shorter */
+UINT JsonType(JSON_VALUE *value);
+JSON_OBJECT * JsonObject(JSON_VALUE *value);
+JSON_ARRAY * JsonArray(JSON_VALUE *value);
+char * JsonString(JSON_VALUE *value);
+UINT64 JsonNumber(JSON_VALUE *value);
+int JsonBool(JSON_VALUE *value);
+
+void SystemTimeToJsonStr(char *dst, UINT size, SYSTEMTIME *t);
+void SystemTime64ToJsonStr(char *dst, UINT size, UINT64 t);
+
+JSON_VALUE *StrToJson(char *str);
#endif // STR_H
diff --git a/src/Mayaqua/Table.c b/src/Mayaqua/Table.c
index af2a2016..089cba51 100644
--- a/src/Mayaqua/Table.c
+++ b/src/Mayaqua/Table.c
@@ -936,6 +936,8 @@ TABLE *ParseTableLine(char *line, char *prefix, UINT prefix_size, LIST *replace_
UniReplaceStrEx(tmp, tmp_size, tmp, (wchar_t *)r->name, r->unistr, false);
}
+ Free(unistr);
+
unistr = CopyUniStr(tmp);
Free(tmp);
diff --git a/src/Mayaqua/Unix.c b/src/Mayaqua/Unix.c
index 54677753..3a5ddf28 100644
--- a/src/Mayaqua/Unix.c
+++ b/src/Mayaqua/Unix.c
@@ -2508,6 +2508,23 @@ UINT UnixGetUID()
return (UINT)getuid();
}
+void UnixPrintVpnServerUrlInfo()
+{
+ IP ip;
+
+ TryGetCurrentAcceptingIPv4Address(&ip);
+
+ Print("\nLet's get started by accessing to the following URL from your PC:\n\n"
+ "https://%r:%u/\n"
+ " or\n"
+ "https://%r/\n\n"
+ "Note: IP address may vary. Specify your server's IP address.\n"
+ "A TLS certificate warning will appear because the server uses self signed certificate by default. That is natural. Continue with ignoring the TLS warning."
+ "\n\n",
+ &ip, GC_DEFAULT_PORT, &ip
+ );
+}
+
// Start the service
void UnixStartService(char *name)
{
@@ -2547,6 +2564,12 @@ void UnixStartService(char *name)
UniPrint(_UU("UNIX_SVC_NONROOT"));
}
+ if (StrCmpi(name, "vpnserver") == 0 || StrCmpi(name, "vpnbridge") == 0)
+ {
+ // Print the IP address information
+ UnixPrintVpnServerUrlInfo();
+ }
+
FreeSingleInstance(inst);
// Create a child process
diff --git a/src/Mayaqua/Unix.h b/src/Mayaqua/Unix.h
index 68077739..b8f2351f 100644
--- a/src/Mayaqua/Unix.h
+++ b/src/Mayaqua/Unix.h
@@ -250,6 +250,7 @@ void UnixDeleteCtlFile();
void UnixStopThread(THREAD *t, void *param);
UINT UnixGetUID();
void UnixIgnoreSignalForThread(int sig);
+void UnixPrintVpnServerUrlInfo();
bool UnixIsInVmMain();
bool UnixIsInVm();