[PATCH 1/2] crypto: allow NULL 'ad' to aes_siv_decrypt
by James Prestwood
---
src/crypto.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/crypto.c b/src/crypto.c
index 475ebf36..cd50d6f7 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -389,7 +389,9 @@ bool aes_siv_decrypt(const void *key, size_t key_len, const void *in,
if (in_len < 16)
return false;
- memcpy(iov, ad, sizeof(struct iovec) * num_ad);
+ if (ad)
+ memcpy(iov, ad, sizeof(struct iovec) * num_ad);
+
iov[num_ad].iov_base = (void *)out;
iov[num_ad].iov_len = in_len - 16;
num_ad++;
--
2.31.1
5 months, 1 week
[PATCH v3 01/11] dpp-util: add dpp_point_to_asn1
by James Prestwood
Converts an l_ecc_point to the DPP ASN.1 structure.
---
src/dpp-util.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/dpp-util.h | 2 ++
2 files changed, 91 insertions(+)
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 0b355311..e5f76bfd 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -361,3 +361,92 @@ bool dpp_derive_ke(const uint8_t *i_nonce, const uint8_t *r_nonce,
/* ke = HKDF-Expand(bk, "DPP Key", length) */
return hkdf_expand(sha, bk, key_len, "DPP Key", ke, key_len);
}
+
+#define ASN1_ID(class, pc, tag) (((class) << 6) | ((pc) << 5) | (tag))
+
+#define ASN1_ID_SEQUENCE ASN1_ID(0, 1, 0x10)
+#define ASN1_ID_BIT_STRING ASN1_ID(0, 0, 0x03)
+#define ASN1_ID_OID ASN1_ID(0, 0, 0x06)
+
+static uint8_t ec_oid[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 };
+static uint8_t ec_p256_oid[] = { 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x03, 0x01, 0x07 };
+static uint8_t ec_p384_oid[] = { 0x2B, 0x81, 0x04, 0x00, 0x22 };
+static uint8_t ec_p512_oid[] = { 0x2B, 0x81, 0x04, 0x00, 0x23 };
+
+uint8_t *dpp_point_to_asn1(const struct l_ecc_point *p, size_t *len_out)
+{
+ uint8_t *asn1;
+ uint8_t *ptr;
+ uint8_t *type_oid;
+ size_t type_oid_len;
+ const struct l_ecc_curve *curve = l_ecc_point_get_curve(p);
+ ssize_t key_size = l_ecc_curve_get_scalar_bytes(curve);
+ uint64_t x[L_ECC_MAX_DIGITS];
+ ssize_t ret;
+ size_t len;
+
+ switch (key_size) {
+ case 32:
+ type_oid = ec_p256_oid;
+ type_oid_len = sizeof(ec_p256_oid);
+ break;
+ case 48:
+ type_oid = ec_p384_oid;
+ type_oid_len = sizeof(ec_p384_oid);
+ break;
+ case 64:
+ type_oid = ec_p512_oid;
+ type_oid_len = sizeof(ec_p512_oid);
+ break;
+ default:
+ return NULL;
+ }
+
+ ret = l_ecc_point_get_x(p, x, sizeof(x));
+ if (ret < 0 || ret != key_size)
+ return NULL;
+
+ len = 2 + sizeof(ec_oid) + 2 + type_oid_len + 2 + key_size + 4;
+
+ if (L_WARN_ON(len > 128))
+ return NULL;
+
+ asn1 = l_malloc(len + 2);
+ ptr = asn1;
+
+ *ptr++ = ASN1_ID_SEQUENCE;
+ /* Length of both OIDs and key, plus tag/len bytes */
+ *ptr++ = len;
+
+ *ptr++ = ASN1_ID_SEQUENCE;
+
+ len = sizeof(ec_oid) + type_oid_len + 4;
+
+ if (L_WARN_ON(len > 128))
+ return NULL;
+
+ *ptr++ = len;
+
+ *ptr++ = ASN1_ID_OID;
+ *ptr++ = sizeof(ec_oid);
+ memcpy(ptr, ec_oid, sizeof(ec_oid));
+ ptr += sizeof(ec_oid);
+
+ *ptr++ = ASN1_ID_OID;
+ *ptr++ = type_oid_len;
+ memcpy(ptr, type_oid, type_oid_len);
+ ptr += type_oid_len;
+
+ *ptr++ = ASN1_ID_BIT_STRING;
+ *ptr++ = key_size + 2;
+ *ptr++ = 0x00;
+ *ptr++ = 0x03;
+ memcpy(ptr, x, key_size);
+ ptr += key_size;
+
+ if (len_out)
+ *len_out = ptr - asn1;
+
+ return asn1;
+}
diff --git a/src/dpp-util.h b/src/dpp-util.h
index 6d0dcccb..0c22e9b7 100644
--- a/src/dpp-util.h
+++ b/src/dpp-util.h
@@ -97,3 +97,5 @@ struct l_ecc_scalar *dpp_derive_k2(const struct l_ecc_point *i_proto_public,
bool dpp_derive_ke(const uint8_t *i_nonce, const uint8_t *r_nonce,
struct l_ecc_scalar *m, struct l_ecc_scalar *n,
void *ke);
+
+uint8_t *dpp_point_to_asn1(const struct l_ecc_point *p, size_t *len_out);
--
2.31.1
5 months, 1 week
[PATCH 1/2] crypto: allow NULL 'ad' to aes_siv_decrypt
by James Prestwood
---
src/crypto.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/crypto.c b/src/crypto.c
index 7d5e42fe..475ebf36 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -330,7 +330,9 @@ bool aes_siv_encrypt(const void *key, size_t key_len, const void *in,
struct iovec iov[num_ad + 1];
uint8_t v[16];
- memcpy(iov, ad, sizeof(struct iovec) * num_ad);
+ if (ad)
+ memcpy(iov, ad, sizeof(struct iovec) * num_ad);
+
iov[num_ad].iov_base = (void *)in;
iov[num_ad].iov_len = in_len;
num_ad++;
--
2.31.1
5 months, 1 week
[PATCH v2 01/10] dbus: add DPP interface
by James Prestwood
---
src/dbus.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/dbus.h b/src/dbus.h
index 7703b37f..bbc76608 100644
--- a/src/dbus.h
+++ b/src/dbus.h
@@ -43,6 +43,7 @@
#define IWD_STATION_DIAGNOSTIC_INTERFACE "net.connman.iwd.StationDiagnostic"
#define IWD_AP_DIAGNOSTIC_INTERFACE "net.connman.iwd.AccessPointDiagnostic"
#define IWD_STATION_DEBUG_INTERFACE "net.connman.iwd.StationDebug"
+#define IWD_DPP_INTERFACE "net.connman.iwd.DeviceProvisioning"
#define IWD_BASE_PATH "/net/connman/iwd"
#define IWD_AGENT_MANAGER_PATH IWD_BASE_PATH
--
2.31.1
5 months, 1 week
iwd configure location and files
by Jupiter
Hi,
I was running connman with wpa_supplicant on an embedded IoT device
with 2 wireless network interfaces, a home WiFi network and a 4G LTE
network interface managed by ofono, it was working well. Given the
connman is moving to using iwd, I am now building iwd 1.19 on Yocto
Honister, while the iwd was running well, it could not connect to the
home WiFi network.
# systemctl status iwd -l
* iwd.service - Wireless service
Loaded: loaded
(8;;file://solar/lib/systemd/system/iwd.service/lib/systemd/system/iwd.service8;;;
enabled; vendor preset: enabled)
Active: active (running) since Wed 2021-08-04 15:11:22 UTC; 1min 34s ago
Main PID: 203 (iwd)
CGroup: /system.slice/iwd.service
`-203 /usr/libexec/iwd
Aug 04 15:11:23 solar iwd[203]: 48.0 Mbps
Aug 04 15:11:23 solar iwd[203]: 54.0 Mbps
Aug 04 15:11:23 solar iwd[203]: HT Capabilities:
Aug 04 15:11:23 solar iwd[203]: HT20
Aug 04 15:11:23 solar iwd[203]: Short GI for 20Mhz
Aug 04 15:11:23 solar iwd[203]: HT RX MCS indexes:
Aug 04 15:11:23 solar iwd[203]: 0-7
Aug 04 15:11:23 solar iwd[203]: 32
Aug 04 15:11:23 solar iwd[203]: Ciphers: CCMP TKIP BIP
Aug 04 15:11:23 solar iwd[203]: Supported iftypes: station ap
p2p-client p2p-go
I could see the WiFi network, but could not scan, nor could connect to
the network:
[iwd]# station mlan0 scan
[iwd]# station wlan0 get-networks
Available networks
--------------------------------------------------------------------------------
Network name Security Signal
--------------------------------------------------------------------------------
JupiterIoT psk ****
# station wlan0 connect JupiterIoT
Type the network passphrase for JupiterIoT psk.
Passphrase: superpassphrase
Operation aborted
# iwctl station wlan0 show
Station: wlan0
--------------------------------------------------------------------------------
Settable Property Value
--------------------------------------------------------------------------------
Scanning no
State disconnected
I believe it is not configured properly. According to the the document
https://iwd.wiki.kernel.org/networkconfigurationsettings, the network
configure files are stored in /var/lib/iwd by default, but I can only
see an empty folder /var/lib/iwd/hotspot, there are several files in
/lib/systed/network:
# ls /lib/systemd/network
80-container-host0.network 80-wifi-ap.network.example
80-container-ve.network 80-wifi-station.network
80-container-vz.network 80-wifi-station.network.example
80-iwd.link 80-wired.network
80-vm-vt.network 99-default.link
80-wifi-adhoc.network
Did you mean I need to copy network files from /lib/systemd/network to
/var/lib/iwd? For example, to run iwd for a home WiFi network, should
I copy the 80-wifi-station.network to /var/lib/iwd?
Thank you and appreciate your kind advice.
Kind regards,
- jh
5 months, 1 week
[PATCH 1/9] dbus: add DPP interface
by James Prestwood
---
src/dbus.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/dbus.h b/src/dbus.h
index 7703b37f..bbc76608 100644
--- a/src/dbus.h
+++ b/src/dbus.h
@@ -43,6 +43,7 @@
#define IWD_STATION_DIAGNOSTIC_INTERFACE "net.connman.iwd.StationDiagnostic"
#define IWD_AP_DIAGNOSTIC_INTERFACE "net.connman.iwd.AccessPointDiagnostic"
#define IWD_STATION_DEBUG_INTERFACE "net.connman.iwd.StationDebug"
+#define IWD_DPP_INTERFACE "net.connman.iwd.DeviceProvisioning"
#define IWD_BASE_PATH "/net/connman/iwd"
#define IWD_AGENT_MANAGER_PATH IWD_BASE_PATH
--
2.31.1
5 months, 1 week
[PATCH] doc: Extend the IP configuration agent API
by Andrew Zaborowski
Basically convert the flat dictionary parameter passed to the agent with
configuration bits such as local address and gateway address, into an
array of such dictionaries. While it's possible to set up multiple
subnets on one IPv4 network this is mostly meant for IPv6 where a set of
RFCs and recommendations document how multiple Router Advertisements can
be received on a network and, if some of them don't have the "managed"
but set, we end up with multiple addresses assigned through SLAAC. On
our own request we may also be assigned multiple addresses within each
subnet through DHCP.
There are no plans to support such setups at this time but being a
public API it needs to be future proof.
---
doc/agent-api.txt | 38 ++++++++++++++++++++++++--------------
1 file changed, 24 insertions(+), 14 deletions(-)
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
index 250628eb..f348a37d 100644
--- a/doc/agent-api.txt
+++ b/doc/agent-api.txt
@@ -145,32 +145,41 @@ Methods void Release() [noreply]
agent, because when this method gets called it has
already been unregistered.
- void ConfigureIPv4(object device, string interface, dict config)
+ void ConfigureIPv4(object device, string interface,
+ array(dict) configs)
This method gets called during a connection setup
when a new set of IPv4 configuration values is to
be written to a network interface. The connection
is aborted if the method call returns an error or
- times out. The 'config' dictionary (a{sv}) maps
- string keys to values of types defined per key.
+ times out. The 'configs' array contains a set of
+ IP configurations, one for each subnet that is
+ present on the network. Each subnet configuration
+ dictionary (a{sv}) maps string keys to values of
+ types defined per key.
The following key/value pairs are used, but more
- may be added in future versions.
+ may be added in future versions. This dictionary
+ is defined in the same way for IPv4 and IPv6.
string Method - Indicates whether the local address
- was set statically (value "static") or obtained
- automatically such as through DHCP (value "auto").
- Even when the address was obtained from the remote
- end some configuration bits, such as DNS addresses,
- may have been overridden locally.
-
- string Address - Local IP address string.
+ on this subnet was set statically (value "static") or
+ obtained automatically such as through DHCP (value
+ "auto", inclused SLAAC for IPv6). Even when the
+ address was obtained from the remote end some
+ configuration bits, such as DNS addresses, may have
+ been overridden locally.
byte PrefixLength - Holds the prefix-length of the
local subnet. For IPv4 this maps to the netmask.
- string Gateway [optional] - Local subnet's gateway
- address if one exists.
+ array(string, uint16) Addresses - Local IP address
+ strings and their integer relative preference values.
+ Addresses with a lower value in the second element
+ are preferred.
+
+ string Gateway [optional] - The subnet's gateway or
+ router address if one exists.
array(string) DomainNameServers [optional] - Holds
the list of DNS addresses configured if any exist.
@@ -180,7 +189,8 @@ Methods void Release() [noreply]
Possible Errors: net.connman.iwd.Agent.Error.Canceled
- void ConfigureIPv6(object device, string interface, dict config)
+ void ConfigureIPv6(object device, string interface,
+ array(dict) configs)
Same as ConfigureIPv4 above but for IPv6.
--
2.32.0
5 months, 1 week
[PATCH] unit: add larger object test
by James Prestwood
This tests the previous JSON bug fix removing % sizeof(jsmntok_t)
Objects with more than 10 (sizeof(jsmntok_t)) would not parse
correctly.
---
unit/test-json.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/unit/test-json.c b/unit/test-json.c
index 4c43a8cd..854b92dd 100644
--- a/unit/test-json.c
+++ b/unit/test-json.c
@@ -219,6 +219,35 @@ static void test_json_out_of_order(const void *data)
json_contents_free(c);
}
+/*
+ * Tests that the token bounds checking works.
+ */
+static void test_json_larger_object(const void *data)
+{
+ char json[] = "{\"test1\":\"tester1\","
+ "\"test2\":\"tester2\","
+ "\"test3\":\"tester3\","
+ "\"test4\":\"tester4\","
+ "\"test5\":\"tester5\","
+ "\"test6\":\"tester6\","
+ "\"test7\":\"tester7\","
+ "\"test8\":\"tester8\","
+ "\"test9\":\"tester9\","
+ "\"test10\":\"tester10\","
+ "\"test11\":\"tester11\","
+ "\"test12\":\"tester12\","
+ "\"test13\":\"tester13\"}";
+
+ struct json_iter iter;
+ struct json_contents *c = json_contents_new(json, strlen(json));
+
+ json_iter_init(&iter, c);
+ assert(json_iter_parse(&iter,
+ JSON_MANDATORY("test13", JSON_STRING, NULL),
+ JSON_UNDEFINED));
+ json_contents_free(c);
+}
+
int main(int argc, char *argv[])
{
l_test_init(&argc, &argv);
@@ -229,6 +258,7 @@ int main(int argc, char *argv[])
l_test_add("json unsupported types", test_json_unsupported_types, NULL);
l_test_add("json empty objects", test_json_empty_objects, NULL);
l_test_add("json parse out of order", test_json_out_of_order, NULL);
+ l_test_add("json larger object", test_json_larger_object, NULL);
return l_test_run();
}
--
2.31.1
5 months, 2 weeks
[PATCH] json: fix pointer arithmetic error
by James Prestwood
Subtracting the pointers is sufficient for counting the tokens,
they do not need to be modulus the size of jsmntok_t
---
src/json.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/json.c b/src/json.c
index 20adcec7..39570e7e 100644
--- a/src/json.c
+++ b/src/json.c
@@ -82,7 +82,7 @@ static int find_object_tokens(struct json_iter *iter, jsmntok_t *object)
if (!next)
next = ITER_END(iter);
- return ((next - object) % sizeof(jsmntok_t)) - 1;
+ return next - object - 1;
}
static void iter_recurse(struct json_iter *iter, jsmntok_t *object,
@@ -91,7 +91,7 @@ static void iter_recurse(struct json_iter *iter, jsmntok_t *object,
struct json_contents *c = iter->contents;
child->contents = c;
- child->start = (object - c->tokens) % sizeof(jsmntok_t);
+ child->start = object - c->tokens;
child->count = find_object_tokens(iter, object);
}
--
2.31.1
5 months, 2 weeks
[PATCH v4 1/3] shared: add JSMN v1.1.0 parser header
by James Prestwood
---
shared/jsmn.h | 468 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 468 insertions(+)
create mode 100644 shared/jsmn.h
diff --git a/shared/jsmn.h b/shared/jsmn.h
new file mode 100644
index 00000000..b95368a2
--- /dev/null
+++ b/shared/jsmn.h
@@ -0,0 +1,468 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2010 Serge Zaitsev
+ *
+ * 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.
+ */
+#ifndef JSMN_H
+#define JSMN_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef JSMN_STATIC
+#define JSMN_API static
+#else
+#define JSMN_API extern
+#endif
+
+/**
+ * JSON type identifier. Basic types are:
+ * o Object
+ * o Array
+ * o String
+ * o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+ JSMN_UNDEFINED = 0,
+ JSMN_OBJECT = 1,
+ JSMN_ARRAY = 2,
+ JSMN_STRING = 3,
+ JSMN_PRIMITIVE = 4
+} jsmntype_t;
+
+enum jsmnerr {
+ /* Not enough tokens were provided */
+ JSMN_ERROR_NOMEM = -1,
+ /* Invalid character inside JSON string */
+ JSMN_ERROR_INVAL = -2,
+ /* The string is not a full JSON packet, more bytes expected */
+ JSMN_ERROR_PART = -3
+};
+
+/**
+ * JSON token description.
+ * type type (object, array, string etc.)
+ * start start position in JSON data string
+ * end end position in JSON data string
+ */
+typedef struct {
+ jsmntype_t type;
+ int start;
+ int end;
+ int size;
+#ifdef JSMN_PARENT_LINKS
+ int parent;
+#endif
+} jsmntok_t;
+
+/**
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string.
+ */
+typedef struct {
+ unsigned int pos; /* offset in the JSON string */
+ unsigned int toknext; /* next token to allocate */
+ int toksuper; /* superior token node, e.g. parent object or array */
+} jsmn_parser;
+
+/**
+ * Create JSON parser over an array of tokens
+ */
+JSMN_API void jsmn_init(jsmn_parser *parser);
+
+/**
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each
+ * describing
+ * a single JSON object.
+ */
+JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
+ jsmntok_t *tokens, const unsigned int num_tokens);
+
+#ifndef JSMN_HEADER
+/**
+ * Allocates a fresh unused token from the token pool.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
+ const size_t num_tokens) {
+ jsmntok_t *tok;
+ if (parser->toknext >= num_tokens) {
+ return NULL;
+ }
+ tok = &tokens[parser->toknext++];
+ tok->start = tok->end = -1;
+ tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+ tok->parent = -1;
+#endif
+ return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
+ const int start, const int end) {
+ token->type = type;
+ token->start = start;
+ token->end = end;
+ token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+ const size_t len, jsmntok_t *tokens,
+ const size_t num_tokens) {
+ jsmntok_t *token;
+ int start;
+
+ start = parser->pos;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+ /* In strict mode primitive must be followed by "," or "}" or "]" */
+ case ':':
+#endif
+ case '\t':
+ case '\r':
+ case '\n':
+ case ' ':
+ case ',':
+ case ']':
+ case '}':
+ goto found;
+ }
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#ifdef JSMN_STRICT
+ /* In strict mode primitive must be followed by a comma/object/array */
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+#endif
+
+found:
+ if (tokens == NULL) {
+ parser->pos--;
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ parser->pos--;
+ return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+ const size_t len, jsmntok_t *tokens,
+ const size_t num_tokens) {
+ jsmntok_t *token;
+
+ int start = parser->pos;
+
+ parser->pos++;
+
+ /* Skip starting quote */
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c = js[parser->pos];
+
+ /* Quote: end of string */
+ if (c == '\"') {
+ if (tokens == NULL) {
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ return 0;
+ }
+
+ /* Backslash: Quoted symbol expected */
+ if (c == '\\' && parser->pos + 1 < len) {
+ int i;
+ parser->pos++;
+ switch (js[parser->pos]) {
+ /* Allowed escaped symbols */
+ case '\"':
+ case '/':
+ case '\\':
+ case 'b':
+ case 'f':
+ case 'r':
+ case 'n':
+ case 't':
+ break;
+ /* Allows escaped symbol \uXXXX */
+ case 'u':
+ parser->pos++;
+ for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
+ i++) {
+ /* If it isn't a hex character we have an error */
+ if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+ (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ parser->pos++;
+ }
+ parser->pos--;
+ break;
+ /* Unexpected symbol */
+ default:
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+ }
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
+ jsmntok_t *tokens, const unsigned int num_tokens) {
+ int r;
+ int i;
+ jsmntok_t *token;
+ int count = parser->toknext;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c;
+ jsmntype_t type;
+
+ c = js[parser->pos];
+ switch (c) {
+ case '{':
+ case '[':
+ count++;
+ if (tokens == NULL) {
+ break;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ return JSMN_ERROR_NOMEM;
+ }
+ if (parser->toksuper != -1) {
+ jsmntok_t *t = &tokens[parser->toksuper];
+#ifdef JSMN_STRICT
+ /* In strict mode an object or array can't become a key */
+ if (t->type == JSMN_OBJECT) {
+ return JSMN_ERROR_INVAL;
+ }
+#endif
+ t->size++;
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ }
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+ token->start = parser->pos;
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case '}':
+ case ']':
+ if (tokens == NULL) {
+ break;
+ }
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+ if (parser->toknext < 1) {
+ return JSMN_ERROR_INVAL;
+ }
+ token = &tokens[parser->toknext - 1];
+ for (;;) {
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ token->end = parser->pos + 1;
+ parser->toksuper = token->parent;
+ break;
+ }
+ if (token->parent == -1) {
+ if (token->type != type || parser->toksuper == -1) {
+ return JSMN_ERROR_INVAL;
+ }
+ break;
+ }
+ token = &tokens[token->parent];
+ }
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ parser->toksuper = -1;
+ token->end = parser->pos + 1;
+ break;
+ }
+ }
+ /* Error if unmatched closing bracket */
+ if (i == -1) {
+ return JSMN_ERROR_INVAL;
+ }
+ for (; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+#endif
+ break;
+ case '\"':
+ r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+ if (r < 0) {
+ return r;
+ }
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL) {
+ tokens[parser->toksuper].size++;
+ }
+ break;
+ case '\t':
+ case '\r':
+ case '\n':
+ case ' ':
+ break;
+ case ':':
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case ',':
+ if (tokens != NULL && parser->toksuper != -1 &&
+ tokens[parser->toksuper].type != JSMN_ARRAY &&
+ tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+ parser->toksuper = tokens[parser->toksuper].parent;
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+ }
+#endif
+ }
+ break;
+#ifdef JSMN_STRICT
+ /* In strict mode primitives are: numbers and booleans */
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 't':
+ case 'f':
+ case 'n':
+ /* And they must not be keys of the object */
+ if (tokens != NULL && parser->toksuper != -1) {
+ const jsmntok_t *t = &tokens[parser->toksuper];
+ if (t->type == JSMN_OBJECT ||
+ (t->type == JSMN_STRING && t->size != 0)) {
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#else
+ /* In non-strict mode every unquoted value is a primitive */
+ default:
+#endif
+ r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+ if (r < 0) {
+ return r;
+ }
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL) {
+ tokens[parser->toksuper].size++;
+ }
+ break;
+
+#ifdef JSMN_STRICT
+ /* Unexpected char in strict mode */
+ default:
+ return JSMN_ERROR_INVAL;
+#endif
+ }
+ }
+
+ if (tokens != NULL) {
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ /* Unmatched opened object or array */
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ return JSMN_ERROR_PART;
+ }
+ }
+ }
+
+ return count;
+}
+
+/**
+ * Creates a new parser based over a given buffer with an array of tokens
+ * available.
+ */
+JSMN_API void jsmn_init(jsmn_parser *parser) {
+ parser->pos = 0;
+ parser->toknext = 0;
+ parser->toksuper = -1;
+}
+
+#endif /* JSMN_HEADER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* JSMN_H */
--
2.31.1
5 months, 2 weeks