Browse Source

json: make json_add_string do partial escapes.

This is useful when we log a JSON-escaped string, so we don't double-escape.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
parent
commit
7ae013202f
  1. 1
      cli/Makefile
  2. 1
      cli/test/Makefile
  3. 13
      common/json.c
  4. 2
      common/json.h
  5. 39
      common/json_escaped.c
  6. 4
      common/json_escaped.h
  7. 44
      common/test/run-json.c

1
cli/Makefile

@ -4,6 +4,7 @@ LIGHTNING_CLI_OBJS := $(LIGHTNING_CLI_SRC:.c=.o)
LIGHTNING_CLI_COMMON_OBJS := \ LIGHTNING_CLI_COMMON_OBJS := \
common/configdir.o \ common/configdir.o \
common/json.o \ common/json.o \
common/json_escaped.o \
common/version.o common/version.o
lightning-cli-all: cli/lightning-cli lightning-cli-all: cli/lightning-cli

1
cli/test/Makefile

@ -12,6 +12,7 @@ CLI_TEST_COMMON_OBJS := \
common/daemon_conn.o \ common/daemon_conn.o \
common/htlc_state.o \ common/htlc_state.o \
common/json.o \ common/json.o \
common/json_escaped.o \
common/pseudorand.o \ common/pseudorand.o \
common/memleak.o \ common/memleak.o \
common/msg_queue.o \ common/msg_queue.o \

13
common/json.c

@ -409,18 +409,11 @@ void json_add_literal(struct json_result *result, const char *fieldname,
void json_add_string(struct json_result *result, const char *fieldname, const char *value) void json_add_string(struct json_result *result, const char *fieldname, const char *value)
{ {
char *escaped = tal_strdup(result, value); struct json_escaped *esc = json_partial_escape(NULL, value);
size_t i;
json_start_member(result, fieldname); json_start_member(result, fieldname);
for (i = 0; escaped[i]; i++) { result_append_fmt(result, "\"%s\"", esc->s);
/* Replace any funny business. Better safe than accurate! */ tal_free(esc);
if (escaped[i] == '\\'
|| escaped[i] == '"'
|| !cisprint(escaped[i]))
escaped[i] = '?';
}
result_append_fmt(result, "\"%s\"", escaped);
} }
void json_add_bool(struct json_result *result, const char *fieldname, bool value) void json_add_bool(struct json_result *result, const char *fieldname, bool value)

2
common/json.h

@ -76,7 +76,7 @@ void json_object_end(struct json_result *ptr);
struct json_result *new_json_result(const tal_t *ctx); struct json_result *new_json_result(const tal_t *ctx);
/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns /* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns
* any unusual chars into ?. * any non-printable chars into JSON escapes, but leaves existing escapes alone.
*/ */
void json_add_string(struct json_result *result, const char *fieldname, const char *value); void json_add_string(struct json_result *result, const char *fieldname, const char *value);

39
common/json_escaped.c

@ -35,7 +35,9 @@ bool json_escaped_eq(const struct json_escaped *a,
return streq(a->s, b->s); return streq(a->s, b->s);
} }
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES) static struct json_escaped *escape(const tal_t *ctx,
const char *str TAKES,
bool partial)
{ {
struct json_escaped *esc; struct json_escaped *esc;
size_t i, n; size_t i, n;
@ -62,6 +64,31 @@ struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
escape = 'r'; escape = 'r';
break; break;
case '\\': case '\\':
if (partial) {
/* Don't double-escape standard escapes. */
if (str[i+1] == 'n'
|| str[i+1] == 'b'
|| str[i+1] == 'f'
|| str[i+1] == 't'
|| str[i+1] == 'r'
|| str[i+1] == '/'
|| str[i+1] == '\\'
|| str[i+1] == '"') {
escape = str[i+1];
i++;
break;
}
if (str[i+1] == 'u'
&& cisxdigit(str[i+2])
&& cisxdigit(str[i+3])
&& cisxdigit(str[i+4])
&& cisxdigit(str[i+5])) {
memcpy(esc->s + n, str + i, 6);
n += 5;
i += 5;
continue;
}
} /* fall thru */
case '"': case '"':
escape = str[i]; escape = str[i];
break; break;
@ -85,6 +112,16 @@ struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
return esc; return esc;
} }
struct json_escaped *json_partial_escape(const tal_t *ctx, const char *str TAKES)
{
return escape(ctx, str, true);
}
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES)
{
return escape(ctx, str, false);
}
/* By policy, we don't handle \u. Use UTF-8. */ /* By policy, we don't handle \u. Use UTF-8. */
const char *json_escaped_unescape(const tal_t *ctx, const char *json_escaped_unescape(const tal_t *ctx,
const struct json_escaped *esc) const struct json_escaped *esc)

4
common/json_escaped.h

@ -12,6 +12,10 @@ struct json_escaped {
/* @str be a valid UTF-8 string */ /* @str be a valid UTF-8 string */
struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES); struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES);
/* @str is a valid UTF-8 string which may already contain escapes. */
struct json_escaped *json_partial_escape(const tal_t *ctx,
const char *str TAKES);
/* Extract a JSON-escaped string. */ /* Extract a JSON-escaped string. */
struct json_escaped *json_tok_escaped_string(const tal_t *ctx, struct json_escaped *json_tok_escaped_string(const tal_t *ctx,
const char *buffer, const char *buffer,

44
common/test/run-json.c

@ -67,12 +67,13 @@ static int test_json_filter(void)
x = json_get_member(str, toks, "x"); x = json_get_member(str, toks, "x");
assert(x); assert(x);
assert(x->type == JSMN_STRING); assert(x->type == JSMN_STRING);
assert((x->end - x->start) == 255);
/* There are 7 one-letter escapes, and (32-5) \uXXXX escapes. */
assert((x->end - x->start) == 255 + 7*1 + (32-5)*5);
/* No control characters. */
for (i = x->start; i < x->end; i++) { for (i = x->start; i < x->end; i++) {
assert(cisprint(str[i])); assert((unsigned)str[i] >= ' ');
assert(str[i] != '\\'); assert((unsigned)str[i] != 127);
assert(str[i] != '"');
assert(str[i] == '?' || str[i] == badstr[i - x->start]);
} }
tal_free(result); tal_free(result);
return 0; return 0;
@ -112,11 +113,44 @@ static void test_json_escape(void)
} }
} }
static void test_json_partial(void)
{
const tal_t *ctx = tal(NULL, char);
assert(streq(json_partial_escape(ctx, "\\")->s, "\\\\"));
assert(streq(json_partial_escape(ctx, "\\\\")->s, "\\\\"));
assert(streq(json_partial_escape(ctx, "\\\\\\")->s, "\\\\\\\\"));
assert(streq(json_partial_escape(ctx, "\\\\\\\\")->s, "\\\\\\\\"));
assert(streq(json_partial_escape(ctx, "\\n")->s, "\\n"));
assert(streq(json_partial_escape(ctx, "\n")->s, "\\n"));
assert(streq(json_partial_escape(ctx, "\\\"")->s, "\\\""));
assert(streq(json_partial_escape(ctx, "\"")->s, "\\\""));
assert(streq(json_partial_escape(ctx, "\\t")->s, "\\t"));
assert(streq(json_partial_escape(ctx, "\t")->s, "\\t"));
assert(streq(json_partial_escape(ctx, "\\b")->s, "\\b"));
assert(streq(json_partial_escape(ctx, "\b")->s, "\\b"));
assert(streq(json_partial_escape(ctx, "\\r")->s, "\\r"));
assert(streq(json_partial_escape(ctx, "\r")->s, "\\r"));
assert(streq(json_partial_escape(ctx, "\\f")->s, "\\f"));
assert(streq(json_partial_escape(ctx, "\f")->s, "\\f"));
/* You're allowed to escape / according to json.org. */
assert(streq(json_partial_escape(ctx, "\\/")->s, "\\/"));
assert(streq(json_partial_escape(ctx, "/")->s, "/"));
assert(streq(json_partial_escape(ctx, "\\u0FFF")->s, "\\u0FFF"));
assert(streq(json_partial_escape(ctx, "\\u0FFFx")->s, "\\u0FFFx"));
/* Unknown escapes should be escaped. */
assert(streq(json_partial_escape(ctx, "\\x")->s, "\\\\x"));
tal_free(ctx);
}
int main(void) int main(void)
{ {
test_json_tok_bitcoin_amount(); test_json_tok_bitcoin_amount();
test_json_filter(); test_json_filter();
test_json_escape(); test_json_escape();
test_json_partial();
assert(!taken_any()); assert(!taken_any());
take_cleanup(); take_cleanup();
} }

Loading…
Cancel
Save